mirror of
https://github.com/boostorg/build.git
synced 2026-03-05 02:42:32 +00:00
904 lines
34 KiB
Plaintext
904 lines
34 KiB
Plaintext
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
|
|
# distribute this software is granted provided this copyright notice appears in
|
|
# all copies. This software is provided "as is" without express or implied
|
|
# warranty, and with no claim as to its suitability for any purpose.
|
|
|
|
|
|
# Supports 'abstract' targets, which are targets explicitly defined in Jamfile.
|
|
#
|
|
# Abstract targets are represented by classes derived from 'abstract-target' class.
|
|
# The first abstract target is 'project-target', which is created for each
|
|
# Jamfile, and can be obtained by the 'target' rule in the Jamfile's module.
|
|
# (see project.jam).
|
|
#
|
|
# Project targets keep a list of 'main-target' instances.
|
|
# A main target is what the user explicitly defines in a Jamfile. It is
|
|
# possible to have several definitions for a main target, for example to have
|
|
# different lists of sources for different platforms. So, main targets
|
|
# keep a list of alternatives.
|
|
#
|
|
# Each alternative is an instance of 'abstract-target'. When a main target
|
|
# subvariant is defined by some rule, that rule will decide what class to
|
|
# use, create an instance of that class and add it to the list of alternatives
|
|
# for the main target.
|
|
#
|
|
# Rules supplied by the build system will use only targets derived
|
|
# from 'basic-target' class, which will provide some default behaviour.
|
|
# There will be two classes derived from it, 'make-target', created by the
|
|
# 'make' rule, and 'typed-target', created by rules such as 'exe' and 'dll'.
|
|
|
|
#
|
|
# +------------------------+
|
|
# |abstract-target |
|
|
# +========================+
|
|
# |name |
|
|
# |project |
|
|
# | |
|
|
# |generate(properties) = 0|
|
|
# +-----------+------------+
|
|
# |
|
|
# ^
|
|
# / \
|
|
# +-+-+
|
|
# |
|
|
# |
|
|
# +------------------------+------+------------------------------+
|
|
# | | |
|
|
# | | |
|
|
# +----------+-----------+ +------+------+ +------+-------+
|
|
# | project-target | | main-target | | basic-target |
|
|
# +======================+ 1 * +=============+ alternatives +==============+
|
|
# | generate(properties) |o-----------+ generate |<>------------->| generate |
|
|
# | main-target | +-------------+ | construct = 0|
|
|
# | reference-properties | +--------------+
|
|
# +----------------------+ |
|
|
# ^
|
|
# / \
|
|
# +-+-+
|
|
# |
|
|
# |
|
|
# ...--+----------------+------------------+----------------+---+
|
|
# | | | |
|
|
# | | | |
|
|
# ... ---+-----+ +------+-------+ +------+------+ +--------+-----+
|
|
# | | typed-target | | make-target | | stage-target |
|
|
# . +==============+ +=============+ +==============+
|
|
# . | construct | | construct | | construct |
|
|
# +--------------+ +-------------+ +--------------+
|
|
|
|
import sequence ;
|
|
import class : class new ;
|
|
import regex ;
|
|
import property ;
|
|
import errors ;
|
|
import common ;
|
|
import property-set ;
|
|
import project ;
|
|
import feature ;
|
|
import virtual-target ;
|
|
|
|
# Base class for all abstract targets.
|
|
rule abstract-target ( name # name of the target in Jamfile
|
|
: project # the project module where the target is declared
|
|
)
|
|
{
|
|
import project ;
|
|
|
|
# Note: it might seem that we don't need either name or project at all.
|
|
# However, there are places where we really need it. One example is error
|
|
# messages which should name problematic targets. Another is setting correct
|
|
# paths for sources and generated files.
|
|
|
|
self.name = $(name) ;
|
|
self.project = $(project) ;
|
|
|
|
# Returns the name of this target.
|
|
rule name ( )
|
|
{
|
|
return $(self.name) ;
|
|
}
|
|
|
|
# Returns the project for this target.
|
|
rule project ( )
|
|
{
|
|
return $(self.project) ;
|
|
}
|
|
|
|
# Returns a user-readable name for this target.
|
|
rule full-name ( )
|
|
{
|
|
local location = [ project.attribute $(self.project) location ] ;
|
|
return $(location)/$(self.name) ;
|
|
}
|
|
|
|
# Takes properties in split form ("<feature1>foo <feature2>bar").
|
|
# Generates virtual targets for this abstract target, using the specified properties,
|
|
# unless a different value of some feature is required by the target. The properties
|
|
# on returned virtual targets should be link-compatible with the requested ones.
|
|
#
|
|
# On success, returns:
|
|
# - a property-set with the usage requirements to be applied to dependents
|
|
# - a list of produced virtual targets, which may be empty.
|
|
#
|
|
# If 'properties' are empty, performs default build of this target, in a way specific
|
|
# to derived class.
|
|
rule generate ( property-set )
|
|
{
|
|
errors.error "method should be defined in derived classes" ;
|
|
}
|
|
|
|
# Set an object which will adjust build properties.
|
|
# It must have 'adjust' method and will be called
|
|
# immediately on enterting the 'generate' rule.
|
|
# Currently used only for implementation of direct
|
|
# build request
|
|
rule set-property-adjuster ( property-adjuster )
|
|
{
|
|
errors.error "method should be defined in derived classes" ;
|
|
}
|
|
|
|
}
|
|
class abstract-target ;
|
|
|
|
# Project target class (derived from 'abstract-target')
|
|
rule project-target ( name : project : requirements * : default-build * )
|
|
{
|
|
import project targets ;
|
|
import path ;
|
|
import print ;
|
|
import property-set ;
|
|
|
|
abstract-target.__init__ $(name) : $(project) ;
|
|
|
|
self.requirements = $(requirements) ;
|
|
self.default-build = $(default-build) ;
|
|
|
|
rule set-property-adjuster ( property-adjuster )
|
|
{
|
|
for local t in [ targets-to-build ]
|
|
{
|
|
$(t).set-property-adjuster $(property-adjuster) ;
|
|
}
|
|
}
|
|
|
|
# Generates all possible targets contained in this project.
|
|
rule generate ( property-set * )
|
|
{
|
|
# Project properties are directly imposed on all main targets.
|
|
# However, we'd need to check if this project can be build at
|
|
# all.
|
|
|
|
local xproperties = [ $(property-set).refine $(self.requirements) ] ;
|
|
|
|
local usage-requirements = [ property-set.empty ] ;
|
|
local targets ;
|
|
|
|
if $(xproperties[1]) = "@error"
|
|
{
|
|
local id = [ project.attribute $(self.project) id ] ;
|
|
print.wrapped-text "warning: skipping build of project $(id:E=) at"
|
|
[ project.attribute $(self.project) location ]
|
|
"due to unsatisfied requirements." ;
|
|
print.wrapped-text "warning: explanation: " $(xproperties[2-]) ;
|
|
print.wrapped-text "warning: build-request: " $(properties) ;
|
|
print.wrapped-text "warning: requirements: " [ $(self.requirements).raw ] ;
|
|
}
|
|
else
|
|
{
|
|
for local t in [ targets-to-build ]
|
|
{
|
|
local g = [ $(t).generate $(property-set) ] ;
|
|
usage-requirements = [ $(usage-requirements).add $(g[1]) ] ;
|
|
targets += $(g[2-]) ;
|
|
}
|
|
}
|
|
return $(usage-requirements) $(targets) ;
|
|
}
|
|
|
|
# Computes and returns a list of abstract-target instances which
|
|
# must be built when this project is built.
|
|
rule targets-to-build ( )
|
|
{
|
|
local result ;
|
|
|
|
# Collect all main targets here, except for "explicit" ones.
|
|
local explicit-targets = [ project.attribute $(self.project) explicit-targets ] ;
|
|
for local name in $(self.main-targets)
|
|
{
|
|
if ! $(name) in $(explicit-targets)
|
|
{
|
|
result += [ main-target $(name) ] ;
|
|
}
|
|
}
|
|
|
|
# Collect all projects referenced via "projects-to-build" attribute.
|
|
local self-location = [ project.attribute $(self.project) location ] ;
|
|
for local pn in [ project.attribute $(self.project) projects-to-build ]
|
|
{
|
|
local p = [ project.module-name [ path.join $(self-location) $(pn) ] ] ;
|
|
result += [ project.target $(p) ] ;
|
|
}
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
|
|
# Returns a 'main-target' class instance corresponding to the 'name'.
|
|
# Creates the instance if needed.
|
|
rule main-target ( name )
|
|
{
|
|
if ! $(self.main-target.$(name))
|
|
{
|
|
self.main-targets += $(name) ;
|
|
self.main-target.$(name) =
|
|
[ new main-target $(name) : $(self.project) ] ;
|
|
}
|
|
return $(self.main-target.$(name)) ;
|
|
}
|
|
|
|
# Tells if a main target with the specified name exists.
|
|
rule has-main-target ( name )
|
|
{
|
|
if $(self.main-target.$(name))
|
|
{
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
# Returns the properties which would be used for a main
|
|
# target in this project, if generation with
|
|
# 'properties' is requested, and that main target
|
|
# does not have any requirements of its own.
|
|
rule reference-properties ( property-set )
|
|
{
|
|
if ! $(self.ref-props.$(property-set))
|
|
{
|
|
local ref = [ project.attribute $(self.project) requirements ] ;
|
|
local ps = [ $(property-set).refine $(ref) ] ;
|
|
ps = [ $(ps).evaluate-conditionals ] ;
|
|
ps = [ $(ps).run-actions ] ;
|
|
|
|
ps = [ targets.generate-dependencies $(ps) : $(self.project) : $(ps) ] ;
|
|
# Exclude the usage-requirements propagated from dependencies.
|
|
# This used to be that way, and while I refactor, I'll keep the
|
|
# old semantic --- which probably is bogus.
|
|
self.ref-props.$(property-set) = $(ps[2-]) ;
|
|
}
|
|
return $(self.ref-props.$(property-set)) ;
|
|
}
|
|
|
|
}
|
|
class project-target : abstract-target ;
|
|
|
|
# A named top-level target in Jamfile
|
|
rule main-target ( name : project )
|
|
{
|
|
abstract-target.__init__ $(name) : $(project) ;
|
|
|
|
import errors : error ;
|
|
import assert ;
|
|
import numbers : range ;
|
|
import sequence ;
|
|
import print ;
|
|
import build-request feature property-set ;
|
|
|
|
# Add a new alternative for this target
|
|
rule add-alternative ( target )
|
|
{
|
|
local d = [ $(target).default-build ] ;
|
|
if $(self.alternatives) && [ $(d).raw ]
|
|
{
|
|
errors.error "default build can be specified only in first alternative"
|
|
: "main target is " [ full-name ] ;
|
|
}
|
|
else
|
|
{
|
|
self.default-build = $(d) ;
|
|
}
|
|
self.alternatives += $(target) ;
|
|
}
|
|
|
|
rule set-property-adjuster ( property-adjuster )
|
|
{
|
|
self.property-adjuster = $(property-adjuster) ;
|
|
}
|
|
|
|
local rule select-alternatives ( property-set )
|
|
{
|
|
local viable ; # alternatives that may be used
|
|
local ranks ; # ranks for viable alternatives
|
|
for local v in $(self.alternatives)
|
|
{
|
|
# For now, alternative should be derived from 'basic-target'.
|
|
# We'll see if this restriction if reasonable.
|
|
assert.equal [ is-a $(v) : basic-target ] : true ;
|
|
local m = [ $(v).match-rank $(property-set) ] ;
|
|
|
|
if $(m)
|
|
{
|
|
viable += $(v) ;
|
|
ranks += $(m) ;
|
|
}
|
|
}
|
|
|
|
local best = [ sequence.select-highest-ranked $(viable) : $(ranks) ] ;
|
|
return $(best) ;
|
|
}
|
|
|
|
|
|
rule apply-default-build ( property-set )
|
|
{
|
|
# 1. First, see what properties from default-build
|
|
# are already present in property-set.
|
|
|
|
local raw = [ $(property-set).raw ] ;
|
|
local specified-features = $(raw:G) ;
|
|
|
|
local defaults-to-apply ;
|
|
for local d in [ $(self.default-build).raw ]
|
|
{
|
|
if ! $(d:G) in $(specified-features)
|
|
{
|
|
defaults-to-apply += $(d) ;
|
|
}
|
|
}
|
|
|
|
# 2. If there's any defaults to be applied, form the new
|
|
# build request. Pass it throw 'expand-no-defaults', since
|
|
# default-build might contain "release debug", which will
|
|
# result in two property-sets.
|
|
local result ;
|
|
if $(defaults-to-apply)
|
|
{
|
|
properties = [ build-request.expand-no-defaults
|
|
$(raw) $(defaults-to-apply) ] ;
|
|
|
|
if $(properties)
|
|
{
|
|
for local p in $(properties)
|
|
{
|
|
result += [ property-set.create [ feature.split $(p) ] ] ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = [ property-set.empty ] ;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
result = $(property-set) ;
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Select an alternative for this main target, by finding all alternatives
|
|
# which requirements are satisfied by 'properties' and picking the one with
|
|
# longest requirements set.
|
|
# Returns the result of calling 'generate' on that alternative.
|
|
rule generate ( property-set )
|
|
{
|
|
local all-property-sets = [ apply-default-build $(property-set) ] ;
|
|
local usage-requirements = [ property-set.empty ] ;
|
|
local result ;
|
|
for local p in $(all-property-sets)
|
|
{
|
|
local r = [ generate-really [ $(p).add-defaults ] ] ;
|
|
usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
|
|
result += $(r[2-]) ;
|
|
}
|
|
return $(usage-requirements) $(result) ;
|
|
}
|
|
|
|
local rule generate-really ( property-set )
|
|
{
|
|
if $(self.property-adjuster)
|
|
{
|
|
property-set = [ $(self.property-adjuster).adjust
|
|
$(property-set) ] ;
|
|
}
|
|
|
|
local best-alternatives = [ select-alternatives $(property-set) ] ;
|
|
if ! $(best-alternatives)
|
|
{
|
|
# TODO: probably, should explain, for each alternative,
|
|
# why it can't be build.
|
|
print.wrapped-text
|
|
"warning: skipped build of" [ full-name ]
|
|
"with properties" [ $(property-set).raw ] ;
|
|
}
|
|
else if $(best-alternatives[2])
|
|
{
|
|
# TODO: again, a better error message in in order.
|
|
errors.error "Ambiguous alternatives for main target" [ full-name ] ;
|
|
}
|
|
else
|
|
{
|
|
local result = [ $(best-alternatives).generate $(property-set) ] ;
|
|
|
|
# Now return virtual targets for the only alternative
|
|
return $(result) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
class main-target : abstract-target ;
|
|
|
|
# Helper for 'find', below.
|
|
local rule remove-trailing-slash ( string )
|
|
{
|
|
local stripped = [ MATCH (.*)/$ : $(string) ] ;
|
|
stripped ?= $(string) ;
|
|
return $(stripped) ;
|
|
}
|
|
|
|
|
|
# Given an 'id' for a target, return an instance of 'abstract-target' that
|
|
# corresponds to it. If there's no such target, returns empty string.
|
|
# The project referred to by id is loaded if it is not already loaded.
|
|
rule find ( id : current-location )
|
|
{
|
|
local target ;
|
|
# Find if this is project
|
|
local project-module = [ project.find $(id) : $(current-location) ] ;
|
|
if $(project-module)
|
|
{
|
|
target = [ project.target $(project-module) ] ;
|
|
}
|
|
else
|
|
{
|
|
# Split 'id' into project and target.
|
|
|
|
local split = [ MATCH ((@?)(.*/)*)([^/]*) : $(id) ] ;
|
|
local project-name = [ remove-trailing-slash $(split[1]) ] ;
|
|
local has-project-id = $(split[2]) ;
|
|
local target-name = $(split[4]) ;
|
|
|
|
local project-module = [ project.find $(project-name) : $(current-location) ] ;
|
|
|
|
if $(project-module) {
|
|
local project-target = [ project.target $(project-module) ] ;
|
|
if [ $(project-target).has-main-target $(target-name) ]
|
|
{
|
|
target = [ $(project-target).main-target $(target-name) ] ;
|
|
}
|
|
}
|
|
else if $(has-project-id)
|
|
{
|
|
# This error checking should be moved somewhere.
|
|
errors.error
|
|
The target id \"$(id)\", specified by project at \"$(current-location)\"
|
|
is invalid (missing 'use-project'?) ;
|
|
}
|
|
}
|
|
return $(target) ;
|
|
}
|
|
|
|
|
|
# Attempts to generate the target given by target reference, which
|
|
# can refer both to a main target or to a file.
|
|
# Returns a list consisting of
|
|
# - usage requirements
|
|
# - generated virtual targets, if any
|
|
rule generate-from-reference
|
|
( target-reference # Target reference
|
|
: project # Project where the reference is made
|
|
: property-set # Properties of the main target that
|
|
# makes the reference
|
|
)
|
|
{
|
|
# Separate target name from properties override
|
|
local split = [ MATCH "^([^<]*)(/(<.*))?$" : $(target-reference) ] ;
|
|
local id = $(split[1]) ;
|
|
local sproperties = ;
|
|
if $(split[3])
|
|
{
|
|
sproperties = [ property.make [ feature.split $(split[3]) ] ] ;
|
|
sproperties = [ feature.expand-composites $(sproperties) ] ;
|
|
}
|
|
|
|
# Check if such target exists
|
|
local main-target =
|
|
[ find $(id) : [ project.attribute $(project) location ] ] ;
|
|
|
|
if $(main-target) {
|
|
# Take properties which should be propagated and refine them
|
|
# with source-specific requirements.
|
|
local propagated = [ $(property-set).propagated ] ;
|
|
local rproperties = [ $(propagated).refine
|
|
[ property-set.create $(sproperties) ] ] ;
|
|
if $(rproperties[1]) = "@error"
|
|
{
|
|
errors.error
|
|
"When building" [ full-name ] " with properties " $(properties) :
|
|
"Invalid properties specified for " $(source) ":"
|
|
$(rproperties[2-]) ;
|
|
}
|
|
return [ $(main-target).generate $(rproperties) ] ;
|
|
}
|
|
else
|
|
{
|
|
if $(sproperties)
|
|
{
|
|
errors.error
|
|
"error: target reference '$(target-reference)' contains properties," :
|
|
"error: but refers to a file" ;
|
|
}
|
|
return [ property-set.empty ] [ virtual-target.from-file $(id) : $(project) ] ;
|
|
}
|
|
}
|
|
|
|
# Returns two property sets.
|
|
# The second one inclues all properties from
|
|
# 'property-set', except that all dependency properties are
|
|
# generated with 'generation-ps', and the obtained virtual targets
|
|
# are added as the values of original features.
|
|
# For example, <library>a/b might become <library>object(virtual-target)@1
|
|
#
|
|
# The first one include all usage requirements propagated from generated
|
|
# targets
|
|
rule generate-dependencies ( property-set : project : generation-ps )
|
|
{
|
|
local usage-requirements = [ property-set.empty ] ;
|
|
local xproperties ;
|
|
for local p in [ $(property-set).dependency ]
|
|
{
|
|
local g = [ targets.generate-from-reference $(p:TG=) : $(project) : $(generation-ps) ] ;
|
|
usage-requirements = [ $(usage-requirements).add $(g[1]) ] ;
|
|
xproperties += $(p:G)$(g[2-]) ;
|
|
}
|
|
local r = [ property-set.create
|
|
[ $(property-set).base ]
|
|
[ $(property-set).free ]
|
|
$(xproperties)
|
|
[ $(property-set).incidental ] ] ;
|
|
return $(usage-requirements) $(r) ;
|
|
}
|
|
|
|
|
|
|
|
# Implements the most standard way of constructing main target
|
|
# alternative from sources. Allows sources to be either file or
|
|
# other main target and handles generation of those dependency
|
|
# targets.
|
|
rule basic-target ( name : project
|
|
: sources * : requirements * :
|
|
[property-set] default-build * : usage-requirements * )
|
|
{
|
|
abstract-target.__init__ $(name) : $(project) ;
|
|
|
|
import build-request ;
|
|
import virtual-target targets ;
|
|
import property-set ;
|
|
import set sequence errors ;
|
|
|
|
self.sources = $(sources) ;
|
|
if ! $(requirements) {
|
|
requirements = [ property-set.empty ] ;
|
|
}
|
|
self.requirements = $(requirements) ;
|
|
if ! $(default-build)
|
|
{
|
|
default-build = [ property-set.empty ] ;
|
|
}
|
|
self.default-build = $(default-build) ;
|
|
if ! $(usage-requirements)
|
|
{
|
|
usage-requirements = [ property-set.empty ] ;
|
|
}
|
|
self.usage-requirements = $(usage-requirements) ;
|
|
|
|
if $(sources:G)
|
|
{
|
|
errors.error "gristed element in sources for" [ full-name ] ;
|
|
}
|
|
|
|
|
|
|
|
rule default-build ( )
|
|
{
|
|
return $(self.default-build) ;
|
|
}
|
|
|
|
# Returns a number which estimates this targets's suitability for
|
|
# building with the given 'property-set'. Among several alternatives
|
|
# for a main target, the one with greatest match-rank will be used
|
|
# to do actual generation. Return of empty value mean this target
|
|
# can't be built with the given 'property-set'.
|
|
rule match-rank ( property-set )
|
|
{
|
|
# First check if our requirements can be satisfied.
|
|
local rproperties = [ $(property-set).refine $(self.requirements) ] ;
|
|
if $(rproperties[1]) != "@error"
|
|
{
|
|
# Returns the number of properties common to requirements
|
|
# and build request.
|
|
return [ sequence.length [ set.intersection
|
|
[ $(self.requirements).base ] :
|
|
[ $(property-set).raw ] ] ] ;
|
|
}
|
|
}
|
|
|
|
# Determine and return properties which should be used for
|
|
# building when given 'build-request'. This includes refining
|
|
# build request with requirements, evaluating conditionals,
|
|
# generating depenendecies and running actions for features.
|
|
local rule refined-properties ( build-request )
|
|
{
|
|
local rproperties = [ $(build-request).refine $(self.requirements) ] ;
|
|
|
|
if $(rproperties[1]) != "@error"
|
|
{
|
|
# TODO: issue a warning when requirements change properties, but
|
|
# link-compatibility is still not broken.
|
|
|
|
rproperties = [ $(rproperties).evaluate-conditionals ] ;
|
|
rproperties = [ $(rproperties).run-actions ] ;
|
|
}
|
|
return $(rproperties) ;
|
|
}
|
|
|
|
# Generate all sources for this target. For each source, the return value will
|
|
# contain a list of virtual targets generated from the source, followed by
|
|
# <@>source-name element.
|
|
# IOW, virtual targets which come from different sources are separated
|
|
# The first element in return value is always property-set with usage requirements
|
|
# of all generated targets.
|
|
local rule generate-sources ( property-set )
|
|
{
|
|
local usage-requirements = [ property-set.empty ] ;
|
|
local source-targets ;
|
|
for local s in $(self.sources)
|
|
{
|
|
|
|
# Try treating this source as reference to main target
|
|
local more-targets =
|
|
[ targets.generate-from-reference $(s) : $(self.project)
|
|
: $(property-set) ] ;
|
|
|
|
usage-requirements = [ $(usage-requirements).add $(more-targets[1]) ] ;
|
|
source-targets += $(more-targets[2-]) ;
|
|
source-targets += <@>$(s) ;
|
|
}
|
|
return $(usage-requirements) $(source-targets) ;
|
|
}
|
|
|
|
# Determines final build properties, generates sources,
|
|
# and calls 'construct'. This method should not be
|
|
# overridden.
|
|
rule generate ( property-set )
|
|
{
|
|
if ! $(self.generated.$(property-set))
|
|
{
|
|
local rproperties = [ refined-properties $(property-set) ] ;
|
|
if $(rproperties[1]) != "@error"
|
|
{
|
|
local usage-requirements = [ property-set.empty ] ;
|
|
|
|
rproperties =
|
|
[ targets.generate-dependencies $(rproperties) : $(self.project)
|
|
: $(rproperties) ] ;
|
|
usage-requirements = [ $(usage-requirements).add $(rproperties[1]) ] ;
|
|
rproperties = $(rproperties[2-]) ;
|
|
|
|
local source-target-groups = [ generate-sources $(rproperties) ] ;
|
|
|
|
usage-requirements =
|
|
[ $(usage-requirements).add $(source-target-groups[1]) ] ;
|
|
source-target-groups = $(source-target-groups[2-]) ;
|
|
|
|
local source-targets = ;
|
|
for local s in $(source-target-groups)
|
|
{
|
|
if $(s:G) != <@>
|
|
{
|
|
source-targets += $(s) ;
|
|
}
|
|
}
|
|
|
|
rproperties = [ $(rproperties).add $(usage-requirements) ] ;
|
|
|
|
local result =
|
|
[ construct $(source-targets) : $(rproperties) ] ;
|
|
check-for-unused-sources
|
|
$(result) : $(source-target-groups) ;
|
|
|
|
create-subvariant-dg $(result) : $(property-set) : $(rproperties) ;
|
|
|
|
self.generated.$(property-set) =
|
|
[ compute-usage-requirements $(rproperties) ]
|
|
$(result) ;
|
|
}
|
|
else
|
|
{
|
|
self.generated.$(property-set) = $(rproperties) ;
|
|
}
|
|
}
|
|
return $(self.generated.$(property-set)) ;
|
|
}
|
|
|
|
# Given the set of generated targets, and refined build
|
|
# properties, determines and sets appripriate usage requirements
|
|
# on those targets.
|
|
local rule compute-usage-requirements ( rproperties )
|
|
{
|
|
xusage-requirements = [ $(self.usage-requirements).evaluate-conditionals
|
|
$(rproperties) ] ;
|
|
|
|
local xusage-requirements =
|
|
[ targets.generate-dependencies
|
|
$(xusage-requirements)
|
|
: $(self.project) : $(rproperties) ] ;
|
|
local recursive-usage-requirements = $(xusage-requirements[1]) ;
|
|
local result = [ $(recursive-usage-requirements).add $(xusage-requirements[2-]) ] ;
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
# Creates a new subvariant-dg instances for 'targets'
|
|
local rule create-subvariant-dg ( targets * : build-request : rproperties )
|
|
{
|
|
for local e in $(targets)
|
|
{
|
|
$(e).root true ;
|
|
}
|
|
|
|
# Process all vtargets that will be created if this main target
|
|
# is created.
|
|
local all-targets =
|
|
[ sequence.transform virtual-target.traverse : $(targets) ] ;
|
|
local dg = [ new subvariant-dg $(__name__) : $(build-request)
|
|
: $(rproperties) : $(all-targets) ] ;
|
|
for local v in $(all-targets)
|
|
{
|
|
$(v).dg $(dg) ;
|
|
}
|
|
}
|
|
|
|
|
|
# Check that 'result' makes use of all the 'sources'. If not,
|
|
# issues a warning. Note that 'result' *can* be empty. For
|
|
# example, in this use case:
|
|
# alias platform-sources ;
|
|
# alias platform-sources : a.cpp : <os>NT ;
|
|
# result will be empty in the first case.
|
|
local rule check-for-unused-sources ( result * : source-groups * )
|
|
{
|
|
local used-sources ;
|
|
for local r in $(result)
|
|
{
|
|
used-sources += [ virtual-target.traverse $(r) : include-roots : 1 ] ; #: includes-sources ] ;
|
|
}
|
|
|
|
local group ;
|
|
for local s in $(source-groups)
|
|
{
|
|
if $(s:G) != <@>
|
|
{
|
|
group += $(s) ;
|
|
}
|
|
else
|
|
{
|
|
# We've collected a group of targets that originates from single
|
|
# dependency main target, and must check that at least one of them
|
|
# is used. If no targets were created for dependency, it's OK.
|
|
if $(group) && ! [ set.intersection $(group) : $(used-sources) ]
|
|
{
|
|
errors.warning "Unused source" $(s:G=) "in main target" [ full-name ] ;
|
|
}
|
|
group = ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Constructs the virtual targets for this abstract targets and
|
|
# the dependecy graph. Returns the list of virtual targets.
|
|
# Should be overrided in derived classes.
|
|
rule construct ( source-targets * : properties * )
|
|
{
|
|
errors.error "method should be defined in derived classes" ;
|
|
}
|
|
}
|
|
|
|
class basic-target : abstract-target ;
|
|
|
|
rule typed-target ( name : project : type
|
|
: sources * : requirements * : default-build * : usage-requirements * )
|
|
{
|
|
basic-target.__init__ $(name) : $(project)
|
|
: $(sources) : $(requirements) : $(default-build) : $(usage-requirements) ;
|
|
|
|
import generators ;
|
|
|
|
self.type = $(type) ;
|
|
|
|
rule construct ( source-targets * : property-set )
|
|
{
|
|
local r = [ generators.construct $(self.project) $(self.name) : $(self.type)
|
|
: [ property-set.create [ $(property-set).raw ] # [ feature.expand
|
|
<main-target-type>$(self.type) ]
|
|
# ]
|
|
: $(source-targets) ] ;
|
|
if ! $(r)
|
|
{
|
|
errors.error "unable to construct" [ full-name ] ;
|
|
}
|
|
|
|
return $(r) ;
|
|
}
|
|
}
|
|
class typed-target : basic-target ;
|
|
|
|
# Returns the requirement to use when declaring a main target,
|
|
# which are obtained by
|
|
# - translating all specified property paths, and
|
|
# - refining project requirements with the one specified for the target
|
|
rule main-target-requirements (
|
|
specification * # Properties explicitly specified for a main target
|
|
: project # Project where the main target is to be declared
|
|
)
|
|
{
|
|
local loc = [ project.attribute $(project) location ] ;
|
|
local requirements = [ property.translate-paths $(specification) : $(loc) ] ;
|
|
local requirements = [ property-set.create $(requirements) ] ;
|
|
local project-requirements = [ project.attribute $(project) requirements ] ;
|
|
requirements = [ $(project-requirements).refine $(requirements) ] ;
|
|
if $(requirements[1]) = "@error"
|
|
{
|
|
errors.error "Conflicting requirements for target:" $(requirements) ;
|
|
}
|
|
return $(requirements) ;
|
|
}
|
|
|
|
# Returns the use requirement to use when declaraing a main target,
|
|
# which are obtained by
|
|
# - translating all specified property paths, and
|
|
# - adding project's usage requirements
|
|
rule main-target-usage-requirements (
|
|
specification * # Use-properties explicitly specified for a main target
|
|
: project # Project where the main target is to be declared
|
|
)
|
|
{
|
|
local loc = [ project.attribute $(project) location ] ;
|
|
local project-usage-requirements = [ project.attribute $(project) usage-requirements ] ;
|
|
|
|
local usage-requirements = [ property-set.create
|
|
[ property.translate-paths $(specification) : $(loc) ] ] ;
|
|
|
|
return [ $(project-usage-requirements).add $(usage-requirements) ] ;
|
|
}
|
|
|
|
# Return the default build value to use when declaring a main target,
|
|
# which is obtained by using specified value if not empty and parent's
|
|
# default build attribute otherwise.
|
|
rule main-target-default-build (
|
|
specification * # Default build explicitly specified for a main target
|
|
: project # Project where the main target is to be declared
|
|
)
|
|
{
|
|
local result ;
|
|
if $(specification)
|
|
{
|
|
result = $(specification) ;
|
|
}
|
|
else
|
|
{
|
|
result = [ project.attribute $(project) default-build ] ;
|
|
}
|
|
return [ property-set.create-with-validation $(result) ] ;
|
|
}
|
|
|
|
# Registers the specified target as a main target alternatives.
|
|
# Gets project and name for 'target', obtains main target
|
|
# from them, and adds targets to the list of alternatives for
|
|
# the main target.
|
|
rule main-target-alternative ( target )
|
|
{
|
|
local ptarget = [ project.target [ $(target).project ] ] ;
|
|
local mtarget = [ $(ptarget).main-target [ $(target).name ] ] ;
|
|
|
|
$(mtarget).add-alternative $(target) ;
|
|
}
|