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