|
|
|
|
|
|
|
|
|
|
|
|
|
import errors ; |
|
import feature ; |
|
import indirect ; |
|
import path ; |
|
import regex ; |
|
import string ; |
|
import sequence ; |
|
import set ; |
|
import utility ; |
|
|
|
|
|
|
|
|
|
|
|
|
|
rule refine ( properties * : requirements * ) |
|
{ |
|
local result ; |
|
local error ; |
|
|
|
|
|
|
|
for local r in $(requirements) |
|
{ |
|
|
|
if ! [ MATCH (:) : $(r:G=) ] |
|
{ |
|
|
|
__require__$(r:G) = $(r:G=) ; |
|
} |
|
} |
|
|
|
for local p in $(properties) |
|
{ |
|
if [ MATCH (:) : $(p:G=) ] |
|
{ |
|
|
|
result += $(p) ; |
|
} |
|
else if free in [ feature.attributes $(p:G) ] |
|
{ |
|
|
|
result += $(p) ; |
|
} |
|
else |
|
{ |
|
local required-value = $(__require__$(p:G)) ; |
|
if $(required-value) |
|
{ |
|
if $(p:G=) != $(required-value) |
|
{ |
|
result += $(p:G)$(required-value) ; |
|
} |
|
else |
|
{ |
|
result += $(p) ; |
|
} |
|
} |
|
else |
|
{ |
|
result += $(p) ; |
|
} |
|
} |
|
} |
|
|
|
|
|
for local r in $(requirements) |
|
{ |
|
__require__$(r:G) = ; |
|
} |
|
|
|
if $(error) |
|
{ |
|
return $(error) ; |
|
} |
|
else |
|
{ |
|
return [ sequence.unique $(result) $(requirements) ] ; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
rule evaluate-conditionals-in-context ( properties * : context * ) |
|
{ |
|
local base ; |
|
local conditionals ; |
|
for local p in $(properties) |
|
{ |
|
if [ MATCH (:<) : $(p) ] |
|
{ |
|
conditionals += $(p) ; |
|
} |
|
else |
|
{ |
|
base += $(p) ; |
|
} |
|
} |
|
|
|
local result = $(base) ; |
|
for local p in $(conditionals) |
|
{ |
|
|
|
local s = [ MATCH (.*):(<.*) : $(p) ] ; |
|
|
|
local condition = [ regex.split $(s[1]) "," ] ; |
|
|
|
if ! [ MATCH (!).* : $(condition:G=) ] |
|
{ |
|
|
|
if $(condition) in $(context) |
|
{ |
|
result += $(s[2]) ; |
|
} |
|
} |
|
else |
|
{ |
|
|
|
local fail ; |
|
while $(condition) |
|
{ |
|
local c = $(condition[1]) ; |
|
local m = [ MATCH !(.*) : $(c) ] ; |
|
if $(m) |
|
{ |
|
local p = $(m:G=$(c:G)) ; |
|
if $(p) in $(context) |
|
{ |
|
fail = true ; |
|
c = ; |
|
} |
|
} |
|
else |
|
{ |
|
if ! $(c) in $(context) |
|
{ |
|
fail = true ; |
|
c = ; |
|
} |
|
} |
|
condition = $(condition[2-]) ; |
|
} |
|
if ! $(fail) |
|
{ |
|
result += $(s[2]) ; |
|
} |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
rule expand-subfeatures-in-conditions ( properties * ) |
|
{ |
|
local result ; |
|
for local p in $(properties) |
|
{ |
|
local s = [ MATCH (.*):(<.*) : $(p) ] ; |
|
if ! $(s) |
|
{ |
|
result += $(p) ; |
|
} |
|
else |
|
{ |
|
local condition = $(s[1]) ; |
|
local value = $(s[2]) ; |
|
|
|
condition = [ regex.split $(condition) "," ] ; |
|
local e ; |
|
for local c in $(condition) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
e += [ feature.expand-subfeatures $(c) : true ] ; |
|
} |
|
if $(e) = $(condition) |
|
{ |
|
|
|
|
|
|
|
|
|
result += $(p) ; |
|
} |
|
else |
|
{ |
|
result += $(e:J=,):$(value) ; |
|
} |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
local rule path-order ( x y ) |
|
{ |
|
if $(y:G) && ! $(x:G) |
|
{ |
|
return true ; |
|
} |
|
else if $(x:G) && ! $(y:G) |
|
{ |
|
return ; |
|
} |
|
else |
|
{ |
|
if ! $(x:G) |
|
{ |
|
x = [ feature.expand-subfeatures $(x) ] ; |
|
y = [ feature.expand-subfeatures $(y) ] ; |
|
} |
|
|
|
if $(x[1]) < $(y[1]) |
|
{ |
|
return true ; |
|
} |
|
} |
|
} |
|
|
|
|
|
local rule abbreviate-dashed ( string ) |
|
{ |
|
local r ; |
|
for local part in [ regex.split $(string) - ] |
|
{ |
|
r += [ string.abbreviate $(part) ] ; |
|
} |
|
return $(r:J=-) ; |
|
} |
|
|
|
|
|
local rule identity ( string ) |
|
{ |
|
return $(string) ; |
|
} |
|
|
|
|
|
if --abbreviate-paths in [ modules.peek : ARGV ] |
|
{ |
|
.abbrev = abbreviate-dashed ; |
|
} |
|
else |
|
{ |
|
.abbrev = identity ; |
|
} |
|
|
|
|
|
|
|
|
|
rule as-path ( properties * ) |
|
{ |
|
local entry = .result.$(properties:J=-) ; |
|
|
|
if ! $($(entry)) |
|
{ |
|
|
|
properties = [ feature.minimize $(properties) ] ; |
|
|
|
|
|
properties = [ sequence.insertion-sort $(properties) : path-order ] ; |
|
|
|
local components ; |
|
for local p in $(properties) |
|
{ |
|
if $(p:G) |
|
{ |
|
local f = [ utility.ungrist $(p:G) ] ; |
|
p = $(f)-$(p:G=) ; |
|
} |
|
components += [ $(.abbrev) $(p) ] ; |
|
} |
|
|
|
$(entry) = $(components:J=/) ; |
|
} |
|
|
|
return $($(entry)) ; |
|
} |
|
|
|
|
|
|
|
|
|
local rule validate1 ( property ) |
|
{ |
|
local msg ; |
|
if $(property:G) |
|
{ |
|
local feature = $(property:G) ; |
|
local value = $(property:G=) ; |
|
|
|
if ! [ feature.valid $(feature) ] |
|
{ |
|
|
|
feature = [ utility.ungrist $(property:G) ] ; |
|
msg = "unknown feature '$(feature)'" ; |
|
} |
|
else if $(value) && ! free in [ feature.attributes $(feature) ] |
|
{ |
|
feature.validate-value-string $(feature) $(value) ; |
|
} |
|
else if ! ( $(value) || ( optional in [ feature.attributes $(feature) ] ) ) |
|
{ |
|
|
|
feature = [ utility.ungrist $(property:G) ] ; |
|
msg = "No value specified for feature '$(feature)'" ; |
|
} |
|
} |
|
else |
|
{ |
|
local feature = [ feature.implied-feature $(property) ] ; |
|
feature.validate-value-string $(feature) $(property) ; |
|
} |
|
if $(msg) |
|
{ |
|
errors.error "Invalid property "'$(property:J=" ")'": "$(msg:J=" "). ; |
|
} |
|
} |
|
|
|
|
|
rule validate ( properties * ) |
|
{ |
|
for local p in $(properties) |
|
{ |
|
validate1 $(p) ; |
|
} |
|
} |
|
|
|
|
|
rule validate-property-sets ( property-sets * ) |
|
{ |
|
for local s in $(property-sets) |
|
{ |
|
validate [ feature.split $(s) ] ; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
rule make ( specification * ) |
|
{ |
|
local result ; |
|
for local e in $(specification) |
|
{ |
|
if $(e:G) |
|
{ |
|
result += $(e) ; |
|
} |
|
else if [ feature.is-implicit-value $(e) ] |
|
{ |
|
local feature = [ feature.implied-feature $(e) ] ; |
|
result += $(feature)$(e) ; |
|
} |
|
else |
|
{ |
|
errors.error "'$(e)' is not a valid property specification" ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
rule remove ( attributes + : properties * ) |
|
{ |
|
local result ; |
|
for local e in $(properties) |
|
{ |
|
if ! [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ] |
|
{ |
|
result += $(e) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
rule take ( attributes + : properties * ) |
|
{ |
|
local result ; |
|
for local e in $(properties) |
|
{ |
|
if [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ] |
|
{ |
|
result += $(e) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
rule select ( features * : properties * ) |
|
{ |
|
local result ; |
|
|
|
|
|
local empty = "" ; |
|
features = $(empty:G=$(features)) ; |
|
|
|
for local p in $(properties) |
|
{ |
|
if $(p:G) in $(features) |
|
{ |
|
result += $(p) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
rule change ( properties * : feature value ? ) |
|
{ |
|
local result ; |
|
for local p in $(properties) |
|
{ |
|
if $(p:G) = $(feature) |
|
{ |
|
result += $(value:G=$(feature)) ; |
|
} |
|
else |
|
{ |
|
result += $(p) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rule split-conditional ( property ) |
|
{ |
|
local m = [ MATCH "(.+):<(.+)" : $(property) ] ; |
|
if $(m) |
|
{ |
|
return $(m[1]) <$(m[2]) ; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
rule translate-paths ( properties * : path ) |
|
{ |
|
local result ; |
|
for local p in $(properties) |
|
{ |
|
local split = [ split-conditional $(p) ] ; |
|
local condition = "" ; |
|
if $(split) |
|
{ |
|
condition = $(split[1]): ; |
|
p = $(split[2]) ; |
|
} |
|
|
|
if path in [ feature.attributes $(p:G) ] |
|
{ |
|
local values = [ regex.split $(p:TG=) "&&" ] ; |
|
local t ; |
|
for local v in $(values) |
|
{ |
|
t += [ path.root [ path.make $(v) ] $(path) ] ; |
|
} |
|
t = $(t:J="&&") ; |
|
result += $(condition)$(t:TG=$(p:G)) ; |
|
} |
|
else |
|
{ |
|
result += $(condition)$(p) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rule translate-indirect ( specification * : context-module ) |
|
{ |
|
local result ; |
|
for local p in $(specification) |
|
{ |
|
local m = [ MATCH ^@(.+) : $(p:G=) ] ; |
|
if $(m) |
|
{ |
|
local v ; |
|
if [ MATCH "^([^%]*)%([^%]+)$" : $(m) ] |
|
{ |
|
|
|
v = $(m) ; |
|
} |
|
else |
|
{ |
|
if ! [ MATCH ".*([.]).*" : $(m) ] |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
m = $(context-module).$(m) ; |
|
} |
|
v = [ indirect.make $(m) : $(context-module) ] ; |
|
} |
|
|
|
v = @$(v) ; |
|
result += $(v:G=$(p:G)) ; |
|
} |
|
else |
|
{ |
|
result += $(p) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rule translate-dependencies ( specification * : project-id : location ) |
|
{ |
|
local result ; |
|
for local p in $(specification) |
|
{ |
|
local split = [ split-conditional $(p) ] ; |
|
local condition = "" ; |
|
if $(split) |
|
{ |
|
condition = $(split[1]): ; |
|
p = $(split[2]) ; |
|
} |
|
if dependency in [ feature.attributes $(p:G) ] |
|
{ |
|
local split-target = [ regex.match (.*)//(.*) : $(p:G=) ] ; |
|
if $(split-target) |
|
{ |
|
local rooted = [ path.root [ path.make $(split-target[1]) ] |
|
[ path.root $(location) [ path.pwd ] ] ] ; |
|
result += $(condition)$(p:G)$(rooted)//$(split-target[2]) ; |
|
} |
|
else if [ path.is-rooted $(p:G=) ] |
|
{ |
|
result += $(condition)$(p) ; |
|
} |
|
else |
|
{ |
|
result += $(condition)$(p:G)$(project-id)//$(p:G=) ; |
|
} |
|
} |
|
else |
|
{ |
|
result += $(condition)$(p) ; |
|
} |
|
} |
|
return $(result) ; |
|
} |
|
|
|
|
|
|
|
|
|
class property-map |
|
{ |
|
import errors ; |
|
import numbers ; |
|
import sequence ; |
|
|
|
rule __init__ ( ) |
|
{ |
|
self.next-flag = 1 ; |
|
} |
|
|
|
|
|
|
|
rule insert ( properties + : value ) |
|
{ |
|
self.all-flags += $(self.next-flag) ; |
|
self.properties.$(self.next-flag) = $(properties) ; |
|
self.value.$(self.next-flag) = $(value) ; |
|
|
|
self.next-flag = [ numbers.increment $(self.next-flag) ] ; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
rule find ( properties + ) |
|
{ |
|
return [ find-replace $(properties) ] ; |
|
} |
|
|
|
|
|
|
|
|
|
rule find-replace ( properties + : value ? ) |
|
{ |
|
|
|
local matches ; |
|
local match-ranks ; |
|
for local i in $(self.all-flags) |
|
{ |
|
if $(self.properties.$(i)) in $(properties) |
|
{ |
|
matches += $(i) ; |
|
match-ranks += [ sequence.length $(self.properties.$(i)) ] ; |
|
} |
|
} |
|
local best = [ sequence.select-highest-ranked $(matches) |
|
: $(match-ranks) ] ; |
|
if $(best[2]) |
|
{ |
|
errors.error "Ambiguous key $(properties:J= :E=)" ; |
|
} |
|
local original = $(self.value.$(best)) ; |
|
if $(value) |
|
{ |
|
self.value.$(best) = $(value) ; |
|
} |
|
return $(original) ; |
|
} |
|
} |
|
|
|
|
|
rule __test__ ( ) |
|
{ |
|
import assert ; |
|
import "class" : new ; |
|
import errors : try catch ; |
|
import feature ; |
|
|
|
|
|
import property : path-order abbreviate-dashed ; |
|
|
|
feature.prepare-test property-test-temp ; |
|
|
|
feature.feature toolset : gcc : implicit symmetric ; |
|
feature.subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 3.0 3.0.1 |
|
3.0.2 : optional ; |
|
feature.feature define : : free ; |
|
feature.feature runtime-link : dynamic static : symmetric link-incompatible ; |
|
feature.feature optimization : on off ; |
|
feature.feature variant : debug release : implicit composite symmetric ; |
|
feature.feature rtti : on off : link-incompatible ; |
|
|
|
feature.compose <variant>debug : <define>_DEBUG <optimization>off ; |
|
feature.compose <variant>release : <define>NDEBUG <optimization>on ; |
|
|
|
validate <toolset>gcc <toolset>gcc-3.0.1 : $(test-space) ; |
|
|
|
assert.true path-order $(test-space) debug <define>foo ; |
|
assert.false path-order $(test-space) <define>foo debug ; |
|
assert.true path-order $(test-space) gcc debug ; |
|
assert.false path-order $(test-space) debug gcc ; |
|
assert.true path-order $(test-space) <optimization>on <rtti>on ; |
|
assert.false path-order $(test-space) <rtti>on <optimization>on ; |
|
|
|
assert.result-set-equal <toolset>gcc <rtti>off <define>FOO |
|
: refine <toolset>gcc <rtti>off |
|
: <define>FOO |
|
: $(test-space) ; |
|
|
|
assert.result-set-equal <toolset>gcc <optimization>on |
|
: refine <toolset>gcc <optimization>off |
|
: <optimization>on |
|
: $(test-space) ; |
|
|
|
assert.result-set-equal <toolset>gcc <rtti>off |
|
: refine <toolset>gcc : <rtti>off : $(test-space) ; |
|
|
|
assert.result-set-equal <toolset>gcc <rtti>off <rtti>off:<define>FOO |
|
: refine <toolset>gcc : <rtti>off <rtti>off:<define>FOO |
|
: $(test-space) ; |
|
|
|
assert.result-set-equal <toolset>gcc:<define>foo <toolset>gcc:<define>bar |
|
: refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar |
|
: $(test-space) ; |
|
|
|
assert.result <define>MY_RELEASE |
|
: evaluate-conditionals-in-context |
|
<variant>release,<rtti>off:<define>MY_RELEASE |
|
: <toolset>gcc <variant>release <rtti>off ; |
|
|
|
assert.result debug |
|
: as-path <optimization>off <variant>debug |
|
: $(test-space) ; |
|
|
|
assert.result gcc/debug/rtti-off |
|
: as-path <toolset>gcc <optimization>off <rtti>off <variant>debug |
|
: $(test-space) ; |
|
|
|
assert.result optmz-off : abbreviate-dashed optimization-off ; |
|
assert.result rntm-lnk-sttc : abbreviate-dashed runtime-link-static ; |
|
|
|
try ; |
|
validate <feature>value : $(test-space) ; |
|
catch "Invalid property '<feature>value': unknown feature 'feature'." ; |
|
|
|
try ; |
|
validate <rtti>default : $(test-space) ; |
|
catch \"default\" is not a known value of feature <rtti> ; |
|
|
|
validate <define>WHATEVER : $(test-space) ; |
|
|
|
try ; |
|
validate <rtti> : $(test-space) ; |
|
catch "Invalid property '<rtti>': No value specified for feature 'rtti'." ; |
|
|
|
try ; |
|
validate value : $(test-space) ; |
|
catch "value" is not a value of an implicit feature ; |
|
|
|
assert.result-set-equal <rtti>on |
|
: remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ; |
|
|
|
assert.result-set-equal <include>a |
|
: select include : <include>a <toolset>gcc ; |
|
|
|
assert.result-set-equal <include>a |
|
: select include bar : <include>a <toolset>gcc ; |
|
|
|
assert.result-set-equal <include>a <toolset>gcc |
|
: select include <bar> <toolset> : <include>a <toolset>gcc ; |
|
|
|
assert.result-set-equal <toolset>kylix <include>a |
|
: change <toolset>gcc <include>a : <toolset> kylix ; |
|
|
|
pm = [ new property-map ] ; |
|
$(pm).insert <toolset>gcc : o ; |
|
$(pm).insert <toolset>gcc <os>NT : obj ; |
|
$(pm).insert <toolset>gcc <os>CYGWIN : obj ; |
|
|
|
assert.equal o : [ $(pm).find <toolset>gcc ] ; |
|
|
|
assert.equal obj : [ $(pm).find <toolset>gcc <os>NT ] ; |
|
|
|
try ; |
|
$(pm).find <toolset>gcc <os>NT <os>CYGWIN ; |
|
catch "Ambiguous key <toolset>gcc <os>NT <os>CYGWIN" ; |
|
|
|
# Test ordinary properties. |
|
assert.result : split-conditional <toolset>gcc ; |
|
|
|
# Test properties with ":". |
|
assert.result : split-conditional <define>FOO=A::B ; |
|
|
|
# Test conditional feature. |
|
assert.result-set-equal <toolset>gcc,<toolset-gcc:version>3.0 <define>FOO |
|
: split-conditional <toolset>gcc,<toolset-gcc:version>3.0:<define>FOO ; |
|
|
|
feature.finish-test property-test-temp ; |
|
} |
|
|