📄 generators.jam
字号:
}
}
}
local rule try-one-generator ( project name ? : generator multiple ? :
target-type : property-set : sources * )
{
local targets =
[ $(generator).run $(project) $(name)
: $(property-set)
: $(sources)
: $(multiple)
] ;
# Generated targets that are of required types
local result ;
# Generated target of other types.
local extra ;
base-to-derived-type-conversion $(targets) : $(target-type)
: result extra ;
# Now try to convert extra targets
# 'construct' will to its best to return only requested
# target types, so if we receive any extra from that call,
# we don't try to do anything about them.
local extra2 ;
if $(multiple)
{
for local e in $(extra)
{
local try2 = [ construct-types $(project) $(name)
: $(target-type)
:
: $(property-set)
: $(e) ] ;
result += $(try2) ;
}
}
else
{
extra2 = $(extra) ;
}
generators.dout [ indent ] " generator" [ $(generator).id ] " spawned " ;
generators.dout [ indent ] " " $(result) -- $(extra2) ;
return $(result) $(extra2) ;
}
rule construct-types ( project name ? : target-types + : multiple ? :
property-set : sources + )
{
local result ;
local matched-types ;
for local t in $(target-types)
{
local r = [ construct $(project) $(name) : $(t) $(multiple) : $(property-set) :
$(sources) ] ;
if $(r)
{
result += $(r) ;
matched-types += $(t) ;
}
}
# TODO: have to introduce parameter controlling if
# several types can be matches 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 $(result) ;
}
else
{
return $(sources) ;
}
}
# Ensures all 'targets' have types. 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 it's bases (in the order returned bt
# type.all-bases.
# - for each type find all generators that generate that type and which 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's
# at least one generator for requested target-type.
local rule find-viable-generators ( target-type : property-set )
{
# Select generators that can create the required target type.
local viable-generators = ;
local generator-rank = ;
import type ;
# Try all-type generators first. Assume they have
# quite specific requirements.
local t = * [ type.all-bases $(target-type) ] ;
generators.dout [ indent ] find-viable-generators target-type= $(target-type)
property-set= [ $(property-set).as-path ]
;
while $(t[1])
{
generators.dout [ indent ] "trying type" $(t[1]) ;
for local g in $(.generators.$(t[1]))
{
generators.dout [ indent ] "trying generator" [ $(g).id ] "(" [ $(g).source-types ] -> [ $(g).target-types ] ")" ;
# Avoid trying the same generator twice on different levels.
if ! $(g) in $(.active-generators)
{
local m = [ $(g).match-rank $(property-set) ] ;
if $(m)
{
generators.dout [ indent ] " matched with rank" $(m) ;
viable-generators += $(g) ;
generator-rank += $(m) ;
t = ;
}
}
}
t = $(t[2-]) ;
}
return [ sequence.select-highest-ranked $(viable-generators) : $(generator-rank) ] ;
}
# Given a vector of vectors, of of them represents results of running some
# generator, returns the 'best' result, it it exists. Otherwise, exit with
# and error. Result is returned as plain jam list.
local rule select-dependency-graph ( options )
{
if [ $(options).size ] = 0
{
return ;
}
else if [ $(options).size ] = 1
{
return [ $(options).get-at 1 ] ;
}
else
{
# We have several alternatives and need to check if they
# are the same.
for local r in [ $(options).get ]
{
normalize-target-list $(r) ;
generators.dout $(r) ;
}
local f = [ $(options).at 1 ] ;
local mismatch ;
for local r in [ $(results).get ]
{
if ! [ utility.equal $(r) $(f) ]
{
mismatch = true ;
}
}
if ! $(mismatch)
{
return [ $(f).get ] ;
}
else
{
error [ $(options).size ] "possible generations for "
$(target-types) "Can't handle this now." ;
}
}
}
.construct-stack = ;
# Attempt to construct the target by looking at transformation cache.
local rule construct-with-caching (
project name ? : target-type multiple ? : property-set : sources * )
{
local result ;
# Caching is only possible when we're not computing cacheable transformation
# already, when there's only one source which has no action -- i.e. source file,
# and name of target is not specified.
if ! $(.caching) && ! $(sources[2]) && $(sources[1]) && ! $(name)
&& ! [ $(sources[1]).action ]
{
local .caching = true ;
local t = $(sources[1]) ;
local signature = [ sequence.join [ $(t).type ] $(target-type) $(property-set) : - ] ;
# Get a transformation template from cache or create it.
local cresult ;
if $(.transformation.cache.$(signature))
{
cresult = $(.transformation.cache.$(signature)) ;
}
else
{
local ut = [ new file-target % : [ $(t).type ] : "no project" ] ;
cresult = [ construct $(project) : $(target-type) $(multiple)
: $(property-set) : $(ut) ] ;
.transformation.cache.$(signature) = $(cresult) ;
}
# Substitute the real source name in the transformation template.
if $(cresult)
{
generators.dout [ indent ] "*** putting to cache?" ;
for local c in $(cresult)
{
local cc = [ virtual-target.clone-template $(c) : $(t) : $(project) ] ;
generators.dout [ indent ] "*** cloning " $(c) ;
generators.dout [ indent ] "*** cloned" $(cc) --- $(cc) ;
result += $(cc) ;
}
}
}
return $(result) ;
}
# Attempts to construct target by finding viable generators, running them
# and selecting the dependency graph
local rule construct-without-caching (
project name ? : target-type multiple ? : property-set : sources * )
{
viable-generators = [ find-viable-generators $(target-type) : $(property-set) ] ;
local results = [ new vector ] ;
generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ]
" viable generators" ;
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) $(multiple) : $(target-type) :
$(property-set) : $(sources) ] ;
if $(r)
{
$(results).push-back [ new vector $(r) ] ;
}
}
return [ select-dependency-graph $(results) ] ;
}
# Attempts to create target of 'target-type' with 'properties'
# from 'sources'. The 'sources' are treated as a collection of
# *possible* ingridients -- i.e. it is not required to consume
# them all. If 'multiple' is true, the rule is allowed to return
# several targets of 'target-type'.
#
#
# Returns a list of target. 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.
#
# Does not return target which are not of 'allowed-type' or of type derived from
# it. If 'allowed-type' is not specified, it's defaulted to 'target-type'.
# See lib-target-class for use case of this.
rule construct ( project name ? : target-type multiple ? : property-set * : sources *
: allowed-type * )
{
allowed-type ?= $(target-type) ;
if (.construct-stack)
{
ensure-type $(sources) ;
}
.construct-stack += 1 ;
increase-indent ;
local m ;
if $(multiple)
{
m = "(may return multiple targets)" ;
}
generators.dout [ indent ] "*** construct" $(target-type) $(m) ;
for local s in $(sources)
{
generators.dout [ indent ] " from" $(s) ;
}
generators.dout [ indent ] " properties:" [ $(property-set).raw ] ;
local result = [ construct-with-caching $(project) $(name)
: $(target-type) $(multiple) : $(property-set) : $(sources) ] ;
if ! $(result) {
result = [ construct-without-caching $(project) $(name)
: $(target-type) $(multiple) : $(property-set) : $(sources) ] ;
}
decrease-indent ;
.construct-stack = $(.construct-stack[2-]) ;
if ! $(.construct-stack) && $(allowed-type) != * # This is first invocation in stack
{
local result2 ;
for local t in $(result)
{
local type = [ $(t).type ] ;
assert.nonempty-variable type ;
assert.nonempty-variable target-type ;
# Return only targets of the requested type, unless 'return-all'
# is specified. If we don't do this, then all targets calling
# 'construct' will get unused target returned, which will break
# checking for unused sources a bit harder.
if $(type) = $(target-type) || [ type.is-derived $(type) $(allowed-type) ]
{
result2 += $(t) ;
}
}
return $(result2) ;
}
else
{
return $(result) ;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -