1# Copyright 2003 Dave Abrahams
2# Copyright 2005 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
8#  Support for toolset definition.
9
10import errors ;
11import feature ;
12import generators ;
13import numbers ;
14import path ;
15import property ;
16import regex ;
17import sequence ;
18import set ;
19import property-set ;
20import order ;
21import "class" : new ;
22
23
24.flag-no = 1 ;
25
26.ignore-requirements = ;
27
28# This is used only for testing, to make sure we do not get random extra
29# elements in paths.
30if --ignore-toolset-requirements in [ modules.peek : ARGV ]
31{
32    .ignore-requirements = 1 ;
33}
34
35
36# Initializes an additional toolset-like module. First load the 'toolset-module'
37# and then calls its 'init' rule with trailing arguments.
38#
39rule using ( toolset-module : * )
40{
41    import $(toolset-module) ;
42    $(toolset-module).init $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9)
43        ;
44}
45
46
47# Expands subfeatures in each property sets, e.g. '<toolset>gcc-3.2' will be
48# converted to '<toolset>gcc/<toolset-version>3.2'.
49#
50local rule normalize-condition ( property-sets * )
51{
52    local result ;
53    for local p in $(property-sets)
54    {
55        local split = [ feature.split $(p) ] ;
56        local expanded = [ feature.expand-subfeatures [ feature.split $(p) ] ] ;
57        result += $(expanded:J=/) ;
58    }
59    return $(result) ;
60}
61
62
63# Specifies if the 'flags' rule should check that the invoking module is the
64# same as the module we are setting the flag for. 'v' can be either 'checked' or
65# 'unchecked'. Subsequent call to 'pop-checking-for-flags-module' will restore
66# the setting that was in effect before calling this rule.
67#
68rule push-checking-for-flags-module ( v )
69{
70    .flags-module-checking = $(v) $(.flags-module-checking) ;
71}
72
73rule pop-checking-for-flags-module ( )
74{
75    .flags-module-checking = $(.flags-module-checking[2-]) ;
76}
77
78
79# Specifies the flags (variables) that must be set on targets under certain
80# conditions, described by arguments.
81#
82rule flags (
83    rule-or-module   # If contains a dot, should be a rule name. The flags will
84                     # be applied when that rule is used to set up build
85                     # actions.
86                     #
87                     # If does not contain dot, should be a module name. The
88                     # flag will be applied for all rules in that module. If
89                     # module for rule is different from the calling module, an
90                     # error is issued.
91
92    variable-name    # Variable that should be set on target.
93    condition * :    # A condition when this flag should be applied. Should be a
94                     # set of property sets. If one of those property sets is
95                     # contained in the build properties, the flag will be used.
96                     # Implied values are not allowed: "<toolset>gcc" should be
97                     # used, not just "gcc". Subfeatures, like in
98                     # "<toolset>gcc-3.2" are allowed. If left empty, the flag
99                     # will be used unconditionally.
100                     #
101                     # Propery sets may use value-less properties ('<a>'  vs.
102                     # '<a>value') to match absent properties. This allows to
103                     # separately match:
104                     #
105                     #   <architecture>/<address-model>64
106                     #   <architecture>ia64/<address-model>
107                     #
108                     # Where both features are optional. Without this syntax
109                     # we would be forced to define "default" values.
110
111    values * :       # The value to add to variable. If <feature> is specified,
112                     # then the value of 'feature' will be added.
113    unchecked ?      # If value 'unchecked' is passed, will not test that flags
114                     # are set for the calling module.
115    : hack-hack ?    # For
116                     #   flags rule OPTIONS <cxx-abi> : -model ansi
117                     # Treat <cxx-abi> as condition
118                     # FIXME: ugly hack.
119)
120{
121    local caller = [ CALLER_MODULE ] ;
122    if ! [ MATCH ".*([.]).*" : $(rule-or-module) ]
123       && [ MATCH "(Jamfile<.*)" : $(caller) ]
124    {
125        # Unqualified rule name, used inside Jamfile. Most likely used with
126        # 'make' or 'notfile' rules. This prevents setting flags on the entire
127        # Jamfile module (this will be considered as rule), but who cares?
128        # Probably, 'flags' rule should be split into 'flags' and
129        # 'flags-on-module'.
130        rule-or-module = $(caller).$(rule-or-module) ;
131    }
132    else
133    {
134        local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ;
135        if $(unchecked) != unchecked
136            && $(.flags-module-checking[1]) != unchecked
137            && $(module_) != $(caller)
138        {
139            errors.error "Module $(caller) attempted to set flags for module $(module_)" ;
140        }
141    }
142
143    if $(condition) && ! $(condition:G=) && ! $(hack-hack)
144    {
145        # We have condition in the form '<feature>', that is, without value.
146        # That is an older syntax:
147        #   flags gcc.link RPATH <dll-path> ;
148        # for compatibility, convert it to
149        #   flags gcc.link RPATH : <dll-path> ;
150        values = $(condition) ;
151        condition = ;
152    }
153
154    if $(condition)
155    {
156        property.validate-property-sets $(condition) ;
157        condition = [ normalize-condition $(condition) ] ;
158    }
159
160    add-flag $(rule-or-module) : $(variable-name) : $(condition) : $(values) ;
161}
162
163
164# Adds a new flag setting with the specified values. Does no checking.
165#
166local rule add-flag ( rule-or-module : variable-name : condition * : values * )
167{
168    .$(rule-or-module).flags += $(.flag-no) ;
169
170    # Store all flags for a module.
171    local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ;
172    .module-flags.$(module_) += $(.flag-no) ;
173    # Store flag-no -> rule-or-module mapping.
174    .rule-or-module.$(.flag-no) = $(rule-or-module) ;
175
176    .$(rule-or-module).variable.$(.flag-no) += $(variable-name) ;
177    .$(rule-or-module).values.$(.flag-no) += $(values) ;
178    .$(rule-or-module).condition.$(.flag-no) += $(condition) ;
179
180    .flag-no = [ numbers.increment $(.flag-no) ] ;
181}
182
183
184# Returns the first element of 'property-sets' which is a subset of
185# 'properties' or an empty list if no such element exists.
186#
187rule find-property-subset ( property-sets * : properties * )
188{
189    # Cut property values off.
190    local prop-keys = $(properties:G) ;
191
192    local result ;
193    for local s in $(property-sets)
194    {
195        if ! $(result)
196        {
197            # Handle value-less properties like '<architecture>' (compare with
198            # '<architecture>x86').
199
200            local set = [ feature.split $(s) ] ;
201
202            # Find the set of features that
203            # - have no property specified in required property set
204            # - are omitted in the build property set.
205            local default-props ;
206            for local i in $(set)
207            {
208                # If $(i) is a value-less property it should match default value
209                # of an optional property. See the first line in the example
210                # below:
211                #
212                #  property set     properties     result
213                # <a> <b>foo      <b>foo           match
214                # <a> <b>foo      <a>foo <b>foo    no match
215                # <a>foo <b>foo   <b>foo           no match
216                # <a>foo <b>foo   <a>foo <b>foo    match
217                if ! ( $(i:G=) || ( $(i:G) in $(prop-keys) ) )
218                {
219                    default-props += $(i) ;
220                }
221            }
222
223            if $(set) in $(properties) $(default-props)
224            {
225                result = $(s) ;
226            }
227        }
228    }
229    return $(result) ;
230}
231
232
233# Returns a value to be added to some flag for some target based on the flag's
234# value definition and the given target's property set.
235#
236rule handle-flag-value ( value * : properties * )
237{
238    local result ;
239    if $(value:G)
240    {
241        local matches = [ property.select $(value) : $(properties) ] ;
242        local order ;
243        for local p in $(matches)
244        {
245            local att = [ feature.attributes $(p:G) ] ;
246            if dependency in $(att)
247            {
248                # The value of a dependency feature is a target and needs to be
249                # actualized.
250                result += [ $(p:G=).actualize ] ;
251            }
252            else if path in $(att) || free in $(att)
253            {
254                local values ;
255                # Treat features with && in the value specially -- each
256                # &&-separated element is considered a separate value. This is
257                # needed to handle searched libraries or include paths, which
258                # may need to be in a specific order.
259                if ! [ MATCH (&&) : $(p:G=) ]
260                {
261                    values = $(p:G=) ;
262                }
263                else
264                {
265                    values = [ regex.split $(p:G=) "&&" ] ;
266                }
267                if path in $(att)
268                {
269                    values = [ sequence.transform path.native : $(values) ] ;
270                }
271                result += $(values) ;
272                if $(values[2])
273                {
274                    if ! $(order)
275                    {
276                        order = [ new order ] ;
277                    }
278                    local prev ;
279                    for local v in $(values)
280                    {
281                        if $(prev)
282                        {
283                            $(order).add-pair $(prev) $(v) ;
284                        }
285                        prev = $(v) ;
286                    }
287                }
288            }
289            else
290            {
291                result += $(p:G=) ;
292            }
293        }
294        if $(order)
295        {
296            result = [ $(order).order [ sequence.unique $(result) : stable ] ] ;
297            DELETE_MODULE $(order) ;
298        }
299    }
300    else
301    {
302        result += $(value) ;
303    }
304    return $(result) ;
305}
306
307
308# Given a rule name and a property set, returns a list of interleaved variables
309# names and values which must be set on targets for that rule/property-set
310# combination.
311#
312rule set-target-variables-aux ( rule-or-module : property-set )
313{
314    local result ;
315    properties = [ $(property-set).raw ] ;
316    for local f in $(.$(rule-or-module).flags)
317    {
318        local variable = $(.$(rule-or-module).variable.$(f)) ;
319        local condition = $(.$(rule-or-module).condition.$(f)) ;
320        local values = $(.$(rule-or-module).values.$(f)) ;
321
322        if ! $(condition) ||
323            [ find-property-subset $(condition) : $(properties) ]
324        {
325            local processed ;
326            for local v in $(values)
327            {
328                # The value might be <feature-name> so needs special treatment.
329                processed += [ handle-flag-value $(v) : $(properties) ] ;
330            }
331            for local r in $(processed)
332            {
333                result += $(variable) $(r) ;
334            }
335        }
336    }
337
338    # Strip away last dot separated part and recurse.
339    local next = [ MATCH ^(.+)\\.([^\\.])* : $(rule-or-module) ] ;
340    if $(next)
341    {
342        result += [ set-target-variables-aux $(next[1]) : $(property-set) ] ;
343    }
344    return $(result) ;
345}
346
347rule relevant-features ( rule-or-module )
348{
349    local result ;
350    if ! $(.relevant-features.$(rule-or-module))
351    {
352        for local f in $(.$(rule-or-module).flags)
353        {
354            local condition = $(.$(rule-or-module).condition.$(f)) ;
355            local values = $(.$(rule-or-module).values.$(f)) ;
356
357            for local c in $(condition)
358            {
359                for local p in [ feature.split $(c) ]
360                {
361                    if $(p:G)
362                    {
363                        result += $(p:G) ;
364                    }
365                    else
366                    {
367                        local temp = [ feature.expand-subfeatures $(p) ] ;
368                        result += $(temp:G) ;
369                    }
370                }
371            }
372
373            for local v in $(values)
374            {
375                if $(v:G)
376                {
377                    result += $(v:G) ;
378                }
379            }
380        }
381
382        # Strip away last dot separated part and recurse.
383        local next = [ MATCH ^(.+)\\.([^\\.])* : $(rule-or-module) ] ;
384        if $(next)
385        {
386            result += [ relevant-features $(next[1]) ] ;
387        }
388        result = [ sequence.unique $(result) ] ;
389        if $(result[1]) = ""
390        {
391            result = $(result) ;
392        }
393        .relevant-features.$(rule-or-module) = $(result) ;
394        return $(result) ;
395    }
396    else
397    {
398        return $(.relevant-features.$(rule-or-module)) ;
399    }
400}
401
402rule filter-property-set ( rule-or-module : property-set )
403{
404    local key = .filtered.property-set.$(rule-or-module).$(property-set) ;
405    if ! $($(key))
406    {
407        local relevant = [ relevant-features $(rule-or-module) ] ;
408        local result ;
409        for local p in [ $(property-set).raw ]
410        {
411            if $(p:G) in $(relevant)
412            {
413                result += $(p) ;
414            }
415        }
416        $(key) = [ property-set.create $(result) ] ;
417    }
418    return $($(key)) ;
419}
420
421rule set-target-variables ( rule-or-module targets + : property-set )
422{
423    property-set = [ filter-property-set $(rule-or-module) : $(property-set) ] ;
424    local key = .stv.$(rule-or-module).$(property-set) ;
425    local settings = $($(key)) ;
426    if ! $(settings)
427    {
428        settings = [ set-target-variables-aux $(rule-or-module) :
429            $(property-set) ] ;
430
431        if ! $(settings)
432        {
433            settings = none ;
434        }
435        $(key) = $(settings) ;
436    }
437
438    if $(settings) != none
439    {
440        local var-name = ;
441        for local name-or-value in $(settings)
442        {
443            if $(var-name)
444            {
445                $(var-name) on $(targets) += $(name-or-value) ;
446                var-name = ;
447            }
448            else
449            {
450                var-name = $(name-or-value) ;
451            }
452        }
453    }
454}
455
456
457# Make toolset 'toolset', defined in a module of the same name, inherit from
458# 'base'.
459# 1. The 'init' rule from 'base' is imported into 'toolset' with full name.
460#    Another 'init' is called, which forwards to the base one.
461# 2. All generators from 'base' are cloned. The ids are adjusted and <toolset>
462#    property in requires is adjusted too.
463# 3. All flags are inherited.
464# 4. All rules are imported.
465#
466rule inherit ( toolset : base )
467{
468    import $(base) ;
469    inherit-generators $(toolset) : $(base) ;
470    inherit-flags      $(toolset) : $(base) ;
471    inherit-rules      $(toolset) : $(base) ;
472}
473
474
475rule inherit-generators ( toolset properties * : base : generators-to-ignore * )
476{
477    properties ?= <toolset>$(toolset) ;
478    local base-generators = [ generators.generators-for-toolset $(base) ] ;
479    for local g in $(base-generators)
480    {
481        local id = [ $(g).id ] ;
482
483        if ! $(id) in $(generators-to-ignore)
484        {
485            # Some generator names have multiple periods in their name, so
486            # $(id:B=$(toolset)) does not generate the right new-id name. E.g.
487            # if id = gcc.compile.c++ then $(id:B=darwin) = darwin.c++, which is
488            # not what we want. Manually parse the base and suffix. If there is
489            # a better way to do this, I would love to see it. See also the
490            # register() rule in the generators module.
491            local base = $(id) ;
492            local suffix = "" ;
493            while $(base:S)
494            {
495                suffix = $(base:S)$(suffix) ;
496                base = $(base:B) ;
497            }
498            local new-id = $(toolset)$(suffix) ;
499
500            generators.register [ $(g).clone $(new-id) : $(properties) ] ;
501        }
502    }
503}
504
505
506# Brings all flag definitions from the 'base' toolset into the 'toolset'
507# toolset. Flag definitions whose conditions make use of properties in
508# 'prohibited-properties' are ignored. Do not confuse property and feature, for
509# example <debug-symbols>on and <debug-symbols>off, so blocking one of them does
510# not block the other one.
511#
512# The flag conditions are not altered at all, so if a condition includes a name,
513# or version of a base toolset, it will not ever match the inheriting toolset.
514# When such flag settings must be inherited, define a rule in base toolset
515# module and call it as needed.
516#
517rule inherit-flags ( toolset : base : prohibited-properties * : prohibited-vars * )
518{
519    for local f in $(.module-flags.$(base))
520    {
521        local rule-or-module = $(.rule-or-module.$(f)) ;
522        if ( [ set.difference
523            $(.$(rule-or-module).condition.$(f)) :
524                  $(prohibited-properties) ]
525                  || ! $(.$(rule-or-module).condition.$(f))
526        ) && ( ! $(.$(rule-or-module).variable.$(f)) in $(prohibited-vars) )
527        {
528            local rule_ = [ MATCH "[^.]*\.(.*)" : $(rule-or-module) ] ;
529            local new-rule-or-module ;
530            if $(rule_)
531            {
532                new-rule-or-module = $(toolset).$(rule_) ;
533            }
534            else
535            {
536                new-rule-or-module = $(toolset) ;
537            }
538
539            add-flag
540                $(new-rule-or-module)
541                : $(.$(rule-or-module).variable.$(f))
542                : $(.$(rule-or-module).condition.$(f))
543                : $(.$(rule-or-module).values.$(f)) ;
544        }
545    }
546}
547
548
549rule inherit-rules ( toolset : base : localize ? )
550{
551    # It appears that "action" creates a local rule.
552    local base-generators = [ generators.generators-for-toolset $(base) ] ;
553    local rules ;
554    for local g in $(base-generators)
555    {
556        rules += [ MATCH "[^.]*\.(.*)" : [ $(g).rule-name ] ] ;
557    }
558    rules = [ sequence.unique $(rules) ] ;
559    IMPORT $(base) : $(rules) : $(toolset) : $(rules) : $(localize) ;
560    IMPORT $(toolset) : $(rules) : : $(toolset).$(rules) ;
561}
562
563.requirements = [ property-set.empty ] ;
564
565# Return the list of global 'toolset requirements'. Those requirements will be
566# automatically added to the requirements of any main target.
567#
568rule requirements ( )
569{
570    return $(.requirements) ;
571}
572
573
574# Adds elements to the list of global 'toolset requirements'. The requirements
575# will be automatically added to the requirements for all main targets, as if
576# they were specified literally. For best results, all requirements added should
577# be conditional or indirect conditional.
578#
579rule add-requirements ( requirements * )
580{
581    if ! $(.ignore-requirements)
582    {
583        requirements = [ property.translate-indirect $(requirements) : [ CALLER_MODULE ] ] ;
584        requirements = [ property.expand-subfeatures-in-conditions $(requirements) ] ;
585        requirements = [ property.make $(requirements) ] ;
586        .requirements = [ $(.requirements).add-raw $(requirements) ] ;
587    }
588}
589
590
591rule __test__ ( )
592{
593    import assert ;
594    local p = <b>0 <c>1 <d>2 <e>3 <f>4 ;
595    assert.result <c>1/<d>2/<e>3 : find-property-subset <c>1/<d>2/<e>3 <a>0/<b>0/<c>1 <d>2/<e>5 <a>9 : $(p) ;
596    assert.result : find-property-subset <a>0/<b>0/<c>9/<d>9/<e>5 <a>9 : $(p) ;
597
598    local p-set = <a>/<b> <a>0/<b> <a>/<b>1 <a>0/<b>1 ;
599    assert.result <a>/<b>   : find-property-subset $(p-set) :                ;
600    assert.result <a>0/<b>  : find-property-subset $(p-set) : <a>0      <c>2 ;
601    assert.result <a>/<b>1  : find-property-subset $(p-set) : <b>1      <c>2 ;
602    assert.result <a>0/<b>1 : find-property-subset $(p-set) : <a>0 <b>1      ;
603}
604