From e9b2c95e8a9d09660eb864d8449fd304f829bc5a Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sun, 10 Feb 2002 00:49:29 +0000 Subject: [PATCH] Build request expansion [SVN r12778] --- src/build/build-request.jam | 141 +++++++++++++ src/build/feature.jam | 411 +++++++++++++++++++++++++++++------- src/build/property.jam | 31 --- src/kernel/class.jam | 8 +- src/util/sequence.jam | 62 +++++- src/util/set.jam | 29 +++ src/util/string.jam | 16 +- test/test.jam | 3 +- 8 files changed, 585 insertions(+), 116 deletions(-) create mode 100644 src/build/build-request.jam diff --git a/src/build/build-request.jam b/src/build/build-request.jam new file mode 100644 index 000000000..924b0819a --- /dev/null +++ b/src/build/build-request.jam @@ -0,0 +1,141 @@ +# (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +import sequence ; +import set ; +import regex ; +import feature ; +import numbers ; + +# given a build request element of the form +# property1/property2/.../propertyN, return true if any of the +# properties has a composite feature. +local rule has-composite-feature ( feature-space element ) +{ + # get the individual properties + local properties = regex.split element / ; + local result = ; + for local p in $(properties) + { + if composite in [ feature.attributes [ property.get-feature $(p) ] ] + { + result = $(p) ; + } + } + return result ; +} + +local rule expand-element ( f element ) +{ + local properties = [ regex.split $(element) / ] ; + return [ string.join [ $(f) $(properties) ] : / ] ; +} + +# expand the given build request by combining all elements which don't +# specify conflicting non-free features. +rule expand ( elements * : feature-space ? ) +{ + feature-space ?= feature ; + + # First make all features and subfeatures explicit + local expanded-elements = [ + sequence.transform expand-element $(feature-space).expand-subfeatures + : $(elements) ] ; + + # Now combine all of the expanded elements + local product = [ x-product $(expanded-elements) : $(feature-space) ] ; + + return [ + sequence.transform expand-element $(feature-space).expand-composites + : $(product) ] ; +} + +local rule x-product-aux ( elements + : feature-space ) +{ + local result ; + local p = [ regex.split $(elements[1]) / ] ; + local f = [ set.difference $(p:G) [ $(feature-space).free-features ] ] ; + + # No conflict with things used at a higher level? + if ! [ set.intersection $(f) : $(x-product-used) ] + { + local x-product-seen ; + { + # don't mix in any conflicting features + local x-product-used = $(x-product-used) $(f) ; + + if $(elements[2]) + { + local rest = [ x-product-aux $(elements[2-]) : $(feature-space) ] ; + result = $(elements[1])/$(rest) ; + } + + result ?= $(elements[1]) ; + } + + # If we didn't encounter a conflicting feature lower down, + # don't recurse again. + if ! [ set.intersection $(f) : $(x-product-seen) ] + { + elements = ; + } + } + + if $(elements[2]) + { + result += [ x-product-aux $(elements[2-]) : $(feature-space) ] ; + } + + # Note that we've seen these features so that higher levels will + # recurse again without them set. + x-product-seen += $(f) ; + return $(result) ; +} + +local rule x-product ( elements * : feature-space ) +{ + local x-product-seen x-product-used ; + return [ x-product-aux $(elements) : $(feature-space) ] ; +} + +local rule __test__ ( ) +{ + import assert ; + import errors : try catch ; + import class ; + + local test-space = [ class.new feature-space ] ; + + module $(test-space) + { + import build-request ; + + feature toolset : gcc msvc : implicit ; + subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 + 3.0 3.0.1 3.0.2 ; + + feature variant : debug release : implicit composite ; + feature inlining : on off ; + + feature stdlib : native stlport : implicit ; + + assert.result + gcc/3.0.1/stlport/debug + msvc/stlport/debug + msvc/debug + + : build-request.expand gcc-3.0.1/stlport msvc/stlport msvc debug + : $(test-space) ; + + assert.result + gcc/3.0.1/stlport/debug/off + gcc/3.0.1/stlport/release/off + + : build-request.expand gcc-3.0.1/stlport debug release off + : $(test-space) ; + } +} + + diff --git a/src/build/feature.jam b/src/build/feature.jam index 954b357a7..02ee3a150 100644 --- a/src/build/feature.jam +++ b/src/build/feature.jam @@ -3,12 +3,36 @@ # all copies. This software is provided "as is" without express or implied # warranty, and with no claim as to its suitability for any purpose. +# Glossary +# +# feature - a normalized aspect of a build configuration +# +# value - a specific available setting for a feature +# +# property - a feature-value pair, expressed ase value +# +# subfeature - a feature which only exists in the presence of its +# parent, and whose identity can be derived (in the +# context of its parent) from the name of its value +# +# value-string - a string of the form value-subvalue1-subvalue2... +# -subvalueN, where value is a feature value and +# subvalue1...subvalueN are values of related +# subfeatures. + +import class : * ; + +# A feature-space is a class to facilitate testing. We want to be able +# to make and populate instances of a feature-space for testing +# purposes without intruding on the default feature-space +local rule feature-space ( ) +{ import errors : error lol->list ; import property ; import sequence ; import regex ; import set ; - + module local all-attributes = implicit # features whose values alone identify the @@ -68,10 +92,22 @@ module local all-attributes = module local all-features ; module local all-implicit-values ; + +# Transform features by bracketing any elements which aren't already +# bracketed by "<>" +local rule grist ( features * ) +{ + local empty = "" ; + return $(empty:G=$(features)) ; +} + +module local empty = "" ; # declare a new feature with the given name, values, and attributes. rule feature ( name : values * : attributes * ) { + name = [ grist $(name) ] ; + local error ; # if there are any unknown attributes... @@ -102,15 +138,38 @@ rule feature ( name : values * : attributes * ) module local $(name).values ; module local $(name).attributes = $(attributes) ; module local $(name).subfeatures = ; + module local $(attributes).features ; + $(attributes).features += $(name) ; all-features += $(name) ; extend $(name) : $(values) ; } +# return the default property values for the given features. +rule defaults ( features * ) +{ + local result ; + for local f in [ grist $(features) ] + { + if free in $($(f).attributes) + { + } + else if optional in $($(f).attributes) + { + result += $(f) ; + } + else + { + result += $(f)$($(f).values[1]) ; + } + } + return $(result) ; +} + # returns true iff all elements of names are valid features. rule valid ( names + ) { - if $(names) in $(all-features) + if [ grist $(names) ] in $(all-features) { return true ; } @@ -123,15 +182,18 @@ rule attributes ( feature ) { error \"$(feature)\" is not a valid feature name ; } + feature = [ grist $(feature) ] ; return $($(feature).attributes) ; } # return the values of the given feature rule values ( feature ) { + feature = [ grist $(feature) ] ; return $($(feature).values) ; } +# return the implicit feature associated with the given implicit value. rule implied-feature ( implicit-value ) { local feature = $($(implicit-value).implicit-feature) ; @@ -144,6 +206,11 @@ rule implied-feature ( implicit-value ) local rule find-implied-subfeature ( feature subvalue : value-string ? ) { + if $(feature) != $(feature:G) + { + error invalid feature $(feature) ; + } + local v = subfeature($(feature),$(subvalue)) subfeature($(feature),$(value-string),$(subvalue)) ; @@ -156,15 +223,18 @@ local rule find-implied-subfeature ( feature subvalue : value-string ? ) return $(subfeature[1]) ; } +# Given a feature name and a subfeature value, find the associated +# subfeature. rule implied-subfeature ( feature subvalue : value-string ? ) { + feature = [ grist $(feature) ] ; local subfeature = [ find-implied-subfeature $(feature) $(subvalue) : $(value-string) ] ; if ! $(subfeature) { error \"$(subvalue)\" is not a known subfeature value of - feature \"$(feature)\" ; + feature $(feature) ; } return $(subfeature) ; @@ -173,19 +243,22 @@ rule implied-subfeature ( feature subvalue : value-string ? ) # generate an error if the feature is unknown local rule validate-feature ( feature ) { - if ! $(feature) in $(all-features) + if ! [ grist $(feature) ] in $(all-features) { error unknown feature \"$(feature)\" ; } } -# expand-subfeatures toolset : gcc-2.95.2-linux-x86 -> gcc 2.95.2 linux x86 +# expand-subfeatures gcc-2.95.2-linux-x86 +# -> gcc 2.95.2 linux x86 +# # equivalent to: -# expand-subfeatures : gcc-2.95.2-linux-x86 -local rule expand-subfeatures ( feature ? : value ) +# expand-subfeatures gcc-2.95.2-linux-x86 +local rule expand-subfeatures-aux ( feature ? : value ) { if $(feature) { + feature = [ grist $(feature) ] ; validate-feature $(feature) ; } @@ -194,6 +267,10 @@ local rule expand-subfeatures ( feature ? : value ) { feature = [ implied-feature $(components[1]) ] ; } + else if ! $(feature) in $(all-features) + { + error unknown feature $(feature) ; + } # get the top-level feature's value local value = $(components[1]:G=) ; @@ -202,12 +279,24 @@ local rule expand-subfeatures ( feature ? : value ) for local subvalue in $(components[2-]) { local subfeature = [ implied-subfeature $(feature) $(subvalue) : $(value) ] ; - result += $(subvalue:G=$(feature)-$(subfeature)) ; + local f = [ SUBST $(feature) ^<(.*)>$ $1 ] ; + result += $(subvalue:G=$(f)-$(subfeature)) ; } return $(result) ; } +rule expand-subfeatures ( properties * ) +{ + local result ; + for local p in $(properties) + { + result += [ expand-subfeatures-aux $(p:G) : $(p:G=) ] ; + } + return $(result) ; +} + +# Helper for extend, below. Handles the case feature case. local rule extend-feature ( feature : values * ) { validate-feature $(feature) ; @@ -228,6 +317,7 @@ local rule extend-feature ( feature : values * ) $(feature).values += $(values) ; } +# Checks that value-string is a valide value-string for the given feature. local rule validate-value-string ( feature value-string ) { local values = $(value-string) ; @@ -238,7 +328,7 @@ local rule validate-value-string ( feature value-string ) if ! ( $(values[1]) in $($(feature).values) ) { - return \"$(values[1])\" is not a known value of feature \"$(feature)\" ; + return \"$(values[1])\" is not a known value of feature $(feature) ; } if $(values[2]) @@ -250,10 +340,16 @@ local rule validate-value-string ( feature value-string ) } -# extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ; -# extend-subfeature toolset : target-platform : mingw ; -local rule extend-subfeature ( feature value-string ? : subfeature : subvalues * ) +# Extends the given subfeature with the subvalues. If the optional +# value-string is provided, the subvalues are specific to the given +# value of the feature. Thus, you could say that +# mingw is specifc to gcc-2.95.2 as follows: +# +# extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ; +# +rule extend-subfeature ( feature value-string ? : subfeature : subvalues * ) { + feature = [ grist $(feature) ] ; validate-feature $(feature) ; if $(value-string) { @@ -269,19 +365,37 @@ local rule extend-subfeature ( feature value-string ? : subfeature : subvalues * } } +# Can be called three ways: +# +# 1. extend feature : values * +# 2. extend subfeature : values * +# 3. extend value-string subfeature : values * +# +# * Form 1 adds the given values to the given feature +# * Forms 2 and 3 add subfeature values to the given feature +# * Form 3 adds the subfeature values as specific to the given +# property value-string. +# rule extend ( feature-or-property subfeature ? : values * ) { - local feature value-string ; - if $(feature-or-property:G) + local + feature # If a property was specified this is its feature + value-string # E.G., the gcc-2.95-2 part of gcc-2.95.2 + ; + + # if a property was specified + if $(feature-or-property:G) && $(feature-or-property:G=) { - feature = [ property.get-feature $(feature-or-property) ] ; + # Extract the feature and value-string, if any. + feature = $(feature-or-property:G) ; value-string = $(feature-or-property:G=) ; } else { - feature = $(feature-or-property) ; + feature = [ grist $(feature-or-property) ] ; } + # Dispatch to the appropriate handler if $(subfeature) { extend-subfeature $(feature) $(value-string) @@ -289,6 +403,8 @@ rule extend ( feature-or-property subfeature ? : values * ) } else { + # If no subfeature was specified, we didn't expect to see a + # value-string if $(value-string) { error can only be specify a property as the first argument @@ -309,6 +425,7 @@ rule extend ( feature-or-property subfeature ? : values * ) # rule subfeature ( feature value-string ? : subfeature : subvalues * ) { + feature = [ grist $(feature) ] ; validate-feature $(feature) ; if $(subfeature) in $($(feature).subfeatures) { @@ -318,70 +435,214 @@ rule subfeature ( feature value-string ? : subfeature : subvalues * ) extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ; } -# tests of module features +# Set the components of the given composite property +rule compose ( composite-property : component-properties * ) +{ + local feature = $(composite-property:G) ; + if ! ( composite in [ attributes $(feature) ] ) + { + error "$(feature)" is not a composite feature ; + } + + module local $(composite-property).components ; + if $($(composite-property).components) + { + error components of "$(composite-property)" already set: + $($(composite-property).components) ; + } + + $(composite-property).components = $(component-properties) ; +} + +local rule has-attribute ( attribute property ) +{ + if $(attribute) in [ attributes [ get-feature $(property) ] ] + { + return true ; + } +} + +local rule expand-composite ( property ) +{ + return $(property) + [ sequence.transform expand-composite : $($(property).components) ] ; +} + +# return all values of the given feature specified by the given property set. +rule get-values ( feature : properties * ) +{ + local result ; + feature = [ grist $(feature) ] ; + for local p in $(properties) + { + if $(p:G) = $(feature) + { + result += $(p:G=) ; + } + } + return $(result) ; +} + +rule free-features ( ) +{ + return $(free.features) ; +} + +rule expand-composites ( property-set * ) +{ + local explicit-features = $(property-set:G) ; + + local result ; + # now expand composite features + for local p in $(property-set) + { + for local x in [ expand-composite $(p) ] + { + if ! $(x) in $(result) + { + if $(x:G) in $(free.features) + { + result += $(x) ; + } + else if $(x:G) in $(result:G) + { + error explicitly-specified values of non-free feature + $(x:G) conflict : + values: [ get-values $(x:G) : $(property-set) ] ; + } + else if + # if it's not the result of composite expansion + $(x) in $(property-set) + # or it is, but it doesn't match any explicitly-specified feature + || ( ! $(x:G) in $(explicit-features) ) + { + result += $(x) ; + } + } + } + } + return $(result) ; +} + +# Given a property set which may consist of composite and implicit +# properties and combined subfeature values, returns an expanded, +# normalized property set with all implicit features expressed +# explicitly, all subfeature values individually expressed, and all +# components of composite properties expanded. Non-free features +# directly expressed in the input property-set cause any values of +# those features due to composite feature expansion to be dropped. If +# two values of a given non-free feature are directly expressed in the +# input, an error is issued. +rule expand ( property-set * ) +{ + local expanded = [ expand-subfeatures $(property-set) ] ; + + return [ expand-composites $(expanded) ] ; +} + +} +class feature-space ; + +# Tricky: makes this module into an instance of feature-space so that +# normally users work with the global feature-space without having to +# be aware that it's a class instance. +instance feature : feature-space ; + +# tests of module feature local rule __test__ ( ) { - import errors : try catch ; - import assert ; - feature __test__toolset : gcc : implicit executed ; - feature __test__define : : free ; + local test-space = [ new feature-space ] ; - extend-feature __test__toolset : msvc metrowerks ; - subfeature __test__toolset gcc : version : 2.95.2 2.95.3 2.95.4 - 3.0 3.0.1 3.0.2 ; - - assert.result <__test__toolset>gcc <__test__toolset-version>3.0.1 : - expand-subfeatures __test__toolset : gcc-3.0.1 ; - - assert.result <__test__toolset>gcc <__test__toolset-version>3.0.1 : - expand-subfeatures : gcc-3.0.1 ; - - feature __test__dummy : dummy1 dummy2 ; - subfeature __test__dummy : subdummy : x y z ; - - # test error checking - try ; + module $(test-space) { - validate-feature __test__foobar ; - } - catch unknown feature ; + import errors : try catch ; + import assert ; - try ; - { - feature __test__foobar : : baz ; - } - catch unknown attributes: baz ; - - feature __test__feature1 ; - try ; - { - feature __test__feature1 ; - } - catch feature already defined: ; - - try ; - { - feature __test__feature2 : : free implicit ; - } - catch free features cannot also be implicit ; - - try ; - { - implied-feature lackluster ; - } - catch \"lackluster\" is not a value of an implicit feature ; - - try ; - { - implied-subfeature __test__toolset 3.0.1 ; - } - catch \"3.0.1\" is not a known subfeature value of - feature \"__test__toolset\" ; + feature toolset : gcc : implicit ; + feature define : : free ; + feature runtime-link : dynamic static : symmetric ; + feature optimization : on off ; + feature variant : debug release : implicit composite ; + + compose debug : _DEBUG off ; + compose release : NDEBUG on ; + + extend-feature toolset : msvc metrowerks ; + subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 + 3.0 3.0.1 3.0.2 ; + + assert.result gcc 3.0.1 + : expand-subfeatures gcc-3.0.1 ; + + assert.result gcc 3.0.1 + : expand-subfeatures gcc-3.0.1 ; + + feature dummy : dummy1 dummy2 ; + subfeature dummy : subdummy : x y z ; + + assert.result a c e + : get-values x : a b c d e ; + + assert.result gcc 3.0.1 + debug _DEBUG on + : expand gcc-3.0.1 debug on + ; + + assert.result debug _DEBUG on + : expand debug on + ; + + # test error checking + + try ; + { + expand release off on ; + } + catch explicitly-specified values of non-free feature conflict ; + + try ; + { + validate-feature foobar ; + } + catch unknown feature ; + + try ; + { + feature foobar : : baz ; + } + catch unknown attributes: baz ; + + feature feature1 ; + try ; + { + feature feature1 ; + } + catch feature already defined: ; + + try ; + { + feature feature2 : : free implicit ; + } + catch free features cannot also be implicit ; + + try ; + { + implied-feature lackluster ; + } + catch \"lackluster\" is not a value of an implicit feature ; + + try ; + { + implied-subfeature toolset 3.0.1 ; + } + catch \"3.0.1\" is not a known subfeature value of + feature ; - try ; - { - implied-subfeature __test__toolset not-a-version : gcc ; + try ; + { + implied-subfeature toolset not-a-version : gcc ; + } + catch \"not-a-version\" is not a known subfeature value of + feature ; } - catch \"not-a-version\" is not a known subfeature value of - feature \"__test__toolset\" ; } \ No newline at end of file diff --git a/src/build/property.jam b/src/build/property.jam index 8e7a064c6..a0cf01088 100644 --- a/src/build/property.jam +++ b/src/build/property.jam @@ -1,33 +1,2 @@ import errors ; -# Given a property name, return the corresponding feature name -rule get-feature ( property ) -{ - return [ SUBST $(property:G) ^<(.*)> $1 ] ; -} - -rule is-valid ( property ) -{ - import feature ; - if ! $(property:G) - { - return ; - } - else - { - local f = [ get-feature $(property) ] ; - local value = $(property:G=) ; - - if [ features.is-valid $(f) ] && - ( free in [ features.attributes $(f) ] - || $(value) in [ features.values $(f) ] ) - - { - return true ; - } - else - { - return ; - } - } -} diff --git a/src/kernel/class.jam b/src/kernel/class.jam index dedc863be..19ef0541f 100644 --- a/src/kernel/class.jam +++ b/src/kernel/class.jam @@ -1,3 +1,8 @@ +# (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + import numbers ; import errors : * ; import set ; @@ -38,7 +43,8 @@ rule class ( name : bases * ) # rules to be visible to the builtin RULENAMES rule. We'll # need them in order to implement subclasses and instances of # the class. - EXPORT class@$(name) : __init__ [ RULENAMES class ] ; + EXPORT class@$(name) : __init__ # [ RULENAMES class ] + ; # Bring the __init__ functions in from the base classes, using # the optional localize parameter so that it will execute in diff --git a/src/util/sequence.jam b/src/util/sequence.jam index e6b4b23be..1221625b3 100644 --- a/src/util/sequence.jam +++ b/src/util/sequence.jam @@ -1,4 +1,4 @@ -# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and +# (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and # distribute this software is granted provided this copyright notice appears in # all copies. This software is provided "as is" without express or implied # warranty, and with no claim as to its suitability for any purpose. @@ -14,7 +14,7 @@ import assert ; # Return the elements e of $(sequence) for which [ $(predicate) e ] is # has a non-null value. -rule filter ( predicate + : sequence * ) +rule filter ( predicate__ + : sequence__ * ) { # trailing underscores hopefully prevent collisions with module # locals in the caller @@ -22,9 +22,9 @@ rule filter ( predicate + : sequence * ) module [ CALLER_MODULE ] { - for local e in $(sequence) + for local e in $(sequence__) { - if [ $(predicate) $(e) ] + if [ $(predicate__) $(e) ] { result__ += $(e) ; } @@ -33,6 +33,25 @@ rule filter ( predicate + : sequence * ) return $(result__) ; } +# return a new sequence consisting of [ $(function) $(e) ] for each +# element e of $(sequence). +rule transform ( function__ + : sequence__ * ) +{ + # trailing underscores hopefully prevent collisions with module + # locals in the caller + local result__ ; + + module [ CALLER_MODULE ] + { + for local e in $(sequence__) + { + result__ += [ $(function__) $(e) ] ; + } + } + return $(result__) ; +} + + rule less ( a b ) { if $(a) < $(b) @@ -115,6 +134,19 @@ rule length ( s * ) return $(length) ; } +rule unique ( list * ) +{ + local result ; + for local f in $(list) + { + if ! $(f) in $(result) + { + result += $(f) ; + } + } + return $(result) ; +} + local rule __test__ ( ) { # use a unique module so we can test the use of local rules. @@ -143,6 +175,20 @@ local rule __test__ ( ) assert.result 3 3 3 : sequence.filter is-equal-test 3 : 1 2 3 4 3 5 3 5 7 ; + local rule append-x ( n ) + { + return $(n)x ; + } + + assert.result 1x 2x 3x : sequence.transform append-x : 1 2 3 ; + + local rule repeat2 ( x ) + { + return $(x) $(x) ; + } + + assert.result 1 1 2 2 3 3 : sequence.transform repeat2 : 1 2 3 ; + local rule test-greater ( a b ) { if $(a) > $(b) @@ -150,7 +196,6 @@ local rule __test__ ( ) return true ; } } - assert.result 1 2 3 4 5 6 7 8 9 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 ; assert.result 9 8 7 6 5 4 3 2 1 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 : test-greater ; assert.result foo-bar-baz : sequence.join foo bar baz : - ; @@ -162,12 +207,15 @@ local rule __test__ ( ) assert.result 1 : sequence.length a ; assert.result 10 : sequence.length a b c d e f g h i j ; - local p2 = x x ; - for local i in 1 2 3 4 5 6 7 + + local p2 = x ; + for local i in 1 2 3 4 5 6 7 8 { p2 = $(p2) $(p2) ; } assert.result 256 : sequence.length $(p2) ; + assert.result 1 2 3 4 5 + : sequence.unique 1 2 3 2 4 3 3 5 5 5 ; } } \ No newline at end of file diff --git a/src/util/set.jam b/src/util/set.jam index 642cd9bd0..833ff8276 100644 --- a/src/util/set.jam +++ b/src/util/set.jam @@ -19,6 +19,30 @@ rule difference ( B * : A * ) return $(result) ; } +# intersection set1 : set2 +# +# Removes from set1 any items which don't appear in set2 and returns the result. +rule intersection ( set1 * : set2 * ) +{ + local result ; + for local v in $(set1) + { + if $(v) in $(set2) + { + result += $(v) ; + } + } + return $(result) ; +} + +rule equal ( set1 * : set2 * ) +{ + if $(set1) in $(set2) && ( $(set2) in $(set1) ) + { + return true ; + } +} + rule __test__ ( ) { import assert ; @@ -26,6 +50,11 @@ rule __test__ ( ) assert.result 0 1 4 6 8 9 : difference 0 1 2 3 4 5 6 7 8 9 : 2 3 5 7 ; + assert.result 2 5 7 : intersection 0 1 2 4 5 6 7 8 9 : 2 3 5 7 ; + + assert.true equal 1 1 2 3 : 3 2 2 1 ; + + assert.false equal 2 3 : 3 2 2 1 ; } diff --git a/src/util/string.jam b/src/util/string.jam index 72bfe3141..485a05f6d 100644 --- a/src/util/string.jam +++ b/src/util/string.jam @@ -1,4 +1,4 @@ -# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and +# (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and # distribute this software is granted provided this copyright notice appears in # all copies. This software is provided "as is" without express or implied # warranty, and with no claim as to its suitability for any purpose. @@ -23,6 +23,17 @@ rule chars ( string ) return $(result) ; } +rule join ( strings * : separator ? ) +{ + separator ?= "" ; + local result = $(strings[1]) ; + for local x in $(strings[2-]) + { + result = $(result)$(separator)$(x) ; + } + return $(result) ; +} + rule __test__ ( ) { import assert ; @@ -35,4 +46,7 @@ rule __test__ ( ) assert.result a b c d e f g h i : chars abcdefghi ; assert.result a b c d e f g h i j : chars abcdefghij ; assert.result a b c d e f g h i j k : chars abcdefghijk ; + + assert.result a//b/c/d : join a "" b c d : / ; + assert.result abcd : join a "" b c d ; } \ No newline at end of file diff --git a/test/test.jam b/test/test.jam index d0ef16c4d..23896a3dc 100644 --- a/test/test.jam +++ b/test/test.jam @@ -1,6 +1,7 @@ +import feature ; +import build-request ; import sequence ; import class ; -import feature ; import os ; import string ; import numbers ;