tools/build/v2/build/targets.jam
# 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|
# +----------------------+ +--------------+
# |
# ^
# / \
# +-+-+
# |
# |
# ...--+----------------+------------------+----------------+---+
# | | | |
# | | | |
# ... ---+-----+ +------+-------+ +------+------+ +--------+-----+
# | | typed-target | | make-target | | stage-target |
# . +==============+ +=============+ +==============+
# . | construct | | construct | | construct |
# +--------------+ +-------------+ +--------------+
import "class" : new ;
import sequence ;
import regex ;
import property ;
import errors ;
import common ;
import property-set ;
import project ;
import feature ;
import virtual-target ;
import path ;
import set ;
# Base class for all abstract targets.
class abstract-target
{
import project ;
rule __init__ ( name # name of the target in Jamfile
: project # the project module where the target is declared
)
{
# 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" ;
}
rule rename ( new-name )
{
self.name = $(new-name) ;
}
}
# Project target class (derived from 'abstract-target')
#
# This class these responsibilities:
# - maintaining a list of main target in this project and
# building it
#
# Main targets are constructed in two stages:
# - When Jamfile is read, a number of calls to 'add-alternative' is made.
# At that time, alternatives can also be renamed to account for inline
# targets.
# - The first time 'main-target' or 'has-main-target' rule is called,
# all alternatives are enumerated an main targets are created.
class project-target : abstract-target
{
import project targets ;
import path ;
import print ;
import property-set ;
import set : difference : set.difference ;
import sequence ;
import "class" : new ;
rule __init__ ( name : project : requirements * : default-build * )
{
abstract-target.__init__ $(name) : $(project) ;
self.requirements = $(requirements) ;
self.default-build = $(default-build) ;
}
# Generates all possible targets contained in this project.
rule generate ( property-set * )
{
local usage-requirements = [ property-set.empty ] ;
local targets ;
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) [ sequence.unique $(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 ;
if ! $(self.built-main-targets)
{
build-main-targets ;
}
# Collect all main targets here, except for "explicit" ones.
for local t in $(self.main-targets)
{
if ! [ $(t).name ] in $(self.explicit-targets)
{
result += $(t) ;
}
}
# 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) ;
}
# Add 'target' to the list of targets in this project that should be build
# only by explicit request
rule mark-target-as-explicit ( target-name )
{
# Record the name of the target, not instance, since this
# rule is called before main target instaces are created.
self.explicit-targets += $(target-name) ;
}
# Add new target alternative
rule add-alternative ( target-instance )
{
if $(self.built-main-targets)
{
errors.error "add-alternative called when main targets are already created." ;
}
self.alternatives += $(target-instance) ;
}
# Returns a 'main-target' class instance corresponding to the 'name'.
rule main-target ( name )
{
if ! $(self.built-main-targets)
{
build-main-targets ;
}
return $(self.main-target.$(name)) ;
}
# Tells if a main target with the specified name exists.
rule has-main-target ( name )
{
if ! $(self.built-main-targets)
{
build-main-targets ;
}
if $(self.main-target.$(name))
{
return true ;
}
}
rule build-main-targets ( )
{
self.built-main-targets = true ;
for local a in $(self.alternatives)
{
local name = [ $(a).name ] ;
local target = $(self.main-target.$(name)) ;
if ! $(target)
{
local t = [ new main-target $(name) : $(self.project) ] ;
self.main-target.$(name) = $(t) ;
self.main-targets += $(t) ;
target = $(self.main-target.$(name)) ;
}
$(target).add-alternative $(a) ;
}
}
}
# Helper rules to detect cycles in main target references
local rule start-building ( main-target-instance )
{
if $(main-target-instance) in $(.targets-being-built)
{
local names ;
for local t in $(.targets-being-built) $(main-target-instance)
{
names += [ $(t).full-name ] ;
}
errors.error "Recursion in main target references"
: "the following target are being built currently:"
: $(names) ;
}
.targets-being-built += $(main-target-instance) ;
}
local rule end-building ( main-target-instance )
{
.targets-being-built = $(.targets-being-built[1--2]) ;
}
# A named top-level target in Jamfile
class main-target : abstract-target
{
import errors : error ;
import assert ;
import sequence ;
import print ;
import build-request feature property-set ;
import targets : start-building end-building ;
import "class" : is-a ;
rule __init__ ( name : project )
{
abstract-target.__init__ $(name) : $(project) ;
}
# Add a new alternative for this target
rule add-alternative ( target )
{
local d = [ $(target).default-build ] ;
if $(self.alternatives) && ( $(self.default-build) != $(d) )
{
errors.error "default build must be identical in all alternatives"
: "main target is" [ full-name ]
: "with" [ $(d).raw ]
: "differing from previous default build" [ $(self.default-build).raw ] ;
}
else
{
self.default-build = $(d) ;
}
self.alternatives += $(target) ;
}
# Returns the best viable alternative for this property-set
# See the documentation for selection rules.
local rule select-alternatives ( property-set )
{
# The algorithm: we keep the current best viable alternative.
# When we've got new best viable alternative, we compare it
# with the current one.
local best ;
local best-properties ;
if $(self.alternatives[2-])
{
local bad ;
local worklist = $(self.alternatives) ;
while $(worklist) && ! $(bad)
{
local v = $(worklist[1]) ;
local properties = [ $(v).match $(property-set) ] ;
if $(properties) != no-match
{
if ! $(best)
{
best = $(v) ;
best-properties = $(properties) ;
}
else
{
if $(properties) = $(best-properties)
{
bad = true ;
}
else if $(properties) in $(best-properties)
{
# Do nothing, this alternative is worse
}
else if $(best-properties) in $(properties)
{
best = $(v) ;
best-properties = $(properties) ;
}
else
{
bad = true ;
}
}
}
worklist = $(worklist[2-]) ;
}
if ! $(bad)
{
return $(best) ;
}
}
else
{
return $(self.alternatives) ;
}
}
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
# We have to compress subproperties here to prevent
# property lists like:
#
# <toolset>msvc <toolset-msvc:version>7.1 <threading>multi
#
# from being expanded into:
#
# <toolset-msvc:version>7.1/<threading>multi
# <toolset>msvc/<toolset-msvc:version>7.1/<threading>multi
#
# due to cross-product property combination. That may
# be an indication that
# build-request.expand-no-defaults is the wrong rule
# to use here.
[ feature.compress-subproperties $(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 )
{
start-building $(__name__) ;
local all-property-sets = [ apply-default-build $(property-set) ] ;
local usage-requirements = [ property-set.empty ] ;
local result ;
for local p in $(all-property-sets)
{
p = [ $(p).expand-composites ] ;
p = [ $(p).add-defaults ] ;
local r = [ generate-really $(p) ] ;
usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
result += $(r[2-]) ;
}
end-building $(__name__) ;
return $(usage-requirements) [ sequence.unique $(result) ] ;
}
# Generates the main target with the given property set
# and returns a list which first element is property-set object
# containing usage-requirements of generated target and with
# generated virtual target in other elements. It's possible
# that no targets are generated.
local rule generate-really ( property-set )
{
local best-alternatives = [ select-alternatives $(property-set) ] ;
if ! $(best-alternatives)
{
errors.error
"failed to build" [ full-name ]
"with properties" [ $(property-set).raw ]
"because no best-matching alternative could be found"
;
return [ property-set.empty ] ;
}
else
{
local result = [ $(best-alternatives).generate $(property-set) ] ;
# Now return virtual targets for the only alternative
return $(result) ;
}
}
rule rename ( new-name )
{
abstract-target.rename $(new-name) ;
for local a in $(self.alternatives)
{
$(a).rename $(new-name) ;
}
}
}
# Abstract target which refers to a source file.
# This is artificial creature; it's usefull so that sources to
# a target can be represented as list of abstract target instances.
class file-reference : abstract-target
{
import virtual-target ;
import property-set ;
import path ;
rule __init__ ( file : project )
{
abstract-target.__init__ $(file) : $(project) ;
}
rule generate ( properties )
{
return [ property-set.empty ]
[ virtual-target.from-file $(self.name) : $(self.project) ] ;
}
# Returns true if the referred file really exists;
rule exists ( )
{
local location = [ path.root $(self.name)
[ project.attribute $(self.project) source-location ] ] ;
return [ path.exists $(location) ] ;
}
}
# Helper for 'find', below.
local rule remove-trailing-slash ( string )
{
local stripped = [ MATCH (.*)/$ : $(string) ] ;
stripped ?= $(string) ;
return $(stripped) ;
}
if "--quiet" in [ modules.peek : ARGV ]
{
.quiet = true ;
}
rule find ( id : project )
{
local current-location = [ project.attribute $(project) location ] ;
local target ;
local split = [ MATCH (.*)//(.*) : $(id) ] ;
local project-part = $(split[1]) ;
local target-part = $(split[2]) ;
if ! $(split)
{
target-part = $(id) ;
}
# Make a more convenient name
local have-project-reference = $(split) ;
# The project used for finding main targets and for providing base directory
# for file paths.
local base-project ;
if $(project-part)
{
base-project = [ project.find $(project-part) : $(current-location) ] ;
}
else
{
# No project part in id. Resolve references relatively to the referring
# project.
base-project = $(project) ;
}
# Interpret target-part as project-id
if ! $(have-project-reference)
{
local project-module = [ project.find $(target-part) : $(current-location) ] ;
if $(project-module)
{
target = [ project.target $(project-module) ] ;
}
}
# Interpret target-name as name of main target
if ! $(target) && $(base-project)
{
local project-target = [ project.target $(base-project) ] ;
if [ $(project-target).has-main-target $(target-part) ]
{
target = [ $(project-target).main-target $(target-part) ] ;
}
}
if ! $(target) && ! $(have-project-reference)
{
target = [ new file-reference $(target-part) : $(project) ] ;
if ! [ $(target).exists ]
{
# File actually does not exist.
# Reset 'target' so that an error is issued.
target = ;
}
}
if ! $(target)
{
errors.error "Unable to resolve target-id $(id)" :
"Reference was made from project at '$(current-location)'" ;
}
return $(target) ;
}
# Given a target-reference, made in context of 'project',
# returns the abstract-target instance that is referred to, as well
# as properties explicitly specified for this reference.
rule resolve-reference ( target-reference : project )
{
# 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) ] ;
}
# Find the target
local target =
[ find $(id) : $(project) ] ;
# Do a sanity check
if $(sproperties) && [ class.is-a $(target) : file-reference ]
{
errors.error
"error: target reference '$(target-reference)' contains properties," :
"error: but refers to a file" ;
}
return $(target) [ property-set.create $(sproperties) ] ;
}
# 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
)
{
local r = [ resolve-reference $(target-reference) : $(project) ] ;
local target = $(r[1]) ;
local sproperties = $(r[2]) ;
# Take properties which should be propagated and refine them
# with source-specific requirements.
local propagated = [ $(property-set).propagated ] ;
local rproperties = [ $(propagated).refine $(sproperties) ] ;
if $(rproperties[1]) = "@error"
{
errors.error
"When building" [ full-name ] " with properties " $(properties) :
"Invalid properties specified for " $(source) ":"
$(rproperties[2-]) ;
}
return [ $(target).generate $(rproperties) ] ;
}
# 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.
class basic-target : abstract-target
{
import build-request ;
import virtual-target targets ;
import property-set ;
import set sequence errors ;
import "class" : new ;
rule __init__ ( name : project
: sources * : requirements * :
default-build * : usage-requirements * )
{
abstract-target.__init__ $(name) : $(project) ;
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 ] ;
}
}
# Returns the list of abstract-targets which are used as sources.
# The extra properties specified for sources are not represented.
rule sources ( )
{
if ! $(self.source-targets) {
for local s in $(self.sources)
{
self.source-targets +=
[ targets.resolve-reference $(s) : $(self.project) ] ;
}
}
return $(self.source-targets) ;
}
rule requirements ( )
{
return $(self.requirements) ;
}
rule default-build ( )
{
return $(self.default-build) ;
}
# Returns the alternative condition for this alternative, if
# the condition is satisfied by 'property-set'.
rule match ( property-set )
{
local condition = [ $(self.requirements).base ] ;
# Weed out conditional properties.
condition = [ MATCH ^([^:]*)\$ : $(condition) ] ;
if $(condition) in [ $(property-set).raw ]
{
return $(condition) ;
}
else
{
return no-match ;
}
}
# 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 erequirements = [ $(self.requirements).evaluate-conditionals
$(build-request) ] ;
erequirements = [ $(erequirements).expand-composites ] ;
local rproperties = [ $(build-request).refine $(erequirements) ] ;
if $(rproperties[1]) != "@error"
{
# TODO: issue a warning when requirements change properties, but
# link-compatibility is still not broken.
rproperties = [ $(rproperties).run-actions ] ;
}
return $(rproperties) ;
}
# Generate all sources for this target. Returns property-set with
# usage requirements, followed by the list of virtual 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) ] ;
check-for-link-compatibility $(more-targets[2-]) : $(property-set) ;
usage-requirements = [ $(usage-requirements).add $(more-targets[1]) ] ;
source-targets += $(more-targets[2-]) ;
}
return $(usage-requirements) $(source-targets) ;
}
#
# Allows the user to tag the name of the target, according to properties.
#
rule tag-name ( name : property-set )
{
local properties = [ $(property-set).raw ] ;
local tagged-name = $(name) ;
if <tag> in $(properties:G)
{
local tags = [ $(property-set).get <tag> ] ;
for local tag in $(tags)
{
tagged-name = $(tagged-name)$(tag) ;
}
}
return $(tagged-name) ;
}
# 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 deps = [ $(rproperties).dependency ] ;
check-for-link-compatibility $(deps:G=) : $(property-set) ;
local source-targets = [ generate-sources $(rproperties) ] ;
usage-requirements =
[ $(usage-requirements).add $(source-targets[1]) ] ;
source-targets = $(source-targets[2-]) ;
rproperties = [ $(rproperties).add $(usage-requirements) ] ;
local tagged-name = [ tag-name $(self.name) : $(rproperties) ] ;
local original-name = $(self.name) ;
self.name = $(tagged-name) ;
local result =
[ construct $(source-targets) : $(rproperties) ] ;
check-for-unused-sources
$(result) : $(source-targets) ;
local s = [ create-subvariant $(result) : $(property-set) : $(source-targets)
: $(rproperties) : $(usage-requirements) ] ;
local ur = [ compute-usage-requirements $(s) ] ;
$(s).set-usage-requirements $(ur) ;
self.generated.$(property-set) = $(ur) $(result) ;
self.name = $(original-name) ;
}
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.
rule compute-usage-requirements ( subvariant )
{
local rproperties = [ $(subvariant).build-properties ] ;
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 ( targets * : build-request : sources * :
rproperties
: usage-requirements )
{
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 s = [ new subvariant $(__name__) : $(build-request) : $(sources)
: $(rproperties) : $(usage-requirements) : $(all-targets) ] ;
for local v in $(all-targets)
{
if ! [ $(v).creating-subvariant ]
{
$(v).creating-subvariant $(s) ;
}
}
return $(s) ;
}
# Check that each source virtual target is either directly present in the
# result, or is in dependency graph of some returned virtual target.
# If this is not the case, 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 * : sources * )
{
local used-sources ;
for local r in $(result)
{
used-sources += [ virtual-target.traverse $(r) : include-roots : 1 ] ;
}
local unused = [ set.difference $(sources) : $(used-sources) ] ;
if $(unused)
{
for local u in $(unused)
{
errors.warning "Unused source" [ $(u).str ] "in main target" [ full-name ] ;
}
}
}
# Checks if 'targets' are link-compatible with 'build-request' and
# issues a warning if that's not the case.
local rule check-for-link-compatibility ( targets * : build-request )
{
local checked ;
for local t in $(targets)
{
local a = [ $(t).action ] ;
if $(a)
{
local p = [ $(a).properties ] ;
if ! $(p) in $(cheched)
{
if [ $(p).link-incompatible-with $(build-request) ]
{
local s = [ $(t).creating-subvariant ] ;
local other-mt = [ $(s).main-target ] ;
ECHO "warning: targets produced from" [ $(other-mt).name ]
"are link incompatible" ;
ECHO "warning: with main target" [ name ] ;
}
}
checked += $(p) ;
}
}
}
# 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 typed-target : basic-target
{
import generators ;
rule __init__ ( name : project : type
: sources * : requirements * : default-build * : usage-requirements * )
{
basic-target.__init__ $(name) : $(project)
: $(sources) : $(requirements) : $(default-build) : $(usage-requirements) ;
self.type = $(type) ;
}
rule type ( )
{
return $(self.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) ;
}
}
# Return the list of sources to use, if main target rule is invoked
# with 'sources'. If there are any objects in 'sources', they are treated
# as main target instances, and WRITEME.
rule main-target-sources ( sources * : main-target-name )
{
local result ;
for local t in $(sources)
{
if [ class.is-instance $(t) ]
{
local name = [ $(t).name ] ;
# NOTE: on windows, this won't work if 'main-target-name'
# if single letter. But other alternatives are even worse.
local new-name = $(main-target-name)..$(name) ;
$(t).rename $(new-name) ;
#local p = [ $(t).project ] ;
#local pt = [ project.target $(p) ] ;
#$(pt).rename-main-target $(name) : $(new-name) ;
result += $(new-name) ;
}
else
{
result += $(t) ;
}
}
return $(result) ;
}
# 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.
# Returns 'target'.
rule main-target-alternative ( target )
{
local ptarget = [ project.target [ $(target).project ] ] ;
$(ptarget).add-alternative $(target) ;
return $(target) ;
}
# Creates a typed-target with the specified properties.
# The 'name', 'sources', 'requirements', 'default-build' and
# 'usage-requirements' are assumed to be in the form specified
# by the user in Jamfile corresponding to 'project'.
rule create-typed-target ( type : project :
name : sources * : requirements * : default-build *
: usage-requirements * )
{
return [
targets.main-target-alternative
[ new typed-target $(name) : $(project) : $(type)
: [ targets.main-target-sources $(sources) : $(name) ]
: [ targets.main-target-requirements $(requirements) : $(project) ]
: [ targets.main-target-default-build $(default-build) : $(project) ]
: [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ]
] ] ;
}