diff options
author | Kenneth Heafield <github@kheafield.com> | 2012-05-12 14:01:52 -0400 |
---|---|---|
committer | Kenneth Heafield <github@kheafield.com> | 2012-05-12 14:01:52 -0400 |
commit | 1a3cb9d9b0ab24d21d7e4edb70bb4a939f621082 (patch) | |
tree | 96f5cbfad3cbb0b8e89c26d6fa2e1a72a9039439 /jam-files/boost-build/build/virtual-target.jam | |
parent | dba1128114d68ed46cdea98ecb887c7657a78474 (diff) |
Give in and copy bjam into cdec source code
Diffstat (limited to 'jam-files/boost-build/build/virtual-target.jam')
-rw-r--r-- | jam-files/boost-build/build/virtual-target.jam | 1317 |
1 files changed, 1317 insertions, 0 deletions
diff --git a/jam-files/boost-build/build/virtual-target.jam b/jam-files/boost-build/build/virtual-target.jam new file mode 100644 index 00000000..2e8446bc --- /dev/null +++ b/jam-files/boost-build/build/virtual-target.jam @@ -0,0 +1,1317 @@ +# 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) ; + + # This works around a bug with -j and actions that + # produce multiple target, where: + # - dependency on the first output is found, and + # the action is started + # - dependency on the second output is found, and + # bjam noticed that command is already running + # - instead of waiting for the command, dependents + # of the second targets are immediately updated. + if $(actual-targets[2]) + { + INCLUDES $(actual-targets) : $(actual-targets) ; + } + + # 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) ; + } +} |