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