diff --git a/new/feature.jam b/new/feature.jam index f8c5e1ba9..11e46c1d3 100644 --- a/new/feature.jam +++ b/new/feature.jam @@ -31,8 +31,9 @@ import errors : error lol->list ; import sequence ; import regex ; import set ; +import utility ; -all-attributes = +.all-attributes = implicit # features whose values alone identify the # feature. For example, a user is not required to @@ -94,8 +95,8 @@ all-attributes = ; -all-features = ; -all-implicit-values = ; +.all-features = ; +.all-implicit-values = ; # Transform features by bracketing any elements which aren't already # bracketed by "<>" @@ -115,12 +116,12 @@ rule feature ( name : values * : attributes * ) local error ; # if there are any unknown attributes... - if ! ( $(attributes) in $(all-attributes) ) + if ! ( $(attributes) in $(.all-attributes) ) { error = unknown attributes: - [ set.difference $(attributes) : $(all-attributes) ] ; + [ set.difference $(attributes) : $(.all-attributes) ] ; } - else if $(name) in $(all-features) + else if $(name) in $(.all-features) { error = feature already defined: ; } @@ -144,7 +145,7 @@ rule feature ( name : values * : attributes * ) $(name).subfeatures ?= ; $(attributes).features += $(name) ; - all-features += $(name) ; + .all-features += $(name) ; extend $(name) : $(values) ; } @@ -172,7 +173,7 @@ rule defaults ( features * ) # returns true iff all elements of names are valid features. rule valid ( names + ) { - if [ grist $(names) ] in $(all-features) + if [ grist $(names) ] in $(.all-features) { return true ; } @@ -214,12 +215,9 @@ local rule find-implied-subfeature ( feature subvalue : value-string ? ) error invalid feature $(feature) ; } - local v - = subfeature($(feature),$(subvalue)) - subfeature($(feature),$(value-string),$(subvalue)) ; + value-string ?= "" ; - local subfeature = $($(v)) ; - return $(subfeature[1]) ; + return $($(feature)$(value-string)<>$(subvalue).subfeature) ; } # Given a feature name and a subfeature value, find the associated @@ -242,7 +240,7 @@ rule implied-subfeature ( feature subvalue : value-string ? ) # generate an error if the feature is unknown local rule validate-feature ( feature ) { - if ! [ grist $(feature) ] in $(all-features) + if ! [ grist $(feature) ] in $(.all-features) { error unknown feature \"$(feature)\" ; } @@ -266,7 +264,7 @@ local rule expand-subfeatures-aux ( feature ? : value ) { feature = [ implied-feature $(components[1]) ] ; } - else if ! $(feature) in $(all-features) + else if ! $(feature) in $(.all-features) { error unknown feature $(feature) ; } @@ -295,7 +293,7 @@ rule expand-subfeatures ( properties * ) return $(result) ; } -# Helper for extend, below. Handles the case feature case. +# Helper for extend, below. Handles the feature case. local rule extend-feature ( feature : values * ) { validate-feature $(feature) ; @@ -310,12 +308,12 @@ local rule extend-feature ( feature : values * ) $(v).implicit-feature = $(feature) ; } - all-implicit-values += $(values) ; + .all-implicit-values += $(values) ; } $(feature).values += $(values) ; } -# Checks that value-string is a valide value-string for the given feature. +# Checks that value-string is a valid value-string for the given feature. local rule validate-value-string ( feature value-string ) { local values = $(value-string) ; @@ -354,12 +352,12 @@ rule extend-subfeature ( feature value-string ? : subfeature : subvalues * ) validate-value-string $(feature) $(value-string) ; } + # provide a way to get from the given feature or property and + # subfeature value to the subfeature name. + value-string ?= "" ; for local subvalue in $(subvalues) { - local v - = subfeature($(feature),$(value-string),$(subvalue)) - subfeature($(feature),$(subvalue)) ; - $(v[1]) = $(subfeature) ; + $(feature)$(value-string)<>$(subvalue).subfeature = $(subfeature) ; } } @@ -431,6 +429,8 @@ rule subfeature ( feature value-string ? : subfeature : subvalues * ) } $(feature).subfeatures += $(subfeature) ; extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ; + local f = [ utility.ungrist $(feature) ] ; + feature $(f)-$(subfeature) : $(subvalues) ; } # Set the components of the given composite property @@ -449,6 +449,10 @@ rule compose ( composite-property : component-properties * ) $($(composite-property).components) ; } + if $(composite-property) in $(components) + { + errror composite property "$(composite-property)" cannot have itself as a component ; + } $(composite-property).components = $(component-properties) ; } @@ -486,6 +490,8 @@ rule free-features ( ) return $(free.features) ; } +# Expand all composite properties in the set so that all components +# are explicitly expressed. rule expand-composites ( properties * ) { local explicit-features = $(properties:G) ; @@ -538,6 +544,76 @@ rule expand ( properties * ) return [ expand-composites $(expanded) ] ; } + +# Helper rule for minimize, below - return true iff property's feature +# is present in the contents of the variable named by feature-set-var. +local rule in-features ( feature-set-var property ) +{ + if $(property:G) in $($(feature-set-var)) + { + return true ; + } +} + +# Given an expanded property set, eliminate all redundancy: properties +# which are elements of other (composite) properties in the set will +# be eliminated, implicit properties will be expressed without feature +# grist, and sub-property values will be expressed as elements joined +# to the corresponding main property. +rule minimize ( properties * ) +{ + # remove properties implied by composite features + local x = $(properties) ; + for local p in $(properties) + { + if ! $(p:G) + { + error minimize requires an expanded property set, but \"$(p)\" + appears to be an un-expanded implicit feature ; + } + x = [ set.difference $(x) : $($(p).components) ] ; + } + + # handle subfeatures and implicit features + local result ; + while $(x) + { + local p = $(x[1]) ; + local f = $(p:G) ; + + # eliminate features in implicit properties. + if implicit in [ attributes $(f) ] + { + p = $(p:G="") ; + } + + # locate all subproperties of f in the property set + local subproperties ; + local subfeatures = $($(f).subfeatures) ; + if $(subfeatures) + { + local f_ = [ utility.ungrist $(f) ] ; + subfeatures = [ grist $(f_)-$(subfeatures) ] ; + subproperties = [ sequence.filter in-features subfeatures : $(x) ] ; + } + + if $(subproperties) + { + # reconstitute the joined property name + local sorted = [ sequence.insertion-sort $(subproperties) ] ; + result += $(p)-$(sorted:G="":J=-) ; + + x = [ set.difference $(x[2-]) : $(subproperties) ] ; + } + else + { + result += $(p) ; + x = $(x[2-]) ; + } + } + return $(result) ; +} + # Given a property-set of the form # v1/v2/...vN-1/vN/vN+1/...vM # @@ -619,6 +695,18 @@ local rule __test__ ( ) : expand debug on ; + assert.result gcc-3.0.1 debug on + : minimize [ expand gcc-3.0.1 debug on ] + ; + + assert.result gcc-3.0.1 debug + : minimize [ expand gcc-3.0.1 debug off ] + ; + + assert.result debug on + : minimize [ expand debug on ] + ; + assert.result y/z b/c e/f : split y/z/b/c/e/f ; diff --git a/new/property.jam b/new/property.jam index 0ca53dcf9..05e738d13 100644 --- a/new/property.jam +++ b/new/property.jam @@ -77,16 +77,57 @@ rule refine ( properties * : requirements * : feature-space ? ) } } -# Returns a path which represents the given property set. -# TODO: this is a quick sketch. We need much better logic. -rule as-path ( properties * ) +# Helper for as-path, below. Orders properties with the implicit ones +# first, and within the two sections in alphabetical order of feature +# name. +local rule path-order ( feature-space x y ) { + if $(y:G) && ! $(x:G) + { + return true ; + } + else if $(x:G) && ! $(y:G) + { + return ; + } + else + { + if ! $(x:G) + { + x = [ $(feature-space).expand-subfeatures $(x) ] ; + y = [ $(feature-space).expand-subfeatures $(y) ] ; + } + + if $(x[0]) < $(y[0]) + { + return true ; + } + } +} + +# Returns a path which represents the given expanded property set. +rule as-path ( properties * : feature-space ? ) +{ + feature-space ?= feature ; + + # trim redundancy + properties = [ $(feature-space).minimize $(properties) ] ; + + # sort according to path-order + properties = [ sequence.insertion-sort $(properties) : path-order $(feature-space) ] ; + local components ; for local p in $(properties) { - local name = [ ungrist $(p:G) ] ; - local value = $(p:G=) ; - components += $(name)-$(value) ; + if $(p:G) + { + local f = [ ungrist $(p:G) ] ; + components += $(f)-$(p:G=) ; + } + else + { + components += $(p) ; + } } return $(components:J=/) ; } @@ -111,6 +152,7 @@ local rule __test__ ( ) compose debug : _DEBUG off ; compose release : NDEBUG on ; + } assert.result gcc off FOO @@ -129,15 +171,20 @@ local rule __test__ ( ) : refine gcc : off : $(test-space) ; + assert.result debug + : as-path off debug + : $(test-space) + ; + + assert.result gcc/debug/rtti-off + : as-path gcc off off debug + : $(test-space) + ; r = [ refine gcc off : on : $(test-space) ] ; assert.equal $(r[1]) : "@error" ; - assert.result optimization-off/variant-debug - : as-path off debug - ; - } diff --git a/test/project_test3.py b/test/project_test3.py index 83f296bb2..ed8b94515 100644 --- a/test/project_test3.py +++ b/test/project_test3.py @@ -11,14 +11,21 @@ t.set_tree("project-test3") t.run_build_system() t.expect_addition("bin/a.obj") -t.fail_test(t.read("bin/a.obj") != \ -"""variant-debug/optimization-off + +expected = """debug a.cpp -""") +""" + +if t.read("bin/a.obj") != expected: + print '***expected:' + print expected + print '***got this instead:' + print t.read("bin/a.obj") + t.fail_test(1) t.expect_addition("bin/a.exe") -t.fail_test(t.read("bin/a.exe") != \ -"""variant-debug/optimization-off +t.fail_test(t.read("bin/a.exe") != +"""debug bin/a.obj """) diff --git a/v2/build/feature.jam b/v2/build/feature.jam index f8c5e1ba9..11e46c1d3 100644 --- a/v2/build/feature.jam +++ b/v2/build/feature.jam @@ -31,8 +31,9 @@ import errors : error lol->list ; import sequence ; import regex ; import set ; +import utility ; -all-attributes = +.all-attributes = implicit # features whose values alone identify the # feature. For example, a user is not required to @@ -94,8 +95,8 @@ all-attributes = ; -all-features = ; -all-implicit-values = ; +.all-features = ; +.all-implicit-values = ; # Transform features by bracketing any elements which aren't already # bracketed by "<>" @@ -115,12 +116,12 @@ rule feature ( name : values * : attributes * ) local error ; # if there are any unknown attributes... - if ! ( $(attributes) in $(all-attributes) ) + if ! ( $(attributes) in $(.all-attributes) ) { error = unknown attributes: - [ set.difference $(attributes) : $(all-attributes) ] ; + [ set.difference $(attributes) : $(.all-attributes) ] ; } - else if $(name) in $(all-features) + else if $(name) in $(.all-features) { error = feature already defined: ; } @@ -144,7 +145,7 @@ rule feature ( name : values * : attributes * ) $(name).subfeatures ?= ; $(attributes).features += $(name) ; - all-features += $(name) ; + .all-features += $(name) ; extend $(name) : $(values) ; } @@ -172,7 +173,7 @@ rule defaults ( features * ) # returns true iff all elements of names are valid features. rule valid ( names + ) { - if [ grist $(names) ] in $(all-features) + if [ grist $(names) ] in $(.all-features) { return true ; } @@ -214,12 +215,9 @@ local rule find-implied-subfeature ( feature subvalue : value-string ? ) error invalid feature $(feature) ; } - local v - = subfeature($(feature),$(subvalue)) - subfeature($(feature),$(value-string),$(subvalue)) ; + value-string ?= "" ; - local subfeature = $($(v)) ; - return $(subfeature[1]) ; + return $($(feature)$(value-string)<>$(subvalue).subfeature) ; } # Given a feature name and a subfeature value, find the associated @@ -242,7 +240,7 @@ rule implied-subfeature ( feature subvalue : value-string ? ) # generate an error if the feature is unknown local rule validate-feature ( feature ) { - if ! [ grist $(feature) ] in $(all-features) + if ! [ grist $(feature) ] in $(.all-features) { error unknown feature \"$(feature)\" ; } @@ -266,7 +264,7 @@ local rule expand-subfeatures-aux ( feature ? : value ) { feature = [ implied-feature $(components[1]) ] ; } - else if ! $(feature) in $(all-features) + else if ! $(feature) in $(.all-features) { error unknown feature $(feature) ; } @@ -295,7 +293,7 @@ rule expand-subfeatures ( properties * ) return $(result) ; } -# Helper for extend, below. Handles the case feature case. +# Helper for extend, below. Handles the feature case. local rule extend-feature ( feature : values * ) { validate-feature $(feature) ; @@ -310,12 +308,12 @@ local rule extend-feature ( feature : values * ) $(v).implicit-feature = $(feature) ; } - all-implicit-values += $(values) ; + .all-implicit-values += $(values) ; } $(feature).values += $(values) ; } -# Checks that value-string is a valide value-string for the given feature. +# Checks that value-string is a valid value-string for the given feature. local rule validate-value-string ( feature value-string ) { local values = $(value-string) ; @@ -354,12 +352,12 @@ rule extend-subfeature ( feature value-string ? : subfeature : subvalues * ) validate-value-string $(feature) $(value-string) ; } + # provide a way to get from the given feature or property and + # subfeature value to the subfeature name. + value-string ?= "" ; for local subvalue in $(subvalues) { - local v - = subfeature($(feature),$(value-string),$(subvalue)) - subfeature($(feature),$(subvalue)) ; - $(v[1]) = $(subfeature) ; + $(feature)$(value-string)<>$(subvalue).subfeature = $(subfeature) ; } } @@ -431,6 +429,8 @@ rule subfeature ( feature value-string ? : subfeature : subvalues * ) } $(feature).subfeatures += $(subfeature) ; extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ; + local f = [ utility.ungrist $(feature) ] ; + feature $(f)-$(subfeature) : $(subvalues) ; } # Set the components of the given composite property @@ -449,6 +449,10 @@ rule compose ( composite-property : component-properties * ) $($(composite-property).components) ; } + if $(composite-property) in $(components) + { + errror composite property "$(composite-property)" cannot have itself as a component ; + } $(composite-property).components = $(component-properties) ; } @@ -486,6 +490,8 @@ rule free-features ( ) return $(free.features) ; } +# Expand all composite properties in the set so that all components +# are explicitly expressed. rule expand-composites ( properties * ) { local explicit-features = $(properties:G) ; @@ -538,6 +544,76 @@ rule expand ( properties * ) return [ expand-composites $(expanded) ] ; } + +# Helper rule for minimize, below - return true iff property's feature +# is present in the contents of the variable named by feature-set-var. +local rule in-features ( feature-set-var property ) +{ + if $(property:G) in $($(feature-set-var)) + { + return true ; + } +} + +# Given an expanded property set, eliminate all redundancy: properties +# which are elements of other (composite) properties in the set will +# be eliminated, implicit properties will be expressed without feature +# grist, and sub-property values will be expressed as elements joined +# to the corresponding main property. +rule minimize ( properties * ) +{ + # remove properties implied by composite features + local x = $(properties) ; + for local p in $(properties) + { + if ! $(p:G) + { + error minimize requires an expanded property set, but \"$(p)\" + appears to be an un-expanded implicit feature ; + } + x = [ set.difference $(x) : $($(p).components) ] ; + } + + # handle subfeatures and implicit features + local result ; + while $(x) + { + local p = $(x[1]) ; + local f = $(p:G) ; + + # eliminate features in implicit properties. + if implicit in [ attributes $(f) ] + { + p = $(p:G="") ; + } + + # locate all subproperties of f in the property set + local subproperties ; + local subfeatures = $($(f).subfeatures) ; + if $(subfeatures) + { + local f_ = [ utility.ungrist $(f) ] ; + subfeatures = [ grist $(f_)-$(subfeatures) ] ; + subproperties = [ sequence.filter in-features subfeatures : $(x) ] ; + } + + if $(subproperties) + { + # reconstitute the joined property name + local sorted = [ sequence.insertion-sort $(subproperties) ] ; + result += $(p)-$(sorted:G="":J=-) ; + + x = [ set.difference $(x[2-]) : $(subproperties) ] ; + } + else + { + result += $(p) ; + x = $(x[2-]) ; + } + } + return $(result) ; +} + # Given a property-set of the form # v1/v2/...vN-1/vN/vN+1/...vM # @@ -619,6 +695,18 @@ local rule __test__ ( ) : expand debug on ; + assert.result gcc-3.0.1 debug on + : minimize [ expand gcc-3.0.1 debug on ] + ; + + assert.result gcc-3.0.1 debug + : minimize [ expand gcc-3.0.1 debug off ] + ; + + assert.result debug on + : minimize [ expand debug on ] + ; + assert.result y/z b/c e/f : split y/z/b/c/e/f ; diff --git a/v2/build/property.jam b/v2/build/property.jam index 0ca53dcf9..05e738d13 100644 --- a/v2/build/property.jam +++ b/v2/build/property.jam @@ -77,16 +77,57 @@ rule refine ( properties * : requirements * : feature-space ? ) } } -# Returns a path which represents the given property set. -# TODO: this is a quick sketch. We need much better logic. -rule as-path ( properties * ) +# Helper for as-path, below. Orders properties with the implicit ones +# first, and within the two sections in alphabetical order of feature +# name. +local rule path-order ( feature-space x y ) { + if $(y:G) && ! $(x:G) + { + return true ; + } + else if $(x:G) && ! $(y:G) + { + return ; + } + else + { + if ! $(x:G) + { + x = [ $(feature-space).expand-subfeatures $(x) ] ; + y = [ $(feature-space).expand-subfeatures $(y) ] ; + } + + if $(x[0]) < $(y[0]) + { + return true ; + } + } +} + +# Returns a path which represents the given expanded property set. +rule as-path ( properties * : feature-space ? ) +{ + feature-space ?= feature ; + + # trim redundancy + properties = [ $(feature-space).minimize $(properties) ] ; + + # sort according to path-order + properties = [ sequence.insertion-sort $(properties) : path-order $(feature-space) ] ; + local components ; for local p in $(properties) { - local name = [ ungrist $(p:G) ] ; - local value = $(p:G=) ; - components += $(name)-$(value) ; + if $(p:G) + { + local f = [ ungrist $(p:G) ] ; + components += $(f)-$(p:G=) ; + } + else + { + components += $(p) ; + } } return $(components:J=/) ; } @@ -111,6 +152,7 @@ local rule __test__ ( ) compose debug : _DEBUG off ; compose release : NDEBUG on ; + } assert.result gcc off FOO @@ -129,15 +171,20 @@ local rule __test__ ( ) : refine gcc : off : $(test-space) ; + assert.result debug + : as-path off debug + : $(test-space) + ; + + assert.result gcc/debug/rtti-off + : as-path gcc off off debug + : $(test-space) + ; r = [ refine gcc off : on : $(test-space) ] ; assert.equal $(r[1]) : "@error" ; - assert.result optimization-off/variant-debug - : as-path off debug - ; - } diff --git a/v2/test/project_test3.py b/v2/test/project_test3.py index 83f296bb2..ed8b94515 100644 --- a/v2/test/project_test3.py +++ b/v2/test/project_test3.py @@ -11,14 +11,21 @@ t.set_tree("project-test3") t.run_build_system() t.expect_addition("bin/a.obj") -t.fail_test(t.read("bin/a.obj") != \ -"""variant-debug/optimization-off + +expected = """debug a.cpp -""") +""" + +if t.read("bin/a.obj") != expected: + print '***expected:' + print expected + print '***got this instead:' + print t.read("bin/a.obj") + t.fail_test(1) t.expect_addition("bin/a.exe") -t.fail_test(t.read("bin/a.exe") != \ -"""variant-debug/optimization-off +t.fail_test(t.read("bin/a.exe") != +"""debug bin/a.obj """)