1 # Copyright 2001, 2002, 2003 Dave Abrahams
2 # Copyright 2006 Rene Rivera
3 # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
4 # Distributed under the Boost Software License, Version 1.0.
5 # (See accompanying file LICENSE_1_0.txt or copy at
6 # http://www.boost.org/LICENSE_1_0.txt)
18 # Refines 'properties' by overriding any non-free and non-conditional properties
19 # for which a different value is specified in 'requirements'. Returns the
20 # resulting list of properties.
22 rule refine ( properties * : requirements * )
27 # Collect all non-free features in requirements
28 for local r in $(requirements)
30 # Do not consider conditional requirements.
31 if ! [ MATCH (:) : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ]
37 # Remove properties that are overridden by requirements
38 for local p in $(properties)
40 if [ MATCH (:) : $(p:G=) ] || ! $(p:G) in $(unset)
46 return [ sequence.unique $(result) $(requirements) ] ;
50 # Removes all conditional properties whose conditions are not met. For those
51 # with met conditions, removes the condition. Properties in conditions are
52 # looked up in 'context'.
54 rule evaluate-conditionals-in-context ( properties * : context * )
58 for local p in $(properties)
60 if [ MATCH (:<) : $(p) ]
62 conditionals += $(p) ;
70 local result = $(base) ;
71 for local p in $(conditionals)
73 # Separate condition and property.
74 local s = [ MATCH ^(.*):(<.*) : $(p) ] ;
75 # Split condition into individual properties.
76 local condition = [ regex.split $(s[1]) "," ] ;
78 if ! [ MATCH ^(!).* : $(condition:G=) ]
80 # Only positive checks
81 if $(condition) in $(context)
88 # Have negative checks
92 local c = $(condition[1]) ;
93 local m = [ MATCH ^!(.*) : $(c) ] ;
96 local p = $(m:G=$(c:G)) ;
105 if ! $(c) in $(context)
111 condition = $(condition[2-]) ;
123 rule expand-subfeatures-in-conditions ( properties * )
126 for local p in $(properties)
128 local s = [ MATCH ^(.*):(<.*) : $(p) ] ;
135 local condition = $(s[1]) ;
136 local value = $(s[2]) ;
137 # Condition might include several elements.
138 condition = [ regex.split $(condition) "," ] ;
140 for local c in $(condition)
142 # It is common for a condition to include a toolset or
143 # subfeatures that have not been defined. In that case we want
144 # the condition to simply 'never be satisfied' and validation
145 # would only produce a spurious error so we prevent it by
146 # passing 'true' as the second parameter.
147 e += [ feature.expand-subfeatures $(c) : true ] ;
149 if $(e) = $(condition)
152 # This is just an optimization and possibly a premature one at
154 # (todo) (12.07.2008.) (Jurko)
159 result += $(e:J=,):$(value) ;
167 # Helper for as-path, below. Orders properties with the implicit ones first, and
168 # within the two sections in alphabetical order of feature name.
170 local rule path-order ( x y )
172 if $(y:G) && ! $(x:G)
176 else if $(x:G) && ! $(y:G)
184 x = [ feature.expand-subfeatures $(x) ] ;
185 y = [ feature.expand-subfeatures $(y) ] ;
196 local rule abbreviate-dashed ( string )
199 for local part in [ regex.split $(string) - ]
201 r += [ string.abbreviate $(part) ] ;
207 local rule identity ( string )
213 if --abbreviate-paths in [ modules.peek : ARGV ]
215 .abbrev = abbreviate-dashed ;
223 # Returns a path representing the given expanded property set.
225 rule as-path ( properties * )
227 local entry = .result.$(properties:J=-) ;
232 properties = [ feature.minimize $(properties) ] ;
234 # Sort according to path-order.
235 properties = [ sequence.insertion-sort $(properties) : path-order ] ;
238 for local p in $(properties)
242 local f = [ utility.ungrist $(p:G) ] ;
245 components += [ $(.abbrev) $(p) ] ;
248 $(entry) = $(components:J=/) ;
255 # Exit with error if property is not valid.
257 local rule validate1 ( property )
262 local feature = $(property:G) ;
263 local value = $(property:G=) ;
265 if ! [ feature.valid $(feature) ]
267 # Ungrist for better error messages.
268 feature = [ utility.ungrist $(property:G) ] ;
269 msg = "unknown feature '$(feature)'" ;
271 else if $(value) && ! free in [ feature.attributes $(feature) ]
273 feature.validate-value-string $(feature) $(value) ;
275 else if ! ( $(value) || ( optional in [ feature.attributes $(feature) ] ) )
277 # Ungrist for better error messages.
278 feature = [ utility.ungrist $(property:G) ] ;
279 msg = "No value specified for feature '$(feature)'" ;
284 local feature = [ feature.implied-feature $(property) ] ;
285 feature.validate-value-string $(feature) $(property) ;
290 errors.error "Invalid property "'$(property:J=" ")'": "$(msg:J=" "). ;
295 rule validate ( properties * )
297 for local p in $(properties)
304 rule validate-property-sets ( property-sets * )
306 for local s in $(property-sets)
308 validate [ feature.split $(s) ] ;
313 # Expands any implicit property values in the given property 'specification' so
314 # they explicitly state their feature.
316 rule make ( specification * )
319 for local e in $(specification)
325 else if [ feature.is-implicit-value $(e) ]
327 local feature = [ feature.implied-feature $(e) ] ;
328 result += $(feature)$(e) ;
333 errors.error "'$(e)' is not a valid property specification" ;
340 # Returns a property set containing all the elements in 'properties' that do not
341 # have their attributes listed in 'attributes'.
343 rule remove ( attributes + : properties * )
346 for local e in $(properties)
348 if ! [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ]
357 # Returns a property set containing all the elements in 'properties' that have
358 # their attributes listed in 'attributes'.
360 rule take ( attributes + : properties * )
363 for local e in $(properties)
365 if [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ]
374 # Selects properties corresponding to any of the given features.
376 rule select ( features * : properties * )
380 # Add any missing angle brackets.
382 features = $(empty:G=$(features)) ;
384 for local p in $(properties)
386 if $(p:G) in $(features)
395 # Returns a modified version of properties with all values of the given feature
396 # replaced by the given value. If 'value' is empty the feature will be removed.
398 rule change ( properties * : feature value ? )
401 for local p in $(properties)
403 if $(p:G) = $(feature)
405 result += $(value:G=$(feature)) ;
416 # If 'property' is a conditional property, returns the condition and the
417 # property. E.g. <variant>debug,<toolset>gcc:<inlining>full will become
418 # <variant>debug,<toolset>gcc <inlining>full. Otherwise, returns an empty
421 rule split-conditional ( property )
423 return [ MATCH "^(.+):(<.+)" : $(property) ] ;
427 rule translate-path-value ( value : path )
430 for local v in [ regex.split $(value) "&&" ]
432 t += [ path.root [ path.make $(v) ] $(path) ] ;
434 return $(t:TJ="&&") ;
437 rule translate-dependency-value ( value : project-id : project-location )
439 local split-target = [ regex.match ^(.*)//(.*) : $(value) ] ;
442 local rooted = [ path.root [ path.make $(split-target[1]) ]
443 [ path.root $(project-location) [ path.pwd ] ] ] ;
444 return $(rooted)//$(split-target[2]) ;
446 else if [ path.is-rooted $(value) ]
452 return $(project-id)//$(value) ;
456 rule translate-indirect-value ( rulename : context-module )
458 if [ MATCH "^([^%]*)%([^%]+)$" : $(rulename) ]
460 # Rule is already in the 'indirect-rule' format.
461 return @$(rulename) ;
466 if ! [ MATCH "([.])" : $(rulename) ]
468 # This is an unqualified rule name. The user might want to
469 # set flags on this rule name and toolset.flag
470 # auto-qualifies it. Need to do the same here so flag
471 # setting works. We can arrange for toolset.flag to *not*
472 # auto-qualify the argument but then two rules defined in
473 # two Jamfiles would conflict.
474 rulename = $(context-module).$(rulename) ;
476 v = [ indirect.make $(rulename) : $(context-module) ] ;
482 # Equivalent to a calling all of:
485 # translate-dependency
486 # expand-subfeatures-in-conditions
489 rule translate ( properties * : project-id : project-location : context-module )
492 for local p in $(properties)
494 local split = [ split-conditional $(p) ] ;
495 local condition property ;
499 condition = $(split[1]) ;
500 property = $(split[2]) ;
503 for local c in [ regex.split $(condition) "," ]
505 e += [ feature.expand-subfeatures $(c) : true ] ;
508 condition = $(e:J=,): ;
515 local feature = $(property:G) ;
518 if [ feature.is-implicit-value $(property) ]
520 feature = [ feature.implied-feature $(property) ] ;
521 result += $(condition:E=)$(feature)$(property) ;
526 errors.error "'$(property)' is not a valid property specification" ;
529 local attributes = [ feature.attributes $(feature) ] ;
531 # Only free features should be translated
532 if free in $(attributes)
534 if path in $(attributes)
536 value = [ translate-path-value $(property:G=) : $(project-location) ] ;
537 result += $(condition:E=)$(feature)$(value) ;
539 else if dependency in $(attributes)
541 value = [ translate-dependency-value $(property:G=) : $(project-id) : $(project-location) ] ;
542 result += $(condition:E=)$(feature)$(value) ;
546 local m = [ MATCH ^@(.+) : $(property:G=) ] ;
549 value = [ translate-indirect-value $(m) : $(context-module) ] ;
550 result += $(condition:E=)$(feature)$(value) ;
554 result += $(condition:E=)$(property) ;
560 result += $(condition:E=)$(property) ;
567 # Interpret all path properties in 'properties' as relative to 'path'. The
568 # property values are assumed to be in system-specific form, and will be
569 # translated into normalized form.
571 rule translate-paths ( properties * : path )
574 for local p in $(properties)
576 local split = [ split-conditional $(p) ] ;
577 local condition = "" ;
580 condition = $(split[1]): ;
584 if path in [ feature.attributes $(p:G) ]
586 local values = [ regex.split $(p:TG=) "&&" ] ;
588 for local v in $(values)
590 t += [ path.root [ path.make $(v) ] $(path) ] ;
593 result += $(condition)$(t:TG=$(p:G)) ;
597 result += $(condition)$(p) ;
604 # Assumes that all feature values that start with '@' are names of rules, used
605 # in 'context-module'. Such rules can be either local to the module or global.
606 # Converts such values into 'indirect-rule' format (see indirect.jam), so they
607 # can be called from other modules. Does nothing for such values that are
608 # already in the 'indirect-rule' format.
610 rule translate-indirect ( specification * : context-module )
613 for local p in $(specification)
615 local m = [ MATCH ^@(.+) : $(p:G=) ] ;
619 if [ MATCH "^([^%]*)%([^%]+)$" : $(m) ]
621 # Rule is already in the 'indirect-rule' format.
626 if ! [ MATCH "([.])" : $(m) ]
628 # This is an unqualified rule name. The user might want to
629 # set flags on this rule name and toolset.flag
630 # auto-qualifies it. Need to do the same here so flag
631 # setting works. We can arrange for toolset.flag to *not*
632 # auto-qualify the argument but then two rules defined in
633 # two Jamfiles would conflict.
634 m = $(context-module).$(m) ;
636 v = [ indirect.make $(m) : $(context-module) ] ;
640 result += $(v:G=$(p:G)) ;
651 # Binds all dependency properties in a list relative to the given project.
652 # Targets with absolute paths will be left unchanged and targets which have a
653 # project specified will have the path to the project interpreted relative to
654 # the specified location.
656 rule translate-dependencies ( specification * : project-id : location )
659 for local p in $(specification)
661 local split = [ split-conditional $(p) ] ;
662 local condition = "" ;
665 condition = $(split[1]): ;
668 if dependency in [ feature.attributes $(p:G) ]
670 local split-target = [ regex.match ^(.*)//(.*) : $(p:G=) ] ;
673 local rooted = [ path.root [ path.make $(split-target[1]) ]
674 [ path.root $(location) [ path.pwd ] ] ] ;
675 result += $(condition)$(p:G)$(rooted)//$(split-target[2]) ;
677 else if [ path.is-rooted $(p:G=) ]
679 result += $(condition)$(p) ;
683 result += $(condition)$(p:G)$(project-id)//$(p:G=) ;
688 result += $(condition)$(p) ;
695 # Class maintaining a property set -> string mapping.
707 # Associate 'value' with 'properties'.
709 rule insert ( properties * : value )
711 self.all-flags += self.$(self.next-flag) ;
712 self.$(self.next-flag) = $(value) $(properties) ;
714 self.next-flag = [ numbers.increment $(self.next-flag) ] ;
717 # Returns the value associated with 'properties' or any subset of it. If
718 # more than one subset has a value assigned to it, returns the value for the
719 # longest subset, if it is unique.
721 rule find ( property-set )
723 # First find all matches.
726 for local i in $(self.all-flags)
728 local list = $($(i)) ;
729 if [ $(property-set).contains-raw $(list[2-]) ]
731 matches += $(list[1]) ;
732 match-ranks += [ sequence.length $(list) ] ;
735 local best = [ sequence.select-highest-ranked $(matches)
739 import errors : error : errors.error ;
740 errors.error "Ambiguous key $(properties:J= :E=)" ;
745 # Returns the value associated with 'properties'. If 'value' parameter is
746 # given, replaces the found value.
748 rule find-replace ( properties * : value ? )
750 # First find all matches.
753 for local i in $(self.all-flags)
755 if $($(i)[2-]) in $(properties)
758 match-ranks += [ sequence.length $($(i)) ] ;
761 local best = [ sequence.select-highest-ranked $(matches)
765 import errors : error : errors.error ;
766 errors.error "Ambiguous key $(properties:J= :E=)" ;
768 local original = $($(best)[1]) ;
771 $(best) = $(value) $($(best)[2-]) ;
781 import "class" : new ;
782 import errors : try catch ;
785 # Local rules must be explicitly re-imported.
786 import property : path-order abbreviate-dashed ;
788 feature.prepare-test property-test-temp ;
790 feature.feature toolset : gcc : implicit symmetric ;
791 feature.subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 3.0 3.0.1
793 feature.feature define : : free ;
794 feature.feature runtime-link : dynamic static : symmetric link-incompatible ;
795 feature.feature optimization : on off ;
796 feature.feature variant : debug release : implicit composite symmetric ;
797 feature.feature rtti : on off : link-incompatible ;
799 feature.compose <variant>debug : <define>_DEBUG <optimization>off ;
800 feature.compose <variant>release : <define>NDEBUG <optimization>on ;
802 validate <toolset>gcc <toolset>gcc-3.0.1 : $(test-space) ;
804 assert.true path-order $(test-space) debug <define>foo ;
805 assert.false path-order $(test-space) <define>foo debug ;
806 assert.true path-order $(test-space) gcc debug ;
807 assert.false path-order $(test-space) debug gcc ;
808 assert.true path-order $(test-space) <optimization>on <rtti>on ;
809 assert.false path-order $(test-space) <rtti>on <optimization>on ;
811 assert.result-set-equal <toolset>gcc <rtti>off <define>FOO
812 : refine <toolset>gcc <rtti>off
816 assert.result-set-equal <toolset>gcc <optimization>on
817 : refine <toolset>gcc <optimization>off
821 assert.result-set-equal <toolset>gcc <rtti>off
822 : refine <toolset>gcc : <rtti>off : $(test-space) ;
824 assert.result-set-equal <toolset>gcc <rtti>off <rtti>off:<define>FOO
825 : refine <toolset>gcc : <rtti>off <rtti>off:<define>FOO
828 assert.result-set-equal <toolset>gcc:<define>foo <toolset>gcc:<define>bar
829 : refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar
832 assert.result <define>MY_RELEASE
833 : evaluate-conditionals-in-context
834 <variant>release,<rtti>off:<define>MY_RELEASE
835 : <toolset>gcc <variant>release <rtti>off ;
838 : as-path <optimization>off <variant>debug
841 assert.result gcc/debug/rtti-off
842 : as-path <toolset>gcc <optimization>off <rtti>off <variant>debug
845 assert.result optmz-off : abbreviate-dashed optimization-off ;
846 assert.result rntm-lnk-sttc : abbreviate-dashed runtime-link-static ;
849 validate <feature>value : $(test-space) ;
850 catch "Invalid property '<feature>value': unknown feature 'feature'." ;
853 validate <rtti>default : $(test-space) ;
854 catch \"default\" is not a known value of feature <rtti> ;
856 validate <define>WHATEVER : $(test-space) ;
859 validate <rtti> : $(test-space) ;
860 catch "Invalid property '<rtti>': No value specified for feature 'rtti'." ;
863 validate value : $(test-space) ;
864 catch \"value\" is not an implicit feature value ;
866 assert.result-set-equal <rtti>on
867 : remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ;
869 assert.result-set-equal <include>a
870 : select include : <include>a <toolset>gcc ;
872 assert.result-set-equal <include>a
873 : select include bar : <include>a <toolset>gcc ;
875 assert.result-set-equal <include>a <toolset>gcc
876 : select include <bar> <toolset> : <include>a <toolset>gcc ;
878 assert.result-set-equal <toolset>kylix <include>a
879 : change <toolset>gcc <include>a : <toolset> kylix ;
881 pm = [ new property-map ] ;
882 $(pm).insert <toolset>gcc : o ;
883 $(pm).insert <toolset>gcc <os>NT : obj ;
884 $(pm).insert <toolset>gcc <os>CYGWIN : obj ;
886 assert.equal o : [ $(pm).find-replace <toolset>gcc ] ;
888 assert.equal obj : [ $(pm).find-replace <toolset>gcc <os>NT ] ;
891 $(pm).find-replace <toolset>gcc <os>NT <os>CYGWIN ;
892 catch "Ambiguous key <toolset>gcc <os>NT <os>CYGWIN" ;
894 # Test ordinary properties.
895 assert.result : split-conditional <toolset>gcc ;
897 # Test properties with ":".
898 assert.result : split-conditional <define>FOO=A::B ;
900 # Test conditional feature.
901 assert.result-set-equal <toolset>gcc,<toolset-gcc:version>3.0 <define>FOO
902 : split-conditional <toolset>gcc,<toolset-gcc:version>3.0:<define>FOO ;
904 feature.finish-test property-test-temp ;