blob: 87661adac63595e608588eafeb62b7db64c5369c [file] [log] [blame]
# Copyright 2003 Dave Abrahams
# Copyright 2005, 2006 Rene Rivera
# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# Implements virtual targets, which correspond to actual files created during a
# build, but are not yet targets in Jam sense. They are needed, for example,
# when searching for possible transformation sequences, when it is not yet known
# whether a particular target should be created at all.
import "class" : new ;
import errors ;
import path ;
import sequence ;
import set ;
import type ;
import utility ;
# +--------------------------+
# | virtual-target |
# +==========================+
# | actualize |
# +--------------------------+
# | actualize-action() = 0 |
# | actualize-location() = 0 |
# +----------------+---------+
# |
# ^
# / \
# +-+-+
# |
# +---------------------+ +-------+--------------+
# | action | | abstract-file-target |
# +=====================| * +======================+
# | action-name | +--+ action |
# | properties | | +----------------------+
# +---------------------+--+ | actualize-action() |
# | actualize() |0..1 +-----------+----------+
# | path() | |
# | adjust-properties() | sources |
# | actualize-sources() | targets |
# +------+--------------+ ^
# | / \
# ^ +-+-+
# / \ |
# +-+-+ +-------------+-------------+
# | | |
# | +------+---------------+ +--------+-------------+
# | | file-target | | searched-lib-target |
# | +======================+ +======================+
# | | actualize-location() | | actualize-location() |
# | +----------------------+ +----------------------+
# |
# +-+------------------------------+
# | |
# +----+----------------+ +---------+-----------+
# | compile-action | | link-action |
# +=====================+ +=====================+
# | adjust-properties() | | adjust-properties() |
# +---------------------+ | actualize-sources() |
# +---------------------+
#
# The 'compile-action' and 'link-action' classes are not defined here but in
# builtin.jam modules. They are shown in the diagram to give the big picture.
# Models a potential target. It can be converted into a Jam target and used in
# building, if needed. However, it can be also dropped, which allows us to
# search for different transformations and select only one.
#
class virtual-target
{
import scanner ;
import sequence ;
import utility ;
import virtual-target ;
rule __init__ (
name # Target/project name.
: project # Project to which this target belongs.
)
{
self.name = $(name) ;
self.project = $(project) ;
self.dependencies = ;
}
# Name of this target.
#
rule name ( )
{
return $(self.name) ;
}
# Project of this target.
#
rule project ( )
{
return $(self.project) ;
}
# Adds additional 'virtual-target' instances this one depends on.
#
rule depends ( d + )
{
self.dependencies = [ sequence.merge $(self.dependencies) :
[ sequence.insertion-sort $(d) ] ] ;
}
rule dependencies ( )
{
return $(self.dependencies) ;
}
rule always ( )
{
.always = 1 ;
}
# Generates all the actual targets and sets up build actions for this
# target.
#
# If 'scanner' is specified, creates an additional target with the same
# location as the actual target, which will depend on the actual target and
# be associated with a 'scanner'. That additional target is returned. See
# the docs (#dependency_scanning) for rationale. Target must correspond to a
# file if 'scanner' is specified.
#
# If scanner is not specified then the actual target is returned.
#
rule actualize ( scanner ? )
{
local actual-name = [ actualize-no-scanner ] ;
if $(.always)
{
ALWAYS $(actual-name) ;
}
if ! $(scanner)
{
return $(actual-name) ;
}
else
{
# Add the scanner instance to the grist for name.
local g = [ sequence.join
[ utility.ungrist $(actual-name:G) ] $(scanner) : - ] ;
local name = $(actual-name:G=$(g)) ;
if ! $(self.made.$(name))
{
self.made.$(name) = true ;
DEPENDS $(name) : $(actual-name) ;
actualize-location $(name) ;
scanner.install $(scanner) : $(name) $(__name__) ;
}
return $(name) ;
}
}
# private: (overridables)
# Sets up build actions for 'target'. Should call appropriate rules and set
# target variables.
#
rule actualize-action ( target )
{
errors.error "method should be defined in derived classes" ;
}
# Sets up variables on 'target' which specify its location.
#
rule actualize-location ( target )
{
errors.error "method should be defined in derived classes" ;
}
# If the target is a generated one, returns the path where it will be
# generated. Otherwise, returns an empty list.
#
rule path ( )
{
errors.error "method should be defined in derived classes" ;
}
# Returns the actual target name to be used in case when no scanner is
# involved.
#
rule actual-name ( )
{
errors.error "method should be defined in derived classes" ;
}
# implementation
rule actualize-no-scanner ( )
{
# In fact, we just need to merge virtual-target with
# abstract-file-target as the latter is the only class derived from the
# former. But that has been left for later.
errors.error "method should be defined in derived classes" ;
}
}
# Target corresponding to a file. The exact mapping for file is not yet
# specified in this class. (TODO: Actually, the class name could be better...)
#
# May be a source file (when no action is specified) or a derived file
# (otherwise).
#
# The target's grist is a concatenation of its project's location, action
# properties (for derived targets) and, optionally, value identifying the main
# target.
#
class abstract-file-target : virtual-target
{
import project ;
import regex ;
import sequence ;
import path ;
import type ;
import property-set ;
import indirect ;
rule __init__ (
name # Target's name.
exact ? # If non-empty, the name is exactly the name created file
# should have. Otherwise, the '__init__' method will add a
# suffix obtained from 'type' by calling
# 'type.generated-target-suffix'.
: type ? # Target's type.
: project
: action ?
)
{
virtual-target.__init__ $(name) : $(project) ;
self.type = $(type) ;
self.action = $(action) ;
if $(action)
{
$(action).add-targets $(__name__) ;
if $(self.type) && ! $(exact)
{
_adjust-name $(name) ;
}
}
}
rule type ( )
{
return $(self.type) ;
}
# Sets the path. When generating target name, it will override any path
# computation from properties.
#
rule set-path ( path )
{
self.path = [ path.native $(path) ] ;
}
# Returns the currently set action.
#
rule action ( )
{
return $(self.action) ;
}
# Sets/gets the 'root' flag. Target is root if it directly corresponds to
# some variant of a main target.
#
rule root ( set ? )
{
if $(set)
{
self.root = true ;
}
return $(self.root) ;
}
# Gets or sets the subvariant which created this target. Subvariant is set
# when target is brought into existance and is never changed after that. In
# particular, if a target is shared by a subvariant, only the first is
# stored.
#
rule creating-subvariant ( s ? # If specified, specifies the value to set,
# which should be a 'subvariant' class
# instance.
)
{
if $(s) && ! $(self.creating-subvariant)
{
self.creating-subvariant = $(s) ;
}
return $(self.creating-subvariant) ;
}
rule actualize-action ( target )
{
if $(self.action)
{
$(self.action).actualize ;
}
}
# Return a human-readable representation of this target. If this target has
# an action, that is:
#
# { <action-name>-<self.name>.<self.type> <action-sources>... }
#
# otherwise, it is:
#
# { <self.name>.<self.type> }
#
rule str ( )
{
local action = [ action ] ;
local name-dot-type = [ sequence.join $(self.name) "." $(self.type) ] ;
if $(action)
{
local sources = [ $(action).sources ] ;
local action-name = [ $(action).action-name ] ;
local ss ;
for local s in $(sources)
{
ss += [ $(s).str ] ;
}
return "{" $(action-name)-$(name-dot-type) $(ss) "}" ;
}
else
{
return "{" $(name-dot-type) "}" ;
}
}
rule less ( a )
{
if [ str ] < [ $(a).str ]
{
return true ;
}
}
rule equal ( a )
{
if [ str ] = [ $(a).str ]
{
return true ;
}
}
# private:
rule actual-name ( )
{
if ! $(self.actual-name)
{
local grist = [ grist ] ;
local basename = [ path.native $(self.name) ] ;
self.actual-name = <$(grist)>$(basename) ;
}
return $(self.actual-name) ;
}
# Helper to 'actual-name', above. Computes a unique prefix used to
# distinguish this target from other targets with the same name creating
# different files.
#
rule grist ( )
{
# Depending on target, there may be different approaches to generating
# unique prefixes. We generate prefixes in the form:
# <one letter approach code> <the actual prefix>
local path = [ path ] ;
if $(path)
{
# The target will be generated to a known path. Just use the path
# for identification, since path is as unique as it can get.
return p$(path) ;
}
else
{
# File is either source, which will be searched for, or is not a
# file at all. Use the location of project for distinguishing.
local project-location = [ $(self.project).get location ] ;
local location-grist = [ sequence.join [ regex.split
$(project-location) "/" ] : "!" ] ;
if $(self.action)
{
local ps = [ $(self.action).properties ] ;
local property-grist = [ $(ps).as-path ] ;
# 'property-grist' can be empty when 'ps' is an empty property
# set.
if $(property-grist)
{
location-grist = $(location-grist)/$(property-grist) ;
}
}
return l$(location-grist) ;
}
}
# Given the target name specified in constructor, returns the name which
# should be really used, by looking at the <tag> properties. Tag properties
# need to be specified as <tag>@rule-name. This makes Boost Build call the
# specified rule with the target name, type and properties to get the new
# name. If no <tag> property is specified or the rule specified by <tag>
# returns nothing, returns the result of calling
# virtual-target.add-prefix-and-suffix.
#
rule _adjust-name ( specified-name )
{
local ps ;
if $(self.action)
{
ps = [ $(self.action).properties ] ;
}
else
{
ps = [ property-set.empty ] ;
}
# We add ourselves to the properties so that any tag rule can get more
# direct information about the target than just that available through
# the properties. This is useful in implementing name changes based on
# the sources of the target. For example to make unique names of object
# files based on the source file. --grafik
ps = [ property-set.create [ $(ps).raw ] <target>$(__name__) ] ;
local tag = [ $(ps).get <tag> ] ;
if $(tag)
{
local rule-name = [ MATCH ^@(.*) : $(tag) ] ;
if $(rule-name)
{
if $(tag[2])
{
errors.error "<tag>@rulename is present but is not the only"
"<tag> feature" ;
}
self.name = [ indirect.call $(rule-name) $(specified-name)
: $(self.type) : $(ps) ] ;
}
else
{
errors.error
"The value of the <tag> feature must be '@rule-name'" ;
}
}
# If there is no tag or the tag rule returned nothing.
if ! $(tag) || ! $(self.name)
{
self.name = [ virtual-target.add-prefix-and-suffix $(specified-name)
: $(self.type) : $(ps) ] ;
}
}
rule actualize-no-scanner ( )
{
local name = [ actual-name ] ;
# Do anything only on the first invocation.
if ! $(self.made.$(name))
{
self.made.$(name) = true ;
if $(self.action)
{
# For non-derived target, we do not care if there are several
# virtual targets that refer to the same name. One case when
# this is unavoidable is when the file name is main.cpp and two
# targets have types CPP (for compiling) and MOCCABLE_CPP (for
# conversion to H via Qt tools).
virtual-target.register-actual-name $(name) : $(__name__) ;
}
for local i in $(self.dependencies)
{
DEPENDS $(name) : [ $(i).actualize ] ;
}
actualize-location $(name) ;
actualize-action $(name) ;
}
return $(name) ;
}
}
# Appends the suffix appropriate to 'type/property-set' combination to the
# specified name and returns the result.
#
rule add-prefix-and-suffix ( specified-name : type ? : property-set )
{
local suffix = [ type.generated-target-suffix $(type) : $(property-set) ] ;
# Handle suffixes for which no leading dot is desired. Those are specified
# by enclosing them in <...>. Needed by python so it can create "_d.so"
# extensions, for example.
if $(suffix:G)
{
suffix = [ utility.ungrist $(suffix) ] ;
}
else
{
suffix = .$(suffix) ;
}
local prefix = [ type.generated-target-prefix $(type) : $(property-set) ] ;
if [ MATCH ^($(prefix)) : $(specified-name) ]
{
prefix = ;
}
return $(prefix:E="")$(specified-name)$(suffix:E="") ;
}
# File targets with explicitly known location.
#
# The file path is determined as
# * Value passed to the 'set-path' method, if any.
# * For derived files, project's build dir, joined with components that
# describe action properties. If free properties are not equal to the
# project's reference properties an element with the name of the main
# target is added.
# * For source files, project's source dir.
#
# The file suffix is determined as:
# * The value passed to the 'suffix' method, if any.
# * The suffix corresponding to the target's type.
#
class file-target : abstract-file-target
{
import "class" : new ;
import common ;
import errors ;
rule __init__ (
name exact ?
: type ? # Optional type for this target.
: project
: action ?
: path ?
)
{
abstract-file-target.__init__ $(name) $(exact) : $(type) : $(project) :
$(action) ;
self.path = $(path) ;
}
rule clone-with-different-type ( new-type )
{
return [ new file-target $(self.name) exact : $(new-type) :
$(self.project) : $(self.action) : $(self.path) ] ;
}
rule actualize-location ( target )
{
if $(self.action)
{
# This is a derived file.
local path = [ path ] ;
LOCATE on $(target) = $(path) ;
# Make sure the path exists.
DEPENDS $(target) : $(path) ;
common.MkDir $(path) ;
# It is possible that the target name includes a directory too, for
# example when installing headers. Create that directory.
if $(target:D)
{
local d = $(target:D) ;
d = $(d:R=$(path)) ;
DEPENDS $(target) : $(d) ;
common.MkDir $(d) ;
}
# For a real file target, we create a fake target depending on the
# real target. This allows us to run
#
# bjam hello.o
#
# without trying to guess the name of the real target. Note that the
# target has no directory name and uses a special <e> grist.
#
# First, that means that "bjam hello.o" will build all known hello.o
# targets. Second, the <e> grist makes sure this target will not be
# confused with other targets, for example, if we have subdir 'test'
# with target 'test' in it that includes a 'test.o' file, then the
# target for directory will be just 'test' the target for test.o
# will be <ptest/bin/gcc/debug>test.o and the target we create below
# will be <e>test.o
DEPENDS $(target:G=e) : $(target) ;
# Allow bjam <path-to-file>/<file> to work. This will not catch all
# possible ways to refer to the path (relative/absolute, extra ".",
# various "..", but should help in obvious cases.
DEPENDS $(target:G=e:R=$(path)) : $(target) ;
}
else
{
SEARCH on $(target) = [ path.native $(self.path) ] ;
}
}
# Returns the directory for this target.
#
rule path ( )
{
if ! $(self.path)
{
if $(self.action)
{
local p = [ $(self.action).properties ] ;
local path,relative-to-build-dir = [ $(p).target-path ] ;
local path = $(path,relative-to-build-dir[1]) ;
local relative-to-build-dir = $(path,relative-to-build-dir[2]) ;
if $(relative-to-build-dir)
{
path = [ path.join [ $(self.project).build-dir ] $(path) ] ;
}
self.path = [ path.native $(path) ] ;
}
}
return $(self.path) ;
}
}
class notfile-target : abstract-file-target
{
rule __init__ ( name : project : action ? )
{
abstract-file-target.__init__ $(name) : : $(project) : $(action) ;
}
# Returns nothing to indicate that the target's path is not known.
#
rule path ( )
{
return ;
}
rule actualize-location ( target )
{
NOTFILE $(target) ;
ALWAYS $(target) ;
# TEMPORARY $(target) ;
NOUPDATE $(target) ;
}
}
# Class representing an action. Both 'targets' and 'sources' should list
# instances of 'virtual-target'. Action name should name a rule with this
# prototype:
# rule action-name ( targets + : sources * : properties * )
# Targets and sources are passed as actual Jam targets. The rule may not
# establish additional dependency relationships.
#
class action
{
import "class" ;
import errors ;
import type ;
import toolset ;
import property-set ;
import indirect ;
import path ;
import set : difference ;
rule __init__ ( sources * : action-name + : property-set ? )
{
self.sources = $(sources) ;
self.action-name = [ indirect.make-qualified $(action-name) ] ;
if ! $(property-set)
{
property-set = [ property-set.empty ] ;
}
if ! [ class.is-instance $(property-set) ]
{
errors.error "Property set instance required" ;
}
self.properties = $(property-set) ;
}
rule add-targets ( targets * )
{
self.targets += $(targets) ;
}
rule replace-targets ( old-targets * : new-targets * )
{
self.targets = [ set.difference $(self.targets) : $(old-targets) ] ;
self.targets += $(new-targets) ;
}
rule targets ( )
{
return $(self.targets) ;
}
rule sources ( )
{
return $(self.sources) ;
}
rule action-name ( )
{
return $(self.action-name) ;
}
rule properties ( )
{
return $(self.properties) ;
}
# Generates actual build instructions.
#
rule actualize ( )
{
if ! $(self.actualized)
{
self.actualized = true ;
local ps = [ properties ] ;
local properties = [ adjust-properties $(ps) ] ;
local actual-targets ;
for local i in [ targets ]
{
actual-targets += [ $(i).actualize ] ;
}
actualize-sources [ sources ] : $(properties) ;
DEPENDS $(actual-targets) : $(self.actual-sources)
$(self.dependency-only-sources) ;
# Action name can include additional argument to rule, which should
# not be passed to 'set-target-variables'
toolset.set-target-variables
[ indirect.get-rule $(self.action-name[1]) ] $(actual-targets)
: $(properties) ;
# Reflect ourselves in a variable for the target. This allows
# looking up additional info for the action given the raw target.
# For example to debug or output action information from action
# rules.
.action on $(actual-targets) = $(__name__) ;
indirect.call $(self.action-name) $(actual-targets)
: $(self.actual-sources) : [ $(properties).raw ] ;
# Since we set up the creating action here, we set up the action for
# cleaning up as well.
common.Clean clean-all : $(actual-targets) ;
}
}
# Helper for 'actualize-sources'. For each passed source, actualizes it with
# the appropriate scanner. Returns the actualized virtual targets.
#
rule actualize-source-type ( sources * : property-set )
{
local result = ;
for local i in $(sources)
{
local scanner ;
if [ $(i).type ]
{
scanner = [ type.get-scanner [ $(i).type ] : $(property-set) ] ;
}
result += [ $(i).actualize $(scanner) ] ;
}
return $(result) ;
}
# Creates actual Jam targets for sources. Initializes the following member
# variables:
# 'self.actual-sources' -- sources passed to the updating action.
# 'self.dependency-only-sources' -- sources marked as dependencies, but
# are not used otherwise.
#
# New values will be *appended* to the variables. They may be non-empty if
# caller wants it.
#
rule actualize-sources ( sources * : property-set )
{
local dependencies = [ $(self.properties).get <dependency> ] ;
self.dependency-only-sources +=
[ actualize-source-type $(dependencies) : $(property-set) ] ;
self.actual-sources +=
[ actualize-source-type $(sources) : $(property-set) ] ;
# This is used to help bjam find dependencies in generated headers and
# other main targets, e.g. in:
#
# make a.h : ....... ;
# exe hello : hello.cpp : <implicit-dependency>a.h ;
#
# For bjam to find the dependency the generated target must be
# actualized (i.e. have its Jam target constructed). In the above case,
# if we are building just hello ("bjam hello"), 'a.h' will not be
# actualized unless we do it here.
local implicit = [ $(self.properties).get <implicit-dependency> ] ;
for local i in $(implicit)
{
$(i:G=).actualize ;
}
}
# Determines real properties when trying to build with 'properties'. This is
# the last chance to fix properties, for example to adjust includes to get
# generated headers correctly. Default implementation simply returns its
# argument.
#
rule adjust-properties ( property-set )
{
return $(property-set) ;
}
}
# Action class which does nothing --- it produces the targets with specific
# properties out of nowhere. It is needed to distinguish virtual targets with
# different properties that are known to exist and have no actions which create
# them.
#
class null-action : action
{
rule __init__ ( property-set ? )
{
action.__init__ : .no-action : $(property-set) ;
}
rule actualize ( )
{
if ! $(self.actualized)
{
self.actualized = true ;
for local i in [ targets ]
{
$(i).actualize ;
}
}
}
}
# Class which acts exactly like 'action', except that its sources are not
# scanned for dependencies.
#
class non-scanning-action : action
{
rule __init__ ( sources * : action-name + : property-set ? )
{
action.__init__ $(sources) : $(action-name) : $(property-set) ;
}
rule actualize-source-type ( sources * : property-set )
{
local result ;
for local i in $(sources)
{
result += [ $(i).actualize ] ;
}
return $(result) ;
}
}
# Creates a virtual target with an appropriate name and type from 'file'. If a
# target with that name in that project already exists, returns that already
# created target.
#
# FIXME: a more correct way would be to compute the path to the file, based on
# name and source location for the project, and use that path to determine if
# the target has already been created. This logic should be shared with how we
# usually find targets identified by a specific target id. It should also be
# updated to work correctly when the file is specified using both relative and
# absolute paths.
#
# TODO: passing a project with all virtual targets is starting to be annoying.
#
rule from-file ( file : file-loc : project )
{
import type ; # Had to do this here to break a circular dependency.
# Check whether we already created a target corresponding to this file.
local path = [ path.root [ path.root $(file) $(file-loc) ] [ path.pwd ] ] ;
if $(.files.$(path))
{
return $(.files.$(path)) ;
}
else
{
local name = [ path.make $(file) ] ;
local type = [ type.type $(file) ] ;
local result ;
result = [ new file-target $(file) : $(type) : $(project) : :
$(file-loc) ] ;
.files.$(path) = $(result) ;
return $(result) ;
}
}
# Registers a new virtual target. Checks if there is already a registered target
# with the same name, type, project and subvariant properties as well as the
# same sources and equal action. If such target is found it is returned and a
# new 'target' is not registered. Otherwise, 'target' is registered and
# returned.
#
rule register ( target )
{
local signature = [ sequence.join
[ $(target).path ] [ $(target).name ] : - ] ;
local result ;
for local t in $(.cache.$(signature))
{
local a1 = [ $(t).action ] ;
local a2 = [ $(target).action ] ;
if ! $(result)
{
if ! $(a1) && ! $(a2)
{
result = $(t) ;
}
else
{
if $(a1) && $(a2) &&
( [ $(a1).action-name ] = [ $(a2).action-name ] ) &&
( [ $(a1).sources ] = [ $(a2).sources ] )
{
local ps1 = [ $(a1).properties ] ;
local ps2 = [ $(a2).properties ] ;
local p1 = [ $(ps1).base ] [ $(ps1).free ] [ set.difference
[ $(ps1).dependency ] : [ $(ps1).incidental ] ] ;
local p2 = [ $(ps2).base ] [ $(ps2).free ] [ set.difference
[ $(ps2).dependency ] : [ $(ps2).incidental ] ] ;
if $(p1) = $(p2)
{
result = $(t) ;
}
}
}
}
}
if ! $(result)
{
.cache.$(signature) += $(target) ;
result = $(target) ;
}
.recent-targets += $(result) ;
.all-targets += $(result) ;
return $(result) ;
}
# Each target returned by 'register' is added to the .recent-targets list,
# returned by this function. This allows us to find all virtual targets created
# when building a specific main target, even those constructed only as
# intermediate targets.
#
rule recent-targets ( )
{
return $(.recent-targets) ;
}
rule clear-recent-targets ( )
{
.recent-targets = ;
}
# Returns all virtual targets ever created.
#
rule all-targets ( )
{
return $(.all-targets) ;
}
# Returns all targets from 'targets' with types equal to 'type' or derived from
# it.
#
rule select-by-type ( type : targets * )
{
local result ;
for local t in $(targets)
{
if [ type.is-subtype [ $(t).type ] $(type) ]
{
result += $(t) ;
}
}
return $(result) ;
}
rule register-actual-name ( actual-name : virtual-target )
{
if $(.actual.$(actual-name))
{
local cs1 = [ $(.actual.$(actual-name)).creating-subvariant ] ;
local cs2 = [ $(virtual-target).creating-subvariant ] ;
local cmt1 = [ $(cs1).main-target ] ;
local cmt2 = [ $(cs2).main-target ] ;
local action1 = [ $(.actual.$(actual-name)).action ] ;
local action2 = [ $(virtual-target).action ] ;
local properties-added ;
local properties-removed ;
if $(action1) && $(action2)
{
local p1 = [ $(action1).properties ] ;
p1 = [ $(p1).raw ] ;
local p2 = [ $(action2).properties ] ;
p2 = [ $(p2).raw ] ;
properties-removed = [ set.difference $(p1) : $(p2) ] ;
properties-removed ?= "none" ;
properties-added = [ set.difference $(p2) : $(p1) ] ;
properties-added ?= "none" ;
}
errors.error "Duplicate name of actual target:" $(actual-name)
: "previous virtual target" [ $(.actual.$(actual-name)).str ]
: "created from" [ $(cmt1).full-name ]
: "another virtual target" [ $(virtual-target).str ]
: "created from" [ $(cmt2).full-name ]
: "added properties:" $(properties-added)
: "removed properties:" $(properties-removed) ;
}
else
{
.actual.$(actual-name) = $(virtual-target) ;
}
}
# Traverses the dependency graph of 'target' and return all targets that will be
# created before this one is created. If the root of some dependency graph is
# found during traversal, it is either included or not, depending on the
# 'include-roots' value. In either case traversal stops at root targets, i.e.
# root target sources are not traversed.
#
rule traverse ( target : include-roots ? : include-sources ? )
{
local result ;
if [ $(target).action ]
{
local action = [ $(target).action ] ;
# This includes the 'target' as well.
result += [ $(action).targets ] ;
for local t in [ $(action).sources ]
{
if ! [ $(t).root ]
{
result += [ traverse $(t) : $(include-roots) : $(include-sources) ] ;
}
else if $(include-roots)
{
result += $(t) ;
}
}
}
else if $(include-sources)
{
result = $(target) ;
}
return $(result) ;
}
# Takes an 'action' instance and creates a new instance of it and all targets
# produced by the action. The rule-name and properties are set to
# 'new-rule-name' and 'new-properties', if those are specified. Returns the
# cloned action.
#
rule clone-action ( action : new-project : new-action-name ? : new-properties ? )
{
if ! $(new-action-name)
{
new-action-name = [ $(action).action-name ] ;
}
if ! $(new-properties)
{
new-properties = [ $(action).properties ] ;
}
local action-class = [ modules.peek $(action) : __class__ ] ;
local cloned-action = [ class.new $(action-class)
[ $(action).sources ] : $(new-action-name) : $(new-properties) ] ;
local cloned-targets ;
for local target in [ $(action).targets ]
{
local n = [ $(target).name ] ;
# Do not modify produced target names.
local cloned-target = [ class.new file-target $(n) exact :
[ $(target).type ] : $(new-project) : $(cloned-action) ] ;
local d = [ $(target).dependencies ] ;
if $(d)
{
$(cloned-target).depends $(d) ;
}
$(cloned-target).root [ $(target).root ] ;
$(cloned-target).creating-subvariant [ $(target).creating-subvariant ] ;
cloned-targets += $(cloned-target) ;
}
return $(cloned-action) ;
}
class subvariant
{
import sequence ;
import type ;
rule __init__ ( main-target # The instance of main-target class.
: property-set # Properties requested for this target.
: sources *
: build-properties # Actually used properties.
: sources-usage-requirements # Properties propagated from sources.
: created-targets * ) # Top-level created targets.
{
self.main-target = $(main-target) ;
self.properties = $(property-set) ;
self.sources = $(sources) ;
self.build-properties = $(build-properties) ;
self.sources-usage-requirements = $(sources-usage-requirements) ;
self.created-targets = $(created-targets) ;
# Pre-compose a list of other dependency graphs this one depends on.
local deps = [ $(build-properties).get <implicit-dependency> ] ;
for local d in $(deps)
{
self.other-dg += [ $(d:G=).creating-subvariant ] ;
}
self.other-dg = [ sequence.unique $(self.other-dg) ] ;
}
rule main-target ( )
{
return $(self.main-target) ;
}
rule created-targets ( )
{
return $(self.created-targets) ;
}
rule requested-properties ( )
{
return $(self.properties) ;
}
rule build-properties ( )
{
return $(self.build-properties) ;
}
rule sources-usage-requirements ( )
{
return $(self.sources-usage-requirements) ;
}
rule set-usage-requirements ( usage-requirements )
{
self.usage-requirements = $(usage-requirements) ;
}
rule usage-requirements ( )
{
return $(self.usage-requirements) ;
}
# Returns all targets referenced by this subvariant, either directly or
# indirectly, and either as sources, or as dependency properties. Targets
# referred to using the dependency property are returned as properties, not
# targets.
#
rule all-referenced-targets ( theset )
{
# Find directly referenced targets.
local deps = [ $(self.build-properties).dependency ] ;
local all-targets = $(self.sources) $(deps) ;
# Find other subvariants.
local r ;
for local t in $(all-targets)
{
if ! [ $(theset).contains $(t) ]
{
$(theset).add $(t) ;
r += [ $(t:G=).creating-subvariant ] ;
}
}
r = [ sequence.unique $(r) ] ;
for local s in $(r)
{
if $(s) != $(__name__)
{
$(s).all-referenced-targets $(theset) ;
}
}
}
# Returns the properties specifying implicit include paths to generated
# headers. This traverses all targets in this subvariant and subvariants
# referred by <implcit-dependecy> properties. For all targets of type
# 'target-type' (or for all targets, if 'target-type' is not specified), the
# result will contain <$(feature)>path-to-that-target.
#
rule implicit-includes ( feature : target-type ? )
{
local key = ii$(feature)-$(target-type:E="") ;
if ! $($(key))-is-not-empty
{
local target-paths = [ all-target-directories $(target-type) ] ;
target-paths = [ sequence.unique $(target-paths) ] ;
local result = $(target-paths:G=$(feature)) ;
if ! $(result)
{
result = "" ;
}
$(key) = $(result) ;
}
if $($(key)) = ""
{
return ;
}
else
{
return $($(key)) ;
}
}
rule all-target-directories ( target-type ? )
{
if ! $(self.target-directories)
{
compute-target-directories $(target-type) ;
}
return $(self.target-directories) ;
}
rule compute-target-directories ( target-type ? )
{
local result ;
for local t in $(self.created-targets)
{
# Skip targets of the wrong type.
if ! $(target-type) ||
[ type.is-derived [ $(t).type ] $(target-type) ]
{
result = [ sequence.merge $(result) : [ $(t).path ] ] ;
}
}
for local d in $(self.other-dg)
{
result += [ $(d).all-target-directories $(target-type) ] ;
}
self.target-directories = $(result) ;
}
}