mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 13:22:11 +00:00
Consequently rolled back the workaround in make_rule.py * Added new indirect rule invocation module for encoding the module to invoke a rule from as well as the exact name to invoke it by, and for subsequent invocation. * Fixed module __test__ rules so they really do execute in a separate module. Associated tweaks to __test__ in path.jam [SVN r18427]
987 lines
29 KiB
Plaintext
987 lines
29 KiB
Plaintext
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
|
|
# distribute this software is granted provided this copyright notice appears in
|
|
# all copies. This software is provided "as is" without express or implied
|
|
# warranty, and with no claim as to its suitability for any purpose.
|
|
|
|
# Implements virtual targets, which correspond to actual files created during
|
|
# build, but are not yet targets in Jam sense. They are needed, for example,
|
|
# when searching for possible transormation sequences, when it's not known
|
|
# if particular target should be created at all.
|
|
|
|
import class : class new ;
|
|
import path property-set utility sequence errors ;
|
|
|
|
# +--------------------------+
|
|
# | 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 defined not here,
|
|
# but in builtin.jam modules. They are shown in the diagram to give
|
|
# the big picture.
|
|
|
|
# Potential target. It can be converted into jam target and used in
|
|
# building, if needed. However, it can be also dropped, which allows
|
|
# to search for different transformation and select only one.
|
|
#
|
|
rule virtual-target ( name # Name of this target -- specifies the name of
|
|
: project # Project to which this target belongs
|
|
)
|
|
{
|
|
import virtual-target utility scanner ;
|
|
|
|
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 instances of 'virtual-target' that this
|
|
# one depends on.
|
|
rule depends ( d + )
|
|
{
|
|
self.dependencies = [ sequence.merge $(self.dependencies)
|
|
: [ sequence.insertion-sort $(d) ] ] ;
|
|
}
|
|
|
|
rule dependencies ( )
|
|
{
|
|
return $(self.dependencies) ;
|
|
}
|
|
|
|
# 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 actual target, which will depend on the
|
|
# actual target and be associated with '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 actual target is returned.
|
|
rule actualize ( scanner ? )
|
|
{
|
|
local actual-name = [ actualize-no-scanner ] ;
|
|
|
|
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) ;
|
|
BINDRULE on $(name) = virtual-target.remember-binding ;
|
|
|
|
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" ;
|
|
}
|
|
|
|
# Returns the path to this target, if it corresponds to a file,
|
|
# and empty list otherwise.
|
|
rule path ( )
|
|
{
|
|
errors.error "method should be defined in derived classes" ;
|
|
}
|
|
|
|
# Return that actual target name that should be used
|
|
# (for the case where no scanner is involved)
|
|
rule actual-name ( )
|
|
{
|
|
errors.error "method should be defined in derived classes" ;
|
|
}
|
|
|
|
# implementation
|
|
rule actualize-no-scanner ( )
|
|
{
|
|
local name = [ actual-name ] ;
|
|
|
|
# Do anything only on the first invocation
|
|
if ! $(self.made.$(name)) {
|
|
self.made.$(name) = true ;
|
|
|
|
virtual-target.register-actual-name $(name) : $(__name__) ;
|
|
|
|
for local i in $(self.dependencies) {
|
|
DEPENDS $(name) : [ $(i).actualize ] ;
|
|
}
|
|
BINDRULE on $(name) = virtual-target.remember-binding ;
|
|
|
|
actualize-location $(name) ;
|
|
actualize-action $(name) ;
|
|
}
|
|
return $(name) ;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
class virtual-target ;
|
|
|
|
|
|
# Target which correspond 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
|
|
# derived file (otherwise).
|
|
#
|
|
# The target's grist is concatenation of project's location,
|
|
# properties of action (for derived files), and, optionally,
|
|
# value identifying the main target.
|
|
rule abstract-file-target ( name
|
|
: type ? # Optional type for this target
|
|
: project
|
|
)
|
|
{
|
|
virtual-target.__init__ $(name) : $(project) ;
|
|
import project regex sequence path type ;
|
|
|
|
self.type = $(type) ;
|
|
self.action = ;
|
|
|
|
rule type ( ) { return $(self.type) ; }
|
|
rule set-type ( type )
|
|
{
|
|
self.type = $(type) ;
|
|
}
|
|
|
|
# Sets the suffix. When generating target name, it will be used in preference to
|
|
# the suffix that is associated with 'type'
|
|
rule suffix ( suffix ? )
|
|
{
|
|
if $(suffix)
|
|
{
|
|
self.suffix = $(suffix) ;
|
|
}
|
|
return $(self.suffix) ;
|
|
}
|
|
|
|
# Sets the path. When generating target name, it will override any path
|
|
# computation from properties.
|
|
rule set-path ( path )
|
|
{
|
|
self.path = [ path.native $(path) ] ;
|
|
}
|
|
|
|
# If 'a' is supplied, sets action to 'a'.
|
|
# Returns the action currently set.
|
|
rule action ( a ? )
|
|
{
|
|
if $(a)
|
|
{
|
|
self.action = $(a) ;
|
|
}
|
|
return $(self.action) ;
|
|
}
|
|
|
|
# Sets/gets the 'root' flag. Target is root is it directly correspods to some
|
|
# variant of a main target.
|
|
rule root ( set ? )
|
|
{
|
|
if $(set)
|
|
{
|
|
self.root = true ;
|
|
}
|
|
return $(self.root) ;
|
|
}
|
|
|
|
# Gets 'use' requirements.
|
|
rule usage-requirements ( )
|
|
{
|
|
return $(self.usage-requirements) ;
|
|
}
|
|
|
|
# Sets 'use' requirements
|
|
rule set-usage-requirements ( requirements * )
|
|
{
|
|
self.usage-requirements = $(requirements) ;
|
|
}
|
|
|
|
# Sets the dependency graph this target is part of.
|
|
# 'dg' is an instance of 'subvariant-dg' class.
|
|
rule dg ( dg ? overwrite ? )
|
|
{
|
|
if $(dg) && ( ! $(self.dg) && ! $(overwrite) )
|
|
{
|
|
self.dg = $(dg) ;
|
|
}
|
|
return $(self.dg) ;
|
|
}
|
|
|
|
rule actualize-action ( target )
|
|
{
|
|
if $(self.action)
|
|
{
|
|
$(self.action).actualize ;
|
|
}
|
|
}
|
|
|
|
# Returns the name of main target this virtual target is specific
|
|
# to.
|
|
#
|
|
# Ordinarily, it's assumed that any grist on the actual Jam target
|
|
# and target path is determined by project, base properties,
|
|
# target name and type. Derived classes may return a non-empty
|
|
# string to indicate that the target is specific to the given main
|
|
# target.
|
|
rule specific-main-target ( )
|
|
{
|
|
return "" ;
|
|
}
|
|
|
|
# Return a human-readable representation of this target
|
|
#
|
|
# If this target has an action, that's:
|
|
#
|
|
# { <action-name>-<self.name>.<self.type> <action-sources>... }
|
|
#
|
|
# otherwise, it's:
|
|
#
|
|
# { <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 ss ;
|
|
for local s in $(sources)
|
|
{
|
|
ss += [ $(s).str ] ;
|
|
}
|
|
|
|
local action-name = [ $(action).action-name ] ;
|
|
|
|
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 project-location = [ project.attribute $(self.project) location ] ;
|
|
local location-grist =
|
|
[ sequence.join [ regex.split $(project-location) "/" ] : "!" ] ;
|
|
local grist ;
|
|
|
|
local properties ;
|
|
if $(self.action)
|
|
{
|
|
local ps = [ $(self.action).properties-ps ] ;
|
|
local property-grist = [ $(ps).as-path ] ;
|
|
grist = $(location-grist)/$(property-grist) ;
|
|
properties = [ $(ps).raw ] ;
|
|
}
|
|
if ! $(grist)
|
|
{
|
|
grist = $(location-grist) ;
|
|
}
|
|
# Adding this to grist looks ugly. It's still here for the
|
|
# same of 'symlink' rule -- got to consider what to do
|
|
# about it.
|
|
if $(self.path)
|
|
{
|
|
grist = $(grist)@$(self.path)@ ;
|
|
}
|
|
local smt = [ specific-main-target ] ;
|
|
if $(smt)
|
|
{
|
|
grist = $(grist)/main-target-$(smt) ;
|
|
}
|
|
|
|
local name = [ path.native $(self.name) ] ;
|
|
if $(self.suffix)
|
|
{
|
|
self.actual-name = [ sequence.join <$(grist)>$(name)
|
|
$(self.suffix) : "." ] ;
|
|
}
|
|
else if $(self.type)
|
|
{
|
|
self.actual-name = [ sequence.join <$(grist)>$(name)
|
|
[ type.generated-target-suffix $(self.type) :
|
|
$(properties)
|
|
] : "." ] ;
|
|
}
|
|
else
|
|
{
|
|
self.actual-name = <$(grist)>$(name) ;
|
|
}
|
|
}
|
|
return $(self.actual-name) ;
|
|
}
|
|
|
|
}
|
|
class abstract-file-target : virtual-target ;
|
|
|
|
# File target 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's properties. If the free properties
|
|
# are not equal to the project's reference properties
|
|
# an element with name of main target is added.
|
|
# - for source files, project's source dir
|
|
#
|
|
# The file suffix is
|
|
# - the value passed to the 'suffix' method, if any, or
|
|
# - the suffix which correspond to the target's type.
|
|
#
|
|
rule file-target (
|
|
name
|
|
: type ? # Optional type for this target
|
|
: project
|
|
)
|
|
{
|
|
abstract-file-target.__init__ $(name) : $(type) : $(project) ;
|
|
import common ;
|
|
|
|
rule actualize-location ( target )
|
|
{
|
|
if $(self.path)
|
|
{
|
|
LOCATE on $(target) = $(self.path) ;
|
|
# Make sure the path exists. Do this only
|
|
# for derived files.
|
|
if $(self.action)
|
|
{
|
|
DEPENDS $(target) : $(self.path) ;
|
|
common.MkDir $(self.path) ;
|
|
}
|
|
}
|
|
else 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) ;
|
|
}
|
|
else
|
|
{
|
|
# This is a source file.
|
|
SEARCH on $(target) =
|
|
[ path.native [ project.attribute $(self.project) source-location ] ] ;
|
|
}
|
|
}
|
|
|
|
# If free properties of this target's actions are equal to reference properties on
|
|
# project, returns empty string. Otherwise, returns the name of main target to
|
|
# which this target belong.
|
|
rule specific-main-target ( )
|
|
{
|
|
if $(self.action)
|
|
{
|
|
local ps = [ $(self.action).properties-ps ] ;
|
|
|
|
local main-target = [ $(self.dg).main-target ] ;
|
|
local project = [ $(main-target).project ] ;
|
|
local plocation = [ project.attribute $(project) location ] ;
|
|
local ptarget = [ project.target $(plocation) ] ;
|
|
local ref-ps = [ $(ptarget).reference-properties [ $(self.dg).properties-ps ] ] ;
|
|
|
|
if [ $(ps).free ] != [ $(ref-ps).free ]
|
|
|| [ $(ps).dependency ] != [ $(ref-ps).dependency ]
|
|
{
|
|
return [ $(main-target).name ] ;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Returns the directory for this target
|
|
rule path ( )
|
|
{
|
|
if ! $(self.path)
|
|
{
|
|
local build-dir = [ project.attribute $(self.project) build-dir ] ;
|
|
if ! $(build-dir)
|
|
{
|
|
build-dir = [ project.attribute $(self.project) location ] ;
|
|
}
|
|
|
|
local smt = [ specific-main-target ] ;
|
|
local path = [ path.join
|
|
$(build-dir)
|
|
bin
|
|
[ $(self.action).path ]
|
|
main-target-$(smt)
|
|
] ;
|
|
|
|
# Store the computed path, so that it's not recomputed
|
|
# any more
|
|
self.path = [ path.native $(path) ] ;
|
|
}
|
|
return $(self.path) ;
|
|
}
|
|
|
|
}
|
|
|
|
class file-target : abstract-file-target ;
|
|
|
|
|
|
|
|
# Returns the binding for the given actual target.
|
|
# CONSIDER: not sure this rule belongs here.
|
|
rule binding ( target )
|
|
{
|
|
return $(.binding.$(target)) ;
|
|
}
|
|
|
|
|
|
rule remember-binding ( target : bound-path )
|
|
{
|
|
.binding.$(target) = $(bound-path) ;
|
|
}
|
|
# virtual-target.remember-binding must be available in the global
|
|
# module in order to be invoked by the builtin binding process.
|
|
IMPORT virtual-target : remember-binding : : virtual-target.remember-binding ;
|
|
|
|
# Class which represents 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 dependency relationship, but should do everything else.
|
|
rule action ( targets + : sources * : action-name + : property-set ? )
|
|
{
|
|
import type toolset property-set indirect ;
|
|
|
|
self.targets = $(targets) ;
|
|
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 targets ( )
|
|
{
|
|
return $(self.targets) ;
|
|
}
|
|
|
|
rule sources ( )
|
|
{
|
|
return $(self.sources) ;
|
|
}
|
|
|
|
rule action-name ( )
|
|
{
|
|
return $(self.action-name) ;
|
|
}
|
|
|
|
rule properties-ps ( )
|
|
{
|
|
return $(self.properties) ;
|
|
}
|
|
|
|
# Generates actual build instructions.
|
|
rule actualize ( )
|
|
{
|
|
if ! $(self.actualized)
|
|
{
|
|
self.actualized = true ;
|
|
|
|
local ps = [ properties-ps ] ;
|
|
local properties = [ adjust-properties [ $(ps).raw ] ] ;
|
|
|
|
|
|
local actual-targets ;
|
|
for local i in [ targets ]
|
|
{
|
|
actual-targets += [ $(i).actualize ] ;
|
|
}
|
|
|
|
actualize-sources [ sources ] ;
|
|
|
|
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) ;
|
|
|
|
indirect.call $(self.action-name)
|
|
$(actual-targets) : $(self.actual-sources) : $(properties)
|
|
;
|
|
|
|
# Since we set up creating action here, we also set up
|
|
# action for cleaning up
|
|
common.Clean clean : $(actual-targets) ;
|
|
}
|
|
}
|
|
|
|
# Creates actual jam targets for sources. Initialized two member
|
|
# variables:.
|
|
# 'self.actual-sources' -- sources which are passed to updating action
|
|
# 'self.dependency-only-sources' -- sources which are made 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 * )
|
|
{
|
|
for local i in $(sources)
|
|
{
|
|
local scanner ;
|
|
if [ $(i).type ]
|
|
{
|
|
scanner =
|
|
[ type.get-scanner [ $(i).type ] : $(properties) ] ;
|
|
}
|
|
self.actual-sources += [ $(i).actualize $(scanner) ] ;
|
|
}
|
|
}
|
|
|
|
rule path ( )
|
|
{
|
|
local p = [ $(self.properties).as-path ] ;
|
|
return $(p) ;
|
|
}
|
|
|
|
# Determined real properties when trying building with 'properties'.
|
|
# This is last chance to fix properties, for example to adjust includes
|
|
# to get generated headers correctly. Default implementation returns
|
|
# its argument.
|
|
rule adjust-properties ( properties * )
|
|
{
|
|
return $(properties) ;
|
|
}
|
|
|
|
|
|
rule set-targets ( targets * )
|
|
{
|
|
self.targets = $(targets) ;
|
|
}
|
|
}
|
|
class action ;
|
|
|
|
# Action class which does nothing --- it produces the targets with
|
|
# specific properties out of nowhere. It's needed to distinguish virtual
|
|
# targets with different properties that are known to exist, and have no
|
|
# actions which create them.
|
|
rule null-action ( targets + : property-set ? )
|
|
{
|
|
action.__init__ $(targets) : : .no-action : $(property-set) ;
|
|
|
|
rule actualize ( )
|
|
{
|
|
if ! $(self.actualized)
|
|
{
|
|
self.actualized = true ;
|
|
|
|
for local i in [ targets ]
|
|
{
|
|
$(i).actualize ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class null-action : action ;
|
|
|
|
|
|
# Creates a virtual target with approariate name and type from 'file'.
|
|
# If a target with that name in that project was already created, returns that already
|
|
# created target.
|
|
# FIXME: more correct way would be to compute path to the file, based on name and source location
|
|
# for the project, and use that path to determine if the target was already created.
|
|
# TODO: passing project with all virtual targets starts to be annoying.
|
|
rule from-file ( file : project )
|
|
{
|
|
import type ; # had to do this here to break a circular dependency
|
|
|
|
if $(.files.$(file).$(project))
|
|
{
|
|
return $(.files.$(file).$(project)) ;
|
|
}
|
|
else
|
|
{
|
|
local name = [ path.make $(file:S=) ] ;
|
|
local type = [ type.type $(file:S) ] ;
|
|
local result ;
|
|
if ! $(type)
|
|
{
|
|
# warning "cannot determine type for file $(file)" ;
|
|
result = [ new file-target $(file) : : $(project) ] ;
|
|
}
|
|
else
|
|
{
|
|
local v = [ new file-target $(name) : $(type) : $(project) ] ;
|
|
$(v).suffix [ MATCH ^.(.*)$ : $(file:S) ] ;
|
|
result = $(v) ;
|
|
}
|
|
.files.$(file).$(project) = $(result) ;
|
|
return $(result) ;
|
|
}
|
|
}
|
|
|
|
# Registers a new virtual target. Checks if there's already registered target, with the same
|
|
# name, type, project and subvariant properties, and also with the same sources
|
|
# and equal action. If such target is found it is retured and 'target' is not registers.
|
|
# Otherwise, 'target' is registered and returned.
|
|
rule register ( target )
|
|
{
|
|
local signature = [ sequence.join
|
|
[ $(target).project ] [ $(target).name ] [ $(target).type ] : - ] ;
|
|
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-ps ] ;
|
|
local ps2 = [ $(a2).properties-ps ] ;
|
|
local p1 = [ $(ps1).base ] [ $(ps1).free ] [ $(ps1).dependency ] ;
|
|
local p2 = [ $(ps2).base ] [ $(ps2).free ] [ $(ps2).dependency ] ;
|
|
if $(p1) = $(p2)
|
|
{
|
|
result = $(t) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ! $(result)
|
|
{
|
|
.cache.$(signature) += $(target) ;
|
|
result = $(target) ;
|
|
}
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
rule register-actual-name ( actual-name : virtual-target )
|
|
{
|
|
if $(.actual.$(actual-name))
|
|
{
|
|
errors.error "Duplicate name of actual target:" $(actual-name)
|
|
: "previous virtual target" [ $(.actual.$(actual-name)).str ]
|
|
: "another virtual target" [ $(virtual-target).str ] ;
|
|
}
|
|
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 root of some dependency graph is
|
|
# found during traversal, it's either included or not, dependencing of the
|
|
# value of 'include-roots'. In either case, sources of root are not traversed.
|
|
rule traverse ( target : include-roots ? : include-sources ? )
|
|
{
|
|
local result ;
|
|
if [ $(target).action ]
|
|
{
|
|
local action = [ $(target).action ] ;
|
|
# This includes '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) ;
|
|
}
|
|
|
|
|
|
# Clones a dependency graph template, given one of its root,
|
|
# and a new source target to instantinate the template with.
|
|
#
|
|
# If 'target's name is "%" and type is equal to 'new-source's
|
|
# return 'new-source', otherwise created a new target:
|
|
# - if there "%" in target's name, its replaced with 'new-target's
|
|
# - project for new target is the same as for 'new-target'
|
|
# - other attributes are copied
|
|
#
|
|
# If 'dont-recurse' is not set, clones action, which results in
|
|
# cloning of other targets, and, ultimately, cloning of the
|
|
# entire dependency graph.
|
|
rule clone-template ( target dont-recurse ? : new-source )
|
|
{
|
|
local name = [ $(new-source).name ] ;
|
|
local old-name = [ $(target).name ] ;
|
|
local new-name = $(old-name) ;
|
|
local m = [ MATCH (.*)(%)(.*) : $(old-name) ] ;
|
|
if $(m)
|
|
{
|
|
if [ $(target).action ]
|
|
{
|
|
new-name = [ sequence.join $(m[1]) $(name:D=) $(m[3]) ] ;
|
|
}
|
|
else
|
|
{
|
|
new-name = [ sequence.join $(m[1]) $(name) $(m[3]) ] ;
|
|
}
|
|
}
|
|
|
|
if $(old-name) = % && [ $(target).type ] = [ $(new-source).type ]
|
|
{
|
|
return $(new-source) ;
|
|
}
|
|
else
|
|
{
|
|
local cloned = [ new file-target $(new-name) : [ $(target).type ] :
|
|
[ $(new-source).project ] ] ;
|
|
|
|
if ! $(dont-recurse) && [ $(target).action ]
|
|
{
|
|
local cloned-action = [ clone-action-template
|
|
[ $(target).action ] $(target) $(cloned) : $(new-source) ] ;
|
|
|
|
cloned-targets = $(cloned) ;
|
|
for t in [ $(cloned-action).targets ]
|
|
{
|
|
if $(t) != $(target)
|
|
{
|
|
cloned-targets +=
|
|
[ clone-template $(t) dont-recurse : $(new-source) ] ;
|
|
}
|
|
}
|
|
local cloned-targets2 ;
|
|
for local t in $(cloned-targets)
|
|
{
|
|
$(t).action $(cloned-action) ;
|
|
|
|
cloned-targets2 += [ register $(t) ] ;
|
|
|
|
}
|
|
$(cloned-action).set-targets $(cloned-targets2) ;
|
|
cloned = $(cloned-targets2[1]) ;
|
|
}
|
|
else
|
|
{
|
|
cloned = [ register $(cloned) ] ;
|
|
}
|
|
return $(cloned) ;
|
|
}
|
|
}
|
|
|
|
# Clones an action template: helper for clone-template above.
|
|
local rule clone-action-template ( action from cloned-from : new-source )
|
|
{
|
|
local targets ;
|
|
local sources ;
|
|
|
|
for local t in [ $(action).sources ]
|
|
{
|
|
sources += [ clone-template $(t) : $(new-source) ] ;
|
|
}
|
|
|
|
local action-class = [ modules.peek $(action) : __class__ ] ;
|
|
|
|
local ps = [ $(action).properties-ps ] ;
|
|
local cloned = [ new $(action-class) [ $(action).targets ] : $(sources)
|
|
: [ $(action).action-name ] : $(ps) ] ;
|
|
|
|
return $(cloned) ;
|
|
}
|
|
|
|
local rule subvariant-dg ( main-target # The instance of main-target class
|
|
: property-set # Properties requested for this target
|
|
: actual-properties # Actual used properties
|
|
: virtual-targets * )
|
|
{
|
|
import sequence ;
|
|
|
|
self.main-target = $(main-target) ;
|
|
self.properties = $(property-set) ;
|
|
self.actual-properties = $(actual-properties) ;
|
|
self.virtual-targets = $(virtual-targets) ;
|
|
|
|
# Pre-compose the list of other dependency graphs, on which this one
|
|
# depends
|
|
local deps = [ $(actual-properties).dependency ] ;
|
|
for local d in $(deps)
|
|
{
|
|
self.other-dg += [ $(d:G=).dg ] ;
|
|
}
|
|
|
|
for local t in $(virtual-targets)
|
|
{
|
|
local a = [ $(t).action ] ;
|
|
if $(a)
|
|
{
|
|
for local s in [ $(a).sources ]
|
|
{
|
|
if [ $(s).root ]
|
|
{
|
|
self.other-dg += [ $(s).dg ] ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.other-dg = [ sequence.unique $(self.other-dg) ] ;
|
|
|
|
rule main-target ( )
|
|
{
|
|
return $(self.main-target) ;
|
|
}
|
|
|
|
rule properties-ps ( )
|
|
{
|
|
return $(self.properties) ;
|
|
}
|
|
|
|
rule all-target-directories ( )
|
|
{
|
|
if ! $(self.target-directories)
|
|
{
|
|
compute-target-directories ;
|
|
}
|
|
return $(self.target-directories) ;
|
|
}
|
|
|
|
rule compute-target-directories ( )
|
|
{
|
|
local result ;
|
|
for local t in $(self.virtual-targets)
|
|
{
|
|
result = [ sequence.merge $(result) : [ $(t).path ] ] ;
|
|
}
|
|
for local d in $(self.other-dg)
|
|
{
|
|
result += [ $(d).all-target-directories ] ;
|
|
}
|
|
self.target-directories = $(result) ;
|
|
}
|
|
}
|
|
|
|
class subvariant-dg ;
|
|
|