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