diff --git a/new/build-system.jam b/new/build-system.jam index 524dd2d67..1ba673929 100644 --- a/new/build-system.jam +++ b/new/build-system.jam @@ -6,6 +6,7 @@ import project ; import sequence ; import modules ; import feature ; +import property-set ; import build-request ; import errors : error ; @@ -109,8 +110,9 @@ if $(expanded) for local p in $(expanded) { for local t in $(targets) - { - $(t).direct-build-request [ feature.split $(p) ] ; + { + $(t).direct-build-request + [ property-set.create [ feature.split $(p) ] ] ; } } @@ -118,7 +120,8 @@ if $(expanded) { for local t in $(targets) { - virtual-targets += [ $(t).generate [ feature.split $(p) ] ] ; + virtual-targets += [ $(t).generate + [ property-set.create [ feature.split $(p) ] ] ] ; } } } @@ -126,7 +129,7 @@ else { for local t in $(targets) { - virtual-targets += [ $(t).generate ] ; + virtual-targets += [ $(t).generate [ property-set.empty ] ] ; } } diff --git a/new/builtin.jam b/new/builtin.jam index 6db1996a4..fc0656f07 100644 --- a/new/builtin.jam +++ b/new/builtin.jam @@ -293,8 +293,9 @@ rule lib-generator ( ) { composing-generator.__init__ lib-generator : : LIB : LIB ; - rule run ( project name ? : properties * : sources * ) + rule run ( project name ? : property-set : sources * ) { + local properties = [ $(property-set).raw ] ; # Determine the needed target type local actual-type ; if in $(properties:G) || in $(properties:G) @@ -311,8 +312,8 @@ rule lib-generator ( ) } # Construct the target. Pass 'allow-composing', since generators for # library types are composing and we need to find them. - return [ generators.construct $(project) $(name) : $(actual-type) : $(properties) - : $(sources) : allow-composing ] ; + return [ generators.construct $(project) $(name) : $(actual-type) + : $(property-set) : $(sources) : allow-composing ] ; } } diff --git a/new/generators.jam b/new/generators.jam index b2cab15ba..6426596c3 100644 --- a/new/generators.jam +++ b/new/generators.jam @@ -157,6 +157,7 @@ rule generator ( # 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) ; @@ -173,7 +174,7 @@ rule generator ( # Returns a number telling how good generator's properties match # the passed properties, or empty list if generator can't be run # at all. - rule match-rank ( properties * ) + rule match-rank ( property-set ) { # See if generator's requirements are satisfied by 'properties'. # Treat feature name in requirements (i.e. grist-only element), @@ -192,7 +193,8 @@ rule generator ( features += $(r) ; } } - + + local properties = [ $(property-set).raw ] ; if $(requirements) in $(properties) && $(features) in $(properties:G) { return [ sequence.length [ set.intersection @@ -217,7 +219,7 @@ rule generator ( rule run ( project # Project for which the targets are generated name ? # Determines the name of 'name' attribute for # all generated targets. See 'generated-targets' method. - : properties * # Desired properties for generated targets. + : property-set # Desired properties for generated targets. : sources + : # Source targets. multiple ? # Allows the rule to run generator several times and return # multiple targets of the same type. When this argument is not @@ -230,7 +232,7 @@ rule generator ( { generators.dout [ indent ] " generator" $(self.id) ; generators.dout [ indent ] " multiple:" $(mutliple) ; - + # Ordinary generators take only one source targets if $(sources[2]) { @@ -248,7 +250,7 @@ rule generator ( local bypassed = ; convert-to-consumable-types $(project) $(name) : - $(properties) : $(sources) : $(multiple) + $(property-set) : $(sources) : $(multiple) : : consumed bypassed ; @@ -256,7 +258,7 @@ rule generator ( if $(consumed) { result = [ construct-result $(consumed) : $(project) $(name) - : $(properties) ] ; + : $(property-set) ] ; } if $(result) @@ -301,7 +303,7 @@ rule generator ( # Otherwise, might contain several targets with the type of # $(self.source-types[1]) : project name ? - : properties * # Properties to be used for all actions create here + : property-set # Properties to be used for all actions create here ) { local result ; @@ -311,7 +313,7 @@ rule generator ( generators.dout [ indent ] "alt1" ; for local r in $(consumed) { - result += [ generated-targets $(r) : $(properties) : $(project) $(name) ] ; #(targets) ; + result += [ generated-targets $(r) : $(property-set) : $(project) $(name) ] ; #(targets) ; } } else @@ -320,7 +322,8 @@ rule generator ( generators.dout [ indent ] "alt2 : consumed is" [ $(v).str ] ; if $(consumed) { - result += [ generated-targets $(consumed) : $(properties) : $(project) $(name) ] ; + result += [ generated-targets $(consumed) : $(property-set) + : $(project) $(name) ] ; } } return $(result) ; @@ -346,7 +349,7 @@ rule generator ( # Note that this pattern mechanism has nothing to do with implicit patterns # in make. It's a way to produce target which name is different for name of # source. - rule generated-targets ( sources + : properties * : project name ? ) + rule generated-targets ( sources + : property-set : project name ? ) { if ! $(name) { @@ -382,7 +385,8 @@ rule generator ( } # Assign an action for each target local action = [ action-class ] ; - local a = [ new $(action) $(targets) : $(sources) : $(self.id) : $(properties) ] ; + local a = [ new $(action) $(targets) : $(sources) : $(self.id) : + $(property-set) ] ; for local t in $(targets) { $(t).action $(a) ; @@ -395,7 +399,7 @@ rule generator ( # handle. The intention is to produce the set of targets can should be # used when generator is run. rule convert-to-consumable-types ( project name ? : - properties * : source : multiple ? + property-set : source : multiple ? : only-one ? # convert 'source' to only one of source types # if there's more that one possibility, report an # error @@ -449,7 +453,7 @@ rule generator ( if $(missing-types) { local transformed = [ generators.construct-types $(project) $(name) - : $(missing-types) : $(multiple) : $(properties) : $(source) ] ; + : $(missing-types) : $(multiple) : $(property-set) : $(source) ] ; # Add targets of right type to 'consumed'. Add others to # 'bypassed'. The 'generators.construct' rule has done @@ -468,8 +472,7 @@ rule generator ( } } } - - + $(consumed-var) += $(_consumed) ; $(bypassed-var) += $(_bypassed) ; } @@ -492,7 +495,7 @@ rule composing-generator ( id : source-types * : target-types + : generator.__init__ $(id) : $(source-types) : $(target-types) : $(requirements) ; - rule run ( project name ? : properties * : sources + ) + rule run ( project name ? : property-set : sources + ) { generators.dout [ indent ] " composing generator" $(self.id) ; @@ -507,7 +510,7 @@ rule composing-generator ( id : source-types * : target-types + : local c ; local b ; # TODO: need to check for failure on each source. - convert-to-consumable-types $(project) : $(properties) + convert-to-consumable-types $(project) : $(property-set) : $(sources[1]) : * : true : c b ; if ! $(c) { @@ -523,7 +526,8 @@ rule composing-generator ( id : source-types * : target-types + : if ! $(failed) { generators.dout [ indent ] " SUCCESS" ; - result += [ generated-targets $(consumed) : $(properties) : $(project) $(name) ] ; + result += [ generated-targets $(consumed) : $(property-set) + : $(project) $(name) ] ; result += $(bypassed) ; } else @@ -622,9 +626,9 @@ rule base-to-derived-type-conversion ( targets * : target-types + rule try-one-generator ( project name ? : generator multiple ? : - target-types + : properties * : sources * ) + target-types + : property-set : sources * ) { - local targets = [ $(generator).run $(project) $(name) : $(properties) : $(sources) + local targets = [ $(generator).run $(project) $(name) : $(property-set) : $(sources) : $(multiple) ] ; # Generated targets that are of required types @@ -644,8 +648,8 @@ rule try-one-generator ( project name ? : generator multiple ? : { for local e in $(extra) { - local try2 = [ construct-types $(project) $(name) : $(target-types) : : $(properties) - : $(e) ] ; + local try2 = [ construct-types $(project) $(name) + : $(target-types) : : $(property-set) : $(e) ] ; result += $(try2) ; } @@ -662,13 +666,13 @@ rule try-one-generator ( project name ? : generator multiple ? : } rule construct-types ( project name ? : target-types + : multiple ? : - properties * : source ) + property-set : source ) { local result ; local matched-types ; for local t in $(target-types) { - local r = [ construct $(project) $(name) : $(t) $(multiple) : $(properties) : + local r = [ construct $(project) $(name) : $(t) $(multiple) : $(property-set) : $(source) ] ; if $(r) { @@ -719,7 +723,7 @@ local rule ensure-type ( targets * ) # # 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 : properties * : allow-composing ? ) +local rule find-viable-generators ( target-type : property-set : allow-composing ? ) { # Select generators that can create the required target type. local viable-generators = ; @@ -738,7 +742,7 @@ local rule find-viable-generators ( target-type : properties * : allow-composing if ! $(g) in $(.active-generators) && ! ( [ is-a $(g) : composing-generator ] && ! $(allow-composing) ) { - local m = [ $(g).match-rank $(properties) ] ; + local m = [ $(g).match-rank $(property-set) ] ; if $(m) { viable-generators += $(g) ; @@ -803,7 +807,7 @@ local rule select-dependency-graph ( options ) # Attempt to construct the target by looking at transformation cache. local rule construct-with-caching ( - project name ? : target-type multiple ? : properties * : sources * ) + project name ? : target-type multiple ? : property-set : sources * ) { local result ; if ! $(.caching) && ! $(sources[2]) && $(sources[1]) && ! $(name) @@ -812,7 +816,7 @@ local rule construct-with-caching ( local t = $(sources[1]) ; - local signature = [ sequence.join [ $(t).type ] $(target-type) $(properties) : - ] ; + local signature = [ sequence.join [ $(t).type ] $(target-type) $(property-set) : - ] ; # Get a transformation template from cache or create it. local cresult ; @@ -823,7 +827,8 @@ local rule construct-with-caching ( else { local ut = [ new file-target % : [ $(t).type ] : "no project" ] ; - cresult = [ construct $(project) : $(target-type) $(multiple) : $(properties) : $(ut) ] ; + cresult = [ construct $(project) : $(target-type) $(multiple) + : $(property-set) : $(ut) ] ; .transformation.cache.$(signature) = $(cresult) ; } @@ -846,9 +851,9 @@ local rule construct-with-caching ( # 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 ? : properties * : sources * ) + project name ? : target-type multiple ? : property-set : sources * ) { - viable-generators = [ find-viable-generators $(target-type) : $(properties) + viable-generators = [ find-viable-generators $(target-type) : $(property-set) : $(allow-composing) ] ; local results = [ new vector ] ; @@ -862,7 +867,7 @@ local rule construct-without-caching ( local .active-generators = $(g) $(.active-generators) ; local r = [ try-one-generator $(project) $(name) : $(g) $(multiple) : $(target-type) : - $(properties) : $(sources) ] ; + $(property-set) : $(sources) ] ; if $(r) { @@ -885,7 +890,7 @@ local rule construct-without-caching ( # 'construct' in stack, returns only targets of requested 'target-type', # otherwise, returns also unused sources and additionally generated # targets. -rule construct ( project name ? : target-type multiple ? : properties * : sources * +rule construct ( project name ? : target-type multiple ? : property-set * : sources * : allow-composing ? # Allows to use composing generators for constructing this # target. This will be typically set when creating main targets, # and unset when called recursively from 'run' method of @@ -905,8 +910,8 @@ rule construct ( project name ? : target-type multiple ? : properties * : source usage-requirements += [ $(e).usage-requirements ] ; } } - properties += $(usage-requirements) ; - properties = [ sequence.unique $(properties) ] ; + property-set = [ $(property-set).add + [ property-set.create $(usage-requirements) ] ] ; .construct-stack += 1 ; @@ -926,11 +931,11 @@ rule construct ( project name ? : target-type multiple ? : properties * : source generators.dout [ indent ] " properties:" $(properties) ; local result = [ construct-with-caching $(project) $(name) - : $(target-type) $(multiple) : $(properties) : $(sources) ] ; + : $(target-type) $(multiple) : $(property-set) : $(sources) ] ; if ! $(result) { result = [ construct-without-caching $(project) $(name) - : $(target-type) $(multiple) : $(properties) : $(sources) ] ; + : $(target-type) $(multiple) : $(property-set) : $(sources) ] ; } decrease-indent ; @@ -940,8 +945,8 @@ rule construct ( project name ? : target-type multiple ? : properties * : source if ! $(.construct-stack) # This is first invocation in stack { # Make roots of dependency graph depend on all 'dependency' features. - local dp = [ property.take dependency : $(properties) ] ; - + local dp = [ $(property-set).dependency ] ; + if $(dp) { for local t in $(result) diff --git a/new/make.jam b/new/make.jam index cc561875f..f4a6c22c2 100644 --- a/new/make.jam +++ b/new/make.jam @@ -12,21 +12,23 @@ import property ; import errors : error ; import type : type ; import regex ; +import property-set ; rule make-target-class ( name : project : sources * : requirements * : make-rule + : default-build * ) { basic-target.__init__ $(name) : $(project) : $(sources) : $(requirements) : $(default-build) ; + self.make-rule = $(make-rule) ; - rule construct ( source-targets * : properties * ) + rule construct ( source-targets * : property-set ) { local t = [ new file-target $(self.name:S=) : [ type.type $(self.name:S) ] : $(self.project) ] ; $(t).suffix [ regex.match .(.*) : $(self.name:S) ] ; local a = [ new action $(t) : $(source-targets) : $(self.make-rule) - : $(properties) ] ; + : $(property-set) ] ; $(t).action $(a) ; return $(t) ; } diff --git a/new/prebuilt.jam b/new/prebuilt.jam index ba278bd41..acd640ca9 100644 --- a/new/prebuilt.jam +++ b/new/prebuilt.jam @@ -21,8 +21,9 @@ rule prebuilt-file-generator { generator.__init__ prebuilt-file-generator : : * : ; - rule run ( project name ? : properties * : sources * ) + rule run ( project name ? : property-set : sources * ) { + local properties = [ $(property-set).raw ] ; local name = [ feature.get-values : $(properties) ] ; local type = [ type.type $(name:S) ] ; if ! $(type) @@ -42,7 +43,7 @@ rule prebuilt-file-generator # Therefore, we encode properties via 'extra-grist', which is ugly. # Maybe, we should just introduce 'do-nothing-action', which only # keps properties. - $(t).extra-grist [ property.as-path $(properties) ] ; + $(t).extra-grist [ $(property-set).as-path ] ; $(t).suffix [ MATCH .(.*) : $(name:S) ] ; return $(t) ; diff --git a/new/project.jam b/new/project.jam index 6cd3fa794..b4628a9b7 100644 --- a/new/project.jam +++ b/new/project.jam @@ -386,6 +386,14 @@ local rule initialize ( [ path.relative $(our-dir) $(parent-dir) ] ] : exact ; } } + else + { + $(attributes).set requirements + : [ property-set.empty ] : exact ; + $(attributes).set usage-requirements + : [ property-set.empty ] : exact ; + } + } # Associate the given id with the given location @@ -416,9 +424,16 @@ rule project-attributes ( location ) { specification = [ property.translate-paths $(specification) : $(self.location) ] ; - local current = $(self.requirements) ; - local result = [ property.refine $(current) : - [ property.make $(specification) ] ] ; + specification = [ property.make $(specification) ] ; + result = [ property-set.create $(specification) ] ; + + # If we have inherited properties, need to refine them with the + # specified. + local current = $(self.requirements) ; + if $(current) + { + result = [ $(current).refine $(result) ] ; + } if $(result[1]) = "@error" { @@ -439,8 +454,17 @@ rule project-attributes ( location ) { errors.error "usage-requirements" $($(real-pos2)) "have non-free properties" $(non-free) ; } - self.usage-requirements += [ property.translate-paths $(specification) + local t = [ property.translate-paths $(specification) : $(self.location) ] ; + if $(self.usage-requirements) + { + self.usage-requirements = [ property-set.create + [ $(self.usage-requirements).raw ] $(t) ] ; + } + else + { + self.usage-requirements = [ property-set.create $(t) ] ; + } } else if $(attribute) = "source-location" { @@ -480,7 +504,7 @@ rule project-attributes ( location ) print.list-start ; print.list-item "Project root:" $(self.project-root) ; print.list-item "Parent project:" $(parent) ; - print.list-item "Requirements:" $(self.requirements) ; + print.list-item "Requirements:" [ $(self.requirements).raw ] ; print.list-item "Default build:" $(self.default-build) ; print.list-item "Source location:" $(self.source-location) ; print.list-item "Projects to build:" diff --git a/new/targets.jam b/new/targets.jam index 8c40dfebf..8d1ebfbd2 100644 --- a/new/targets.jam +++ b/new/targets.jam @@ -72,6 +72,7 @@ import regex ; import property ; import errors ; import common ; +import property-set ; # Base class for all abstract targets. @@ -109,7 +110,7 @@ rule abstract-target ( name # name of the target in Jamfile # Adds one more direct build request for this target. If later generate # is called with the same non-free non-incidental properties as in one # of direct build requests, then that build request is used instead. - rule direct-build-request ( properties * ) + rule direct-build-request ( property-set ) { } @@ -122,7 +123,7 @@ rule abstract-target ( name # name of the target in Jamfile # # If 'properties' are empty, performs default build of this target, in a way specific # to derived class. - rule generate ( properties * ) + rule generate ( property-set ) { errors.error "method should be defined in derived classes" ; } @@ -137,29 +138,30 @@ rule project-target ( name : project : requirements * : default-build * ) self.requirements = $(requirements) ; self.default-build = $(default-build) ; - rule direct-build-request ( properties * ) + rule direct-build-request ( property-set ) { for local name in $(self.main-targets) { local t = [ main-target $(name) ] ; - result += [ $(t).direct-build-request $(properties) ] ; + result += [ $(t).direct-build-request $(property-set) ] ; } for local pn in [ project.attribute $(self.project) projects-to-build ] { local p = [ project.module-name $(pn) ] ; local t = [ project.target [ project.attribute $(p) location ] ] ; - result += [ $(t).direct-build-request $(properties) ] ; + result += [ $(t).direct-build-request $(property-set) ] ; } } # Generates all possible targets contained in this project. - rule generate ( properties * ) + rule generate ( property-set * ) { # Project properties are directly imposed on all main targets. # However, we'd need to check if this project can be build at # all. - local xproperties = - [ property.refine $(properties) : $(self.requirements) ] ; + + local xproperties = [ $(property-set).refine $(self.requirements) ] ; + if $(xproperties[1]) = "@error" { local id = [ project.attribute $(self.project) id ] ; @@ -168,7 +170,7 @@ rule project-target ( name : project : requirements * : default-build * ) "due to unsatisfied requirements." ; print.wrapped-text "warning: explanation: " $(xproperties[2-]) ; print.wrapped-text "warning: build-request: " $(properties) ; - print.wrapped-text "warning: requirements: " $(self.requirements) ; + print.wrapped-text "warning: requirements: " [ $(self.requirements).raw ] ; } else { @@ -176,14 +178,14 @@ rule project-target ( name : project : requirements * : default-build * ) for local name in $(self.main-targets) { local t = [ main-target $(name) ] ; - result += [ $(t).generate $(properties) ] ; + result += [ $(t).generate $(property-set) ] ; } local self-location = [ project.attribute $(self.project) location ] ; for local pn in [ project.attribute $(self.project) projects-to-build ] { local p = [ project.module-name [ path.join $(self-location) $(pn) ] ] ; local t = [ project.target [ project.attribute $(p) location ] ] ; - result += [ $(t).generate $(properties) ] ; + result += [ $(t).generate $(property-set) ] ; } return $(result) ; } @@ -215,19 +217,19 @@ rule project-target ( name : project : requirements * : default-build * ) # target in this project, if generation with # 'properties' is requested, and that main target # does not have any requirements of its own. - rule reference-properties ( properties * ) + rule reference-properties ( property-set ) { - # Note that free properties can appear on 'properties' only - # from direct build request, because free properties are not - # allowed to be propagated. - - local ref = [ project.attribute $(self.project) requirements ] ; - ref = [ property.refine $(properties) : $(ref) ] ; - ref = [ property.evaluate-conditionals $(ref) ] ; - ref = [ property.take free : [ property.remove incidental : $(ref) ] ] ; - ref = [ targets.generate-dependencies $(ref) : $(self.project) ] ; - return $(ref) ; - } + if ! $(self.ref-props.$(property-set)) + { + local ref = [ project.attribute $(self.project) requirements ] ; + local ps = [ $(property-set).refine $(ref) ] ; + ps = [ $(ps).evaluate-conditionals ] ; + + ps = [ targets.generate-dependencies $(ps) : $(self.project) : $(ps) ] ; + self.ref-props.$(property-set) = $(ps) ; + } + return $(self.ref-props.$(property-set)) ; + } } class project-target : abstract-target ; @@ -246,9 +248,9 @@ rule main-target ( name : project ) self.alternatives += $(target) ; } - rule direct-build-request ( properties * ) + rule direct-build-request ( property-set ) { - local base = [ property.remove free incidental : $(properties) ] ; + local base = [ $(property-set).base ] ; local ep = $(self.direct-request.$(base:J=-)) ; if $(ep) && $(ep) != $(properties) { @@ -256,7 +258,7 @@ rule main-target ( name : project ) } else { - self.direct-request.$(base:J=-) = $(properties) ; + self.direct-request.$(base:J=-) = $(property-set) ; } } @@ -265,13 +267,13 @@ rule main-target ( name : project ) # which requirements are satisfied by 'properties' and picking the one with # longest requirements set. # Returns the result of calling 'generate' on that alternative. - rule generate ( properties * ) + rule generate ( property-set ) { - local base = [ property.remove free incidental : $(properties) ] ; + local base = [ $(property-set).base ] ; local ep = $(self.direct-request.$(base:J=-)) ; if $(ep) { - properties = $(ep) ; + property-set = $(ep) ; } # Try to generate all the alternatives. @@ -279,7 +281,7 @@ rule main-target ( name : project ) for local v in $(self.alternatives) { - local vtargets = [ $(v).generate $(properties) ] ; + local vtargets = [ $(v).generate $(property-set) ] ; if $(vtargets) && $(vtargets[1]) != "@error" { $(alternatives).push-back [ new vector $(v) $(vtargets) ] ; @@ -291,7 +293,7 @@ rule main-target ( name : project ) # why it can't be build. print.wrapped-text "warning: skipped build of" [ full-name ] - "with properties" $(properties) ; + "with properties" [ $(property-set).raw ] ; } else { local result ; if [ $(alternatives).size ] = 1 @@ -314,9 +316,9 @@ rule main-target ( name : project ) # have 'requirements' method. # assert.equal [ is-a $(target) : basic-target ] : true ; local req = [ $(target).requirements ] ; - req = [ property.remove free incidental : $(req) ] ; + req = [ $(req).base ] ; req-length += [ sequence.length - [ set.intersection $(req) : $(properties) ] ] ; + [ set.intersection $(req) : [ $(property-set).raw ] ] ] ; } local best = [ sequence.select-highest-ranked $(r) : $(req-length) ] ; @@ -343,7 +345,7 @@ rule main-target ( name : project ) # is created. local all-targets = [ sequence.transform virtual-target.traverse : $(result) ] ; - local dg = [ new subvariant-dg $(__name__) : $(properties) : $(all-targets) ] ; + local dg = [ new subvariant-dg $(__name__) : $(property-set) : $(all-targets) ] ; for local v in $(all-targets) { $(v).dg $(dg) ; @@ -361,7 +363,7 @@ class main-target : abstract-target ; # reference. rule generate ( target-reference # Target reference : project # Project where the reference is made - : properties * # Properties of the main target that + : property-set # Properties of the main target that # makes the reference ) { @@ -382,8 +384,9 @@ rule generate ( target-reference # Target reference if $(main-target) { # Take properties which should be propagated and refine them # with source-specific requirements. - local propagated = [ property.take propagated : $(properties) ] ; - local rproperties = [ property.refine $(propagated) : $(sproperties) ] ; + local propagated = [ $(property-set).propagated ] ; + local rproperties = [ $(propagated).refine + [ property-set.create $(sproperties) ] ] ; if $(rproperties[1]) = "@error" { errors.error @@ -395,42 +398,32 @@ rule generate ( target-reference # Target reference } } -# Returns a list of properties, where all dependency properties are -# replaced as follows: -# - the value of dependency property is treated as target reference, -# which is generated with 'properties' + 'extra-properties' -# - the created virtual target's become replace the property value, -# for example a/b might become -# object(virtual-target)@1 -# In addition, usage requirements for all created virtual targets -# are added to the properties. +# Returns new property set which inclues all properties from +# 'property-set', except that all dependency properties are +# generated with 'generation-ps', and the obtained virtual targets +# are added as the values of original features. # -# The purpose of 'extra-properties' is to be able to replace dependency -# features only in certain set of properties, which does not include -# all build properties. A concrete example is when we process use properties. -# Dependency features must be replaced using full build properties of target, -# but we don't want to add build properties to usage requirements. -rule generate-dependencies ( properties * : project : extra-properties * ) +# For example, a/b might become object(virtual-target)@1 +# In addition, usage requirements for all created virtual targets +# are added to the created property set. +rule generate-dependencies ( property-set : project : generation-ps ) { - local xproperties ; - for local p in $(properties) + local xproperties ; + for local p in [ $(property-set).dependency ] { - if dependency in [ feature.attributes $(p:G) ] + local g = [ targets.generate $(p:TG=) : $(project) : $(generation-ps) ] ; + if ! $(g) { - local g = [ targets.generate $(p:TG=) : $(project) - : $(properties) $(extra-properties) ] ; - if ! $(g) - { - errors.error "cannot generate dependency " $(p) ; - } - xproperties += $(p:G)$(g) [ $(g).usage-requirements ] ; + errors.error "cannot generate dependency " $(p) ; } - else - { - xproperties += $(p) ; - } + xproperties += $(p:G)$(g) [ $(g).usage-requirements ] ; } - return $(xproperties) ; + local r = [ property-set.create + [ $(property-set).base ] + [ $(property-set).free ] + $(xproperties) + [ $(property-set).incidental ] ] ; + return $(r) ; } @@ -448,8 +441,15 @@ rule basic-target ( name : project abstract-target.__init__ $(name) : $(project) ; self.sources = $(sources) ; + if ! $(requirements) { + requirements = [ property-set.empty ] ; + } self.requirements = $(requirements) ; self.default-build = $(default-build) ; + if ! $(usage-requirements) + { + usage-requirements = [ property-set.empty ] ; + } self.usage-requirements = $(usage-requirements) ; if $(sources:G) @@ -463,35 +463,31 @@ rule basic-target ( name : project # Generates sources. Calls 'construct' # This method should not be overriden. # - rule generate ( properties * ) + rule generate ( property-set ) { - if ! $(properties) || $(properties:G) = + if ! [ $(property-set).raw ] { # CONSIDER: I'm really not sure if this is correct... # FIXME: as in 'build-system' we stick default toolset - properties = [ build-request.expand $(properties) - $(self.default-build) ] ; + properties = [ build-request.expand $(self.default-build) ] ; local result = ; for local p in $(properties) { - result += [ generate [ feature.split $(p) ] ] ; + result += [ generate [ property-set.create [ feature.split $(p) ] ] ] ; } return $(result) ; } else { - property-path = [ property.as-path - [ property.remove free incidental : $(properties) ] ] ; + + property-path = [ property.as-path [ $(property-set).base ] ] ; if ! $(property-path) { property-path = X ; } if ! $(self.generated.$(property-path)) { - local rproperties = - [ property.refine $(properties) : $(self.requirements) ] ; - - rproperties = [ property.evaluate-conditionals $(rproperties) ] ; + local rproperties = [ $(property-set).refine $(self.requirements) ] ; if $(rproperties[1]) != "@error" { @@ -503,9 +499,12 @@ rule basic-target ( name : project # requirements may also code from dependencies. However, when they # come from dependencies, the value is not target id, but rather # virtual target names, so generators.construct can use them. + + rproperties = [ $(rproperties).evaluate-conditionals ] ; local xproperties = - [ targets.generate-dependencies $(rproperties) : $(self.project) ] ; + [ targets.generate-dependencies $(rproperties) : $(self.project) + : $(rproperties) ] ; local source-targets ; @@ -513,7 +512,7 @@ rule basic-target ( name : project { # Try treating this source as reference to main target local more-targets = - [ targets.generate $(s) : $(self.project) : $(properties) ] ; + [ targets.generate $(s) : $(self.project) : $(property-set) ] ; if $(more-targets) { source-targets += $(more-targets) ; @@ -529,16 +528,19 @@ rule basic-target ( name : project # unqual to project's reference properties. As the # result, we create per-target bin directory while # it's not really needed. - xproperties = [ feature.run-actions $(xproperties) ] ; - + + xproperties = [ $(xproperties).run-actions ] ; + self.generated.$(property-path) = [ construct $(source-targets) : $(xproperties) ] ; # Apply use requirement of this target to all generated # virtual targets. local xusage-requirements = - [ targets.generate-dependencies $(self.usage-requirements) + [ targets.generate-dependencies + $(self.usage-requirements) : $(self.project) : $(rproperties) ] ; + xusage-requirements = [ $(xusage-requirements).raw ] ; for local e in $(self.generated.$(property-path)) { @@ -579,11 +581,11 @@ rule typed-target ( name : project : type self.type = $(type) ; - rule construct ( source-targets * : properties * ) + rule construct ( source-targets * : property-set ) { local r = [ generators.construct $(self.project) $(self.name) : $(self.type) - : $(properties) # [ feature.expand - $(self.type) + : [ property-set.create [ $(property-set).raw ] # [ feature.expand + $(self.type) ] # ] : $(source-targets) : allow-composing ] ; @@ -609,8 +611,9 @@ rule main-target-requirements ( { local loc = [ project.attribute $(project) location ] ; local requirements = [ property.translate-paths $(specification) : $(loc) ] ; + local requirements = [ property-set.create $(requirements) ] ; local project-requirements = [ project.attribute $(project) requirements ] ; - requirements = [ property.refine $(project-requirements) : $(requirements) ] ; + requirements = [ $(project-requirements).refine $(requirements) ] ; if $(requirements[1]) = "@error" { errors.error "Conflicting requirements for target:" $(requirements) ; @@ -629,11 +632,11 @@ rule main-target-usage-requirements ( { local loc = [ project.attribute $(project) location ] ; local project-usage-requirements = [ project.attribute $(project) usage-requirements ] ; - - local usage-requirements = [ property.translate-paths $(specification) : $(loc) ] ; - usage-requirements += $(project-usage-requirements) ; - - return $(usage-requirements) ; + + local usage-requirements = [ property-set.create + [ property.translate-paths $(specification) : $(loc) ] ] ; + + return [ $(project-usage-requirements).add $(usage-requirements) ] ; } # Return the default build value to use when declaring a main target, diff --git a/new/virtual-target.jam b/new/virtual-target.jam index 7f3be6f9b..ec63fde8d 100644 --- a/new/virtual-target.jam +++ b/new/virtual-target.jam @@ -10,6 +10,8 @@ import class : class new ; import type ; +import property-set ; +import utility ; # +--------------------------+ # | virtual-target | @@ -349,8 +351,8 @@ rule abstract-file-target ( name local properties ; if $(self.action) { - properties = [ property.remove free incidental : [ $(self.action).properties ] ] ; - local property-grist = [ property.as-path $(properties) ] ; + local ps = [ $(self.action).properties-ps ] ; + local property-grist = [ $(ps).as-path ] ; grist = $(location-grist)/$(property-grist) ; } if ! $(grist) @@ -447,21 +449,16 @@ rule file-target ( { if $(self.action) { - local properties = [ property.take free : [ property.remove incidental : - [ $(self.action).properties ] ] ] ; - + 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 = [ $(ptarget).reference-properties [ $(self.dg).properties ] ] ; + local ref-ps = [ $(ptarget).reference-properties [ $(self.dg).properties-ps ] ] ; - properties = [ sequence.insertion-sort - [ sequence.unique $(properties) ] ] ; - ref = [ sequence.insertion-sort - [ sequence.unique $(ref) ] ] ; - if $(properties) != $(ref) + if [ $(ps).free ] != [ $(ref-ps).free ] { self.extra-path = [ sequence.join main-target- [ $(main-target).name ] ] ; } @@ -491,7 +488,7 @@ rule file-target ( [ $(self.action).path ] $(self.extra-path) ] ; - + return [ path.native $(path) ] ; } } @@ -522,13 +519,24 @@ rule remember-binding ( target : bound-path ) # 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 : properties * ) +rule action ( targets + : sources * : action-name : property-set ? ) { self.targets = $(targets) ; self.sources = $(sources) ; self.action-name = $(action-name) ; - self.properties = $(properties) ; - + + 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) ; @@ -544,7 +552,7 @@ rule action ( targets + : sources * : action-name : properties * ) return $(self.action-name) ; } - rule properties ( ) + rule properties-ps ( ) { return $(self.properties) ; } @@ -556,7 +564,8 @@ rule action ( targets + : sources * : action-name : properties * ) { self.actualized = true ; - local properties = [ adjust-properties [ properties ] ] ; + local ps = [ properties-ps ] ; + local properties = [ adjust-properties [ $(ps).raw ] ] ; local actual-targets ; @@ -601,12 +610,10 @@ rule action ( targets + : sources * : action-name : properties * ) rule path ( ) { - local subvariant = [ property.remove free incidental : $(self.properties) ] ; - local pp = [ property.as-path $(subvariant) ] ; - return $(pp) ; + 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 @@ -678,7 +685,7 @@ rule register ( target ) result = $(t) ; } else if $(a1) && $(a2) && [ $(a1).action-name ] = [ $(a2).action-name ] - && [ $(a1).properties ] = [ $(a2).properties ] && [ $(a1).sources ] = [ $(a2).sources ] + && [ $(a1).properties-ps ] = [ $(a2).properties-ps ] && [ $(a1).sources ] = [ $(a2).sources ] { result = $(t) ; } @@ -801,18 +808,19 @@ local rule clone-action-template ( action from cloned-from : 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 ] : [ $(action).properties ] ] ; + : [ $(action).action-name ] : $(ps) ] ; return $(cloned) ; } local rule subvariant-dg ( main-target # The instance of main-target class - : properties * # Properties requested for this target + : property-set # Properties requested for this target : virtual-targets * ) { self.main-target = $(main-target) ; - self.properties = $(properties) ; + self.properties = $(property-set) ; self.virtual-targets = $(virtual-targets) ; # Pre-compose the list of other dependency graphs, on which this one @@ -836,19 +844,28 @@ local rule subvariant-dg ( main-target # The instance of main-target class } } self.other-dg = [ sequence.unique $(self.other-dg) ] ; - + rule main-target ( ) { return $(self.main-target) ; } - rule properties ( ) + 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) { @@ -858,8 +875,8 @@ local rule subvariant-dg ( main-target # The instance of main-target class { result += [ $(d).all-target-directories ] ; } - return $(result) ; - } + self.target-directories = $(result) ; + } } class subvariant-dg ; diff --git a/v2/build/generators.jam b/v2/build/generators.jam index b2cab15ba..6426596c3 100644 --- a/v2/build/generators.jam +++ b/v2/build/generators.jam @@ -157,6 +157,7 @@ rule generator ( # 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) ; @@ -173,7 +174,7 @@ rule generator ( # Returns a number telling how good generator's properties match # the passed properties, or empty list if generator can't be run # at all. - rule match-rank ( properties * ) + rule match-rank ( property-set ) { # See if generator's requirements are satisfied by 'properties'. # Treat feature name in requirements (i.e. grist-only element), @@ -192,7 +193,8 @@ rule generator ( features += $(r) ; } } - + + local properties = [ $(property-set).raw ] ; if $(requirements) in $(properties) && $(features) in $(properties:G) { return [ sequence.length [ set.intersection @@ -217,7 +219,7 @@ rule generator ( rule run ( project # Project for which the targets are generated name ? # Determines the name of 'name' attribute for # all generated targets. See 'generated-targets' method. - : properties * # Desired properties for generated targets. + : property-set # Desired properties for generated targets. : sources + : # Source targets. multiple ? # Allows the rule to run generator several times and return # multiple targets of the same type. When this argument is not @@ -230,7 +232,7 @@ rule generator ( { generators.dout [ indent ] " generator" $(self.id) ; generators.dout [ indent ] " multiple:" $(mutliple) ; - + # Ordinary generators take only one source targets if $(sources[2]) { @@ -248,7 +250,7 @@ rule generator ( local bypassed = ; convert-to-consumable-types $(project) $(name) : - $(properties) : $(sources) : $(multiple) + $(property-set) : $(sources) : $(multiple) : : consumed bypassed ; @@ -256,7 +258,7 @@ rule generator ( if $(consumed) { result = [ construct-result $(consumed) : $(project) $(name) - : $(properties) ] ; + : $(property-set) ] ; } if $(result) @@ -301,7 +303,7 @@ rule generator ( # Otherwise, might contain several targets with the type of # $(self.source-types[1]) : project name ? - : properties * # Properties to be used for all actions create here + : property-set # Properties to be used for all actions create here ) { local result ; @@ -311,7 +313,7 @@ rule generator ( generators.dout [ indent ] "alt1" ; for local r in $(consumed) { - result += [ generated-targets $(r) : $(properties) : $(project) $(name) ] ; #(targets) ; + result += [ generated-targets $(r) : $(property-set) : $(project) $(name) ] ; #(targets) ; } } else @@ -320,7 +322,8 @@ rule generator ( generators.dout [ indent ] "alt2 : consumed is" [ $(v).str ] ; if $(consumed) { - result += [ generated-targets $(consumed) : $(properties) : $(project) $(name) ] ; + result += [ generated-targets $(consumed) : $(property-set) + : $(project) $(name) ] ; } } return $(result) ; @@ -346,7 +349,7 @@ rule generator ( # Note that this pattern mechanism has nothing to do with implicit patterns # in make. It's a way to produce target which name is different for name of # source. - rule generated-targets ( sources + : properties * : project name ? ) + rule generated-targets ( sources + : property-set : project name ? ) { if ! $(name) { @@ -382,7 +385,8 @@ rule generator ( } # Assign an action for each target local action = [ action-class ] ; - local a = [ new $(action) $(targets) : $(sources) : $(self.id) : $(properties) ] ; + local a = [ new $(action) $(targets) : $(sources) : $(self.id) : + $(property-set) ] ; for local t in $(targets) { $(t).action $(a) ; @@ -395,7 +399,7 @@ rule generator ( # handle. The intention is to produce the set of targets can should be # used when generator is run. rule convert-to-consumable-types ( project name ? : - properties * : source : multiple ? + property-set : source : multiple ? : only-one ? # convert 'source' to only one of source types # if there's more that one possibility, report an # error @@ -449,7 +453,7 @@ rule generator ( if $(missing-types) { local transformed = [ generators.construct-types $(project) $(name) - : $(missing-types) : $(multiple) : $(properties) : $(source) ] ; + : $(missing-types) : $(multiple) : $(property-set) : $(source) ] ; # Add targets of right type to 'consumed'. Add others to # 'bypassed'. The 'generators.construct' rule has done @@ -468,8 +472,7 @@ rule generator ( } } } - - + $(consumed-var) += $(_consumed) ; $(bypassed-var) += $(_bypassed) ; } @@ -492,7 +495,7 @@ rule composing-generator ( id : source-types * : target-types + : generator.__init__ $(id) : $(source-types) : $(target-types) : $(requirements) ; - rule run ( project name ? : properties * : sources + ) + rule run ( project name ? : property-set : sources + ) { generators.dout [ indent ] " composing generator" $(self.id) ; @@ -507,7 +510,7 @@ rule composing-generator ( id : source-types * : target-types + : local c ; local b ; # TODO: need to check for failure on each source. - convert-to-consumable-types $(project) : $(properties) + convert-to-consumable-types $(project) : $(property-set) : $(sources[1]) : * : true : c b ; if ! $(c) { @@ -523,7 +526,8 @@ rule composing-generator ( id : source-types * : target-types + : if ! $(failed) { generators.dout [ indent ] " SUCCESS" ; - result += [ generated-targets $(consumed) : $(properties) : $(project) $(name) ] ; + result += [ generated-targets $(consumed) : $(property-set) + : $(project) $(name) ] ; result += $(bypassed) ; } else @@ -622,9 +626,9 @@ rule base-to-derived-type-conversion ( targets * : target-types + rule try-one-generator ( project name ? : generator multiple ? : - target-types + : properties * : sources * ) + target-types + : property-set : sources * ) { - local targets = [ $(generator).run $(project) $(name) : $(properties) : $(sources) + local targets = [ $(generator).run $(project) $(name) : $(property-set) : $(sources) : $(multiple) ] ; # Generated targets that are of required types @@ -644,8 +648,8 @@ rule try-one-generator ( project name ? : generator multiple ? : { for local e in $(extra) { - local try2 = [ construct-types $(project) $(name) : $(target-types) : : $(properties) - : $(e) ] ; + local try2 = [ construct-types $(project) $(name) + : $(target-types) : : $(property-set) : $(e) ] ; result += $(try2) ; } @@ -662,13 +666,13 @@ rule try-one-generator ( project name ? : generator multiple ? : } rule construct-types ( project name ? : target-types + : multiple ? : - properties * : source ) + property-set : source ) { local result ; local matched-types ; for local t in $(target-types) { - local r = [ construct $(project) $(name) : $(t) $(multiple) : $(properties) : + local r = [ construct $(project) $(name) : $(t) $(multiple) : $(property-set) : $(source) ] ; if $(r) { @@ -719,7 +723,7 @@ local rule ensure-type ( targets * ) # # 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 : properties * : allow-composing ? ) +local rule find-viable-generators ( target-type : property-set : allow-composing ? ) { # Select generators that can create the required target type. local viable-generators = ; @@ -738,7 +742,7 @@ local rule find-viable-generators ( target-type : properties * : allow-composing if ! $(g) in $(.active-generators) && ! ( [ is-a $(g) : composing-generator ] && ! $(allow-composing) ) { - local m = [ $(g).match-rank $(properties) ] ; + local m = [ $(g).match-rank $(property-set) ] ; if $(m) { viable-generators += $(g) ; @@ -803,7 +807,7 @@ local rule select-dependency-graph ( options ) # Attempt to construct the target by looking at transformation cache. local rule construct-with-caching ( - project name ? : target-type multiple ? : properties * : sources * ) + project name ? : target-type multiple ? : property-set : sources * ) { local result ; if ! $(.caching) && ! $(sources[2]) && $(sources[1]) && ! $(name) @@ -812,7 +816,7 @@ local rule construct-with-caching ( local t = $(sources[1]) ; - local signature = [ sequence.join [ $(t).type ] $(target-type) $(properties) : - ] ; + local signature = [ sequence.join [ $(t).type ] $(target-type) $(property-set) : - ] ; # Get a transformation template from cache or create it. local cresult ; @@ -823,7 +827,8 @@ local rule construct-with-caching ( else { local ut = [ new file-target % : [ $(t).type ] : "no project" ] ; - cresult = [ construct $(project) : $(target-type) $(multiple) : $(properties) : $(ut) ] ; + cresult = [ construct $(project) : $(target-type) $(multiple) + : $(property-set) : $(ut) ] ; .transformation.cache.$(signature) = $(cresult) ; } @@ -846,9 +851,9 @@ local rule construct-with-caching ( # 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 ? : properties * : sources * ) + project name ? : target-type multiple ? : property-set : sources * ) { - viable-generators = [ find-viable-generators $(target-type) : $(properties) + viable-generators = [ find-viable-generators $(target-type) : $(property-set) : $(allow-composing) ] ; local results = [ new vector ] ; @@ -862,7 +867,7 @@ local rule construct-without-caching ( local .active-generators = $(g) $(.active-generators) ; local r = [ try-one-generator $(project) $(name) : $(g) $(multiple) : $(target-type) : - $(properties) : $(sources) ] ; + $(property-set) : $(sources) ] ; if $(r) { @@ -885,7 +890,7 @@ local rule construct-without-caching ( # 'construct' in stack, returns only targets of requested 'target-type', # otherwise, returns also unused sources and additionally generated # targets. -rule construct ( project name ? : target-type multiple ? : properties * : sources * +rule construct ( project name ? : target-type multiple ? : property-set * : sources * : allow-composing ? # Allows to use composing generators for constructing this # target. This will be typically set when creating main targets, # and unset when called recursively from 'run' method of @@ -905,8 +910,8 @@ rule construct ( project name ? : target-type multiple ? : properties * : source usage-requirements += [ $(e).usage-requirements ] ; } } - properties += $(usage-requirements) ; - properties = [ sequence.unique $(properties) ] ; + property-set = [ $(property-set).add + [ property-set.create $(usage-requirements) ] ] ; .construct-stack += 1 ; @@ -926,11 +931,11 @@ rule construct ( project name ? : target-type multiple ? : properties * : source generators.dout [ indent ] " properties:" $(properties) ; local result = [ construct-with-caching $(project) $(name) - : $(target-type) $(multiple) : $(properties) : $(sources) ] ; + : $(target-type) $(multiple) : $(property-set) : $(sources) ] ; if ! $(result) { result = [ construct-without-caching $(project) $(name) - : $(target-type) $(multiple) : $(properties) : $(sources) ] ; + : $(target-type) $(multiple) : $(property-set) : $(sources) ] ; } decrease-indent ; @@ -940,8 +945,8 @@ rule construct ( project name ? : target-type multiple ? : properties * : source if ! $(.construct-stack) # This is first invocation in stack { # Make roots of dependency graph depend on all 'dependency' features. - local dp = [ property.take dependency : $(properties) ] ; - + local dp = [ $(property-set).dependency ] ; + if $(dp) { for local t in $(result) diff --git a/v2/build/project.jam b/v2/build/project.jam index 6cd3fa794..b4628a9b7 100644 --- a/v2/build/project.jam +++ b/v2/build/project.jam @@ -386,6 +386,14 @@ local rule initialize ( [ path.relative $(our-dir) $(parent-dir) ] ] : exact ; } } + else + { + $(attributes).set requirements + : [ property-set.empty ] : exact ; + $(attributes).set usage-requirements + : [ property-set.empty ] : exact ; + } + } # Associate the given id with the given location @@ -416,9 +424,16 @@ rule project-attributes ( location ) { specification = [ property.translate-paths $(specification) : $(self.location) ] ; - local current = $(self.requirements) ; - local result = [ property.refine $(current) : - [ property.make $(specification) ] ] ; + specification = [ property.make $(specification) ] ; + result = [ property-set.create $(specification) ] ; + + # If we have inherited properties, need to refine them with the + # specified. + local current = $(self.requirements) ; + if $(current) + { + result = [ $(current).refine $(result) ] ; + } if $(result[1]) = "@error" { @@ -439,8 +454,17 @@ rule project-attributes ( location ) { errors.error "usage-requirements" $($(real-pos2)) "have non-free properties" $(non-free) ; } - self.usage-requirements += [ property.translate-paths $(specification) + local t = [ property.translate-paths $(specification) : $(self.location) ] ; + if $(self.usage-requirements) + { + self.usage-requirements = [ property-set.create + [ $(self.usage-requirements).raw ] $(t) ] ; + } + else + { + self.usage-requirements = [ property-set.create $(t) ] ; + } } else if $(attribute) = "source-location" { @@ -480,7 +504,7 @@ rule project-attributes ( location ) print.list-start ; print.list-item "Project root:" $(self.project-root) ; print.list-item "Parent project:" $(parent) ; - print.list-item "Requirements:" $(self.requirements) ; + print.list-item "Requirements:" [ $(self.requirements).raw ] ; print.list-item "Default build:" $(self.default-build) ; print.list-item "Source location:" $(self.source-location) ; print.list-item "Projects to build:" diff --git a/v2/build/targets.jam b/v2/build/targets.jam index 8c40dfebf..8d1ebfbd2 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -72,6 +72,7 @@ import regex ; import property ; import errors ; import common ; +import property-set ; # Base class for all abstract targets. @@ -109,7 +110,7 @@ rule abstract-target ( name # name of the target in Jamfile # Adds one more direct build request for this target. If later generate # is called with the same non-free non-incidental properties as in one # of direct build requests, then that build request is used instead. - rule direct-build-request ( properties * ) + rule direct-build-request ( property-set ) { } @@ -122,7 +123,7 @@ rule abstract-target ( name # name of the target in Jamfile # # If 'properties' are empty, performs default build of this target, in a way specific # to derived class. - rule generate ( properties * ) + rule generate ( property-set ) { errors.error "method should be defined in derived classes" ; } @@ -137,29 +138,30 @@ rule project-target ( name : project : requirements * : default-build * ) self.requirements = $(requirements) ; self.default-build = $(default-build) ; - rule direct-build-request ( properties * ) + rule direct-build-request ( property-set ) { for local name in $(self.main-targets) { local t = [ main-target $(name) ] ; - result += [ $(t).direct-build-request $(properties) ] ; + result += [ $(t).direct-build-request $(property-set) ] ; } for local pn in [ project.attribute $(self.project) projects-to-build ] { local p = [ project.module-name $(pn) ] ; local t = [ project.target [ project.attribute $(p) location ] ] ; - result += [ $(t).direct-build-request $(properties) ] ; + result += [ $(t).direct-build-request $(property-set) ] ; } } # Generates all possible targets contained in this project. - rule generate ( properties * ) + rule generate ( property-set * ) { # Project properties are directly imposed on all main targets. # However, we'd need to check if this project can be build at # all. - local xproperties = - [ property.refine $(properties) : $(self.requirements) ] ; + + local xproperties = [ $(property-set).refine $(self.requirements) ] ; + if $(xproperties[1]) = "@error" { local id = [ project.attribute $(self.project) id ] ; @@ -168,7 +170,7 @@ rule project-target ( name : project : requirements * : default-build * ) "due to unsatisfied requirements." ; print.wrapped-text "warning: explanation: " $(xproperties[2-]) ; print.wrapped-text "warning: build-request: " $(properties) ; - print.wrapped-text "warning: requirements: " $(self.requirements) ; + print.wrapped-text "warning: requirements: " [ $(self.requirements).raw ] ; } else { @@ -176,14 +178,14 @@ rule project-target ( name : project : requirements * : default-build * ) for local name in $(self.main-targets) { local t = [ main-target $(name) ] ; - result += [ $(t).generate $(properties) ] ; + result += [ $(t).generate $(property-set) ] ; } local self-location = [ project.attribute $(self.project) location ] ; for local pn in [ project.attribute $(self.project) projects-to-build ] { local p = [ project.module-name [ path.join $(self-location) $(pn) ] ] ; local t = [ project.target [ project.attribute $(p) location ] ] ; - result += [ $(t).generate $(properties) ] ; + result += [ $(t).generate $(property-set) ] ; } return $(result) ; } @@ -215,19 +217,19 @@ rule project-target ( name : project : requirements * : default-build * ) # target in this project, if generation with # 'properties' is requested, and that main target # does not have any requirements of its own. - rule reference-properties ( properties * ) + rule reference-properties ( property-set ) { - # Note that free properties can appear on 'properties' only - # from direct build request, because free properties are not - # allowed to be propagated. - - local ref = [ project.attribute $(self.project) requirements ] ; - ref = [ property.refine $(properties) : $(ref) ] ; - ref = [ property.evaluate-conditionals $(ref) ] ; - ref = [ property.take free : [ property.remove incidental : $(ref) ] ] ; - ref = [ targets.generate-dependencies $(ref) : $(self.project) ] ; - return $(ref) ; - } + if ! $(self.ref-props.$(property-set)) + { + local ref = [ project.attribute $(self.project) requirements ] ; + local ps = [ $(property-set).refine $(ref) ] ; + ps = [ $(ps).evaluate-conditionals ] ; + + ps = [ targets.generate-dependencies $(ps) : $(self.project) : $(ps) ] ; + self.ref-props.$(property-set) = $(ps) ; + } + return $(self.ref-props.$(property-set)) ; + } } class project-target : abstract-target ; @@ -246,9 +248,9 @@ rule main-target ( name : project ) self.alternatives += $(target) ; } - rule direct-build-request ( properties * ) + rule direct-build-request ( property-set ) { - local base = [ property.remove free incidental : $(properties) ] ; + local base = [ $(property-set).base ] ; local ep = $(self.direct-request.$(base:J=-)) ; if $(ep) && $(ep) != $(properties) { @@ -256,7 +258,7 @@ rule main-target ( name : project ) } else { - self.direct-request.$(base:J=-) = $(properties) ; + self.direct-request.$(base:J=-) = $(property-set) ; } } @@ -265,13 +267,13 @@ rule main-target ( name : project ) # which requirements are satisfied by 'properties' and picking the one with # longest requirements set. # Returns the result of calling 'generate' on that alternative. - rule generate ( properties * ) + rule generate ( property-set ) { - local base = [ property.remove free incidental : $(properties) ] ; + local base = [ $(property-set).base ] ; local ep = $(self.direct-request.$(base:J=-)) ; if $(ep) { - properties = $(ep) ; + property-set = $(ep) ; } # Try to generate all the alternatives. @@ -279,7 +281,7 @@ rule main-target ( name : project ) for local v in $(self.alternatives) { - local vtargets = [ $(v).generate $(properties) ] ; + local vtargets = [ $(v).generate $(property-set) ] ; if $(vtargets) && $(vtargets[1]) != "@error" { $(alternatives).push-back [ new vector $(v) $(vtargets) ] ; @@ -291,7 +293,7 @@ rule main-target ( name : project ) # why it can't be build. print.wrapped-text "warning: skipped build of" [ full-name ] - "with properties" $(properties) ; + "with properties" [ $(property-set).raw ] ; } else { local result ; if [ $(alternatives).size ] = 1 @@ -314,9 +316,9 @@ rule main-target ( name : project ) # have 'requirements' method. # assert.equal [ is-a $(target) : basic-target ] : true ; local req = [ $(target).requirements ] ; - req = [ property.remove free incidental : $(req) ] ; + req = [ $(req).base ] ; req-length += [ sequence.length - [ set.intersection $(req) : $(properties) ] ] ; + [ set.intersection $(req) : [ $(property-set).raw ] ] ] ; } local best = [ sequence.select-highest-ranked $(r) : $(req-length) ] ; @@ -343,7 +345,7 @@ rule main-target ( name : project ) # is created. local all-targets = [ sequence.transform virtual-target.traverse : $(result) ] ; - local dg = [ new subvariant-dg $(__name__) : $(properties) : $(all-targets) ] ; + local dg = [ new subvariant-dg $(__name__) : $(property-set) : $(all-targets) ] ; for local v in $(all-targets) { $(v).dg $(dg) ; @@ -361,7 +363,7 @@ class main-target : abstract-target ; # reference. rule generate ( target-reference # Target reference : project # Project where the reference is made - : properties * # Properties of the main target that + : property-set # Properties of the main target that # makes the reference ) { @@ -382,8 +384,9 @@ rule generate ( target-reference # Target reference if $(main-target) { # Take properties which should be propagated and refine them # with source-specific requirements. - local propagated = [ property.take propagated : $(properties) ] ; - local rproperties = [ property.refine $(propagated) : $(sproperties) ] ; + local propagated = [ $(property-set).propagated ] ; + local rproperties = [ $(propagated).refine + [ property-set.create $(sproperties) ] ] ; if $(rproperties[1]) = "@error" { errors.error @@ -395,42 +398,32 @@ rule generate ( target-reference # Target reference } } -# Returns a list of properties, where all dependency properties are -# replaced as follows: -# - the value of dependency property is treated as target reference, -# which is generated with 'properties' + 'extra-properties' -# - the created virtual target's become replace the property value, -# for example a/b might become -# object(virtual-target)@1 -# In addition, usage requirements for all created virtual targets -# are added to the properties. +# Returns new property set which inclues all properties from +# 'property-set', except that all dependency properties are +# generated with 'generation-ps', and the obtained virtual targets +# are added as the values of original features. # -# The purpose of 'extra-properties' is to be able to replace dependency -# features only in certain set of properties, which does not include -# all build properties. A concrete example is when we process use properties. -# Dependency features must be replaced using full build properties of target, -# but we don't want to add build properties to usage requirements. -rule generate-dependencies ( properties * : project : extra-properties * ) +# For example, a/b might become object(virtual-target)@1 +# In addition, usage requirements for all created virtual targets +# are added to the created property set. +rule generate-dependencies ( property-set : project : generation-ps ) { - local xproperties ; - for local p in $(properties) + local xproperties ; + for local p in [ $(property-set).dependency ] { - if dependency in [ feature.attributes $(p:G) ] + local g = [ targets.generate $(p:TG=) : $(project) : $(generation-ps) ] ; + if ! $(g) { - local g = [ targets.generate $(p:TG=) : $(project) - : $(properties) $(extra-properties) ] ; - if ! $(g) - { - errors.error "cannot generate dependency " $(p) ; - } - xproperties += $(p:G)$(g) [ $(g).usage-requirements ] ; + errors.error "cannot generate dependency " $(p) ; } - else - { - xproperties += $(p) ; - } + xproperties += $(p:G)$(g) [ $(g).usage-requirements ] ; } - return $(xproperties) ; + local r = [ property-set.create + [ $(property-set).base ] + [ $(property-set).free ] + $(xproperties) + [ $(property-set).incidental ] ] ; + return $(r) ; } @@ -448,8 +441,15 @@ rule basic-target ( name : project abstract-target.__init__ $(name) : $(project) ; self.sources = $(sources) ; + if ! $(requirements) { + requirements = [ property-set.empty ] ; + } self.requirements = $(requirements) ; self.default-build = $(default-build) ; + if ! $(usage-requirements) + { + usage-requirements = [ property-set.empty ] ; + } self.usage-requirements = $(usage-requirements) ; if $(sources:G) @@ -463,35 +463,31 @@ rule basic-target ( name : project # Generates sources. Calls 'construct' # This method should not be overriden. # - rule generate ( properties * ) + rule generate ( property-set ) { - if ! $(properties) || $(properties:G) = + if ! [ $(property-set).raw ] { # CONSIDER: I'm really not sure if this is correct... # FIXME: as in 'build-system' we stick default toolset - properties = [ build-request.expand $(properties) - $(self.default-build) ] ; + properties = [ build-request.expand $(self.default-build) ] ; local result = ; for local p in $(properties) { - result += [ generate [ feature.split $(p) ] ] ; + result += [ generate [ property-set.create [ feature.split $(p) ] ] ] ; } return $(result) ; } else { - property-path = [ property.as-path - [ property.remove free incidental : $(properties) ] ] ; + + property-path = [ property.as-path [ $(property-set).base ] ] ; if ! $(property-path) { property-path = X ; } if ! $(self.generated.$(property-path)) { - local rproperties = - [ property.refine $(properties) : $(self.requirements) ] ; - - rproperties = [ property.evaluate-conditionals $(rproperties) ] ; + local rproperties = [ $(property-set).refine $(self.requirements) ] ; if $(rproperties[1]) != "@error" { @@ -503,9 +499,12 @@ rule basic-target ( name : project # requirements may also code from dependencies. However, when they # come from dependencies, the value is not target id, but rather # virtual target names, so generators.construct can use them. + + rproperties = [ $(rproperties).evaluate-conditionals ] ; local xproperties = - [ targets.generate-dependencies $(rproperties) : $(self.project) ] ; + [ targets.generate-dependencies $(rproperties) : $(self.project) + : $(rproperties) ] ; local source-targets ; @@ -513,7 +512,7 @@ rule basic-target ( name : project { # Try treating this source as reference to main target local more-targets = - [ targets.generate $(s) : $(self.project) : $(properties) ] ; + [ targets.generate $(s) : $(self.project) : $(property-set) ] ; if $(more-targets) { source-targets += $(more-targets) ; @@ -529,16 +528,19 @@ rule basic-target ( name : project # unqual to project's reference properties. As the # result, we create per-target bin directory while # it's not really needed. - xproperties = [ feature.run-actions $(xproperties) ] ; - + + xproperties = [ $(xproperties).run-actions ] ; + self.generated.$(property-path) = [ construct $(source-targets) : $(xproperties) ] ; # Apply use requirement of this target to all generated # virtual targets. local xusage-requirements = - [ targets.generate-dependencies $(self.usage-requirements) + [ targets.generate-dependencies + $(self.usage-requirements) : $(self.project) : $(rproperties) ] ; + xusage-requirements = [ $(xusage-requirements).raw ] ; for local e in $(self.generated.$(property-path)) { @@ -579,11 +581,11 @@ rule typed-target ( name : project : type self.type = $(type) ; - rule construct ( source-targets * : properties * ) + rule construct ( source-targets * : property-set ) { local r = [ generators.construct $(self.project) $(self.name) : $(self.type) - : $(properties) # [ feature.expand - $(self.type) + : [ property-set.create [ $(property-set).raw ] # [ feature.expand + $(self.type) ] # ] : $(source-targets) : allow-composing ] ; @@ -609,8 +611,9 @@ rule main-target-requirements ( { local loc = [ project.attribute $(project) location ] ; local requirements = [ property.translate-paths $(specification) : $(loc) ] ; + local requirements = [ property-set.create $(requirements) ] ; local project-requirements = [ project.attribute $(project) requirements ] ; - requirements = [ property.refine $(project-requirements) : $(requirements) ] ; + requirements = [ $(project-requirements).refine $(requirements) ] ; if $(requirements[1]) = "@error" { errors.error "Conflicting requirements for target:" $(requirements) ; @@ -629,11 +632,11 @@ rule main-target-usage-requirements ( { local loc = [ project.attribute $(project) location ] ; local project-usage-requirements = [ project.attribute $(project) usage-requirements ] ; - - local usage-requirements = [ property.translate-paths $(specification) : $(loc) ] ; - usage-requirements += $(project-usage-requirements) ; - - return $(usage-requirements) ; + + local usage-requirements = [ property-set.create + [ property.translate-paths $(specification) : $(loc) ] ] ; + + return [ $(project-usage-requirements).add $(usage-requirements) ] ; } # Return the default build value to use when declaring a main target, diff --git a/v2/build/virtual-target.jam b/v2/build/virtual-target.jam index 7f3be6f9b..ec63fde8d 100644 --- a/v2/build/virtual-target.jam +++ b/v2/build/virtual-target.jam @@ -10,6 +10,8 @@ import class : class new ; import type ; +import property-set ; +import utility ; # +--------------------------+ # | virtual-target | @@ -349,8 +351,8 @@ rule abstract-file-target ( name local properties ; if $(self.action) { - properties = [ property.remove free incidental : [ $(self.action).properties ] ] ; - local property-grist = [ property.as-path $(properties) ] ; + local ps = [ $(self.action).properties-ps ] ; + local property-grist = [ $(ps).as-path ] ; grist = $(location-grist)/$(property-grist) ; } if ! $(grist) @@ -447,21 +449,16 @@ rule file-target ( { if $(self.action) { - local properties = [ property.take free : [ property.remove incidental : - [ $(self.action).properties ] ] ] ; - + 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 = [ $(ptarget).reference-properties [ $(self.dg).properties ] ] ; + local ref-ps = [ $(ptarget).reference-properties [ $(self.dg).properties-ps ] ] ; - properties = [ sequence.insertion-sort - [ sequence.unique $(properties) ] ] ; - ref = [ sequence.insertion-sort - [ sequence.unique $(ref) ] ] ; - if $(properties) != $(ref) + if [ $(ps).free ] != [ $(ref-ps).free ] { self.extra-path = [ sequence.join main-target- [ $(main-target).name ] ] ; } @@ -491,7 +488,7 @@ rule file-target ( [ $(self.action).path ] $(self.extra-path) ] ; - + return [ path.native $(path) ] ; } } @@ -522,13 +519,24 @@ rule remember-binding ( target : bound-path ) # 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 : properties * ) +rule action ( targets + : sources * : action-name : property-set ? ) { self.targets = $(targets) ; self.sources = $(sources) ; self.action-name = $(action-name) ; - self.properties = $(properties) ; - + + 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) ; @@ -544,7 +552,7 @@ rule action ( targets + : sources * : action-name : properties * ) return $(self.action-name) ; } - rule properties ( ) + rule properties-ps ( ) { return $(self.properties) ; } @@ -556,7 +564,8 @@ rule action ( targets + : sources * : action-name : properties * ) { self.actualized = true ; - local properties = [ adjust-properties [ properties ] ] ; + local ps = [ properties-ps ] ; + local properties = [ adjust-properties [ $(ps).raw ] ] ; local actual-targets ; @@ -601,12 +610,10 @@ rule action ( targets + : sources * : action-name : properties * ) rule path ( ) { - local subvariant = [ property.remove free incidental : $(self.properties) ] ; - local pp = [ property.as-path $(subvariant) ] ; - return $(pp) ; + 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 @@ -678,7 +685,7 @@ rule register ( target ) result = $(t) ; } else if $(a1) && $(a2) && [ $(a1).action-name ] = [ $(a2).action-name ] - && [ $(a1).properties ] = [ $(a2).properties ] && [ $(a1).sources ] = [ $(a2).sources ] + && [ $(a1).properties-ps ] = [ $(a2).properties-ps ] && [ $(a1).sources ] = [ $(a2).sources ] { result = $(t) ; } @@ -801,18 +808,19 @@ local rule clone-action-template ( action from cloned-from : 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 ] : [ $(action).properties ] ] ; + : [ $(action).action-name ] : $(ps) ] ; return $(cloned) ; } local rule subvariant-dg ( main-target # The instance of main-target class - : properties * # Properties requested for this target + : property-set # Properties requested for this target : virtual-targets * ) { self.main-target = $(main-target) ; - self.properties = $(properties) ; + self.properties = $(property-set) ; self.virtual-targets = $(virtual-targets) ; # Pre-compose the list of other dependency graphs, on which this one @@ -836,19 +844,28 @@ local rule subvariant-dg ( main-target # The instance of main-target class } } self.other-dg = [ sequence.unique $(self.other-dg) ] ; - + rule main-target ( ) { return $(self.main-target) ; } - rule properties ( ) + 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) { @@ -858,8 +875,8 @@ local rule subvariant-dg ( main-target # The instance of main-target class { result += [ $(d).all-target-directories ] ; } - return $(result) ; - } + self.target-directories = $(result) ; + } } class subvariant-dg ; diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index 6db1996a4..fc0656f07 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -293,8 +293,9 @@ rule lib-generator ( ) { composing-generator.__init__ lib-generator : : LIB : LIB ; - rule run ( project name ? : properties * : sources * ) + rule run ( project name ? : property-set : sources * ) { + local properties = [ $(property-set).raw ] ; # Determine the needed target type local actual-type ; if in $(properties:G) || in $(properties:G) @@ -311,8 +312,8 @@ rule lib-generator ( ) } # Construct the target. Pass 'allow-composing', since generators for # library types are composing and we need to find them. - return [ generators.construct $(project) $(name) : $(actual-type) : $(properties) - : $(sources) : allow-composing ] ; + return [ generators.construct $(project) $(name) : $(actual-type) + : $(property-set) : $(sources) : allow-composing ] ; } } diff --git a/v2/tools/make.jam b/v2/tools/make.jam index cc561875f..f4a6c22c2 100644 --- a/v2/tools/make.jam +++ b/v2/tools/make.jam @@ -12,21 +12,23 @@ import property ; import errors : error ; import type : type ; import regex ; +import property-set ; rule make-target-class ( name : project : sources * : requirements * : make-rule + : default-build * ) { basic-target.__init__ $(name) : $(project) : $(sources) : $(requirements) : $(default-build) ; + self.make-rule = $(make-rule) ; - rule construct ( source-targets * : properties * ) + rule construct ( source-targets * : property-set ) { local t = [ new file-target $(self.name:S=) : [ type.type $(self.name:S) ] : $(self.project) ] ; $(t).suffix [ regex.match .(.*) : $(self.name:S) ] ; local a = [ new action $(t) : $(source-targets) : $(self.make-rule) - : $(properties) ] ; + : $(property-set) ] ; $(t).action $(a) ; return $(t) ; } diff --git a/v2/tools/prebuilt.jam b/v2/tools/prebuilt.jam index ba278bd41..acd640ca9 100644 --- a/v2/tools/prebuilt.jam +++ b/v2/tools/prebuilt.jam @@ -21,8 +21,9 @@ rule prebuilt-file-generator { generator.__init__ prebuilt-file-generator : : * : ; - rule run ( project name ? : properties * : sources * ) + rule run ( project name ? : property-set : sources * ) { + local properties = [ $(property-set).raw ] ; local name = [ feature.get-values : $(properties) ] ; local type = [ type.type $(name:S) ] ; if ! $(type) @@ -42,7 +43,7 @@ rule prebuilt-file-generator # Therefore, we encode properties via 'extra-grist', which is ugly. # Maybe, we should just introduce 'do-nothing-action', which only # keps properties. - $(t).extra-grist [ property.as-path $(properties) ] ; + $(t).extra-grist [ $(property-set).as-path ] ; $(t).suffix [ MATCH .(.*) : $(name:S) ] ; return $(t) ;