📄 generators.jam
字号:
# 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 ; 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.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) ; } # 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 + ) { return [ 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) ] ; } # 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) ; } } return [ new $(__class__) $(self.id) $(self.composing) : $(self.source-types) : $(target-types) : $(self.requirements) ] ; } # 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) { convert-multiple-sources-to-consumable-types $(project) : $(property-set) : $(sources) : consumed bypassed ; } else { convert-to-consumable-types $(project) $(name) : $(property-set) : $(sources) : : consumed bypassed ; } 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) ] ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -