]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/build/src/build/property.jam
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / tools / build / src / build / property.jam
CommitLineData
7c673cae
FG
1# Copyright 2001, 2002, 2003 Dave Abrahams
2# Copyright 2006 Rene Rivera
3# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
20effc67 4# Copyright 2020 Nikita Kniazev
7c673cae
FG
5# Distributed under the Boost Software License, Version 1.0.
6# (See accompanying file LICENSE_1_0.txt or copy at
7# http://www.boost.org/LICENSE_1_0.txt)
8
9import feature ;
10import indirect ;
11import path ;
12import regex ;
13import string ;
14import sequence ;
15import set ;
16import utility ;
17
18
19# Refines 'properties' by overriding any non-free and non-conditional properties
20# for which a different value is specified in 'requirements'. Returns the
21# resulting list of properties.
22#
23rule refine ( properties * : requirements * )
24{
25 local result ;
26 local unset ;
27
28 # Collect all non-free features in requirements
29 for local r in $(requirements)
30 {
31 # Do not consider conditional requirements.
11fdf7f2 32 if ! [ MATCH "(:<)" : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ]
7c673cae 33 {
11fdf7f2
TL
34 if ! $(r) in $(properties)
35 {
36 # Kill subfeatures of properties that we're changing
37 local sub = [ modules.peek feature : $(r:G).subfeatures ] ;
38 if $(sub)
39 {
40 # non-specific subfeatures are still valid
41 sub = [ MATCH "(.*:.*)" : $(sub) ] ;
42 local name = [ utility.ungrist $(r:G) ] ;
43 unset += <$(name)-$(sub)> ;
44 }
45 }
7c673cae
FG
46 unset += $(r:G) ;
47 }
48 }
49
50 # Remove properties that are overridden by requirements
51 for local p in $(properties)
52 {
11fdf7f2 53 if [ MATCH "(:<)" : $(p:G=) ] || ! $(p:G) in $(unset)
7c673cae
FG
54 {
55 result += $(p) ;
56 }
57 }
58
59 return [ sequence.unique $(result) $(requirements) ] ;
60}
61
62
63# Removes all conditional properties whose conditions are not met. For those
64# with met conditions, removes the condition. Properties in conditions are
65# looked up in 'context'.
66#
67rule evaluate-conditionals-in-context ( properties * : context * )
68{
69 local base ;
70 local conditionals ;
11fdf7f2 71 local indirect ;
7c673cae
FG
72 for local p in $(properties)
73 {
11fdf7f2 74 if [ MATCH "(:<)" : $(p) ] && ! free in [ feature.attributes $(p:G) ]
7c673cae
FG
75 {
76 conditionals += $(p) ;
77 }
11fdf7f2
TL
78 else if $(p:G) = <conditional>
79 {
80 indirect += $(p) ;
81 }
7c673cae
FG
82 else
83 {
84 base += $(p) ;
85 }
86 }
87
88 local result = $(base) ;
89 for local p in $(conditionals)
90 {
91 # Separate condition and property.
11fdf7f2 92 local s = [ MATCH "^(.*):(<.*)" : $(p) ] ;
7c673cae
FG
93 # Split condition into individual properties.
94 local condition = [ regex.split $(s[1]) "," ] ;
95 # Evaluate condition.
96 if ! [ MATCH ^(!).* : $(condition:G=) ]
97 {
98 # Only positive checks
99 if $(condition) in $(context)
100 {
101 result += $(s[2]) ;
102 }
103 }
104 else
105 {
106 # Have negative checks
107 local fail ;
20effc67 108 for local c in $(condition)
7c673cae 109 {
20effc67
TL
110 local c = [ MATCH ^(!)?(.*) : $(c) ] ;
111 # It is XOR: $(c[1]) = "!" ^ $(c[2]) in $(context)
112 if $(c[1]) = "!" && $(c[2]) in $(context) || $(c[1]) != "!" && ! ( $(c[2]) in $(context) )
7c673cae 113 {
20effc67
TL
114 fail = true ;
115 break ;
7c673cae 116 }
7c673cae
FG
117 }
118 if ! $(fail)
119 {
120 result += $(s[2]) ;
121 }
122 }
123 }
11fdf7f2
TL
124 # Import here to avoid cyclic dependency
125 import project ;
126 for local i in [ MATCH "^@(.*)" : $(indirect:G=) ]
127 {
128 # If the rule was set in a project module, translate paths
129 # relative to that project's location.
130 local m = [ indirect.get-module $(i) ] ;
131 local p = [ project.target $(m) : allow-missing ] ;
132 local new = [ indirect.call $(i) $(context) ] ;
133 if $(p) && [ $(p).location ]
134 {
92f5a8d4
TL
135 local location = [ $(p).location ] ;
136 local project-id = [ project.attribute $(m) id ] ;
137 project-id ?= [ path.root $(location) [ path.pwd ] ] ;
138 result +=
139 [ translate $(new) : $(project-id) : $(location) : $(m) ] ;
11fdf7f2
TL
140 }
141 else
142 {
143 result += $(new) ;
144 }
145 }
7c673cae
FG
146 return $(result) ;
147}
148
149
11fdf7f2
TL
150# Returns <relevant> properties indicating how the conditionals in
151# properties affect feature relevance. If the optional argument cond
152# is passed, it is treated as extra conditions for all properties.
153#
154rule evaluate-conditional-relevance ( properties * : cond * )
155{
156 cond = [ sequence.transform utility.ungrist : $(cond:G) ] ;
157 local result ;
158 for local p in $(properties)
159 {
160 # Separate condition and property.
161 local s = [ MATCH "^(.*):(<.*)" : $(p) ] ;
162 if ! $(s) || free in [ feature.attributes $(p:G) ]
163 {
164 local value = [ utility.ungrist $(p:G) ] ;
165 result += <relevant>$(value):<relevant>$(cond) ;
166 }
167 else
168 {
169 local condition = [ regex.split $(s[1]) "," ] ;
170 condition = [ MATCH "^!?(.*)" : $(condition) ] ;
171 condition = [ sequence.transform utility.ungrist : $(condition:G) ] $(cond) ;
172 local value = [ utility.ungrist $(s[2]:G) ] ;
173 result += <relevant>$(value):<relevant>$(condition) ;
174 }
175 }
176 return [ sequence.unique $(result) ] ;
177}
178
179
7c673cae
FG
180rule expand-subfeatures-in-conditions ( properties * )
181{
182 local result ;
183 for local p in $(properties)
184 {
11fdf7f2 185 local s = [ MATCH "^(.*):(<.*)" : $(p) ] ;
7c673cae
FG
186 if ! $(s)
187 {
188 result += $(p) ;
189 }
190 else
191 {
192 local condition = $(s[1]) ;
193 local value = $(s[2]) ;
194 # Condition might include several elements.
195 condition = [ regex.split $(condition) "," ] ;
196 local e ;
197 for local c in $(condition)
198 {
199 # It is common for a condition to include a toolset or
200 # subfeatures that have not been defined. In that case we want
201 # the condition to simply 'never be satisfied' and validation
202 # would only produce a spurious error so we prevent it by
203 # passing 'true' as the second parameter.
204 e += [ feature.expand-subfeatures $(c) : true ] ;
205 }
206 if $(e) = $(condition)
207 {
208 # (todo)
209 # This is just an optimization and possibly a premature one at
210 # that.
211 # (todo) (12.07.2008.) (Jurko)
212 result += $(p) ;
213 }
214 else
215 {
11fdf7f2 216 result += "$(e:J=,):$(value)" ;
7c673cae
FG
217 }
218 }
219 }
220 return $(result) ;
221}
222
223
224# Helper for as-path, below. Orders properties with the implicit ones first, and
225# within the two sections in alphabetical order of feature name.
226#
227local rule path-order ( x y )
228{
229 if $(y:G) && ! $(x:G)
230 {
231 return true ;
232 }
233 else if $(x:G) && ! $(y:G)
234 {
235 return ;
236 }
237 else
238 {
239 if ! $(x:G)
240 {
241 x = [ feature.expand-subfeatures $(x) ] ;
242 y = [ feature.expand-subfeatures $(y) ] ;
243 }
244
245 if $(x[1]) < $(y[1])
246 {
247 return true ;
248 }
249 }
250}
251
252
253local rule abbreviate-dashed ( string )
254{
255 local r ;
256 for local part in [ regex.split $(string) - ]
257 {
258 r += [ string.abbreviate $(part) ] ;
259 }
260 return $(r:J=-) ;
261}
262
263
264local rule identity ( string )
265{
266 return $(string) ;
267}
268
269
270if --abbreviate-paths in [ modules.peek : ARGV ]
271{
272 .abbrev = abbreviate-dashed ;
273}
274else
275{
276 .abbrev = identity ;
277}
278
279
280# Returns a path representing the given expanded property set.
281#
282rule as-path ( properties * )
283{
284 local entry = .result.$(properties:J=-) ;
285
286 if ! $($(entry))
287 {
288 # Trim redundancy.
289 properties = [ feature.minimize $(properties) ] ;
290
291 # Sort according to path-order.
292 properties = [ sequence.insertion-sort $(properties) : path-order ] ;
293
294 local components ;
295 for local p in $(properties)
296 {
297 if ! hidden in [ feature.attributes $(p:G) ]
298 {
299 if $(p:G)
300 {
301 local f = [ utility.ungrist $(p:G) ] ;
302 p = $(f)-$(p:G=) ;
303 }
304 components += [ $(.abbrev) $(p) ] ;
305 }
306 }
307
308 $(entry) = $(components:J=/) ;
309 }
310
311 return $($(entry)) ;
312}
313
314
315# Exit with error if property is not valid.
316#
317local rule validate1 ( property )
318{
319 local msg ;
320 if $(property:G)
321 {
322 local feature = $(property:G) ;
323 local value = $(property:G=) ;
324
325 if ! [ feature.valid $(feature) ]
326 {
327 # Ungrist for better error messages.
328 feature = [ utility.ungrist $(property:G) ] ;
329 msg = "unknown feature '$(feature)'" ;
330 }
331 else if $(value) && ! free in [ feature.attributes $(feature) ]
332 {
333 feature.validate-value-string $(feature) $(value) ;
334 }
335 else if ! ( $(value) || ( optional in [ feature.attributes $(feature) ] ) )
336 {
337 # Ungrist for better error messages.
338 feature = [ utility.ungrist $(property:G) ] ;
339 msg = "No value specified for feature '$(feature)'" ;
340 }
341 }
342 else
343 {
344 local feature = [ feature.implied-feature $(property) ] ;
345 feature.validate-value-string $(feature) $(property) ;
346 }
347 if $(msg)
348 {
349 import errors ;
350 errors.error "Invalid property "'$(property:J=" ")'": "$(msg:J=" "). ;
351 }
352}
353
354
355rule validate ( properties * )
356{
357 for local p in $(properties)
358 {
359 validate1 $(p) ;
360 }
361}
362
363
364rule validate-property-sets ( property-sets * )
365{
366 for local s in $(property-sets)
367 {
368 validate [ feature.split $(s) ] ;
369 }
370}
371
372
373# Expands any implicit property values in the given property 'specification' so
374# they explicitly state their feature.
375#
376rule make ( specification * )
377{
378 local result ;
379 for local e in $(specification)
380 {
381 if $(e:G)
382 {
383 result += $(e) ;
384 }
385 else if [ feature.is-implicit-value $(e) ]
386 {
387 local feature = [ feature.implied-feature $(e) ] ;
388 result += $(feature)$(e) ;
389 }
390 else
391 {
392 import errors ;
393 errors.error "'$(e)' is not a valid property specification" ;
394 }
395 }
396 return $(result) ;
397}
398
399
400# Returns a property set containing all the elements in 'properties' that do not
401# have their attributes listed in 'attributes'.
402#
403rule remove ( attributes + : properties * )
404{
405 local result ;
406 for local e in $(properties)
407 {
408 if ! [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ]
409 {
410 result += $(e) ;
411 }
412 }
413 return $(result) ;
414}
415
416
417# Returns a property set containing all the elements in 'properties' that have
418# their attributes listed in 'attributes'.
419#
420rule take ( attributes + : properties * )
421{
422 local result ;
423 for local e in $(properties)
424 {
425 if [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ]
426 {
427 result += $(e) ;
428 }
429 }
430 return $(result) ;
431}
432
433
434# Selects properties corresponding to any of the given features.
435#
436rule select ( features * : properties * )
437{
438 local result ;
439
440 # Add any missing angle brackets.
441 local empty = "" ;
442 features = $(empty:G=$(features)) ;
443
444 for local p in $(properties)
445 {
446 if $(p:G) in $(features)
447 {
448 result += $(p) ;
449 }
450 }
451 return $(result) ;
452}
453
454
455# Returns a modified version of properties with all values of the given feature
456# replaced by the given value. If 'value' is empty the feature will be removed.
457#
458rule change ( properties * : feature value ? )
459{
460 local result ;
461 for local p in $(properties)
462 {
463 if $(p:G) = $(feature)
464 {
465 result += $(value:G=$(feature)) ;
466 }
467 else
468 {
469 result += $(p) ;
470 }
471 }
472 return $(result) ;
473}
474
475
476# If 'property' is a conditional property, returns the condition and the
477# property. E.g. <variant>debug,<toolset>gcc:<inlining>full will become
478# <variant>debug,<toolset>gcc <inlining>full. Otherwise, returns an empty
479# string.
480#
481rule split-conditional ( property )
482{
483 return [ MATCH "^(.+):(<.+)" : $(property) ] ;
484}
485
486
487rule translate-path-value ( value : path )
488{
489 local t ;
490 for local v in [ regex.split $(value) "&&" ]
491 {
492 t += [ path.root [ path.make $(v) ] $(path) ] ;
493 }
494 return $(t:TJ="&&") ;
495}
496
497rule translate-dependency-value ( value : project-id : project-location )
498{
499 local split-target = [ regex.match ^(.*)//(.*) : $(value) ] ;
500 if $(split-target)
501 {
502 local rooted = [ path.root [ path.make $(split-target[1]) ]
503 [ path.root $(project-location) [ path.pwd ] ] ] ;
504 return $(rooted)//$(split-target[2]) ;
505 }
506 else if [ path.is-rooted $(value) ]
507 {
508 return $(value) ;
509 }
510 else
511 {
512 return $(project-id)//$(value) ;
513 }
514}
515
516rule translate-indirect-value ( rulename : context-module )
517{
518 if [ MATCH "^([^%]*)%([^%]+)$" : $(rulename) ]
519 {
520 # Rule is already in the 'indirect-rule' format.
521 return @$(rulename) ;
522 }
523 else
524 {
525 local v ;
526 if ! [ MATCH "([.])" : $(rulename) ]
527 {
528 # This is an unqualified rule name. The user might want to
529 # set flags on this rule name and toolset.flag
530 # auto-qualifies it. Need to do the same here so flag
531 # setting works. We can arrange for toolset.flag to *not*
532 # auto-qualify the argument but then two rules defined in
533 # two Jamfiles would conflict.
534 rulename = $(context-module).$(rulename) ;
535 }
536 v = [ indirect.make $(rulename) : $(context-module) ] ;
537 return @$(v) ;
538 }
539
540}
541
542# Equivalent to a calling all of:
543# translate-path
544# translate-indirect
545# translate-dependency
546# expand-subfeatures-in-conditions
547# make
548#
549rule translate ( properties * : project-id : project-location : context-module )
550{
20effc67 551 local translate-path-rule = [ MATCH "^<translate-path>[@](.*)$" : "$(properties)" ] ;
7c673cae
FG
552 local result ;
553 for local p in $(properties)
554 {
555 local split = [ split-conditional $(p) ] ;
556 local condition property ;
557
558 if $(split)
559 {
560 condition = $(split[1]) ;
561 property = $(split[2]) ;
562
563 local e ;
564 for local c in [ regex.split $(condition) "," ]
565 {
20effc67
TL
566 # strip negation for expansion and readd after
567 c = [ MATCH "^(!)?(.*)" : $(c) ] ;
568 local expanded = [ feature.expand-subfeatures $(c[2]) : true ] ;
569 e += $(c[1])$(expanded) ;
7c673cae
FG
570 }
571
11fdf7f2 572 condition = "$(e:J=,):" ;
7c673cae
FG
573 }
574 else
575 {
576 property = $(p) ;
577 }
578
579 local feature = $(property:G) ;
580 if ! $(feature)
581 {
582 if [ feature.is-implicit-value $(property) ]
583 {
584 feature = [ feature.implied-feature $(property) ] ;
585 result += $(condition:E=)$(feature)$(property) ;
586 }
587 else
588 {
589 import errors ;
590 errors.error "'$(property)' is not a valid property specification" ;
591 }
592 } else {
593 local attributes = [ feature.attributes $(feature) ] ;
594 local value ;
595 # Only free features should be translated
596 if free in $(attributes)
597 {
598 if path in $(attributes)
599 {
20effc67
TL
600 if $(translate-path-rule)
601 {
602 value = [ $(translate-path-rule) $(feature) $(property:G=) : $(properties) : $(project-id) : $(project-location) ] ;
603 }
604 if ! $(value)
605 {
606 value = [ translate-path-value $(property:G=) : $(project-location) ] ;
607 }
7c673cae
FG
608 result += $(condition:E=)$(feature)$(value) ;
609 }
610 else if dependency in $(attributes)
611 {
612 value = [ translate-dependency-value $(property:G=) : $(project-id) : $(project-location) ] ;
613 result += $(condition:E=)$(feature)$(value) ;
614 }
615 else
616 {
617 local m = [ MATCH ^@(.+) : $(property:G=) ] ;
618 if $(m)
619 {
620 value = [ translate-indirect-value $(m) : $(context-module) ] ;
621 result += $(condition:E=)$(feature)$(value) ;
622 }
623 else
624 {
625 result += $(condition:E=)$(property) ;
626 }
627 }
628 }
629 else
630 {
631 result += $(condition:E=)$(property) ;
632 }
633 }
634 }
635 return $(result) ;
636}
637
638# Interpret all path properties in 'properties' as relative to 'path'. The
639# property values are assumed to be in system-specific form, and will be
640# translated into normalized form.
641#
642rule translate-paths ( properties * : path )
643{
644 local result ;
645 for local p in $(properties)
646 {
647 local split = [ split-conditional $(p) ] ;
648 local condition = "" ;
649 if $(split)
650 {
11fdf7f2 651 condition = "$(split[1]):" ;
7c673cae
FG
652 p = $(split[2]) ;
653 }
654
655 if path in [ feature.attributes $(p:G) ]
656 {
657 local values = [ regex.split $(p:TG=) "&&" ] ;
658 local t ;
659 for local v in $(values)
660 {
661 t += [ path.root [ path.make $(v) ] $(path) ] ;
662 }
663 t = $(t:J="&&") ;
664 result += $(condition)$(t:TG=$(p:G)) ;
665 }
666 else
667 {
668 result += $(condition)$(p) ;
669 }
670 }
671 return $(result) ;
672}
673
674
675# Assumes that all feature values that start with '@' are names of rules, used
676# in 'context-module'. Such rules can be either local to the module or global.
677# Converts such values into 'indirect-rule' format (see indirect.jam), so they
678# can be called from other modules. Does nothing for such values that are
679# already in the 'indirect-rule' format.
680#
681rule translate-indirect ( specification * : context-module )
682{
683 local result ;
684 for local p in $(specification)
685 {
686 local m = [ MATCH ^@(.+) : $(p:G=) ] ;
687 if $(m)
688 {
689 local v ;
690 if [ MATCH "^([^%]*)%([^%]+)$" : $(m) ]
691 {
692 # Rule is already in the 'indirect-rule' format.
693 v = $(m) ;
694 }
695 else
696 {
697 if ! [ MATCH "([.])" : $(m) ]
698 {
699 # This is an unqualified rule name. The user might want to
700 # set flags on this rule name and toolset.flag
701 # auto-qualifies it. Need to do the same here so flag
702 # setting works. We can arrange for toolset.flag to *not*
703 # auto-qualify the argument but then two rules defined in
704 # two Jamfiles would conflict.
705 m = $(context-module).$(m) ;
706 }
707 v = [ indirect.make $(m) : $(context-module) ] ;
708 }
709
710 v = @$(v) ;
711 result += $(v:G=$(p:G)) ;
712 }
713 else
714 {
715 result += $(p) ;
716 }
717 }
718 return $(result) ;
719}
720
721
722# Binds all dependency properties in a list relative to the given project.
723# Targets with absolute paths will be left unchanged and targets which have a
724# project specified will have the path to the project interpreted relative to
725# the specified location.
726#
727rule translate-dependencies ( specification * : project-id : location )
728{
729 local result ;
730 for local p in $(specification)
731 {
732 local split = [ split-conditional $(p) ] ;
733 local condition = "" ;
734 if $(split)
735 {
11fdf7f2 736 condition = "$(split[1]):" ;
7c673cae
FG
737 p = $(split[2]) ;
738 }
739 if dependency in [ feature.attributes $(p:G) ]
740 {
741 local split-target = [ regex.match ^(.*)//(.*) : $(p:G=) ] ;
742 if $(split-target)
743 {
744 local rooted = [ path.root [ path.make $(split-target[1]) ]
745 [ path.root $(location) [ path.pwd ] ] ] ;
746 result += $(condition)$(p:G)$(rooted)//$(split-target[2]) ;
747 }
748 else if [ path.is-rooted $(p:G=) ]
749 {
750 result += $(condition)$(p) ;
751 }
752 else
753 {
754 result += $(condition)$(p:G)$(project-id)//$(p:G=) ;
755 }
756 }
757 else
758 {
759 result += $(condition)$(p) ;
760 }
761 }
762 return $(result) ;
763}
764
765
766# Class maintaining a property set -> string mapping.
767#
768class property-map
769{
770 import numbers ;
771 import sequence ;
772
773 rule __init__ ( )
774 {
775 self.next-flag = 1 ;
776 }
777
778 # Associate 'value' with 'properties'.
779 #
780 rule insert ( properties * : value )
781 {
782 self.all-flags += self.$(self.next-flag) ;
783 self.$(self.next-flag) = $(value) $(properties) ;
784
785 self.next-flag = [ numbers.increment $(self.next-flag) ] ;
786 }
787
788 # Returns the value associated with 'properties' or any subset of it. If
789 # more than one subset has a value assigned to it, returns the value for the
790 # longest subset, if it is unique.
791 #
792 rule find ( property-set )
793 {
794 # First find all matches.
795 local matches ;
796 local match-ranks ;
797 for local i in $(self.all-flags)
798 {
799 local list = $($(i)) ;
800 if [ $(property-set).contains-raw $(list[2-]) ]
801 {
802 matches += $(list[1]) ;
803 match-ranks += [ sequence.length $(list) ] ;
804 }
805 }
806 local best = [ sequence.select-highest-ranked $(matches)
807 : $(match-ranks) ] ;
808 if $(best[2])
809 {
810 import errors : error : errors.error ;
811 errors.error "Ambiguous key $(properties:J= :E=)" ;
812 }
813 return $(best) ;
814 }
815
816 # Returns the value associated with 'properties'. If 'value' parameter is
817 # given, replaces the found value.
818 #
819 rule find-replace ( properties * : value ? )
820 {
821 # First find all matches.
822 local matches ;
823 local match-ranks ;
824 for local i in $(self.all-flags)
825 {
826 if $($(i)[2-]) in $(properties)
827 {
828 matches += $(i) ;
829 match-ranks += [ sequence.length $($(i)) ] ;
830 }
831 }
832 local best = [ sequence.select-highest-ranked $(matches)
833 : $(match-ranks) ] ;
834 if $(best[2])
835 {
836 import errors : error : errors.error ;
837 errors.error "Ambiguous key $(properties:J= :E=)" ;
838 }
839 local original = $($(best)[1]) ;
840 if $(value)-is-set
841 {
842 $(best) = $(value) $($(best)[2-]) ;
843 }
844 return $(original) ;
845 }
846}
847
848
849rule __test__ ( )
850{
851 import assert ;
852 import "class" : new ;
853 import errors : try catch ;
854 import feature ;
855
856 # Local rules must be explicitly re-imported.
857 import property : path-order abbreviate-dashed ;
858
859 feature.prepare-test property-test-temp ;
860
861 feature.feature toolset : gcc : implicit symmetric ;
862 feature.subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 3.0 3.0.1
863 3.0.2 : optional ;
864 feature.feature define : : free ;
865 feature.feature runtime-link : dynamic static : symmetric link-incompatible ;
866 feature.feature optimization : on off ;
867 feature.feature variant : debug release : implicit composite symmetric ;
868 feature.feature rtti : on off : link-incompatible ;
869
870 feature.compose <variant>debug : <define>_DEBUG <optimization>off ;
871 feature.compose <variant>release : <define>NDEBUG <optimization>on ;
872
873 validate <toolset>gcc <toolset>gcc-3.0.1 : $(test-space) ;
874
875 assert.true path-order $(test-space) debug <define>foo ;
876 assert.false path-order $(test-space) <define>foo debug ;
877 assert.true path-order $(test-space) gcc debug ;
878 assert.false path-order $(test-space) debug gcc ;
879 assert.true path-order $(test-space) <optimization>on <rtti>on ;
880 assert.false path-order $(test-space) <rtti>on <optimization>on ;
881
882 assert.result-set-equal <toolset>gcc <rtti>off <define>FOO
883 : refine <toolset>gcc <rtti>off
884 : <define>FOO
885 : $(test-space) ;
886
887 assert.result-set-equal <toolset>gcc <optimization>on
888 : refine <toolset>gcc <optimization>off
889 : <optimization>on
890 : $(test-space) ;
891
892 assert.result-set-equal <toolset>gcc <rtti>off
893 : refine <toolset>gcc : <rtti>off : $(test-space) ;
894
895 assert.result-set-equal <toolset>gcc <rtti>off <rtti>off:<define>FOO
896 : refine <toolset>gcc : <rtti>off <rtti>off:<define>FOO
897 : $(test-space) ;
898
899 assert.result-set-equal <toolset>gcc:<define>foo <toolset>gcc:<define>bar
900 : refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar
901 : $(test-space) ;
902
20effc67
TL
903 assert.result
904 : evaluate-conditionals-in-context
905 <variant>release,<rtti>off:<define>MY_RELEASE
906 : <toolset>gcc <variant>release <rtti>on ;
907
7c673cae
FG
908 assert.result <define>MY_RELEASE
909 : evaluate-conditionals-in-context
910 <variant>release,<rtti>off:<define>MY_RELEASE
911 : <toolset>gcc <variant>release <rtti>off ;
912
20effc67
TL
913 assert.result <define>MY_RELEASE
914 : evaluate-conditionals-in-context
915 <variant>release,!<rtti>off:<define>MY_RELEASE
916 : <toolset>gcc <variant>release <rtti>on ;
917
918 assert.result
919 : evaluate-conditionals-in-context
920 <variant>release,!<rtti>off:<define>MY_RELEASE
921 : <toolset>gcc <variant>release <rtti>off ;
922
7c673cae
FG
923 assert.result debug
924 : as-path <optimization>off <variant>debug
925 : $(test-space) ;
926
927 assert.result gcc/debug/rtti-off
928 : as-path <toolset>gcc <optimization>off <rtti>off <variant>debug
929 : $(test-space) ;
930
931 assert.result optmz-off : abbreviate-dashed optimization-off ;
932 assert.result rntm-lnk-sttc : abbreviate-dashed runtime-link-static ;
933
934 try ;
935 validate <feature>value : $(test-space) ;
936 catch "Invalid property '<feature>value': unknown feature 'feature'." ;
937
938 try ;
939 validate <rtti>default : $(test-space) ;
940 catch \"default\" is not a known value of feature <rtti> ;
941
942 validate <define>WHATEVER : $(test-space) ;
943
944 try ;
945 validate <rtti> : $(test-space) ;
946 catch "Invalid property '<rtti>': No value specified for feature 'rtti'." ;
947
948 try ;
949 validate value : $(test-space) ;
950 catch \"value\" is not an implicit feature value ;
951
952 assert.result-set-equal <rtti>on
953 : remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ;
954
955 assert.result-set-equal <include>a
956 : select include : <include>a <toolset>gcc ;
957
958 assert.result-set-equal <include>a
959 : select include bar : <include>a <toolset>gcc ;
960
961 assert.result-set-equal <include>a <toolset>gcc
962 : select include <bar> <toolset> : <include>a <toolset>gcc ;
963
964 assert.result-set-equal <toolset>kylix <include>a
965 : change <toolset>gcc <include>a : <toolset> kylix ;
966
967 pm = [ new property-map ] ;
968 $(pm).insert <toolset>gcc : o ;
969 $(pm).insert <toolset>gcc <os>NT : obj ;
970 $(pm).insert <toolset>gcc <os>CYGWIN : obj ;
971
972 assert.equal o : [ $(pm).find-replace <toolset>gcc ] ;
973
974 assert.equal obj : [ $(pm).find-replace <toolset>gcc <os>NT ] ;
975
976 try ;
977 $(pm).find-replace <toolset>gcc <os>NT <os>CYGWIN ;
978 catch "Ambiguous key <toolset>gcc <os>NT <os>CYGWIN" ;
979
980 # Test ordinary properties.
981 assert.result : split-conditional <toolset>gcc ;
982
983 # Test properties with ":".
11fdf7f2 984 assert.result : split-conditional <define>"FOO=A::B" ;
7c673cae
FG
985
986 # Test conditional feature.
987 assert.result-set-equal <toolset>gcc,<toolset-gcc:version>3.0 <define>FOO
988 : split-conditional <toolset>gcc,<toolset-gcc:version>3.0:<define>FOO ;
989
20effc67
TL
990 # Test translate does not choke on negations in conditional
991 assert.result <toolset>gcc,!<rtti>off:<define>HELLO
992 : translate <toolset>gcc,!<rtti>off:<define>HELLO
993 : project-id : project-location : context-module ;
994
7c673cae
FG
995 feature.finish-test property-test-temp ;
996}