1# Copyright 2001, 2002, 2003 Dave Abrahams 2# Copyright 2006 Rene Rivera 3# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 4# Copyright 2020 Nikita Kniazev 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. 32 if ! [ MATCH "(:<)" : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ] 33 { 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 } 46 unset += $(r:G) ; 47 } 48 } 49 50 # Remove properties that are overridden by requirements 51 for local p in $(properties) 52 { 53 if [ MATCH "(:<)" : $(p:G=) ] || ! $(p:G) in $(unset) 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 ; 71 local indirect ; 72 for local p in $(properties) 73 { 74 if [ MATCH "(:<)" : $(p) ] && ! free in [ feature.attributes $(p:G) ] 75 { 76 conditionals += $(p) ; 77 } 78 else if $(p:G) = <conditional> 79 { 80 indirect += $(p) ; 81 } 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. 92 local s = [ MATCH "^(.*):(<.*)" : $(p) ] ; 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 ; 108 for local c in $(condition) 109 { 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) ) 113 { 114 fail = true ; 115 break ; 116 } 117 } 118 if ! $(fail) 119 { 120 result += $(s[2]) ; 121 } 122 } 123 } 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 { 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) ] ; 140 } 141 else 142 { 143 result += $(new) ; 144 } 145 } 146 return $(result) ; 147} 148 149 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 180rule expand-subfeatures-in-conditions ( properties * ) 181{ 182 local result ; 183 for local p in $(properties) 184 { 185 local s = [ MATCH "^(.*):(<.*)" : $(p) ] ; 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 { 216 result += "$(e:J=,):$(value)" ; 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{ 551 local translate-path-rule = [ MATCH "^<translate-path>[@](.*)$" : "$(properties)" ] ; 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 { 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) ; 570 } 571 572 condition = "$(e:J=,):" ; 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 { 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 } 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 { 651 condition = "$(split[1]):" ; 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 { 736 condition = "$(split[1]):" ; 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 903 assert.result 904 : evaluate-conditionals-in-context 905 <variant>release,<rtti>off:<define>MY_RELEASE 906 : <toolset>gcc <variant>release <rtti>on ; 907 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 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 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 ":". 984 assert.result : split-conditional <define>"FOO=A::B" ; 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 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 995 feature.finish-test property-test-temp ; 996} 997