mirror of
https://github.com/moses-smt/mosesdecoder.git
synced 2024-09-20 07:42:21 +03:00
1381 lines
44 KiB
Plaintext
1381 lines
44 KiB
Plaintext
# Copyright Vladimir Prus 2002.
|
|
# Copyright Rene Rivera 2006.
|
|
#
|
|
# Distributed under the Boost Software License, Version 1.0.
|
|
# (See accompanying file LICENSE_1_0.txt or copy at
|
|
# http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
# Manages 'generators' --- objects which can do transformation between different
|
|
# target types and contain algorithm for finding transformation from sources to
|
|
# targets.
|
|
#
|
|
# The main entry point to this module is generators.construct rule. It is given
|
|
# a list of source targets, desired target type and a set of properties. It
|
|
# starts by selecting 'viable generators', which have any chances of producing
|
|
# the desired target type with the required properties. Generators are ranked
|
|
# and a set of the most specific ones is selected.
|
|
#
|
|
# The most specific generators have their 'run' methods called, with the
|
|
# properties and list of sources. Each one selects a target which can be
|
|
# directly consumed, and tries to convert the remaining ones to the types it can
|
|
# consume. This is done by recursively calling 'construct' with all consumable
|
|
# types.
|
|
#
|
|
# If the generator has collected all the targets it needs, it creates targets
|
|
# corresponding to result, and returns it. When all generators have been run,
|
|
# results of one of them are selected and returned as a result.
|
|
#
|
|
# It is quite possible for 'construct' to return more targets that it was asked
|
|
# for. For example, if it were asked to generate a target of type EXE, but the
|
|
# only found generator produces both EXE and TDS (file with debug) information.
|
|
# The extra target will be returned.
|
|
#
|
|
# Likewise, when generator tries to convert sources to consumable types, it can
|
|
# get more targets that it was asked for. The question is what to do with extra
|
|
# targets. Boost.Build attempts to convert them to requested types, and attempts
|
|
# that as early as possible. Specifically, this is done after invoking each
|
|
# generator. TODO: An example is needed to document the rationale for trying
|
|
# extra target conversion at that point.
|
|
#
|
|
# In order for the system to be able to use a specific generator instance 'when
|
|
# needed', the instance needs to be registered with the system using
|
|
# generators.register() or one of its related rules. Unregistered generators may
|
|
# only be run explicitly and will not be considered by Boost.Build when when
|
|
# converting between given target types.
|
|
|
|
import "class" : new ;
|
|
import errors ;
|
|
import property-set ;
|
|
import sequence ;
|
|
import set ;
|
|
import type ;
|
|
import utility ;
|
|
import virtual-target ;
|
|
|
|
|
|
if "--debug-generators" in [ modules.peek : ARGV ]
|
|
{
|
|
.debug = true ;
|
|
}
|
|
|
|
|
|
# Updated cached viable source target type information as needed after a new
|
|
# target type gets defined. This is needed because if a target type is a viable
|
|
# source target type for some generator then all of the target type's derived
|
|
# target types should automatically be considered as viable source target types
|
|
# for the same generator as well. Does nothing if a non-derived target type is
|
|
# passed to it.
|
|
#
|
|
rule update-cached-information-with-a-new-type ( type )
|
|
{
|
|
local base-type = [ type.base $(type) ] ;
|
|
if $(base-type)
|
|
{
|
|
for local g in $(.vstg-cached-generators)
|
|
{
|
|
if $(base-type) in $(.vstg.$(g))
|
|
{
|
|
.vstg.$(g) += $(type) ;
|
|
}
|
|
}
|
|
|
|
for local t in $(.vst-cached-types)
|
|
{
|
|
if $(base-type) in $(.vst.$(t))
|
|
{
|
|
.vst.$(t) += $(type) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Clears cached viable source target type information except for target types
|
|
# and generators with all source types listed as viable. Should be called when
|
|
# something invalidates those cached values by possibly causing some new source
|
|
# types to become viable.
|
|
#
|
|
local rule invalidate-extendable-viable-source-target-type-cache ( )
|
|
{
|
|
local generators-with-cached-source-types = $(.vstg-cached-generators) ;
|
|
.vstg-cached-generators = ;
|
|
for local g in $(generators-with-cached-source-types)
|
|
{
|
|
if $(.vstg.$(g)) = *
|
|
{
|
|
.vstg-cached-generators += $(g) ;
|
|
}
|
|
else
|
|
{
|
|
.vstg.$(g) = ;
|
|
}
|
|
}
|
|
|
|
local types-with-cached-source-types = $(.vst-cached-types) ;
|
|
.vst-cached-types = ;
|
|
for local t in $(types-with-cached-source-types)
|
|
{
|
|
if $(.vst.$(t)) = *
|
|
{
|
|
.vst-cached-types += $(t) ;
|
|
}
|
|
else
|
|
{
|
|
.vst.$(t) = ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Outputs a debug message if generators debugging is on. Each element of
|
|
# 'message' is checked to see if it is a class instance. If so, instead of the
|
|
# value, the result of 'str' call is output.
|
|
#
|
|
local rule generators.dout ( message * )
|
|
{
|
|
if $(.debug)
|
|
{
|
|
ECHO [ sequence.transform utility.str : $(message) ] ;
|
|
}
|
|
}
|
|
|
|
|
|
local rule indent ( )
|
|
{
|
|
return $(.indent:J="") ;
|
|
}
|
|
|
|
|
|
local rule increase-indent ( )
|
|
{
|
|
.indent += " " ;
|
|
}
|
|
|
|
|
|
local rule decrease-indent ( )
|
|
{
|
|
.indent = $(.indent[2-]) ;
|
|
}
|
|
|
|
|
|
# Models a generator.
|
|
#
|
|
class generator
|
|
{
|
|
import generators : indent increase-indent decrease-indent generators.dout ;
|
|
import set ;
|
|
import utility ;
|
|
import feature ;
|
|
import errors ;
|
|
import sequence ;
|
|
import type ;
|
|
import virtual-target ;
|
|
import "class" : new ;
|
|
import property ;
|
|
import path ;
|
|
|
|
EXPORT class@generator : indent increase-indent decrease-indent
|
|
generators.dout ;
|
|
|
|
rule __init__ (
|
|
id # Identifies the generator - should be name
|
|
# of the rule which sets up the build
|
|
# actions.
|
|
|
|
composing ? # Whether generator processes each source
|
|
# target in turn, converting it to required
|
|
# types. Ordinary generators pass all
|
|
# sources together to the recursive
|
|
# generators.construct-types call.
|
|
|
|
: source-types * # Types that this generator can handle. If
|
|
# empty, the generator can consume anything.
|
|
|
|
: target-types-and-names + # Types the generator will create and,
|
|
# optionally, names for created targets.
|
|
# Each element should have the form
|
|
# type["(" name-pattern ")"], for example,
|
|
# obj(%_x). Generated target name will be
|
|
# found by replacing % with the name of
|
|
# source, provided an explicit name was not
|
|
# specified.
|
|
|
|
: requirements *
|
|
)
|
|
{
|
|
self.id = $(id) ;
|
|
self.rule-name = $(id) ;
|
|
self.composing = $(composing) ;
|
|
self.source-types = $(source-types) ;
|
|
self.target-types-and-names = $(target-types-and-names) ;
|
|
self.requirements = $(requirements) ;
|
|
|
|
for local e in $(target-types-and-names)
|
|
{
|
|
# Create three parallel lists: one with the list of target types,
|
|
# and two other with prefixes and postfixes to be added to target
|
|
# name. We use parallel lists for prefix and postfix (as opposed to
|
|
# mapping), because given target type might occur several times, for
|
|
# example "H H(%_symbols)".
|
|
local m = [ MATCH ([^\\(]*)(\\((.*)%(.*)\\))? : $(e) ] ;
|
|
self.target-types += $(m[1]) ;
|
|
self.name-prefix += $(m[3]:E="") ;
|
|
self.name-postfix += $(m[4]:E="") ;
|
|
}
|
|
|
|
# Note that 'transform' here, is the same as 'for_each'.
|
|
sequence.transform type.validate : $(self.source-types) ;
|
|
sequence.transform type.validate : $(self.target-types) ;
|
|
}
|
|
|
|
################# End of constructor #################
|
|
|
|
rule id ( )
|
|
{
|
|
return $(self.id) ;
|
|
}
|
|
|
|
# Returns the list of target type the generator accepts.
|
|
#
|
|
rule source-types ( )
|
|
{
|
|
return $(self.source-types) ;
|
|
}
|
|
|
|
# Returns the list of target types that this generator produces. It is
|
|
# assumed to be always the same -- i.e. it can not change depending on some
|
|
# provided list of sources.
|
|
#
|
|
rule target-types ( )
|
|
{
|
|
return $(self.target-types) ;
|
|
}
|
|
|
|
# Returns the required properties for this generator. Properties in returned
|
|
# set must be present in build properties if this generator is to be used.
|
|
# If result has grist-only element, that build properties must include some
|
|
# value of that feature.
|
|
#
|
|
# XXX: remove this method?
|
|
#
|
|
rule requirements ( )
|
|
{
|
|
return $(self.requirements) ;
|
|
}
|
|
|
|
rule set-rule-name ( rule-name )
|
|
{
|
|
self.rule-name = $(rule-name) ;
|
|
}
|
|
|
|
rule rule-name ( )
|
|
{
|
|
return $(self.rule-name) ;
|
|
}
|
|
|
|
# Returns a true value if the generator can be run with the specified
|
|
# properties.
|
|
#
|
|
rule match-rank ( property-set-to-match )
|
|
{
|
|
# See if generator requirements are satisfied by 'properties'. Treat a
|
|
# feature name in requirements (i.e. grist-only element), as matching
|
|
# any value of the feature.
|
|
local all-requirements = [ requirements ] ;
|
|
|
|
local property-requirements feature-requirements ;
|
|
for local r in $(all-requirements)
|
|
{
|
|
if $(r:G=)
|
|
{
|
|
property-requirements += $(r) ;
|
|
}
|
|
else
|
|
{
|
|
feature-requirements += $(r) ;
|
|
}
|
|
}
|
|
|
|
local properties-to-match = [ $(property-set-to-match).raw ] ;
|
|
if $(property-requirements) in $(properties-to-match) &&
|
|
$(feature-requirements) in $(properties-to-match:G)
|
|
{
|
|
return true ;
|
|
}
|
|
else
|
|
{
|
|
return ;
|
|
}
|
|
}
|
|
|
|
# Returns another generator which differs from $(self) in
|
|
# - id
|
|
# - value to <toolset> feature in properties
|
|
#
|
|
rule clone ( new-id : new-toolset-properties + )
|
|
{
|
|
local g = [ new $(__class__) $(new-id) $(self.composing) :
|
|
$(self.source-types) : $(self.target-types-and-names) :
|
|
# Note: this does not remove any subfeatures of <toolset> which
|
|
# might cause problems.
|
|
[ property.change $(self.requirements) : <toolset> ]
|
|
$(new-toolset-properties) ] ;
|
|
return $(g) ;
|
|
}
|
|
|
|
# Creates another generator that is the same as $(self), except that if
|
|
# 'base' is in target types of $(self), 'type' will in target types of the
|
|
# new generator.
|
|
#
|
|
rule clone-and-change-target-type ( base : type )
|
|
{
|
|
local target-types ;
|
|
for local t in $(self.target-types-and-names)
|
|
{
|
|
local m = [ MATCH ([^\\(]*)(\\(.*\\))? : $(t) ] ;
|
|
if $(m) = $(base)
|
|
{
|
|
target-types += $(type)$(m[2]:E="") ;
|
|
}
|
|
else
|
|
{
|
|
target-types += $(t) ;
|
|
}
|
|
}
|
|
|
|
local g = [ new $(__class__) $(self.id) $(self.composing) :
|
|
$(self.source-types) : $(target-types) : $(self.requirements) ] ;
|
|
if $(self.rule-name)
|
|
{
|
|
$(g).set-rule-name $(self.rule-name) ;
|
|
}
|
|
return $(g) ;
|
|
}
|
|
|
|
# Tries to invoke this generator on the given sources. Returns a list of
|
|
# generated targets (instances of 'virtual-target') and optionally a set of
|
|
# properties to be added to the usage-requirements for all the generated
|
|
# targets. Returning nothing from run indicates that the generator was
|
|
# unable to create the target.
|
|
#
|
|
rule run
|
|
(
|
|
project # Project for which the targets are generated.
|
|
name ? # Used when determining the 'name' attribute for all
|
|
# generated targets. See the 'generated-targets' method.
|
|
: property-set # Desired properties for generated targets.
|
|
: sources + # Source targets.
|
|
)
|
|
{
|
|
generators.dout [ indent ] " ** generator" $(self.id) ;
|
|
generators.dout [ indent ] " composing:" $(self.composing) ;
|
|
|
|
if ! $(self.composing) && $(sources[2]) && $(self.source-types[2])
|
|
{
|
|
errors.error "Unsupported source/source-type combination" ;
|
|
}
|
|
|
|
# We do not run composing generators if no name is specified. The reason
|
|
# is that composing generator combines several targets, which can have
|
|
# different names, and it cannot decide which name to give for produced
|
|
# target. Therefore, the name must be passed.
|
|
#
|
|
# This in effect, means that composing generators are runnable only at
|
|
# the top-level of a transformation graph, or if their name is passed
|
|
# explicitly. Thus, we dissallow composing generators in the middle. For
|
|
# example, the transformation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE
|
|
# will not be allowed as the OBJ -> STATIC_LIB generator is composing.
|
|
if ! $(self.composing) || $(name)
|
|
{
|
|
run-really $(project) $(name) : $(property-set) : $(sources) ;
|
|
}
|
|
}
|
|
|
|
rule run-really ( project name ? : property-set : sources + )
|
|
{
|
|
# Targets that this generator will consume directly.
|
|
local consumed = ;
|
|
# Targets that can not be consumed and will be returned as-is.
|
|
local bypassed = ;
|
|
|
|
if $(self.composing)
|
|
{
|
|
consumed = [ convert-multiple-sources-to-consumable-types $(project)
|
|
: $(property-set) : $(sources) ] ;
|
|
}
|
|
else
|
|
{
|
|
consumed = [ convert-to-consumable-types $(project) $(name)
|
|
: $(property-set) : $(sources) ] ;
|
|
}
|
|
|
|
local result ;
|
|
if $(consumed)
|
|
{
|
|
result = [ construct-result $(consumed) : $(project) $(name) :
|
|
$(property-set) ] ;
|
|
}
|
|
|
|
if $(result)
|
|
{
|
|
generators.dout [ indent ] " SUCCESS: " $(result) ;
|
|
}
|
|
else
|
|
{
|
|
generators.dout [ indent ] " FAILURE" ;
|
|
}
|
|
generators.dout ;
|
|
return $(result) ;
|
|
}
|
|
|
|
# Constructs the dependency graph to be returned by this generator.
|
|
#
|
|
rule construct-result
|
|
(
|
|
consumed + # Already prepared list of consumable targets.
|
|
# Composing generators may receive multiple sources
|
|
# all of which will have types matching those in
|
|
# $(self.source-types). Non-composing generators with
|
|
# multiple $(self.source-types) will receive exactly
|
|
# len $(self.source-types) sources with types matching
|
|
# those in $(self.source-types). And non-composing
|
|
# generators with only a single source type may
|
|
# receive multiple sources with all of them of the
|
|
# type listed in $(self.source-types).
|
|
: project name ?
|
|
: property-set # Properties to be used for all actions created here.
|
|
)
|
|
{
|
|
local result ;
|
|
# If this is 1->1 transformation, apply it to all consumed targets in
|
|
# order.
|
|
if ! $(self.source-types[2]) && ! $(self.composing)
|
|
{
|
|
for local r in $(consumed)
|
|
{
|
|
result += [ generated-targets $(r) : $(property-set) :
|
|
$(project) $(name) ] ;
|
|
}
|
|
}
|
|
else if $(consumed)
|
|
{
|
|
result += [ generated-targets $(consumed) : $(property-set) :
|
|
$(project) $(name) ] ;
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Determine target name from fullname (maybe including path components)
|
|
# Place optional prefix and postfix around basename
|
|
#
|
|
rule determine-target-name ( fullname : prefix ? : postfix ? )
|
|
{
|
|
# See if we need to add directory to the target name.
|
|
local dir = $(fullname:D) ;
|
|
local name = $(fullname:B) ;
|
|
|
|
name = $(prefix:E=)$(name) ;
|
|
name = $(name)$(postfix:E=) ;
|
|
|
|
if $(dir) &&
|
|
# Never append '..' to target path.
|
|
! [ MATCH .*(\\.\\.).* : $(dir) ]
|
|
&&
|
|
! [ path.is-rooted $(dir) ]
|
|
{
|
|
# Relative path is always relative to the source
|
|
# directory. Retain it, so that users can have files
|
|
# with the same in two different subdirectories.
|
|
name = $(dir)/$(name) ;
|
|
}
|
|
return $(name) ;
|
|
}
|
|
|
|
# Determine the name of the produced target from the names of the sources.
|
|
#
|
|
rule determine-output-name ( sources + )
|
|
{
|
|
# The simple case if when a name of source has single dot. Then, we take
|
|
# the part before dot. Several dots can be caused by:
|
|
# - using source file like a.host.cpp, or
|
|
# - a type whose suffix has a dot. Say, we can type 'host_cpp' with
|
|
# extension 'host.cpp'.
|
|
# In the first case, we want to take the part up to the last dot. In the
|
|
# second case -- not sure, but for now take the part up to the last dot
|
|
# too.
|
|
name = [ utility.basename [ $(sources[1]).name ] ] ;
|
|
|
|
for local s in $(sources[2])
|
|
{
|
|
local n2 = [ utility.basename [ $(s).name ] ] ;
|
|
if $(n2) != $(name)
|
|
{
|
|
errors.error "$(self.id): source targets have different names: cannot determine target name" ;
|
|
}
|
|
}
|
|
name = [ determine-target-name [ $(sources[1]).name ] ] ;
|
|
return $(name) ;
|
|
}
|
|
|
|
# Constructs targets that are created after consuming 'sources'. The result
|
|
# will be the list of virtual-target, which has the same length as the
|
|
# 'target-types' attribute and with corresponding types.
|
|
#
|
|
# When 'name' is empty, all source targets must have the same 'name'
|
|
# attribute value, which will be used instead of the 'name' argument.
|
|
#
|
|
# The 'name' attribute value for each generated target will be equal to
|
|
# the 'name' parameter if there is no name pattern for this type. Otherwise,
|
|
# the '%' symbol in the name pattern will be replaced with the 'name'
|
|
# parameter to obtain the 'name' attribute.
|
|
#
|
|
# For example, if targets types are T1 and T2 (with name pattern "%_x"),
|
|
# suffixes for T1 and T2 are .t1 and .t2, and source is foo.z, then created
|
|
# files would be "foo.t1" and "foo_x.t2". The 'name' attribute actually
|
|
# determines the basename of a file.
|
|
#
|
|
# Note that this pattern mechanism has nothing to do with implicit patterns
|
|
# in make. It is a way to produce a target whose name is different than the
|
|
# name of its source.
|
|
#
|
|
rule generated-targets ( sources + : property-set : project name ? )
|
|
{
|
|
if ! $(name)
|
|
{
|
|
name = [ determine-output-name $(sources) ] ;
|
|
}
|
|
|
|
# Assign an action for each target.
|
|
local action = [ action-class ] ;
|
|
local a = [ class.new $(action) $(sources) : $(self.rule-name) :
|
|
$(property-set) ] ;
|
|
|
|
# Create generated target for each target type.
|
|
local targets ;
|
|
local pre = $(self.name-prefix) ;
|
|
local post = $(self.name-postfix) ;
|
|
for local t in $(self.target-types)
|
|
{
|
|
local generated-name = $(pre[1])$(name:BS)$(post[1]) ;
|
|
generated-name = $(generated-name:R=$(name:D)) ;
|
|
pre = $(pre[2-]) ;
|
|
post = $(post[2-]) ;
|
|
|
|
targets += [ class.new file-target $(generated-name) : $(t) :
|
|
$(project) : $(a) ] ;
|
|
}
|
|
|
|
return [ sequence.transform virtual-target.register : $(targets) ] ;
|
|
}
|
|
|
|
# Attempts to convert 'sources' to targets of types that this generator can
|
|
# handle. The intention is to produce the set of targets that can be used
|
|
# when the generator is run.
|
|
#
|
|
rule convert-to-consumable-types
|
|
(
|
|
project name ?
|
|
: property-set
|
|
: sources +
|
|
: only-one ? # Convert 'source' to only one of the source types. If
|
|
# there is more that one possibility, report an error.
|
|
)
|
|
{
|
|
local _consumed ;
|
|
local missing-types ;
|
|
|
|
if $(sources[2])
|
|
{
|
|
# Do not know how to handle several sources yet. Just try to pass
|
|
# the request to other generator.
|
|
missing-types = $(self.source-types) ;
|
|
}
|
|
else
|
|
{
|
|
local temp = [ consume-directly $(sources) ] ;
|
|
if $(temp[1])
|
|
{
|
|
_consumed = $(temp[1]) ;
|
|
}
|
|
missing-types = $(temp[2-]) ;
|
|
}
|
|
|
|
# No need to search for transformation if some source type has consumed
|
|
# source and no more source types are needed.
|
|
if $(only-one) && $(_consumed)
|
|
{
|
|
missing-types = ;
|
|
}
|
|
|
|
# TODO: we should check that only one source type if create of
|
|
# 'only-one' is true.
|
|
|
|
if $(missing-types)
|
|
{
|
|
local transformed = [ generators.construct-types $(project) $(name)
|
|
: $(missing-types) : $(property-set) : $(sources) ] ;
|
|
|
|
# Add targets of right type to 'consumed'. Add others to 'bypassed'.
|
|
# The 'generators.construct' rule has done its best to convert
|
|
# everything to the required type. There is no need to rerun it on
|
|
# targets of different types.
|
|
|
|
# NOTE: ignoring usage requirements.
|
|
for local t in $(transformed[2-])
|
|
{
|
|
if [ $(t).type ] in $(missing-types)
|
|
{
|
|
_consumed += $(t) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return [ sequence.unique $(_consumed) ] ;
|
|
}
|
|
|
|
# Converts several files to consumable types. Called for composing
|
|
# generators only.
|
|
#
|
|
rule convert-multiple-sources-to-consumable-types ( project : property-set :
|
|
sources * )
|
|
{
|
|
local result ;
|
|
# We process each source one-by-one, trying to convert it to a usable
|
|
# type.
|
|
for local source in $(sources)
|
|
{
|
|
local _c = [ convert-to-consumable-types $(project) : $(property-set)
|
|
: $(source) : true ] ;
|
|
if ! $(_c)
|
|
{
|
|
generators.dout [ indent ] " failed to convert " $(source) ;
|
|
}
|
|
result += $(_c) ;
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
rule consume-directly ( source )
|
|
{
|
|
local real-source-type = [ $(source).type ] ;
|
|
|
|
# If there are no source types, we can consume anything.
|
|
local source-types = $(self.source-types) ;
|
|
source-types ?= $(real-source-type) ;
|
|
|
|
local result = "" ;
|
|
local missing-types ;
|
|
|
|
for local st in $(source-types)
|
|
{
|
|
# The 'source' if of the right type already.
|
|
if $(real-source-type) = $(st) || [ type.is-derived
|
|
$(real-source-type) $(st) ]
|
|
{
|
|
result = $(source) ;
|
|
}
|
|
else
|
|
{
|
|
missing-types += $(st) ;
|
|
}
|
|
}
|
|
return $(result) $(missing-types) ;
|
|
}
|
|
|
|
# Returns the class to be used to actions. Default implementation returns
|
|
# "action".
|
|
#
|
|
rule action-class ( )
|
|
{
|
|
return "action" ;
|
|
}
|
|
}
|
|
|
|
|
|
# Registers a new generator instance 'g'.
|
|
#
|
|
rule register ( g )
|
|
{
|
|
.all-generators += $(g) ;
|
|
|
|
# A generator can produce several targets of the same type. We want unique
|
|
# occurrence of that generator in .generators.$(t) in that case, otherwise,
|
|
# it will be tried twice and we will get a false ambiguity.
|
|
for local t in [ sequence.unique [ $(g).target-types ] ]
|
|
{
|
|
.generators.$(t) += $(g) ;
|
|
}
|
|
|
|
# Update the set of generators for toolset.
|
|
|
|
# TODO: should we check that generator with this id is not already
|
|
# registered. For example, the fop.jam module intentionally declared two
|
|
# generators with the same id, so such check will break it.
|
|
local id = [ $(g).id ] ;
|
|
|
|
# Some generators have multiple periods in their name, so a simple $(id:S=)
|
|
# will not generate the right toolset name. E.g. if id = gcc.compile.c++,
|
|
# then .generators-for-toolset.$(id:S=) will append to
|
|
# .generators-for-toolset.gcc.compile, which is a separate value from
|
|
# .generators-for-toolset.gcc. Correcting this makes generator inheritance
|
|
# work properly. See also inherit-generators in the toolset module.
|
|
local base = $(id) ;
|
|
while $(base:S)
|
|
{
|
|
base = $(base:B) ;
|
|
}
|
|
.generators-for-toolset.$(base) += $(g) ;
|
|
|
|
|
|
# After adding a new generator that can construct new target types, we need
|
|
# to clear the related cached viable source target type information for
|
|
# constructing a specific target type or using a specific generator. Cached
|
|
# viable source target type lists affected by this are those containing any
|
|
# of the target types constructed by the new generator or any of their base
|
|
# target types.
|
|
#
|
|
# A more advanced alternative to clearing that cached viable source target
|
|
# type information would be to expand it with additional source types or
|
|
# even better - mark it as needing to be expanded on next use.
|
|
#
|
|
# Also see the http://thread.gmane.org/gmane.comp.lib.boost.build/19077
|
|
# mailing list thread for an even more advanced idea of how we could convert
|
|
# Boost Build's Jamfile processing, target selection and generator selection
|
|
# into separate steps which would prevent these caches from ever being
|
|
# invalidated.
|
|
#
|
|
# For now we just clear all the cached viable source target type information
|
|
# that does not simply state 'all types' and may implement a more detailed
|
|
# algorithm later on if it becomes needed.
|
|
|
|
invalidate-extendable-viable-source-target-type-cache ;
|
|
}
|
|
|
|
|
|
# Creates a new non-composing 'generator' class instance and registers it.
|
|
# Returns the created instance. Rationale: the instance is returned so that it
|
|
# is possible to first register a generator and then call its 'run' method,
|
|
# bypassing the whole generator selection process.
|
|
#
|
|
rule register-standard ( id : source-types * : target-types + : requirements * )
|
|
{
|
|
local g = [ new generator $(id) : $(source-types) : $(target-types) :
|
|
$(requirements) ] ;
|
|
register $(g) ;
|
|
return $(g) ;
|
|
}
|
|
|
|
|
|
# Creates a new composing 'generator' class instance and registers it.
|
|
#
|
|
rule register-composing ( id : source-types * : target-types + : requirements *
|
|
)
|
|
{
|
|
local g = [ new generator $(id) true : $(source-types) : $(target-types) :
|
|
$(requirements) ] ;
|
|
register $(g) ;
|
|
return $(g) ;
|
|
}
|
|
|
|
|
|
# Returns all generators belonging to the given 'toolset', i.e. whose ids are
|
|
# '$(toolset).<something>'.
|
|
#
|
|
rule generators-for-toolset ( toolset )
|
|
{
|
|
return $(.generators-for-toolset.$(toolset)) ;
|
|
}
|
|
|
|
|
|
# Make generator 'overrider-id' be preferred to 'overridee-id'. If, when
|
|
# searching for generators that could produce a target of a certain type, both
|
|
# those generators are among viable generators, the overridden generator is
|
|
# immediately discarded.
|
|
#
|
|
# The overridden generators are discarded immediately after computing the list
|
|
# of viable generators but before running any of them.
|
|
#
|
|
rule override ( overrider-id : overridee-id )
|
|
{
|
|
.override.$(overrider-id) += $(overridee-id) ;
|
|
}
|
|
|
|
|
|
# Returns a list of source type which can possibly be converted to 'target-type'
|
|
# by some chain of generator invocation.
|
|
#
|
|
# More formally, takes all generators for 'target-type' and returns a union of
|
|
# source types for those generators and result of calling itself recursively on
|
|
# source types.
|
|
#
|
|
# Returns '*' in case any type should be considered a viable source type for the
|
|
# given type.
|
|
#
|
|
local rule viable-source-types-real ( target-type )
|
|
{
|
|
local result ;
|
|
|
|
# 't0' is the initial list of target types we need to process to get a list
|
|
# of their viable source target types. New target types will not be added to
|
|
# this list.
|
|
local t0 = [ type.all-bases $(target-type) ] ;
|
|
|
|
# 't' is the list of target types which have not yet been processed to get a
|
|
# list of their viable source target types. This list will get expanded as
|
|
# we locate more target types to process.
|
|
local t = $(t0) ;
|
|
|
|
while $(t)
|
|
{
|
|
# Find all generators for the current type. Unlike
|
|
# 'find-viable-generators' we do not care about the property-set.
|
|
local generators = $(.generators.$(t[1])) ;
|
|
t = $(t[2-]) ;
|
|
|
|
while $(generators)
|
|
{
|
|
local g = $(generators[1]) ;
|
|
generators = $(generators[2-]) ;
|
|
|
|
if ! [ $(g).source-types ]
|
|
{
|
|
# Empty source types -- everything can be accepted.
|
|
result = * ;
|
|
# This will terminate this loop.
|
|
generators = ;
|
|
# This will terminate the outer loop.
|
|
t = ;
|
|
}
|
|
|
|
for local source-type in [ $(g).source-types ]
|
|
{
|
|
if ! $(source-type) in $(result)
|
|
{
|
|
# If a generator accepts a 'source-type' it will also
|
|
# happily accept any type derived from it.
|
|
for local n in [ type.all-derived $(source-type) ]
|
|
{
|
|
if ! $(n) in $(result)
|
|
{
|
|
# Here there is no point in adding target types to
|
|
# the list of types to process in case they are or
|
|
# have already been on that list. We optimize this
|
|
# check by realizing that we only need to avoid the
|
|
# original target type's base types. Other target
|
|
# types that are or have been on the list of target
|
|
# types to process have been added to the 'result'
|
|
# list as well and have thus already been eliminated
|
|
# by the previous if.
|
|
if ! $(n) in $(t0)
|
|
{
|
|
t += $(n) ;
|
|
}
|
|
result += $(n) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
|
|
# Helper rule, caches the result of 'viable-source-types-real'.
|
|
#
|
|
rule viable-source-types ( target-type )
|
|
{
|
|
local key = .vst.$(target-type) ;
|
|
if ! $($(key))
|
|
{
|
|
.vst-cached-types += $(target-type) ;
|
|
local v = [ viable-source-types-real $(target-type) ] ;
|
|
if ! $(v)
|
|
{
|
|
v = none ;
|
|
}
|
|
$(key) = $(v) ;
|
|
}
|
|
|
|
if $($(key)) != none
|
|
{
|
|
return $($(key)) ;
|
|
}
|
|
}
|
|
|
|
|
|
# Returns the list of source types, which, when passed to 'run' method of
|
|
# 'generator', has some change of being eventually used (probably after
|
|
# conversion by other generators).
|
|
#
|
|
# Returns '*' in case any type should be considered a viable source type for the
|
|
# given generator.
|
|
#
|
|
rule viable-source-types-for-generator-real ( generator )
|
|
{
|
|
local source-types = [ $(generator).source-types ] ;
|
|
if ! $(source-types)
|
|
{
|
|
# If generator does not specify any source types, it might be a special
|
|
# generator like builtin.lib-generator which just relays to other
|
|
# generators. Return '*' to indicate that any source type is possibly
|
|
# OK, since we do not know for sure.
|
|
return * ;
|
|
}
|
|
else
|
|
{
|
|
local result ;
|
|
while $(source-types)
|
|
{
|
|
local s = $(source-types[1]) ;
|
|
source-types = $(source-types[2-]) ;
|
|
local viable-sources = [ generators.viable-source-types $(s) ] ;
|
|
if $(viable-sources) = *
|
|
{
|
|
result = * ;
|
|
source-types = ; # Terminate the loop.
|
|
}
|
|
else
|
|
{
|
|
result += [ type.all-derived $(s) ] $(viable-sources) ;
|
|
}
|
|
}
|
|
return [ sequence.unique $(result) ] ;
|
|
}
|
|
}
|
|
|
|
|
|
# Helper rule, caches the result of 'viable-source-types-for-generator'.
|
|
#
|
|
local rule viable-source-types-for-generator ( generator )
|
|
{
|
|
local key = .vstg.$(generator) ;
|
|
if ! $($(key))
|
|
{
|
|
.vstg-cached-generators += $(generator) ;
|
|
local v = [ viable-source-types-for-generator-real $(generator) ] ;
|
|
if ! $(v)
|
|
{
|
|
v = none ;
|
|
}
|
|
$(key) = $(v) ;
|
|
}
|
|
|
|
if $($(key)) != none
|
|
{
|
|
return $($(key)) ;
|
|
}
|
|
}
|
|
|
|
|
|
# Returns usage requirements + list of created targets.
|
|
#
|
|
local rule try-one-generator-really ( project name ? : generator : target-type
|
|
: property-set : sources * )
|
|
{
|
|
local targets =
|
|
[ $(generator).run $(project) $(name) : $(property-set) : $(sources) ] ;
|
|
|
|
local usage-requirements ;
|
|
local success ;
|
|
|
|
generators.dout [ indent ] returned $(targets) ;
|
|
|
|
if $(targets)
|
|
{
|
|
success = true ;
|
|
|
|
if [ class.is-a $(targets[1]) : property-set ]
|
|
{
|
|
usage-requirements = $(targets[1]) ;
|
|
targets = $(targets[2-]) ;
|
|
}
|
|
else
|
|
{
|
|
usage-requirements = [ property-set.empty ] ;
|
|
}
|
|
}
|
|
|
|
generators.dout [ indent ] " generator" [ $(generator).id ] " spawned " ;
|
|
generators.dout [ indent ] " " $(targets) ;
|
|
if $(usage-requirements)
|
|
{
|
|
generators.dout [ indent ] " with usage requirements:" $(x) ;
|
|
}
|
|
|
|
if $(success)
|
|
{
|
|
return $(usage-requirements) $(targets) ;
|
|
}
|
|
}
|
|
|
|
|
|
# Checks if generator invocation can be pruned, because it is guaranteed to
|
|
# fail. If so, quickly returns an empty list. Otherwise, calls
|
|
# try-one-generator-really.
|
|
#
|
|
local rule try-one-generator ( project name ? : generator : target-type
|
|
: property-set : sources * )
|
|
{
|
|
local source-types ;
|
|
for local s in $(sources)
|
|
{
|
|
source-types += [ $(s).type ] ;
|
|
}
|
|
local viable-source-types = [ viable-source-types-for-generator $(generator)
|
|
] ;
|
|
|
|
if $(source-types) && $(viable-source-types) != * &&
|
|
! [ set.intersection $(source-types) : $(viable-source-types) ]
|
|
{
|
|
local id = [ $(generator).id ] ;
|
|
generators.dout [ indent ] " ** generator '$(id)' pruned" ;
|
|
#generators.dout [ indent ] "source-types" '$(source-types)' ;
|
|
#generators.dout [ indent ] "viable-source-types" '$(viable-source-types)' ;
|
|
}
|
|
else
|
|
{
|
|
return [ try-one-generator-really $(project) $(name) : $(generator) :
|
|
$(target-type) : $(property-set) : $(sources) ] ;
|
|
}
|
|
}
|
|
|
|
|
|
rule construct-types ( project name ? : target-types + : property-set
|
|
: sources + )
|
|
{
|
|
local result ;
|
|
local usage-requirements = [ property-set.empty ] ;
|
|
for local t in $(target-types)
|
|
{
|
|
local r = [ construct $(project) $(name) : $(t) : $(property-set) :
|
|
$(sources) ] ;
|
|
if $(r)
|
|
{
|
|
usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
|
|
result += $(r[2-]) ;
|
|
}
|
|
}
|
|
# TODO: have to introduce parameter controlling if several types can be
|
|
# matched and add appropriate checks.
|
|
|
|
# TODO: need to review the documentation for 'construct' to see if it should
|
|
# return $(source) even if nothing can be done with it. Currents docs seem
|
|
# to imply that, contrary to the behaviour.
|
|
if $(result)
|
|
{
|
|
return $(usage-requirements) $(result) ;
|
|
}
|
|
else
|
|
{
|
|
return $(usage-requirements) $(sources) ;
|
|
}
|
|
}
|
|
|
|
|
|
# Ensures all 'targets' have their type. If this is not so, exists with error.
|
|
#
|
|
local rule ensure-type ( targets * )
|
|
{
|
|
for local t in $(targets)
|
|
{
|
|
if ! [ $(t).type ]
|
|
{
|
|
errors.error "target" [ $(t).str ] "has no type" ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Returns generators which can be used to construct target of specified type
|
|
# with specified properties. Uses the following algorithm:
|
|
# - iterates over requested target-type and all its bases (in the order returned
|
|
# by type.all-bases).
|
|
# - for each type find all generators that generate that type and whose
|
|
# requirements are satisfied by properties.
|
|
# - if the set of generators is not empty, returns that set.
|
|
#
|
|
# Note: this algorithm explicitly ignores generators for base classes if there
|
|
# is at least one generator for the requested target-type.
|
|
#
|
|
local rule find-viable-generators-aux ( target-type : property-set )
|
|
{
|
|
# Select generators that can create the required target type.
|
|
local viable-generators = ;
|
|
local generator-rank = ;
|
|
|
|
import type ;
|
|
local t = [ type.all-bases $(target-type) ] ;
|
|
|
|
generators.dout [ indent ] find-viable-generators target-type= $(target-type)
|
|
property-set= [ $(property-set).as-path ] ;
|
|
|
|
# Get the list of generators for the requested type. If no generator is
|
|
# registered, try base type, and so on.
|
|
local generators ;
|
|
while $(t[1])
|
|
{
|
|
generators.dout [ indent ] "trying type" $(t[1]) ;
|
|
if $(.generators.$(t[1]))
|
|
{
|
|
generators.dout [ indent ] "there are generators for this type" ;
|
|
generators = $(.generators.$(t[1])) ;
|
|
|
|
if $(t[1]) != $(target-type)
|
|
{
|
|
# We are here because there were no generators found for
|
|
# target-type but there are some generators for its base type.
|
|
# We will try to use them, but they will produce targets of
|
|
# base type, not of 'target-type'. So, we clone the generators
|
|
# and modify the list of target types.
|
|
local generators2 ;
|
|
for local g in $(generators)
|
|
{
|
|
# generators.register adds a generator to the list of
|
|
# generators for toolsets, which is a bit strange, but
|
|
# should work. That list is only used when inheriting a
|
|
# toolset, which should have been done before running
|
|
# generators.
|
|
generators2 += [ $(g).clone-and-change-target-type $(t[1]) :
|
|
$(target-type) ] ;
|
|
generators.register $(generators2[-1]) ;
|
|
}
|
|
generators = $(generators2) ;
|
|
}
|
|
t = ;
|
|
}
|
|
t = $(t[2-]) ;
|
|
}
|
|
|
|
for local g in $(generators)
|
|
{
|
|
generators.dout [ indent ] "trying generator" [ $(g).id ] "(" [ $(g).source-types ] -> [ $(g).target-types ] ")" ;
|
|
|
|
local m = [ $(g).match-rank $(property-set) ] ;
|
|
if $(m)
|
|
{
|
|
generators.dout [ indent ] " is viable" ;
|
|
viable-generators += $(g) ;
|
|
}
|
|
}
|
|
|
|
return $(viable-generators) ;
|
|
}
|
|
|
|
|
|
rule find-viable-generators ( target-type : property-set )
|
|
{
|
|
local key = $(target-type).$(property-set) ;
|
|
local l = $(.fv.$(key)) ;
|
|
if ! $(l)
|
|
{
|
|
l = [ find-viable-generators-aux $(target-type) : $(property-set) ] ;
|
|
if ! $(l)
|
|
{
|
|
l = none ;
|
|
}
|
|
.fv.$(key) = $(l) ;
|
|
}
|
|
|
|
if $(l) = none
|
|
{
|
|
l = ;
|
|
}
|
|
|
|
local viable-generators ;
|
|
for local g in $(l)
|
|
{
|
|
# Avoid trying the same generator twice on different levels.
|
|
if ! $(g) in $(.active-generators)
|
|
{
|
|
viable-generators += $(g) ;
|
|
}
|
|
else
|
|
{
|
|
generators.dout [ indent ] " generator " [ $(g).id ] "is active, discaring" ;
|
|
}
|
|
}
|
|
|
|
# Generators which override 'all'.
|
|
local all-overrides ;
|
|
# Generators which are overriden.
|
|
local overriden-ids ;
|
|
for local g in $(viable-generators)
|
|
{
|
|
local id = [ $(g).id ] ;
|
|
local this-overrides = $(.override.$(id)) ;
|
|
overriden-ids += $(this-overrides) ;
|
|
if all in $(this-overrides)
|
|
{
|
|
all-overrides += $(g) ;
|
|
}
|
|
}
|
|
if $(all-overrides)
|
|
{
|
|
viable-generators = $(all-overrides) ;
|
|
}
|
|
local result ;
|
|
for local g in $(viable-generators)
|
|
{
|
|
if ! [ $(g).id ] in $(overriden-ids)
|
|
{
|
|
result += $(g) ;
|
|
}
|
|
}
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
|
|
.construct-stack = ;
|
|
|
|
|
|
# Attempts to construct a target by finding viable generators, running them and
|
|
# selecting the dependency graph.
|
|
#
|
|
local rule construct-really ( project name ? : target-type : property-set :
|
|
sources * )
|
|
{
|
|
viable-generators = [ find-viable-generators $(target-type) :
|
|
$(property-set) ] ;
|
|
|
|
generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ]
|
|
" viable generators" ;
|
|
|
|
local result ;
|
|
local generators-that-succeeded ;
|
|
for local g in $(viable-generators)
|
|
{
|
|
# This variable will be restored on exit from this scope.
|
|
local .active-generators = $(g) $(.active-generators) ;
|
|
|
|
local r = [ try-one-generator $(project) $(name) : $(g) : $(target-type)
|
|
: $(property-set) : $(sources) ] ;
|
|
|
|
if $(r)
|
|
{
|
|
generators-that-succeeded += $(g) ;
|
|
if $(result)
|
|
{
|
|
ECHO "Error: ambiguity found when searching for best transformation" ;
|
|
ECHO "Trying to produce type '$(target-type)' from: " ;
|
|
for local s in $(sources)
|
|
{
|
|
ECHO " - " [ $(s).str ] ;
|
|
}
|
|
ECHO "Generators that succeeded:" ;
|
|
for local g in $(generators-that-succeeded)
|
|
{
|
|
ECHO " - " [ $(g).id ] ;
|
|
}
|
|
ECHO "First generator produced: " ;
|
|
for local t in $(result[2-])
|
|
{
|
|
ECHO " - " [ $(t).str ] ;
|
|
}
|
|
ECHO "Second generator produced: " ;
|
|
for local t in $(r[2-])
|
|
{
|
|
ECHO " - " [ $(t).str ] ;
|
|
}
|
|
EXIT ;
|
|
}
|
|
else
|
|
{
|
|
result = $(r) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
|
|
# Attempts to create a target of 'target-type' with 'properties' from 'sources'.
|
|
# The 'sources' are treated as a collection of *possible* ingridients, i.e.
|
|
# there is no obligation to consume them all.
|
|
#
|
|
# Returns a list of targets. When this invocation is first instance of
|
|
# 'construct' in stack, returns only targets of requested 'target-type',
|
|
# otherwise, returns also unused sources and additionally generated targets.
|
|
#
|
|
# If 'top-level' is set, does not suppress generators that are already
|
|
# used in the stack. This may be useful in cases where a generator
|
|
# has to build a metatargets -- for example a target corresponding to
|
|
# built tool.
|
|
#
|
|
rule construct ( project name ? : target-type : property-set * : sources * : top-level ? )
|
|
{
|
|
local saved-stack ;
|
|
if $(top-level)
|
|
{
|
|
saved-active = $(.active-generators) ;
|
|
.active-generators = ;
|
|
}
|
|
|
|
if (.construct-stack)
|
|
{
|
|
ensure-type $(sources) ;
|
|
}
|
|
|
|
.construct-stack += 1 ;
|
|
|
|
increase-indent ;
|
|
|
|
if $(.debug)
|
|
{
|
|
generators.dout [ indent ] "*** construct" $(target-type) ;
|
|
|
|
for local s in $(sources)
|
|
{
|
|
generators.dout [ indent ] " from" $(s) ;
|
|
}
|
|
generators.dout [ indent ] " properties:" [ $(property-set).raw ] ;
|
|
}
|
|
|
|
local result = [ construct-really $(project) $(name) : $(target-type) :
|
|
$(property-set) : $(sources) ] ;
|
|
|
|
decrease-indent ;
|
|
|
|
.construct-stack = $(.construct-stack[2-]) ;
|
|
|
|
if $(top-level)
|
|
{
|
|
.active-generators = $(saved-active) ;
|
|
}
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
# Given 'result', obtained from some generator or generators.construct, adds
|
|
# 'raw-properties' as usage requirements to it. If result already contains usage
|
|
# requirements -- that is the first element of result of an instance of the
|
|
# property-set class, the existing usage requirements and 'raw-properties' are
|
|
# combined.
|
|
#
|
|
rule add-usage-requirements ( result * : raw-properties * )
|
|
{
|
|
if $(result)
|
|
{
|
|
if [ class.is-a $(result[1]) : property-set ]
|
|
{
|
|
return [ $(result[1]).add-raw $(raw-properties) ] $(result[2-]) ;
|
|
}
|
|
else
|
|
{
|
|
return [ property-set.create $(raw-properties) ] $(result) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
rule dump ( )
|
|
{
|
|
for local g in $(.all-generators)
|
|
{
|
|
ECHO [ $(g).id ] ":" [ $(g).source-types ] -> [ $(g).target-types ] ;
|
|
}
|
|
}
|
|
|