mirror of
https://github.com/boostorg/build.git
synced 2026-02-17 01:32:12 +00:00
they are located. The problem with using the directory name is that we might want toolset modules to act as project, and directory name is not unique then. We might even want to declare two projects in the same module. [SVN r18542]
986 lines
29 KiB
Plaintext
986 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 ptarget = [ project.target $(project) ] ;
|
|
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 ;
|
|
|