1# Copyright 2001, 2002, 2003 Dave Abrahams 2# Copyright 2002, 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 assert : * ; 9import "class" : * ; 10import indirect ; 11import modules ; 12import regex ; 13import sequence ; 14import set ; 15import utility ; 16 17 18local rule setup ( ) 19{ 20 .all-attributes = 21 implicit 22 composite 23 optional 24 symmetric 25 free 26 incidental 27 path 28 dependency 29 propagated 30 link-incompatible 31 subfeature 32 order-sensitive 33 hidden 34 ; 35 36 .all-features = ; 37 .all-subfeatures = ; 38 .all-top-features = ; # non-subfeatures 39 .all-implicit-values = ; 40} 41setup ; 42 43 44# Prepare a fresh space to test in by moving all global variable settings into 45# the given temporary module and erasing them here. 46# 47rule prepare-test ( temp-module ) 48{ 49 DELETE_MODULE $(temp-module) ; 50 51 # Transfer globals to temp-module. 52 for local v in [ VARNAMES feature ] 53 { 54 if [ MATCH (\\.) : $(v) ] 55 { 56 modules.poke $(temp-module) : $(v) : $($(v)) ; 57 $(v) = ; 58 } 59 } 60 setup ; 61} 62 63 64# Clear out all global variables and recover all variables from the given 65# temporary module. 66# 67rule finish-test ( temp-module ) 68{ 69 # Clear globals. 70 for local v in [ VARNAMES feature ] 71 { 72 if [ MATCH (\\.) : $(v) ] 73 { 74 $(v) = ; 75 } 76 } 77 78 for local v in [ VARNAMES $(temp-module) ] 79 { 80 $(v) = [ modules.peek $(temp-module) : $(v) ] ; 81 } 82 DELETE_MODULE $(temp-module) ; 83} 84 85 86# Transform features by bracketing any elements which are not already bracketed 87# by "<>". 88# 89local rule grist ( features * ) 90{ 91 local empty = "" ; 92 return $(empty:G=$(features)) ; 93} 94 95 96# Declare a new feature with the given name, values, and attributes. 97# 98rule feature ( 99 name # Feature name. 100 : values * # Allowable values - may be extended later using feature.extend. 101 : attributes * # Feature attributes (e.g. implicit, free, propagated...). 102) 103{ 104 name = [ grist $(name) ] ; 105 106 local error ; 107 108 # Check for any unknown attributes. 109 if ! ( $(attributes) in $(.all-attributes) ) 110 { 111 error = unknown attributes: 112 [ set.difference $(attributes) : $(.all-attributes) ] ; 113 } 114 else if $(name) in $(.all-features) 115 { 116 error = feature already defined: ; 117 } 118 else if implicit in $(attributes) && free in $(attributes) 119 { 120 error = free features cannot also be implicit ; 121 } 122 else if free in $(attributes) && propagated in $(attributes) 123 { 124 error = free features cannot be propagated ; 125 } 126 else 127 { 128 local m = [ MATCH (.*=.*) : $(values) ] ; 129 if $(m[1]) 130 { 131 error = "feature value may not contain '='" ; 132 } 133 } 134 135 if $(error) 136 { 137 import errors ; 138 errors.error $(error) 139 : "in" feature declaration: 140 : feature [ errors.lol->list $(1) : $(2) : $(3) ] ; 141 } 142 143 $(name).values ?= ; 144 $(name).attributes = $(attributes) ; 145 $(name).subfeatures ?= ; 146 $(attributes).features += $(name) ; 147 148 .all-features += $(name) ; 149 if subfeature in $(attributes) 150 { 151 .all-subfeatures += $(name) ; 152 } 153 else 154 { 155 .all-top-features += $(name) ; 156 } 157 extend $(name) : $(values) ; 158} 159 160 161# Sets the default value of the given feature, overriding any previous default. 162# 163rule set-default ( feature : value ) 164{ 165 local f = [ grist $(feature) ] ; 166 local a = $($(f).attributes) ; 167 local bad-attribute = ; 168 if free in $(a) 169 { 170 bad-attribute = free ; 171 } 172 else if optional in $(a) 173 { 174 bad-attribute = optional ; 175 } 176 if $(bad-attribute) 177 { 178 import errors ; 179 errors.error $(bad-attribute) property $(f) cannot have a default. ; 180 } 181 if ! $(value) in $($(f).values) 182 { 183 import errors ; 184 errors.error The specified default value, '$(value)' is invalid : 185 allowed values are: $($(f).values) ; 186 } 187 $(f).default = $(value) ; 188} 189 190 191# Returns the default property values for the given features. 192# 193rule defaults ( features * ) 194{ 195 local result ; 196 for local f in $(features) 197 { 198 local gf = $(:E=:G=$(f)) ; 199 local a = $($(gf).attributes) ; 200 if ( free in $(a) ) || ( optional in $(a) ) 201 { 202 } 203 else 204 { 205 result += $(gf)$($(gf).default) ; 206 } 207 } 208 return $(result) ; 209} 210 211 212# Returns true iff all 'names' elements are valid features. 213# 214rule valid ( names + ) 215{ 216 if $(names) in $(.all-features) 217 { 218 return true ; 219 } 220} 221 222 223# Returns the attibutes of the given feature. 224# 225rule attributes ( feature ) 226{ 227 return $($(feature).attributes) ; 228} 229 230 231# Returns the values of the given feature. 232# 233rule values ( feature ) 234{ 235 return $($(:E=:G=$(feature)).values) ; 236} 237 238 239# Returns true iff 'value-string' is a value-string of an implicit feature. 240# 241rule is-implicit-value ( value-string ) 242{ 243 local v = [ regex.split $(value-string) - ] ; 244 local failed ; 245 if ! $(v[1]) in $(.all-implicit-values) 246 { 247 failed = true ; 248 } 249 else 250 { 251 local feature = $($(v[1]).implicit-feature) ; 252 for local subvalue in $(v[2-]) 253 { 254 if ! [ find-implied-subfeature $(feature) $(subvalue) : $(v[1]) ] 255 { 256 failed = true ; 257 } 258 } 259 } 260 261 if ! $(failed) 262 { 263 return true ; 264 } 265} 266 267 268# Returns the implicit feature associated with the given implicit value. 269# 270rule implied-feature ( implicit-value ) 271{ 272 local components = [ regex.split $(implicit-value) "-" ] ; 273 local feature = $($(components[1]).implicit-feature) ; 274 if ! $(feature) 275 { 276 import errors ; 277 errors.error \"$(implicit-value)\" is not an implicit feature value ; 278 feature = "" ; # Keep testing happy; it expects a result. 279 } 280 return $(feature) ; 281} 282 283 284local rule find-implied-subfeature ( feature subvalue : value-string ? ) 285{ 286 # Feature should be of the form <feature-name>. 287 if $(feature) != $(feature:G) 288 { 289 import errors ; 290 errors.error invalid feature $(feature) ; 291 } 292 return $($(feature)$(value-string:E="")<>$(subvalue).subfeature) ; 293} 294 295 296# Given a feature and a value of one of its subfeatures, find the name of the 297# subfeature. If value-string is supplied, looks for implied subfeatures that 298# are specific to that value of feature 299# 300rule implied-subfeature ( 301 feature # The main feature name. 302 subvalue # The value of one of its subfeatures. 303 : value-string ? # The value of the main feature. 304) 305{ 306 local subfeature = [ find-implied-subfeature $(feature) $(subvalue) 307 : $(value-string) ] ; 308 if ! $(subfeature) 309 { 310 value-string ?= "" ; 311 import errors ; 312 errors.error \"$(subvalue)\" is not a known subfeature value of 313 $(feature)$(value-string) ; 314 } 315 return $(subfeature) ; 316} 317 318 319# Generate an error if the feature is unknown. 320# 321local rule validate-feature ( feature ) 322{ 323 if ! $(feature) in $(.all-features) 324 { 325 import errors ; 326 errors.error unknown feature \"$(feature)\" ; 327 } 328} 329 330 331# Given a feature and its value or just a value corresponding to an implicit 332# feature, returns a property set consisting of all component subfeatures and 333# their values. For example all the following calls: 334# 335# expand-subfeatures-aux <toolset>gcc-2.95.2-linux-x86 336# expand-subfeatures-aux gcc-2.95.2-linux-x86 337# 338# return: 339# 340# <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86 341# 342local rule expand-subfeatures-aux ( 343 feature ? # Feature name or empty if value corresponds to an 344 # implicit property. 345 : value # Feature value. 346 : dont-validate ? # If set, no value string validation will be done. 347) 348{ 349 if $(feature) 350 { 351 feature = $(feature) ; 352 } 353 354 if ! $(feature) 355 { 356 feature = [ implied-feature $(value) ] ; 357 } 358 else 359 { 360 validate-feature $(feature) ; 361 } 362 if ! $(dont-validate) 363 { 364 validate-value-string $(feature) $(value) ; 365 } 366 367 local components = [ regex.split $(value) "-" ] ; 368 369 # Get the top-level feature's value. 370 local value = $(components[1]:G=) ; 371 372 local result = $(components[1]:G=$(feature)) ; 373 374 local subvalues = $(components[2-]) ; 375 while $(subvalues) 376 { 377 local subvalue = $(subvalues[1]) ; # Pop the head off of subvalues. 378 subvalues = $(subvalues[2-]) ; 379 380 local subfeature = [ find-implied-subfeature $(feature) $(subvalue) : 381 $(value) ] ; 382 383 # If no subfeature was found reconstitute the value string and use that. 384 if ! $(subfeature) 385 { 386 result = $(components:J=-) ; 387 result = $(result:G=$(feature)) ; 388 subvalues = ; # Stop looping. 389 } 390 else 391 { 392 local f = [ MATCH ^<(.*)>$ : $(feature) ] ; 393 result += $(subvalue:G=$(f)-$(subfeature)) ; 394 } 395 } 396 397 return $(result) ; 398} 399 400 401# Make all elements of properties corresponding to implicit features explicit, 402# and express all subfeature values as separate properties in their own right. 403# For example, all of the following properties 404# 405# gcc-2.95.2-linux-x86 406# <toolset>gcc-2.95.2-linux-x86 407# 408# might expand to 409# 410# <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86 411# 412rule expand-subfeatures ( 413 properties * # Property set with elements of the form 414 # <feature>value-string or just value-string in the case 415 # of implicit features. 416 : dont-validate ? 417) 418{ 419 local result ; 420 for local p in $(properties) 421 { 422 # Don't expand subfeatures in subfeatures 423 if ! [ MATCH "(:)" : $(p:G) ] 424 { 425 result += [ expand-subfeatures-aux $(p:G) : $(p:G=) : $(dont-validate) ] ; 426 } 427 else 428 { 429 result += $(p) ; 430 } 431 } 432 return $(result) ; 433} 434 435 436# Helper for extend, below. Handles the feature case. 437# 438local rule extend-feature ( feature : values * ) 439{ 440 feature = [ grist $(feature) ] ; 441 validate-feature $(feature) ; 442 if implicit in $($(feature).attributes) 443 { 444 for local v in $(values) 445 { 446 if $($(v).implicit-feature) 447 { 448 import errors ; 449 errors.error $(v) is already associated with the 450 \"$($(v).implicit-feature)\" feature ; 451 } 452 $(v).implicit-feature = $(feature) ; 453 } 454 455 .all-implicit-values += $(values) ; 456 } 457 if ! $($(feature).values) 458 { 459 # This is the first value specified for this feature so make it be the 460 # default. 461 $(feature).default = $(values[1]) ; 462 } 463 $(feature).values += $(values) ; 464} 465 466 467# Checks that value-string is a valid value-string for the given feature. 468# 469rule validate-value-string ( feature value-string ) 470{ 471 if ! ( 472 free in $($(feature).attributes) 473 || ( $(value-string) in $(feature).values ) 474 ) 475 { 476 local values = $(value-string) ; 477 478 if $($(feature).subfeatures) 479 { 480 if ! $(value-string) in $($(feature).values) 481 $($(feature).subfeatures) 482 { 483 values = [ regex.split $(value-string) - ] ; 484 } 485 } 486 487 if ! ( $(values[1]) in $($(feature).values) ) && 488 489 # An empty value is allowed for optional features. 490 ( $(values[1]) || ! ( optional in $($(feature).attributes) ) ) 491 { 492 import errors ; 493 errors.error \"$(values[1])\" is not a known value of feature 494 $(feature) : legal values: \"$($(feature).values)\" ; 495 } 496 497 for local v in $(values[2-]) 498 { 499 # This will validate any subfeature values in value-string. 500 implied-subfeature $(feature) $(v) : $(values[1]) ; 501 } 502 } 503} 504 505 506# A helper that computes: 507# * name(s) of module-local variable(s) used to record the correspondence 508# between subvalue(s) and a subfeature 509# * value of that variable when such a subfeature/subvalue has been defined and 510# returns a list consisting of the latter followed by the former. 511# 512local rule subvalue-var ( 513 feature # Main feature name. 514 value-string ? # If supplied, specifies a specific value of the main 515 # feature for which the subfeature values are valid. 516 : subfeature # Subfeature name. 517 : subvalues * # Subfeature values. 518) 519{ 520 feature = [ grist $(feature) ] ; 521 validate-feature $(feature) ; 522 if $(value-string) 523 { 524 validate-value-string $(feature) $(value-string) ; 525 } 526 527 local subfeature-name = [ get-subfeature-name $(subfeature) $(value-string) ] ; 528 529 return $(subfeature-name) 530 $(feature)$(value-string:E="")<>$(subvalues).subfeature ; 531} 532 533 534# Extends the given subfeature with the subvalues. If the optional value-string 535# is provided, the subvalues are only valid for the given value of the feature. 536# Thus, you could say that <target-platform>mingw is specific to 537# <toolset>gcc-2.95.2 as follows: 538# 539# extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ; 540# 541rule extend-subfeature ( 542 feature # The feature whose subfeature is being extended. 543 544 value-string ? # If supplied, specifies a specific value of the main 545 # feature for which the new subfeature values are valid. 546 547 : subfeature # Subfeature name. 548 : subvalues * # Additional subfeature values. 549) 550{ 551 local subfeature-vars = [ subvalue-var $(feature) $(value-string) 552 : $(subfeature) : $(subvalues) ] ; 553 554 local f = [ utility.ungrist [ grist $(feature) ] ] ; 555 extend $(f)-$(subfeature-vars[1]) : $(subvalues) ; 556 557 # Provide a way to get from the given feature or property and subfeature 558 # value to the subfeature name. 559 $(subfeature-vars[2-]) = $(subfeature-vars[1]) ; 560} 561 562 563# Returns true iff the subvalues are valid for the feature. When the optional 564# value-string is provided, returns true iff the subvalues are valid for the 565# given value of the feature. 566# 567rule is-subvalue ( feature : value-string ? : subfeature : subvalue ) 568{ 569 local subfeature-vars = [ subvalue-var $(feature) $(value-string) 570 : $(subfeature) : $(subvalue) ] ; 571 572 if $($(subfeature-vars[2])) = $(subfeature-vars[1]) 573 { 574 return true ; 575 } 576} 577 578 579# Can be called three ways: 580# 581# 1. extend feature : values * 582# 2. extend <feature> subfeature : values * 583# 3. extend <feature>value-string subfeature : values * 584# 585# * Form 1 adds the given values to the given feature. 586# * Forms 2 and 3 add subfeature values to the given feature. 587# * Form 3 adds the subfeature values as specific to the given property 588# value-string. 589# 590rule extend ( feature-or-property subfeature ? : values * ) 591{ 592 local feature ; # If a property was specified this is its feature. 593 local value-string ; # E.g., the gcc-2.95-2 part of <toolset>gcc-2.95.2. 594 595 # If a property was specified. 596 if $(feature-or-property:G) && $(feature-or-property:G=) 597 { 598 # Extract the feature and value-string, if any. 599 feature = $(feature-or-property:G) ; 600 value-string = $(feature-or-property:G=) ; 601 } 602 else 603 { 604 feature = [ grist $(feature-or-property) ] ; 605 } 606 607 # Dispatch to the appropriate handler. 608 if $(subfeature) 609 { 610 extend-subfeature $(feature) $(value-string) : $(subfeature) 611 : $(values) ; 612 } 613 else 614 { 615 # If no subfeature was specified, we do not expect to see a 616 # value-string. 617 if $(value-string) 618 { 619 import errors ; 620 errors.error can only specify a property as the first argument when 621 extending a subfeature 622 : usage: 623 : " extend" feature ":" values... 624 : " | extend" <feature>value-string subfeature ":" values... ; 625 } 626 627 extend-feature $(feature) : $(values) ; 628 } 629} 630 631 632local rule get-subfeature-name ( subfeature value-string ? ) 633{ 634 local prefix = $(value-string): ; 635 return $(prefix:E="")$(subfeature) ; 636} 637 638 639# Declares a subfeature. 640# 641rule subfeature ( 642 feature # Root feature that is not a subfeature. 643 value-string ? # A value-string specifying which feature or subfeature 644 # values this subfeature is specific to, if any. 645 : subfeature # The name of the subfeature being declared. 646 : subvalues * # The allowed values of this subfeature. 647 : attributes * # The attributes of the subfeature. 648) 649{ 650 feature = [ grist $(feature) ] ; 651 validate-feature $(feature) ; 652 653 # Add grist to the subfeature name if a value-string was supplied. 654 local subfeature-name = [ get-subfeature-name $(subfeature) $(value-string) ] ; 655 656 if $(subfeature-name) in $($(feature).subfeatures) 657 { 658 import errors ; 659 errors.error \"$(subfeature)\" already declared as a subfeature of 660 \"$(feature)\" "specific to "$(value-string) ; 661 } 662 $(feature).subfeatures += $(subfeature-name) ; 663 664 # First declare the subfeature as a feature in its own right. 665 local f = [ utility.ungrist $(feature) ] ; 666 feature $(f)-$(subfeature-name) : $(subvalues) : $(attributes) subfeature ; 667 668 # Now make sure the subfeature values are known. 669 extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ; 670} 671 672 673# Set components of the given composite property. 674# 675rule compose ( composite-property : component-properties * ) 676{ 677 local feature = $(composite-property:G) ; 678 if ! ( composite in [ attributes $(feature) ] ) 679 { 680 import errors ; 681 errors.error "$(feature)" is not a composite feature ; 682 } 683 684 $(composite-property).components ?= ; 685 if $($(composite-property).components) 686 { 687 import errors ; 688 errors.error components of "$(composite-property)" already set: 689 $($(composite-property).components) ; 690 } 691 692 if $(composite-property) in $(component-properties) 693 { 694 import errors ; 695 errors.error composite property "$(composite-property)" cannot have itself as a component ; 696 } 697 $(composite-property).components = $(component-properties) ; 698} 699 700 701local rule expand-composite ( property ) 702{ 703 return $(property) 704 [ sequence.transform expand-composite : $($(property).components) ] ; 705} 706 707 708# Return all values of the given feature specified by the given property set. 709# 710rule get-values ( feature : properties * ) 711{ 712 local result ; 713 714 feature = $(:E=:G=$(feature)) ; # Add <> if necessary. 715 for local p in $(properties) 716 { 717 if $(p:G) = $(feature) 718 { 719 # Use MATCH instead of :G= to get the value, in order to preserve 720 # the value intact instead of having bjam treat it as a decomposable 721 # path. 722 result += [ MATCH ">(.*)" : $(p) ] ; 723 } 724 } 725 return $(result) ; 726} 727 728 729rule free-features ( ) 730{ 731 return $(free.features) ; 732} 733 734 735# Expand all composite properties in the set so that all components are 736# explicitly expressed. 737# 738rule expand-composites ( properties * ) 739{ 740 local explicit-features = $(properties:G) ; 741 local result ; 742 743 # Now expand composite features. 744 for local p in $(properties) 745 { 746 local expanded = [ expand-composite $(p) ] ; 747 748 for local x in $(expanded) 749 { 750 if ! $(x) in $(result) 751 { 752 local f = $(x:G) ; 753 754 if $(f) in $(free.features) 755 { 756 result += $(x) ; 757 } 758 else if ! $(x) in $(properties) # x is the result of expansion 759 { 760 if ! $(f) in $(explicit-features) # not explicitly-specified 761 { 762 if $(f) in $(result:G) 763 { 764 import errors ; 765 errors.error expansions of composite features result 766 in conflicting values for $(f) 767 : values: [ get-values $(f) : $(result) ] $(x:G=) 768 : one contributing composite property was $(p) ; 769 } 770 else 771 { 772 result += $(x) ; 773 } 774 } 775 } 776 else if $(f) in $(result:G) 777 { 778 import errors ; 779 errors.error explicitly-specified values of non-free feature 780 $(f) conflict : 781 "existing values:" [ get-values $(f) : $(properties) ] : 782 "value from expanding " $(p) ":" $(x:G=) ; 783 } 784 else 785 { 786 result += $(x) ; 787 } 788 } 789 } 790 } 791 return $(result) ; 792} 793 794 795# Return true iff f is an ordinary subfeature of the parent-property's feature, 796# or if f is a subfeature of the parent-property's feature specific to the 797# parent-property's value. 798# 799local rule is-subfeature-of ( parent-property f ) 800{ 801 if subfeature in $($(f).attributes) 802 { 803 local specific-subfeature = [ MATCH <(.*):(.*)> : $(f) ] ; 804 if $(specific-subfeature) 805 { 806 # The feature has the form <topfeature-topvalue:subfeature>, e.g. 807 # <toolset-msvc:version>. 808 local feature-value = [ split-top-feature $(specific-subfeature[1]) 809 ] ; 810 if <$(feature-value[1])>$(feature-value[2]) = $(parent-property) 811 { 812 return true ; 813 } 814 } 815 else 816 { 817 # The feature has the form <topfeature-subfeature>, e.g. 818 # <toolset-version> 819 local top-sub = [ split-top-feature [ utility.ungrist $(f) ] ] ; 820 if $(top-sub[2]) && <$(top-sub[1])> = $(parent-property:G) 821 { 822 return true ; 823 } 824 } 825 } 826} 827 828 829# As for is-subfeature-of but for subproperties. 830# 831local rule is-subproperty-of ( parent-property p ) 832{ 833 return [ is-subfeature-of $(parent-property) $(p:G) ] ; 834} 835 836 837# Given a property, return the subset of features consisting of all ordinary 838# subfeatures of the property's feature, and all specific subfeatures of the 839# property's feature which are conditional on the property's value. 840# 841local rule select-subfeatures ( parent-property : features * ) 842{ 843 return [ sequence.filter is-subfeature-of $(parent-property) : $(features) ] ; 844} 845 846 847# As for select-subfeatures but for subproperties. 848# 849local rule select-subproperties ( parent-property : properties * ) 850{ 851 return [ sequence.filter is-subproperty-of $(parent-property) : $(properties) ] ; 852} 853 854 855# Given a property set which may consist of composite and implicit properties 856# and combined subfeature values, returns an expanded, normalized property set 857# with all implicit features expressed explicitly, all subfeature values 858# individually expressed, and all components of composite properties expanded. 859# Non-free features directly expressed in the input properties cause any values 860# of those features due to composite feature expansion to be dropped. If two 861# values of a given non-free feature are directly expressed in the input, an 862# error is issued. 863# 864rule expand ( properties * ) 865{ 866 local expanded = [ expand-subfeatures $(properties) ] ; 867 return [ expand-composites $(expanded) ] ; 868} 869 870 871# Helper rule for minimize. Returns true iff property's feature is present in 872# the contents of the variable named by feature-set-var. 873# 874local rule in-features ( feature-set-var property ) 875{ 876 if $(property:G) in $($(feature-set-var)) 877 { 878 return true ; 879 } 880} 881 882 883# Helper rule for minimize. Returns the list with the same properties, but with 884# all subfeatures moved to the end of the list. 885# 886local rule move-subfeatures-to-the-end ( properties * ) 887{ 888 local x1 ; 889 local x2 ; 890 for local p in $(properties) 891 { 892 if subfeature in $($(p:G).attributes) 893 { 894 x2 += $(p) ; 895 } 896 else 897 { 898 x1 += $(p) ; 899 } 900 } 901 return $(x1) $(x2) ; 902} 903 904 905# Given an expanded property set, eliminate all redundancy: properties that are 906# elements of other (composite) properties in the set will be eliminated. 907# Non-symmetric properties equal to default values will be eliminated unless 908# they override a value from some composite property. Implicit properties will 909# be expressed without feature grist, and sub-property values will be expressed 910# as elements joined to the corresponding main property. 911# 912rule minimize ( properties * ) 913{ 914 # Precondition checking 915 local implicits = [ set.intersection $(p:G=) : $(p:G) ] ; 916 if $(implicits) 917 { 918 import errors ; 919 errors.error minimize requires an expanded property set, but 920 \"$(implicits[1])\" appears to be the value of an un-expanded 921 implicit feature ; 922 } 923 924 # Remove properties implied by composite features. 925 local components = $($(properties).components) ; 926 local x = [ set.difference $(properties) : $(components) ] ; 927 928 # Handle subfeatures and implicit features. 929 x = [ move-subfeatures-to-the-end $(x) ] ; 930 local result ; 931 while $(x) 932 { 933 local p fullp = $(x[1]) ; 934 local f = $(p:G) ; 935 local v = $(p:G=) ; 936 937 # Eliminate features in implicit properties. 938 if implicit in [ attributes $(f) ] 939 { 940 p = $(v) ; 941 } 942 943 # Locate all subproperties of $(x[1]) in the property set. 944 local subproperties = [ select-subproperties $(fullp) : $(x) ] ; 945 if $(subproperties) 946 { 947 # Reconstitute the joined property name. 948 local sorted = [ sequence.insertion-sort $(subproperties) ] ; 949 result += $(p)-$(sorted:G="":J=-) ; 950 951 x = [ set.difference $(x[2-]) : $(subproperties) ] ; 952 } 953 else 954 { 955 # Eliminate properties whose value is equal to feature's default, 956 # which are not symmetric and which do not contradict values implied 957 # by composite properties. 958 959 # Since all component properties of composites in the set have been 960 # eliminated, any remaining property whose feature is the same as a 961 # component of a composite in the set must have a non-redundant 962 # value. 963 if $(fullp) != [ defaults $(f) ] 964 || symmetric in [ attributes $(f) ] 965 || $(fullp:G) in $(components:G) 966 { 967 result += $(p) ; 968 } 969 970 x = $(x[2-]) ; 971 } 972 } 973 return $(result) ; 974} 975 976 977# Combine all subproperties into their parent properties 978# 979# Requires: for every subproperty, there is a parent property. All features are 980# explicitly expressed. 981# 982# This rule probably should not be needed, but build-request.expand-no-defaults 983# is being abused for unintended purposes and it needs help. 984# 985rule compress-subproperties ( properties * ) 986{ 987 local all-subs ; 988 local matched-subs ; 989 local result ; 990 991 for local p in $(properties) 992 { 993 if ! $(p:G) 994 { 995 # Expecting fully-gristed properties. 996 assert.variable-not-empty p:G ; 997 } 998 999 if ! subfeature in $($(p:G).attributes) 1000 { 1001 local subs = [ sequence.insertion-sort 1002 [ sequence.filter is-subproperty-of $(p) : $(properties) ] ] ; 1003 1004 matched-subs += $(subs) ; 1005 1006 local subvalues = -$(subs:G=:J=-) ; 1007 subvalues ?= "" ; 1008 result += $(p)$(subvalues) ; 1009 } 1010 else 1011 { 1012 all-subs += $(p) ; 1013 } 1014 } 1015 assert.result true : set.equal $(all-subs) : $(matched-subs) ; 1016 return $(result) ; 1017} 1018 1019 1020# Given an ungristed string, finds the longest prefix which is a top-level 1021# feature name followed by a dash, and return a pair consisting of the parts 1022# before and after that dash. More interesting than a simple split because 1023# feature names may contain dashes. 1024# 1025local rule split-top-feature ( feature-plus ) 1026{ 1027 local e = [ regex.split $(feature-plus) - ] ; 1028 local f = $(e[1]) ; 1029 local v ; 1030 while $(e) 1031 { 1032 if <$(f)> in $(.all-top-features) 1033 { 1034 v = $(f) $(e[2-]:J=-) ; 1035 } 1036 e = $(e[2-]) ; 1037 f = $(f)-$(e[1]) ; 1038 } 1039 return $(v) ; 1040} 1041 1042 1043# Given a set of properties, add default values for features not represented in 1044# the set. 1045# 1046# Note: if there's an ordinary feature F1 and a composite feature F2 which 1047# includes some value for F1 and both feature have default values then the 1048# default value of F1 will be added (as opposed to the value in F2). This might 1049# not be the right idea, e.g. consider: 1050# 1051# feature variant : debug ... ; 1052# <variant>debug : .... <runtime-debugging>on 1053# feature <runtime-debugging> : off on ; 1054# 1055# Here, when adding default for an empty property set, we'll get 1056# 1057# <variant>debug <runtime_debugging>off 1058# 1059# and that's kind of strange. 1060# 1061rule add-defaults ( properties * ) 1062{ 1063 for local v in $(properties:G=) 1064 { 1065 if $(v) in $(properties) 1066 { 1067 import errors ; 1068 errors.error add-defaults requires explicitly specified features, 1069 but \"$(v)\" appears to be the value of an un-expanded implicit 1070 feature ; 1071 } 1072 } 1073 # We don't add default for elements with ":" inside. This catches: 1074 # 1. Conditional properties --- we don't want <variant>debug:<define>DEBUG 1075 # to be takes as specified value for <variant> 1076 # 2. Free properties with ":" in values. We don't care, since free 1077 # properties don't have defaults. 1078 local xproperties = [ MATCH "^([^:]+)$" : $(properties) ] ; 1079 local missing-top = [ set.difference $(.all-top-features) : $(xproperties:G) ] ; 1080 local more = [ defaults $(missing-top) ] ; 1081 properties += $(more) ; 1082 xproperties += $(more) ; 1083 1084 # Add defaults for subfeatures of features which are present. 1085 for local p in $(xproperties) 1086 { 1087 local s = $($(p:G).subfeatures) ; 1088 local f = [ utility.ungrist $(p:G) ] ; 1089 local missing-subs = [ set.difference <$(f)-$(s)> : $(properties:G) ] ; 1090 properties += [ defaults [ select-subfeatures $(p) : $(missing-subs) ] ] ; 1091 } 1092 1093 return $(properties) ; 1094} 1095 1096 1097# Given a property-set of the form 1098# v1/v2/...vN-1/<fN>vN/<fN+1>vN+1/...<fM>vM 1099# 1100# Returns 1101# v1 v2 ... vN-1 <fN>vN <fN+1>vN+1 ... <fM>vM 1102# 1103# Note that vN...vM may contain slashes. This needs to be resilient to the 1104# substitution of backslashes for slashes, since Jam, unbidden, sometimes swaps 1105# slash direction on NT. 1106# 1107rule split ( property-set ) 1108{ 1109 local pieces = [ regex.split $(property-set) [\\/] ] ; 1110 local result ; 1111 1112 for local x in $(pieces) 1113 { 1114 if ( ! $(x:G) ) && $(result[-1]:G) 1115 { 1116 result = $(result[1--2]) $(result[-1])/$(x) ; 1117 } 1118 else 1119 { 1120 result += $(x) ; 1121 } 1122 } 1123 1124 return $(result) ; 1125} 1126 1127 1128# Tests of module feature. 1129# 1130rule __test__ ( ) 1131{ 1132 # Use a fresh copy of the feature module. 1133 prepare-test feature-test-temp ; 1134 1135 import assert ; 1136 import errors : try catch ; 1137 1138 # These are local rules and so must be explicitly reimported into the 1139 # testing module. 1140 import feature : extend-feature validate-feature select-subfeatures ; 1141 1142 feature toolset : gcc : implicit ; 1143 feature define : : free ; 1144 feature runtime-link : dynamic static : symmetric ; 1145 feature optimization : on off ; 1146 feature variant : debug release profile : implicit composite symmetric ; 1147 feature stdlib : native stlport ; 1148 feature magic : : free ; 1149 1150 compose <variant>debug : <define>_DEBUG <optimization>off ; 1151 compose <variant>release : <define>NDEBUG <optimization>on ; 1152 1153 assert.result dynamic static : values <runtime-link> ; 1154 assert.result dynamic static : values runtime-link ; 1155 1156 try ; 1157 { 1158 compose <variant>profile : <variant>profile ; 1159 } 1160 catch composite property <variant>profile cannot have itself as a component ; 1161 1162 extend-feature toolset : msvc metrowerks ; 1163 subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 3.0 3.0.1 3.0.2 ; 1164 1165 assert.true is-subvalue toolset : gcc : version : 2.95.3 ; 1166 assert.false is-subvalue toolset : gcc : version : 1.1 ; 1167 1168 assert.false is-subvalue toolset : msvc : version : 2.95.3 ; 1169 assert.false is-subvalue toolset : : version : yabba ; 1170 1171 feature yabba ; 1172 subfeature yabba : version : dabba ; 1173 assert.true is-subvalue yabba : : version : dabba ; 1174 1175 subfeature toolset gcc : platform : linux cygwin : optional ; 1176 1177 assert.result <toolset-gcc:version> 1178 : select-subfeatures <toolset>gcc 1179 : <toolset-gcc:version> 1180 <toolset-msvc:version> 1181 <toolset-version> 1182 <stdlib> ; 1183 1184 subfeature stdlib : version : 3 4 : optional ; 1185 1186 assert.result <stdlib-version> 1187 : select-subfeatures <stdlib>native 1188 : <toolset-gcc:version> 1189 <toolset-msvc:version> 1190 <toolset-version> 1191 <stdlib-version> ; 1192 1193 assert.result <toolset>gcc <toolset-gcc:version>3.0.1 1194 : expand-subfeatures <toolset>gcc-3.0.1 ; 1195 1196 assert.result <toolset>gcc <toolset-gcc:version>3.0.1 <toolset-gcc:platform>linux 1197 : expand-subfeatures <toolset>gcc-3.0.1-linux ; 1198 1199 assert.result <toolset>gcc <toolset-gcc:version>3.0.1 1200 : expand <toolset>gcc <toolset-gcc:version>3.0.1 ; 1201 1202 assert.result <define>foo=x-y 1203 : expand-subfeatures <define>foo=x-y ; 1204 1205 assert.result <toolset>gcc <toolset-gcc:version>3.0.1 1206 : expand-subfeatures gcc-3.0.1 ; 1207 1208 assert.result a c e 1209 : get-values <x> : <x>a <y>b <x>c <y>d <x>e ; 1210 1211 assert.result <toolset>gcc <toolset-gcc:version>3.0.1 1212 <variant>debug <define>_DEBUG <optimization>on 1213 : expand gcc-3.0.1 debug <optimization>on ; 1214 1215 assert.result <variant>debug <define>_DEBUG <optimization>on 1216 : expand debug <optimization>on ; 1217 1218 assert.result <optimization>on <variant>debug <define>_DEBUG 1219 : expand <optimization>on debug ; 1220 1221 assert.result <runtime-link>dynamic <optimization>on 1222 : defaults <runtime-link> <define> <optimization> ; 1223 1224 # Make sure defaults is resilient to missing grist. 1225 assert.result <runtime-link>dynamic <optimization>on 1226 : defaults runtime-link define optimization ; 1227 1228 feature dummy : dummy1 dummy2 ; 1229 subfeature dummy : subdummy : x y z : optional ; 1230 1231 feature fu : fu1 fu2 : optional ; 1232 subfeature fu : subfu : x y z : optional ; 1233 subfeature fu : subfu2 : q r s ; 1234 1235 assert.result optional : attributes <fu> ; 1236 1237 assert.result <runtime-link>static <define>foobar <optimization>on 1238 <toolset>gcc:<define>FOO <toolset>gcc <variant>debug <stdlib>native 1239 <dummy>dummy1 <toolset-gcc:version>2.95.2 1240 : add-defaults <runtime-link>static <define>foobar <optimization>on 1241 <toolset>gcc:<define>FOO ; 1242 1243 assert.result <runtime-link>static <define>foobar <optimization>on 1244 <toolset>gcc:<define>FOO <fu>fu1 <toolset>gcc <variant>debug 1245 <stdlib>native <dummy>dummy1 <fu-subfu2>q <toolset-gcc:version>2.95.2 1246 : add-defaults <runtime-link>static <define>foobar <optimization>on 1247 <toolset>gcc:<define>FOO <fu>fu1 ; 1248 1249 set-default <runtime-link> : static ; 1250 assert.result <runtime-link>static : defaults <runtime-link> ; 1251 1252 assert.result gcc-3.0.1 debug <optimization>on 1253 : minimize [ expand gcc-3.0.1 debug <optimization>on <stdlib>native ] ; 1254 1255 assert.result gcc-3.0.1 debug <runtime-link>dynamic 1256 : minimize 1257 [ expand gcc-3.0.1 debug <optimization>off <runtime-link>dynamic ] ; 1258 1259 assert.result gcc-3.0.1 debug 1260 : minimize [ expand gcc-3.0.1 debug <optimization>off ] ; 1261 1262 assert.result debug <optimization>on 1263 : minimize [ expand debug <optimization>on ] ; 1264 1265 assert.result gcc-3.0 1266 : minimize <toolset>gcc <toolset-gcc:version>3.0 ; 1267 1268 assert.result gcc-3.0 1269 : minimize <toolset-gcc:version>3.0 <toolset>gcc ; 1270 1271 assert.result <x>y/z <a>b/c <d>e/f 1272 : split <x>y/z/<a>b/c/<d>e/f ; 1273 1274 assert.result <x>y/z <a>b/c <d>e/f 1275 : split <x>y\\z\\<a>b\\c\\<d>e\\f ; 1276 1277 assert.result a b c <d>e/f/g <h>i/j/k 1278 : split a/b/c/<d>e/f/g/<h>i/j/k ; 1279 1280 assert.result a b c <d>e/f/g <h>i/j/k 1281 : split a\\b\\c\\<d>e\\f\\g\\<h>i\\j\\k ; 1282 1283 # Test error checking. 1284 1285 try ; 1286 { 1287 expand release <optimization>off <optimization>on ; 1288 } 1289 catch explicitly-specified values of non-free feature <optimization> conflict ; 1290 1291 try ; 1292 { 1293 validate-feature <foobar> ; 1294 } 1295 catch unknown feature ; 1296 1297 validate-value-string <toolset> gcc ; 1298 validate-value-string <toolset> gcc-3.0.1 ; 1299 1300 try ; 1301 { 1302 validate-value-string <toolset> digital_mars ; 1303 } 1304 catch \"digital_mars\" is not a known value of <toolset> ; 1305 1306 try ; 1307 { 1308 feature foobar : : baz ; 1309 } 1310 catch unknown attributes: baz ; 1311 1312 feature feature1 ; 1313 try ; 1314 { 1315 feature feature1 ; 1316 } 1317 catch feature already defined: ; 1318 1319 try ; 1320 { 1321 feature feature2 : : free implicit ; 1322 } 1323 catch free features cannot also be implicit ; 1324 1325 try ; 1326 { 1327 feature feature3 : : free propagated ; 1328 } 1329 catch free features cannot be propagated ; 1330 1331 try ; 1332 { 1333 implied-feature lackluster ; 1334 } 1335 catch \"lackluster\" is not an implicit feature value ; 1336 1337 try ; 1338 { 1339 implied-subfeature <toolset> 3.0.1 ; 1340 } 1341 catch \"3.0.1\" is not a known subfeature value of <toolset> ; 1342 1343 try ; 1344 { 1345 implied-subfeature <toolset> not-a-version : gcc ; 1346 } 1347 catch \"not-a-version\" is not a known subfeature value of <toolset>gcc ; 1348 1349 # Leave a clean copy of the features module behind. 1350 finish-test feature-test-temp ; 1351} 1352