mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 01:12:13 +00:00
Build request expansion
[SVN r12778]
This commit is contained in:
141
new/build-request.jam
Normal file
141
new/build-request.jam
Normal file
@@ -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
|
||||
<toolset>gcc/<toolset-version>3.0.1/<stdlib>stlport/<variant>debug
|
||||
<toolset>msvc/<stdlib>stlport/<variant>debug
|
||||
<toolset>msvc/<variant>debug
|
||||
|
||||
: build-request.expand gcc-3.0.1/stlport msvc/stlport msvc debug
|
||||
: $(test-space) ;
|
||||
|
||||
assert.result
|
||||
<toolset>gcc/<toolset-version>3.0.1/<stdlib>stlport/<variant>debug/<inlining>off
|
||||
<toolset>gcc/<toolset-version>3.0.1/<stdlib>stlport/<variant>release/<inlining>off
|
||||
|
||||
: build-request.expand gcc-3.0.1/stlport debug release <inlining>off
|
||||
: $(test-space) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
411
new/feature.jam
411
new/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 <feature>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 -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86
|
||||
# expand-subfeatures <toolset>gcc-2.95.2-linux-x86
|
||||
# -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>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
|
||||
# <target-platform>mingw is specifc to <toolset>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 <feature> subfeature : values *
|
||||
# 3. extend <feature>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 <toolset>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 <variant>debug : <define>_DEBUG <optimization>off ;
|
||||
compose <variant>release : <define>NDEBUG <optimization>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 <toolset>gcc <toolset-version>3.0.1
|
||||
: expand-subfeatures <toolset>gcc-3.0.1 ;
|
||||
|
||||
assert.result <toolset>gcc <toolset-version>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 : <x>a <y>b <x>c <y>d <x>e ;
|
||||
|
||||
assert.result <toolset>gcc <toolset-version>3.0.1
|
||||
<variant>debug <define>_DEBUG <optimization>on
|
||||
: expand gcc-3.0.1 debug <optimization>on
|
||||
;
|
||||
|
||||
assert.result <variant>debug <define>_DEBUG <optimization>on
|
||||
: expand debug <optimization>on
|
||||
;
|
||||
|
||||
# test error checking
|
||||
|
||||
try ;
|
||||
{
|
||||
expand release <optimization>off <optimization>on ;
|
||||
}
|
||||
catch explicitly-specified values of non-free feature <optimization> 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 <toolset> ;
|
||||
|
||||
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 <toolset> ;
|
||||
}
|
||||
catch \"not-a-version\" is not a known subfeature value of
|
||||
feature \"__test__toolset\" ;
|
||||
}
|
||||
@@ -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 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ;
|
||||
}
|
||||
}
|
||||
29
new/set.jam
29
new/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 ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import feature ;
|
||||
import build-request ;
|
||||
import sequence ;
|
||||
import class ;
|
||||
import feature ;
|
||||
import os ;
|
||||
import string ;
|
||||
import numbers ;
|
||||
|
||||
141
v2/build/build-request.jam
Normal file
141
v2/build/build-request.jam
Normal file
@@ -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
|
||||
<toolset>gcc/<toolset-version>3.0.1/<stdlib>stlport/<variant>debug
|
||||
<toolset>msvc/<stdlib>stlport/<variant>debug
|
||||
<toolset>msvc/<variant>debug
|
||||
|
||||
: build-request.expand gcc-3.0.1/stlport msvc/stlport msvc debug
|
||||
: $(test-space) ;
|
||||
|
||||
assert.result
|
||||
<toolset>gcc/<toolset-version>3.0.1/<stdlib>stlport/<variant>debug/<inlining>off
|
||||
<toolset>gcc/<toolset-version>3.0.1/<stdlib>stlport/<variant>release/<inlining>off
|
||||
|
||||
: build-request.expand gcc-3.0.1/stlport debug release <inlining>off
|
||||
: $(test-space) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <feature>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 -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86
|
||||
# expand-subfeatures <toolset>gcc-2.95.2-linux-x86
|
||||
# -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>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
|
||||
# <target-platform>mingw is specifc to <toolset>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 <feature> subfeature : values *
|
||||
# 3. extend <feature>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 <toolset>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 <variant>debug : <define>_DEBUG <optimization>off ;
|
||||
compose <variant>release : <define>NDEBUG <optimization>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 <toolset>gcc <toolset-version>3.0.1
|
||||
: expand-subfeatures <toolset>gcc-3.0.1 ;
|
||||
|
||||
assert.result <toolset>gcc <toolset-version>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 : <x>a <y>b <x>c <y>d <x>e ;
|
||||
|
||||
assert.result <toolset>gcc <toolset-version>3.0.1
|
||||
<variant>debug <define>_DEBUG <optimization>on
|
||||
: expand gcc-3.0.1 debug <optimization>on
|
||||
;
|
||||
|
||||
assert.result <variant>debug <define>_DEBUG <optimization>on
|
||||
: expand debug <optimization>on
|
||||
;
|
||||
|
||||
# test error checking
|
||||
|
||||
try ;
|
||||
{
|
||||
expand release <optimization>off <optimization>on ;
|
||||
}
|
||||
catch explicitly-specified values of non-free feature <optimization> 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 <toolset> ;
|
||||
|
||||
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 <toolset> ;
|
||||
}
|
||||
catch \"not-a-version\" is not a known subfeature value of
|
||||
feature \"__test__toolset\" ;
|
||||
}
|
||||
@@ -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 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import feature ;
|
||||
import build-request ;
|
||||
import sequence ;
|
||||
import class ;
|
||||
import feature ;
|
||||
import os ;
|
||||
import string ;
|
||||
import numbers ;
|
||||
|
||||
@@ -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 ;
|
||||
}
|
||||
}
|
||||
@@ -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 ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ;
|
||||
}
|
||||
Reference in New Issue
Block a user