|
# Copyright 2001, 2002, 2003 Dave Abrahams |
|
# Copyright 2002, 2006 Rene Rivera |
|
# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus |
|
# Distributed under the Boost Software License, Version 1.0. |
|
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) |
|
|
|
import assert : * ; |
|
import "class" : * ; |
|
import errors : lol->list ; |
|
import indirect ; |
|
import modules ; |
|
import regex ; |
|
import sequence ; |
|
import set ; |
|
import utility ; |
|
|
|
|
|
local rule setup ( ) |
|
{ |
|
.all-attributes = |
|
implicit |
|
composite |
|
optional |
|
symmetric |
|
free |
|
incidental |
|
path |
|
dependency |
|
propagated |
|
link-incompatible |
|
subfeature |
|
order-sensitive |
|
; |
|
|
|
.all-features = ; |
|
.all-subfeatures = ; |
|
.all-top-features = ; # non-subfeatures |
|
.all-implicit-values = ; |
|
} |
|
setup ; |
|
|
|
|
|
# Prepare a fresh space to test in by moving all global variable settings into |
|
# the given temporary module and erasing them here. |
|
# |
|
rule prepare-test ( temp-module ) |
|
{ |
|
DELETE_MODULE $(temp-module) ; |
|
|
|
# Transfer globals to temp-module. |
|
for local v in [ VARNAMES feature ] |
|
{ |
|
if [ MATCH (\\.) : $(v) ] |
|
{ |
|
modules.poke $(temp-module) : $(v) : $($(v)) ; |
|
$(v) = ; |
|
} |
|
} |
|
setup ; |
|
} |
|
|
|
|
|
# Clear out all global variables and recover all variables from the given |
|
# temporary module. |
|
# |
|
rule finish-test ( temp-module ) |
|
{ |
|
# Clear globals. |
|
for local v in [ VARNAMES feature ] |
|
{ |
|
if [ MATCH (\\.) : $(v) ] |
|
{ |
|
$(v) = ; |
|
} |
|
} |
|
|
|
for local v in [ VARNAMES $(temp-module) ] |
|
{ |
|
$(v) = [ modules.peek $(temp-module) : $(v) ] ; |
|
} |
|
DELETE_MODULE $(temp-module) ; |
|
} |
|
|
|
|
|
# Transform features by bracketing any elements which are not already bracketed |
|
# by "<>". |
|
# |
|
local rule grist ( features * ) |
|
{ |
|
local empty = "" ; |
|
return $(empty:G=$(features)) ; |
|
} |
|
|
|
|
|
# Declare a new feature with the given name, values, and attributes. |
|
# |
|
rule feature ( |
|
name # Feature name. |
|
: values * # Allowable values - may be extended later using feature.extend. |
|
: attributes * # Feature attributes (e.g. implicit, free, propagated...). |
|
) |
|
{ |
|
name = [ grist $(name) ] ; |
|
|
|
local error ; |
|
|
|
# Check for any unknown attributes. |
|
if ! ( $(attributes) in $(.all-attributes) ) |
|
{ |
|
error = unknown attributes: |
|
[ set.difference $(attributes) : $(.all-attributes) ] ; |
|
} |
|
else if $(name) in $(.all-features) |
|
{ |
|
error = feature already defined: ; |
|
} |
|
else if implicit in $(attributes) && free in $(attributes) |
|
{ |
|
error = free features cannot also be implicit ; |
|
} |
|
else if free in $(attributes) && propagated in $(attributes) |
|
{ |
|
error = free features cannot be propagated ; |
|
} |
|
else |
|
{ |
|
local m = [ MATCH (.*=.*) : $(values) ] ; |
|
if $(m[1]) |
|
{ |
|
error = "feature value may not contain '='" ; |
|
} |
|
} |
|
|
|
if $(error) |
|
{ |
|
errors.error $(error) |
|
: "in" feature declaration: |
|
: feature [ lol->list $(1) : $(2) : $(3) ] ; |
|
} |
|
|
|
$(name).values ?= ; |
|
$(name).attributes = $(attributes) ; |
|
$(name).subfeatures ?= ; |
|
$(attributes).features += $(name) ; |
|
|
|
.all-features += $(name) ; |
|
if subfeature in $(attributes) |
|
{ |
|
.all-subfeatures += $(name) ; |
|
} |
|
else |
|
{ |
|
.all-top-features += $(name) ; |
|
} |
|
extend $(name) : $(values) ; |
|
} |
|
|
|
|
|
# Sets the default value of the given feature, overriding any previous default. |
|
# |
|
rule set-default ( feature : value ) |
|
{ |
|
local f = [ grist $(feature) ] ; |
|
local a = $($(f).attributes) ; |
|
local bad-attribute = ; |
|
if free in $(a) |
|
{ |
|
bad-attribute = free ; |
|
} |
|
else if optional in $(a) |
|
{ |
|
bad-attribute = optional ; |
|
} |
|
if $(bad-attribute) |
|
{ |
|
errors.error "$(bad-attribute) property $(f) cannot have a default." ; |
|
} |
|
if ! $(value) in $($(f).values) |
|
{ |
|
errors.error "The specified default value, '$(value)' is invalid" |
|
: "allowed values are: " $($(f).values) ; |
|
} |
|
$(f).default = $(value) ; |
|
} |
|
|
|
|
|
# Returns the default property values for the given features. |
|
# |
|
rule defaults ( features * ) |
|
{ |
|
local result ; |
|
for local f in $(features) |
|
{ |
|
local gf = $(:E=:G=$(f)) ; |
|
local a = $($(gf).attributes) ; |
|
if ( free in $(a) ) || ( optional in $(a) ) |
|
{ |
|
} |
|
else |
|
{ |
|
result += $(gf)$($(gf).default) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
# Returns true iff all 'names' elements are valid features. |
|
# |
|
rule valid ( names + ) |
|
{ |
|
if $(names) in $(.all-features) |
|
{ |
|
return true ; |
|
} |
|
} |
|
|
|
|
|
# Returns the attibutes of the given feature. |
|
# |
|
rule attributes ( feature ) |
|
{ |
|
return $($(:E=:G=$(feature)).attributes) ; |
|
} |
|
|
|
|
|
# Returns the values of the given feature. |
|
# |
|
rule values ( feature ) |
|
{ |
|
return $($(:E=:G=$(feature)).values) ; |
|
} |
|
|
|
|
|
# Returns true iff 'value-string' is a value-string of an implicit feature. |
|
# |
|
rule is-implicit-value ( value-string ) |
|
{ |
|
local v = [ regex.split $(value-string) - ] ; |
|
local failed ; |
|
if ! $(v[1]) in $(.all-implicit-values) |
|
{ |
|
failed = true ; |
|
} |
|
else |
|
{ |
|
local feature = $($(v[1]).implicit-feature) ; |
|
for local subvalue in $(v[2-]) |
|
{ |
|
if ! [ find-implied-subfeature $(feature) $(subvalue) : $(v[1]) ] |
|
{ |
|
failed = true ; |
|
} |
|
} |
|
} |
|
|
|
if ! $(failed) |
|
{ |
|
return true ; |
|
} |
|
} |
|
|
|
|
|
# Returns the implicit feature associated with the given implicit value. |
|
# |
|
rule implied-feature ( implicit-value ) |
|
{ |
|
local components = [ regex.split $(implicit-value) "-" ] ; |
|
|
|
local feature = $($(components[1]).implicit-feature) ; |
|
if ! $(feature) |
|
{ |
|
errors.error \"$(implicit-value)\" is not a value of an implicit feature ; |
|
feature = "" ; # Keep testing happy; it expects a result. |
|
} |
|
return $(feature) ; |
|
} |
|
|
|
|
|
local rule find-implied-subfeature ( feature subvalue : value-string ? ) |
|
{ |
|
# Feature should be of the form <feature-name>. |
|
if $(feature) != $(feature:G) |
|
{ |
|
errors.error invalid feature $(feature) ; |
|
} |
|
|
|
return $($(feature)$(value-string:E="")<>$(subvalue).subfeature) ; |
|
} |
|
|
|
|
|
# Given a feature and a value of one of its subfeatures, find the name of the |
|
# subfeature. If value-string is supplied, looks for implied subfeatures that |
|
# are specific to that value of feature |
|
# |
|
rule implied-subfeature ( |
|
feature # The main feature name. |
|
subvalue # The value of one of its subfeatures. |
|
: value-string ? # The value of the main feature. |
|
) |
|
{ |
|
local subfeature = [ find-implied-subfeature $(feature) $(subvalue) |
|
: $(value-string) ] ; |
|
if ! $(subfeature) |
|
{ |
|
value-string ?= "" ; |
|
errors.error \"$(subvalue)\" is not a known subfeature value of |
|
$(feature)$(value-string) ; |
|
} |
|
return $(subfeature) ; |
|
} |
|
|
|
|
|
# Generate an error if the feature is unknown. |
|
# |
|
local rule validate-feature ( feature ) |
|
{ |
|
if ! $(feature) in $(.all-features) |
|
{ |
|
errors.error unknown feature \"$(feature)\" ; |
|
} |
|
} |
|
|
|
|
|
# Given a feature and its value or just a value corresponding to an implicit |
|
# feature, returns a property set consisting of all component subfeatures and |
|
# their values. For example all the following calls: |
|
# |
|
# expand-subfeatures-aux <toolset>gcc-2.95.2-linux-x86 |
|
# expand-subfeatures-aux gcc-2.95.2-linux-x86 |
|
# |
|
# return: |
|
# |
|
# <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86 |
|
# |
|
local rule expand-subfeatures-aux ( |
|
feature ? # Feature name or empty if value corresponds to an |
|
# implicit property. |
|
: value # Feature value. |
|
: dont-validate ? # If set, no value string validation will be done. |
|
) |
|
{ |
|
if $(feature) |
|
{ |
|
feature = $(feature) ; |
|
} |
|
|
|
if ! $(feature) |
|
{ |
|
feature = [ implied-feature $(value) ] ; |
|
} |
|
else |
|
{ |
|
validate-feature $(feature) ; |
|
} |
|
if ! $(dont-validate) |
|
{ |
|
validate-value-string $(feature) $(value) ; |
|
} |
|
|
|
local components = [ regex.split $(value) "-" ] ; |
|
|
|
# Get the top-level feature's value. |
|
local value = $(components[1]:G=) ; |
|
|
|
local result = $(components[1]:G=$(feature)) ; |
|
|
|
local subvalues = $(components[2-]) ; |
|
while $(subvalues) |
|
{ |
|
local subvalue = $(subvalues[1]) ; # Pop the head off of subvalues. |
|
subvalues = $(subvalues[2-]) ; |
|
|
|
local subfeature = [ find-implied-subfeature $(feature) $(subvalue) : |
|
$(value) ] ; |
|
|
|
# If no subfeature was found reconstitute the value string and use that. |
|
if ! $(subfeature) |
|
{ |
|
result = $(components:J=-) ; |
|
result = $(result:G=$(feature)) ; |
|
subvalues = ; # Stop looping. |
|
} |
|
else |
|
{ |
|
local f = [ MATCH ^<(.*)>$ : $(feature) ] ; |
|
result += $(subvalue:G=$(f)-$(subfeature)) ; |
|
} |
|
} |
|
|
|
return $(result) ; |
|
} |
|
|
|
|
|
# Make all elements of properties corresponding to implicit features explicit, |
|
# and express all subfeature values as separate properties in their own right. |
|
# For example, all of the following properties |
|
# |
|
# gcc-2.95.2-linux-x86 |
|
# <toolset>gcc-2.95.2-linux-x86 |
|
# |
|
# might expand to |
|
# |
|
# <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86 |
|
# |
|
rule expand-subfeatures ( |
|
properties * # Property set with elements of the form |
|
# <feature>value-string or just value-string in the case |
|
# of implicit features. |
|
: dont-validate ? |
|
) |
|
{ |
|
local result ; |
|
for local p in $(properties) |
|
{ |
|
# Don't expand subfeatures in subfeatures |
|
if ! [ MATCH "(:)" : $(p:G) ] |
|
{ |
|
result += [ expand-subfeatures-aux $(p:G) : $(p:G=) : $(dont-validate) ] ; |
|
} |
|
else |
|
{ |
|
result += $(p) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
# Helper for extend, below. Handles the feature case. |
|
# |
|
local rule extend-feature ( feature : values * ) |
|
{ |
|
feature = [ grist $(feature) ] ; |
|
validate-feature $(feature) ; |
|
if implicit in $($(feature).attributes) |
|
{ |
|
for local v in $(values) |
|
{ |
|
if $($(v).implicit-feature) |
|
{ |
|
errors.error $(v) is already associated with the \"$($(v).implicit-feature)\" feature ; |
|
} |
|
$(v).implicit-feature = $(feature) ; |
|
} |
|
|
|
.all-implicit-values += $(values) ; |
|
} |
|
if ! $($(feature).values) |
|
{ |
|
# This is the first value specified for this feature so make it be the |
|
# default. |
|
$(feature).default = $(values[1]) ; |
|
} |
|
$(feature).values += $(values) ; |
|
} |
|
|
|
|
|
# Checks that value-string is a valid value-string for the given feature. |
|
# |
|
rule validate-value-string ( feature value-string ) |
|
{ |
|
if ! ( |
|
free in $($(feature).attributes) |
|
|| ( $(value-string) in $(feature).values ) |
|
) |
|
{ |
|
local values = $(value-string) ; |
|
|
|
if $($(feature).subfeatures) |
|
{ |
|
if ! ( $(value-string) in $($(feature).values) ) |
|
&& ! ( $(value-string) in $($(feature).subfeatures) ) |
|
{ |
|
values = [ regex.split $(value-string) - ] ; |
|
} |
|
} |
|
|
|
if ! ( $(values[1]) in $($(feature).values) ) && |
|
|
|
# An empty value is allowed for optional features. |
|
( $(values[1]) || ! ( optional in $($(feature).attributes) ) ) |
|
{ |
|
errors.error \"$(values[1])\" is not a known value of feature $(feature) |
|
: legal values: \"$($(feature).values)\" ; |
|
} |
|
|
|
for local v in $(values[2-]) |
|
{ |
|
# This will validate any subfeature values in value-string. |
|
implied-subfeature $(feature) $(v) : $(values[1]) ; |
|
} |
|
} |
|
} |
|
|
|
|
|
# A helper that computes: |
|
# * name(s) of module-local variable(s) used to record the correspondence |
|
# between subvalue(s) and a subfeature |
|
# * value of that variable when such a subfeature/subvalue has been defined and |
|
# returns a list consisting of the latter followed by the former. |
|
# |
|
local rule subvalue-var ( |
|
feature # Main feature name. |
|
value-string ? # If supplied, specifies a specific value of the main |
|
# feature for which the subfeature values are valid. |
|
: subfeature # Subfeature name. |
|
: subvalues * # Subfeature values. |
|
) |
|
{ |
|
feature = [ grist $(feature) ] ; |
|
validate-feature $(feature) ; |
|
if $(value-string) |
|
{ |
|
validate-value-string $(feature) $(value-string) ; |
|
} |
|
|
|
local subfeature-name = [ get-subfeature-name $(subfeature) $(value-string) ] ; |
|
|
|
return $(subfeature-name) |
|
$(feature)$(value-string:E="")<>$(subvalues).subfeature ; |
|
} |
|
|
|
|
|
# Extends the given subfeature with the subvalues. If the optional value-string |
|
# is provided, the subvalues are only valid for the given value of the feature. |
|
# Thus, you could say that <target-platform>mingw is specific to |
|
# <toolset>gcc-2.95.2 as follows: |
|
# |
|
# extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ; |
|
# |
|
rule extend-subfeature ( |
|
feature # The feature whose subfeature is being extended. |
|
|
|
value-string ? # If supplied, specifies a specific value of the main |
|
# feature for which the new subfeature values are valid. |
|
|
|
: subfeature # Subfeature name. |
|
: subvalues * # Additional subfeature values. |
|
) |
|
{ |
|
local subfeature-vars = [ subvalue-var $(feature) $(value-string) |
|
: $(subfeature) : $(subvalues) ] ; |
|
|
|
local f = [ utility.ungrist [ grist $(feature) ] ] ; |
|
extend $(f)-$(subfeature-vars[1]) : $(subvalues) ; |
|
|
|
# Provide a way to get from the given feature or property and subfeature |
|
# value to the subfeature name. |
|
$(subfeature-vars[2-]) = $(subfeature-vars[1]) ; |
|
} |
|
|
|
|
|
# Returns true iff the subvalues are valid for the feature. When the optional |
|
# value-string is provided, returns true iff the subvalues are valid for the |
|
# given value of the feature. |
|
# |
|
rule is-subvalue ( feature : value-string ? : subfeature : subvalue ) |
|
{ |
|
local subfeature-vars = [ subvalue-var $(feature) $(value-string) |
|
: $(subfeature) : $(subvalue) ] ; |
|
|
|
if $($(subfeature-vars[2])) = $(subfeature-vars[1]) |
|
{ |
|
return true ; |
|
} |
|
} |
|
|
|
|
|
# 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 ; # If a property was specified this is its feature. |
|
local 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=) |
|
{ |
|
# Extract the feature and value-string, if any. |
|
feature = $(feature-or-property:G) ; |
|
value-string = $(feature-or-property:G=) ; |
|
} |
|
else |
|
{ |
|
feature = [ grist $(feature-or-property) ] ; |
|
} |
|
|
|
# Dispatch to the appropriate handler. |
|
if $(subfeature) |
|
{ |
|
extend-subfeature $(feature) $(value-string) : $(subfeature) |
|
: $(values) ; |
|
} |
|
else |
|
{ |
|
# If no subfeature was specified, we do not expect to see a |
|
# value-string. |
|
if $(value-string) |
|
{ |
|
errors.error can only specify a property as the first argument when |
|
extending a subfeature |
|
: usage: |
|
: " extend" feature ":" values... |
|
: " | extend" <feature>value-string subfeature ":" values... |
|
; |
|
} |
|
|
|
extend-feature $(feature) : $(values) ; |
|
} |
|
} |
|
|
|
|
|
local rule get-subfeature-name ( subfeature value-string ? ) |
|
{ |
|
local prefix = $(value-string): ; |
|
return $(prefix:E="")$(subfeature) ; |
|
} |
|
|
|
|
|
# Declares a subfeature. |
|
# |
|
rule subfeature ( |
|
feature # Root feature that is not a subfeature. |
|
value-string ? # A value-string specifying which feature or subfeature |
|
# values this subfeature is specific to, if any. |
|
: subfeature # The name of the subfeature being declared. |
|
: subvalues * # The allowed values of this subfeature. |
|
: attributes * # The attributes of the subfeature. |
|
) |
|
{ |
|
feature = [ grist $(feature) ] ; |
|
validate-feature $(feature) ; |
|
|
|
# Add grist to the subfeature name if a value-string was supplied. |
|
local subfeature-name = [ get-subfeature-name $(subfeature) $(value-string) ] ; |
|
|
|
if $(subfeature-name) in $($(feature).subfeatures) |
|
{ |
|
errors.error \"$(subfeature)\" already declared as a subfeature of \"$(feature)\" |
|
"specific to "$(value-string) ; |
|
} |
|
$(feature).subfeatures += $(subfeature-name) ; |
|
|
|
# First declare the subfeature as a feature in its own right. |
|
local f = [ utility.ungrist $(feature) ] ; |
|
feature $(f)-$(subfeature-name) : $(subvalues) : $(attributes) subfeature ; |
|
|
|
# Now make sure the subfeature values are known. |
|
extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ; |
|
} |
|
|
|
|
|
# Set components of the given composite property. |
|
# |
|
rule compose ( composite-property : component-properties * ) |
|
{ |
|
local feature = $(composite-property:G) ; |
|
if ! ( composite in [ attributes $(feature) ] ) |
|
{ |
|
errors.error "$(feature)" is not a composite feature ; |
|
} |
|
|
|
$(composite-property).components ?= ; |
|
if $($(composite-property).components) |
|
{ |
|
errors.error components of "$(composite-property)" already set: |
|
$($(composite-property).components) ; |
|
} |
|
|
|
if $(composite-property) in $(component-properties) |
|
{ |
|
errors.error composite property "$(composite-property)" cannot have itself as a component ; |
|
} |
|
$(composite-property).components = $(component-properties) ; |
|
} |
|
|
|
|
|
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 = $(:E=:G=$(feature)) ; # Add <> if necessary. |
|
for local p in $(properties) |
|
{ |
|
if $(p:G) = $(feature) |
|
{ |
|
# Use MATCH instead of :G= to get the value, in order to preserve |
|
# the value intact instead of having bjam treat it as a decomposable |
|
# path. |
|
result += [ MATCH ">(.*)" : $(p) ] ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
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) ; |
|
local result ; |
|
|
|
# Now expand composite features. |
|
for local p in $(properties) |
|
{ |
|
local expanded = [ expand-composite $(p) ] ; |
|
|
|
for local x in $(expanded) |
|
{ |
|
if ! $(x) in $(result) |
|
{ |
|
local f = $(x:G) ; |
|
|
|
if $(f) in $(free.features) |
|
{ |
|
result += $(x) ; |
|
} |
|
else if ! $(x) in $(properties) # x is the result of expansion |
|
{ |
|
if ! $(f) in $(explicit-features) # not explicitly-specified |
|
{ |
|
if $(f) in $(result:G) |
|
{ |
|
errors.error expansions of composite features result |
|
in conflicting values for $(f) |
|
: values: [ get-values $(f) : $(result) ] $(x:G=) |
|
: one contributing composite property was $(p) ; |
|
} |
|
else |
|
{ |
|
result += $(x) ; |
|
} |
|
} |
|
} |
|
else if $(f) in $(result:G) |
|
{ |
|
errors.error explicitly-specified values of non-free feature |
|
$(f) conflict : |
|
"existing values:" [ get-values $(f) : $(properties) ] : |
|
"value from expanding " $(p) ":" $(x:G=) ; |
|
} |
|
else |
|
{ |
|
result += $(x) ; |
|
} |
|
} |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
# Return true iff f is an ordinary subfeature of the parent-property's feature, |
|
# or if f is a subfeature of the parent-property's feature specific to the |
|
# parent-property's value. |
|
# |
|
local rule is-subfeature-of ( parent-property f ) |
|
{ |
|
if subfeature in $($(f).attributes) |
|
{ |
|
local specific-subfeature = [ MATCH <(.*):(.*)> : $(f) ] ; |
|
if $(specific-subfeature) |
|
{ |
|
# The feature has the form <topfeature-topvalue:subfeature>, e.g. |
|
# <toolset-msvc:version>. |
|
local feature-value = [ split-top-feature $(specific-subfeature[1]) |
|
] ; |
|
if <$(feature-value[1])>$(feature-value[2]) = $(parent-property) |
|
{ |
|
return true ; |
|
} |
|
} |
|
else |
|
{ |
|
# The feature has the form <topfeature-subfeature>, e.g. |
|
# <toolset-version> |
|
local top-sub = [ split-top-feature [ utility.ungrist $(f) ] ] ; |
|
if $(top-sub[2]) && <$(top-sub[1])> = $(parent-property:G) |
|
{ |
|
return true ; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
# As for is-subfeature-of but for subproperties. |
|
# |
|
local rule is-subproperty-of ( parent-property p ) |
|
{ |
|
return [ is-subfeature-of $(parent-property) $(p:G) ] ; |
|
} |
|
|
|
|
|
# Given a property, return the subset of features consisting of all ordinary |
|
# subfeatures of the property's feature, and all specific subfeatures of the |
|
# property's feature which are conditional on the property's value. |
|
# |
|
local rule select-subfeatures ( parent-property : features * ) |
|
{ |
|
return [ sequence.filter is-subfeature-of $(parent-property) : $(features) ] ; |
|
} |
|
|
|
|
|
# As for select-subfeatures but for subproperties. |
|
# |
|
local rule select-subproperties ( parent-property : properties * ) |
|
{ |
|
return [ sequence.filter is-subproperty-of $(parent-property) : $(properties) ] ; |
|
} |
|
|
|
|
|
# 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 properties 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 ( properties * ) |
|
{ |
|
local expanded = [ expand-subfeatures $(properties) ] ; |
|
return [ expand-composites $(expanded) ] ; |
|
} |
|
|
|
|
|
# Helper rule for minimize. Returns 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 ; |
|
} |
|
} |
|
|
|
|
|
# Helper rule for minimize. Returns the list with the same properties, but with |
|
# all subfeatures moved to the end of the list. |
|
# |
|
local rule move-subfeatures-to-the-end ( properties * ) |
|
{ |
|
local x1 ; |
|
local x2 ; |
|
for local p in $(properties) |
|
{ |
|
if subfeature in $($(p:G).attributes) |
|
{ |
|
x2 += $(p) ; |
|
} |
|
else |
|
{ |
|
x1 += $(p) ; |
|
} |
|
} |
|
return $(x1) $(x2) ; |
|
} |
|
|
|
|
|
# Given an expanded property set, eliminate all redundancy: properties that are |
|
# elements of other (composite) properties in the set will be eliminated. |
|
# Non-symmetric properties equal to default values will be eliminated unless |
|
# they override a value from some composite property. 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 * ) |
|
{ |
|
# Precondition checking |
|
local implicits = [ set.intersection $(p:G=) : $(p:G) ] ; |
|
if $(implicits) |
|
{ |
|
errors.error minimize requires an expanded property set, but |
|
\"$(implicits[1])\" appears to be the value of an un-expanded |
|
implicit feature ; |
|
} |
|
|
|
# Remove properties implied by composite features. |
|
local components = $($(properties).components) ; |
|
local x = [ set.difference $(properties) : $(components) ] ; |
|
|
|
# Handle subfeatures and implicit features. |
|
x = [ move-subfeatures-to-the-end $(x) ] ; |
|
local result ; |
|
while $(x) |
|
{ |
|
local p fullp = $(x[1]) ; |
|
local f = $(p:G) ; |
|
local v = $(p:G=) ; |
|
|
|
# Eliminate features in implicit properties. |
|
if implicit in [ attributes $(f) ] |
|
{ |
|
p = $(v) ; |
|
} |
|
|
|
# Locate all subproperties of $(x[1]) in the property set. |
|
local subproperties = [ select-subproperties $(fullp) : $(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 |
|
{ |
|
# Eliminate properties whose value is equal to feature's default, |
|
# which are not symmetric and which do not contradict values implied |
|
# by composite properties. |
|
|
|
# Since all component properties of composites in the set have been |
|
# eliminated, any remaining property whose feature is the same as a |
|
# component of a composite in the set must have a non-redundant |
|
# value. |
|
if $(fullp) != [ defaults $(f) ] |
|
|| symmetric in [ attributes $(f) ] |
|
|| $(fullp:G) in $(components:G) |
|
{ |
|
result += $(p) ; |
|
} |
|
|
|
x = $(x[2-]) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
# Combine all subproperties into their parent properties |
|
# |
|
# Requires: for every subproperty, there is a parent property. All features are |
|
# explicitly expressed. |
|
# |
|
# This rule probably should not be needed, but build-request.expand-no-defaults |
|
# is being abused for unintended purposes and it needs help. |
|
# |
|
rule compress-subproperties ( properties * ) |
|
{ |
|
local all-subs ; |
|
local matched-subs ; |
|
local result ; |
|
|
|
for local p in $(properties) |
|
{ |
|
if ! $(p:G) |
|
{ |
|
# Expecting fully-gristed properties. |
|
assert.variable-not-empty p:G ; |
|
} |
|
|
|
if ! subfeature in $($(p:G).attributes) |
|
{ |
|
local subs = [ sequence.insertion-sort |
|
[ sequence.filter is-subproperty-of $(p) : $(properties) ] ] ; |
|
|
|
matched-subs += $(subs) ; |
|
|
|
local subvalues = -$(subs:G=:J=-) ; |
|
subvalues ?= "" ; |
|
result += $(p)$(subvalues) ; |
|
} |
|
else |
|
{ |
|
all-subs += $(p) ; |
|
} |
|
} |
|
assert.result true : set.equal $(all-subs) : $(matched-subs) ; |
|
return $(result) ; |
|
} |
|
|
|
|
|
# Given an ungristed string, finds the longest prefix which is a top-level |
|
# feature name followed by a dash, and return a pair consisting of the parts |
|
# before and after that dash. More interesting than a simple split because |
|
# feature names may contain dashes. |
|
# |
|
local rule split-top-feature ( feature-plus ) |
|
{ |
|
local e = [ regex.split $(feature-plus) - ] ; |
|
local f = $(e[1]) ; |
|
local v ; |
|
while $(e) |
|
{ |
|
if <$(f)> in $(.all-top-features) |
|
{ |
|
v = $(f) $(e[2-]:J=-) ; |
|
} |
|
e = $(e[2-]) ; |
|
f = $(f)-$(e[1]) ; |
|
} |
|
return $(v) ; |
|
} |
|
|
|
|
|
# Given a set of properties, add default values for features not represented in |
|
# the set. |
|
# |
|
# Note: if there's an ordinary feature F1 and a composite feature F2 which |
|
# includes some value for F1 and both feature have default values then the |
|
# default value of F1 will be added (as opposed to the value in F2). This might |
|
# not be the right idea, e.g. consider: |
|
# |
|
# feature variant : debug ... ; |
|
# <variant>debug : .... <runtime-debugging>on |
|
# feature <runtime-debugging> : off on ; |
|
# |
|
# Here, when adding default for an empty property set, we'll get |
|
# |
|
# <variant>debug <runtime_debugging>off |
|
# |
|
# and that's kind of strange. |
|
# |
|
rule add-defaults ( properties * ) |
|
{ |
|
for local v in $(properties:G=) |
|
{ |
|
if $(v) in $(properties) |
|
{ |
|
errors.error add-defaults requires explicitly specified features, |
|
but \"$(v)\" appears to be the value of an un-expanded implicit |
|
feature ; |
|
} |
|
} |
|
# We don't add default for elements with ":" inside. This catches: |
|
# 1. Conditional properties |
|
# to be takes as specified value for <variant> |
|
# 2. Free properties with ":" in values. We don't care, since free |
|
# properties don't have defaults. |
|
local xproperties = [ MATCH "^([^:]+)$" : $(properties) ] ; |
|
local missing-top = [ set.difference $(.all-top-features) : $(xproperties:G) ] ; |
|
local more = [ defaults $(missing-top) ] ; |
|
properties += $(more) ; |
|
xproperties += $(more) ; |
|
|
|
# Add defaults for subfeatures of features which are present. |
|
for local p in $(xproperties) |
|
{ |
|
local s = $($(p:G).subfeatures) ; |
|
local f = [ utility.ungrist $(p:G) ] ; |
|
local missing-subs = [ set.difference <$(f)-$(s)> : $(properties:G) ] ; |
|
properties += [ defaults [ select-subfeatures $(p) : $(missing-subs) ] ] ; |
|
} |
|
|
|
return $(properties) ; |
|
} |
|
|
|
|
|
# Given a property-set of the form |
|
# v1/v2/...vN-1/<fN>vN/<fN+1>vN+1/...<fM>vM |
|
# |
|
# Returns |
|
# v1 v2 ... vN-1 <fN>vN <fN+1>vN+1 ... <fM>vM |
|
# |
|
# Note that vN...vM may contain slashes. This needs to be resilient to the |
|
# substitution of backslashes for slashes, since Jam, unbidden, sometimes swaps |
|
# slash direction on NT. |
|
# |
|
rule split ( property-set ) |
|
{ |
|
local pieces = [ regex.split $(property-set) [\\/] ] ; |
|
local result ; |
|
|
|
for local x in $(pieces) |
|
{ |
|
if ( ! $(x:G) ) && $(result[-1]:G) |
|
{ |
|
result = $(result[1 |
|
} |
|
else |
|
{ |
|
result += $(x) ; |
|
} |
|
} |
|
|
|
return $(result) ; |
|
} |
|
|
|
|
|
# Tests of module feature. |
|
# |
|
rule __test__ ( ) |
|
{ |
|
# Use a fresh copy of the feature module. |
|
prepare-test feature-test-temp ; |
|
|
|
import assert ; |
|
import errors : try catch ; |
|
|
|
# These are local rules and so must be explicitly reimported into the |
|
# testing module. |
|
import feature : extend-feature validate-feature select-subfeatures ; |
|
|
|
feature toolset : gcc : implicit ; |
|
feature define : : free ; |
|
feature runtime-link : dynamic static : symmetric ; |
|
feature optimization : on off ; |
|
feature variant : debug release profile : implicit composite symmetric ; |
|
feature stdlib : native stlport ; |
|
feature magic : : free ; |
|
|
|
compose <variant>debug : <define>_DEBUG <optimization>off ; |
|
compose <variant>release : <define>NDEBUG <optimization>on ; |
|
|
|
assert.result dynamic static : values <runtime-link> ; |
|
assert.result dynamic static : values runtime-link ; |
|
|
|
try ; |
|
{ |
|
compose <variant>profile : <variant>profile ; |
|
} |
|
catch composite property <variant>profile cannot have itself as a component ; |
|
|
|
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.true is-subvalue toolset : gcc : version : 2.95.3 ; |
|
assert.false is-subvalue toolset : gcc : version : 1.1 ; |
|
|
|
assert.false is-subvalue toolset : msvc : version : 2.95.3 ; |
|
assert.false is-subvalue toolset : : version : yabba ; |
|
|
|
feature yabba ; |
|
subfeature yabba : version : dabba ; |
|
assert.true is-subvalue yabba : : version : dabba ; |
|
|
|
subfeature toolset gcc : platform : linux cygwin : optional ; |
|
|
|
assert.result <toolset-gcc:version> |
|
: select-subfeatures <toolset>gcc |
|
: <toolset-gcc:version> |
|
<toolset-msvc:version> |
|
<toolset-version> |
|
<stdlib> ; |
|
|
|
subfeature stdlib : version : 3 4 : optional ; |
|
|
|
assert.result <stdlib-version> |
|
: select-subfeatures <stdlib>native |
|
: <toolset-gcc:version> |
|
<toolset-msvc:version> |
|
<toolset-version> |
|
<stdlib-version> ; |
|
|
|
assert.result <toolset>gcc <toolset-gcc:version>3.0.1 |
|
: expand-subfeatures <toolset>gcc-3.0.1 ; |
|
|
|
assert.result <toolset>gcc <toolset-gcc:version>3.0.1 <toolset-gcc:platform>linux |
|
: expand-subfeatures <toolset>gcc-3.0.1-linux ; |
|
|
|
assert.result <toolset>gcc <toolset-gcc:version>3.0.1 |
|
: expand <toolset>gcc <toolset-gcc:version>3.0.1 ; |
|
|
|
assert.result <define>foo=x-y |
|
: expand-subfeatures <define>foo=x-y ; |
|
|
|
assert.result <toolset>gcc <toolset-gcc:version>3.0.1 |
|
: expand-subfeatures gcc-3.0.1 ; |
|
|
|
assert.result a c e |
|
: get-values <x> : <x>a <y>b <x>c <y>d <x>e ; |
|
|
|
assert.result <toolset>gcc <toolset-gcc: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 ; |
|
|
|
assert.result <optimization>on <variant>debug <define>_DEBUG |
|
: expand <optimization>on debug ; |
|
|
|
assert.result <runtime-link>dynamic <optimization>on |
|
: defaults <runtime-link> <define> <optimization> ; |
|
|
|
# Make sure defaults is resilient to missing grist. |
|
assert.result <runtime-link>dynamic <optimization>on |
|
: defaults runtime-link define optimization ; |
|
|
|
feature dummy : dummy1 dummy2 ; |
|
subfeature dummy : subdummy : x y z : optional ; |
|
|
|
feature fu : fu1 fu2 : optional ; |
|
subfeature fu : subfu : x y z : optional ; |
|
subfeature fu : subfu2 : q r s ; |
|
|
|
assert.result optional : attributes <fu> ; |
|
assert.result optional : attributes fu ; |
|
|
|
assert.result <runtime-link>static <define>foobar <optimization>on |
|
<toolset>gcc:<define>FOO <toolset>gcc <variant>debug <stdlib>native |
|
<dummy>dummy1 <toolset-gcc:version>2.95.2 |
|
: add-defaults <runtime-link>static <define>foobar <optimization>on |
|
<toolset>gcc:<define>FOO ; |
|
|
|
assert.result <runtime-link>static <define>foobar <optimization>on |
|
<toolset>gcc:<define>FOO <fu>fu1 <toolset>gcc <variant>debug |
|
<stdlib>native <dummy>dummy1 <fu-subfu2>q <toolset-gcc:version>2.95.2 |
|
: add-defaults <runtime-link>static <define>foobar <optimization>on |
|
<toolset>gcc:<define>FOO <fu>fu1 ; |
|
|
|
set-default <runtime-link> : static ; |
|
assert.result <runtime-link>static : defaults <runtime-link> ; |
|
|
|
assert.result gcc-3.0.1 debug <optimization>on |
|
: minimize [ expand gcc-3.0.1 debug <optimization>on <stdlib>native ] ; |
|
|
|
assert.result gcc-3.0.1 debug <runtime-link>dynamic |
|
: minimize |
|
[ expand gcc-3.0.1 debug <optimization>off <runtime-link>dynamic ] ; |
|
|
|
assert.result gcc-3.0.1 debug |
|
: minimize [ expand gcc-3.0.1 debug <optimization>off ] ; |
|
|
|
assert.result debug <optimization>on |
|
: minimize [ expand debug <optimization>on ] ; |
|
|
|
assert.result gcc-3.0 |
|
: minimize <toolset>gcc <toolset-gcc:version>3.0 ; |
|
|
|
assert.result gcc-3.0 |
|
: minimize <toolset-gcc:version>3.0 <toolset>gcc ; |
|
|
|
assert.result <x>y/z <a>b/c <d>e/f |
|
: split <x>y/z/<a>b/c/<d>e/f ; |
|
|
|
assert.result <x>y/z <a>b/c <d>e/f |
|
: split <x>y\\z\\<a>b\\c\\<d>e\\f ; |
|
|
|
assert.result a b c <d>e/f/g <h>i/j/k |
|
: split a/b/c/<d>e/f/g/<h>i/j/k ; |
|
|
|
assert.result a b c <d>e/f/g <h>i/j/k |
|
: split a\\b\\c\\<d>e\\f\\g\\<h>i\\j\\k ; |
|
|
|
# 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 ; |
|
|
|
validate-value-string <toolset> gcc ; |
|
validate-value-string <toolset> gcc-3.0.1 ; |
|
|
|
try ; |
|
{ |
|
validate-value-string <toolset> digital_mars ; |
|
} |
|
catch \"digital_mars\" is not a known value of <toolset> ; |
|
|
|
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 ; |
|
{ |
|
feature feature3 : : free propagated ; |
|
} |
|
catch free features cannot be propagated ; |
|
|
|
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 <toolset> ; |
|
|
|
try ; |
|
{ |
|
implied-subfeature <toolset> not-a-version : gcc ; |
|
} |
|
catch \"not-a-version\" is not a known subfeature value of <toolset>gcc ; |
|
|
|
# Leave a clean copy of the features module behind. |
|
finish-test feature-test-temp ; |
|
} |
|
|