1 # Status: being ported by Vladimir Prus
4 # Copyright 2003 Dave Abrahams
5 # Copyright 2005 Rene Rivera
6 # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
7 # Distributed under the Boost Software License, Version 1.0.
8 # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
10 """ Support for toolset definition.
14 import feature
, property, generators
, property_set
18 from b2
.util
import cached
, qualify_jam_action
, is_iterable_typed
, is_iterable
19 from b2
.util
.utility
import *
20 from b2
.util
import bjam_signature
, sequence
21 from b2
.manager
import get_manager
23 __re_split_last_segment
= re
.compile (r
'^(.+)\.([^\.])*')
24 __re_two_ampersands
= re
.compile ('(&&)')
25 __re_first_segment
= re
.compile ('([^.]*).*')
26 __re_first_group
= re
.compile (r
'[^.]*\.(.*)')
27 _ignore_toolset_requirements
= '--ignore-toolset-requirements' not in sys
.argv
29 # Flag is a mechanism to set a value
30 # A single toolset flag. Specifies that when certain
31 # properties are in build property set, certain values
32 # should be appended to some variable.
34 # A flag applies to a specific action in specific module.
35 # The list of all flags for a module is stored, and each
36 # flag further contains the name of the rule it applies
40 def __init__(self
, variable_name
, values
, condition
, rule
= None):
41 assert isinstance(variable_name
, basestring
)
42 assert is_iterable(values
) and all(
43 isinstance(v
, (basestring
, type(None))) for v
in values
)
44 assert is_iterable_typed(condition
, property_set
.PropertySet
)
45 assert isinstance(rule
, (basestring
, type(None)))
46 self
.variable_name
= variable_name
48 self
.condition
= condition
52 return("Flag(" + str(self
.variable_name
) + ", " + str(self
.values
) +\
53 ", " + str(self
.condition
) + ", " + str(self
.rule
) + ")")
56 """ Clear the module state. This is mainly for testing purposes.
58 global __module_flags
, __flags
, __stv
60 # Mapping from module name to a list of all flags that apply
61 # to either that module directly, or to any rule in that module.
62 # Each element of the list is Flag instance.
63 # So, for module named xxx this might contain flags for 'xxx',
64 # for 'xxx.compile', for 'xxx.compile.c++', etc.
67 # Mapping from specific rule or module name to a list of Flag instances
68 # that apply to that name.
69 # Say, it might contain flags for 'xxx.compile.c++'. If there are
70 # entries for module name 'xxx', they are flags for 'xxx' itself,
71 # not including any rules in that module.
74 # A cache for varaible settings. The key is generated from the rule name and the properties.
79 # FIXME: --ignore-toolset-requirements
80 def using(toolset_module
, *args
):
81 if isinstance(toolset_module
, (list, tuple)):
82 toolset_module
= toolset_module
[0]
83 loaded_toolset_module
= get_manager().projects().load_module(toolset_module
, [os
.getcwd()]);
84 loaded_toolset_module
.init(*args
)
86 # FIXME push-checking-for-flags-module ....
87 # FIXME: investigate existing uses of 'hack-hack' parameter
90 @bjam_signature((["rule_or_module", "variable_name", "condition", "*"],
92 def flags(rule_or_module
, variable_name
, condition
, values
= []):
93 """ Specifies the flags (variables) that must be set on targets under certain
94 conditions, described by arguments.
95 rule_or_module: If contains dot, should be a rule name.
96 The flags will be applied when that rule is
97 used to set up build actions.
99 If does not contain dot, should be a module name.
100 The flags will be applied for all rules in that
102 If module for rule is different from the calling
103 module, an error is issued.
105 variable_name: Variable that should be set on target
107 condition A condition when this flag should be applied.
108 Should be set of property sets. If one of
109 those property sets is contained in build
110 properties, the flag will be used.
111 Implied values are not allowed:
112 "<toolset>gcc" should be used, not just
113 "gcc". Subfeatures, like in "<toolset>gcc-3.2"
114 are allowed. If left empty, the flag will
117 Propery sets may use value-less properties
118 ('<a>' vs. '<a>value') to match absent
119 properties. This allows to separately match
121 <architecture>/<address-model>64
122 <architecture>ia64/<address-model>
124 Where both features are optional. Without this
125 syntax we'd be forced to define "default" value.
127 values: The value to add to variable. If <feature>
128 is specified, then the value of 'feature'
131 assert isinstance(rule_or_module
, basestring
)
132 assert isinstance(variable_name
, basestring
)
133 assert is_iterable_typed(condition
, basestring
)
134 assert is_iterable(values
) and all(isinstance(v
, (basestring
, type(None))) for v
in values
)
135 caller
= bjam
.caller()
136 if not '.' in rule_or_module
and caller
and caller
[:-1].startswith("Jamfile"):
137 # Unqualified rule name, used inside Jamfile. Most likely used with
138 # 'make' or 'notfile' rules. This prevents setting flags on the entire
139 # Jamfile module (this will be considered as rule), but who cares?
140 # Probably, 'flags' rule should be split into 'flags' and
142 rule_or_module
= qualify_jam_action(rule_or_module
, caller
)
144 # FIXME: revive checking that we don't set flags for a different
145 # module unintentionally
148 if condition
and not replace_grist (condition
, ''):
149 # We have condition in the form '<feature>', that is, without
150 # value. That's a previous syntax:
152 # flags gcc.link RPATH <dll-path> ;
153 # for compatibility, convert it to
154 # flags gcc.link RPATH : <dll-path> ;
155 values
= [ condition
]
161 # FIXME: 'split' might be a too raw tool here.
162 pl
= [property.create_from_string(s
,False,True) for s
in c
.split('/')]
163 pl
= feature
.expand_subfeatures(pl
);
164 transformed
.append(property_set
.create(pl
))
165 condition
= transformed
167 property.validate_property_sets(condition
)
169 __add_flag (rule_or_module
, variable_name
, condition
, values
)
171 def set_target_variables (manager
, rule_or_module
, targets
, ps
):
174 assert isinstance(rule_or_module
, basestring
)
175 assert is_iterable_typed(targets
, basestring
)
176 assert isinstance(ps
, property_set
.PropertySet
)
177 settings
= __set_target_variables_aux(manager
, rule_or_module
, ps
)
181 for target
in targets
:
182 manager
.engine ().set_target_variable (target
, s
[0], s
[1], True)
184 def find_satisfied_condition(conditions
, ps
):
185 """Returns the first element of 'property-sets' which is a subset of
186 'properties', or an empty list if no such element exists."""
187 assert is_iterable_typed(conditions
, property_set
.PropertySet
)
188 assert isinstance(ps
, property_set
.PropertySet
)
190 for condition
in conditions
:
193 for i
in condition
.all():
196 found
= i
.value
in ps
.get(i
.feature
)
198 # Handle value-less properties like '<architecture>' (compare with
199 # '<architecture>x86').
200 # If $(i) is a value-less property it should match default
201 # value of an optional property. See the first line in the
204 # property set properties result
205 # <a> <b>foo <b>foo match
206 # <a> <b>foo <a>foo <b>foo no match
207 # <a>foo <b>foo <b>foo no match
208 # <a>foo <b>foo <a>foo <b>foo match
209 found
= not ps
.get(i
.feature
)
211 found_all
= found_all
and found
219 def register (toolset
):
220 """ Registers a new toolset.
222 assert isinstance(toolset
, basestring
)
223 feature
.extend('toolset', [toolset
])
225 def inherit_generators (toolset
, properties
, base
, generators_to_ignore
= []):
226 assert isinstance(toolset
, basestring
)
227 assert is_iterable_typed(properties
, basestring
)
228 assert isinstance(base
, basestring
)
229 assert is_iterable_typed(generators_to_ignore
, basestring
)
231 properties
= [replace_grist (toolset
, '<toolset>')]
233 base_generators
= generators
.generators_for_toolset(base
)
235 for g
in base_generators
:
238 if not id in generators_to_ignore
:
239 # Some generator names have multiple periods in their name, so
240 # $(id:B=$(toolset)) doesn't generate the right new_id name.
241 # e.g. if id = gcc.compile.c++, $(id:B=darwin) = darwin.c++,
242 # which is not what we want. Manually parse the base and suffix
243 # (if there's a better way to do this, I'd love to see it.)
244 # See also register in module generators.
245 (base
, suffix
) = split_action_id(id)
247 new_id
= toolset
+ '.' + suffix
249 generators
.register(g
.clone(new_id
, properties
))
251 def inherit_flags(toolset
, base
, prohibited_properties
= []):
252 """Brings all flag definitions from the 'base' toolset into the 'toolset'
253 toolset. Flag definitions whose conditions make use of properties in
254 'prohibited-properties' are ignored. Don't confuse property and feature, for
255 example <debug-symbols>on and <debug-symbols>off, so blocking one of them does
256 not block the other one.
258 The flag conditions are not altered at all, so if a condition includes a name,
259 or version of a base toolset, it won't ever match the inheriting toolset. When
260 such flag settings must be inherited, define a rule in base toolset module and
261 call it as needed."""
262 assert isinstance(toolset
, basestring
)
263 assert isinstance(base
, basestring
)
264 assert is_iterable_typed(prohibited_properties
, basestring
)
265 for f
in __module_flags
.get(base
, []):
267 if not f
.condition
or b2
.util
.set.difference(f
.condition
, prohibited_properties
):
268 match
= __re_first_group
.match(f
.rule
)
271 rule_
= match
.group(1)
273 new_rule_or_module
= ''
276 new_rule_or_module
= toolset
+ '.' + rule_
278 new_rule_or_module
= toolset
280 __add_flag (new_rule_or_module
, f
.variable_name
, f
.condition
, f
.values
)
283 def inherit_rules(toolset
, base
):
284 engine
= get_manager().engine()
286 for action_name
, action
in engine
.actions
.iteritems():
287 module
, id = split_action_id(action_name
)
289 new_action_name
= toolset
+ '.' + id
290 # make sure not to override any existing actions
291 # that may have been declared already
292 if new_action_name
not in engine
.actions
:
293 new_actions
[new_action_name
] = action
295 engine
.actions
.update(new_actions
)
297 ######################################################################################
301 def __set_target_variables_aux (manager
, rule_or_module
, ps
):
302 """ Given a rule name and a property set, returns a list of tuples of
303 variables names and values, which must be set on targets for that
304 rule/properties combination.
306 assert isinstance(rule_or_module
, basestring
)
307 assert isinstance(ps
, property_set
.PropertySet
)
310 for f
in __flags
.get(rule_or_module
, []):
312 if not f
.condition
or find_satisfied_condition (f
.condition
, ps
):
315 # The value might be <feature-name> so needs special
317 processed
+= __handle_flag_value (manager
, v
, ps
)
320 result
.append ((f
.variable_name
, r
))
322 # strip away last dot separated part and recurse.
323 next
= __re_split_last_segment
.match(rule_or_module
)
326 result
.extend(__set_target_variables_aux(
327 manager
, next
.group(1), ps
))
331 def __handle_flag_value (manager
, value
, ps
):
332 assert isinstance(value
, basestring
)
333 assert isinstance(ps
, property_set
.PropertySet
)
336 if get_grist (value
):
337 f
= feature
.get(value
)
343 # the value of a dependency feature is a target
344 # and must be actualized
345 result
.append(value
.actualize())
347 elif f
.path
or f
.free
:
349 # Treat features with && in the value
350 # specially -- each &&-separated element is considered
351 # separate value. This is needed to handle searched
352 # libraries, which must be in specific order.
353 if not __re_two_ampersands
.search(value
):
357 result
.extend(value
.split ('&&'))
359 result
.append (value
)
361 result
.append (value
)
363 return sequence
.unique(result
, stable
=True)
365 def __add_flag (rule_or_module
, variable_name
, condition
, values
):
366 """ Adds a new flag setting with the specified values.
369 assert isinstance(rule_or_module
, basestring
)
370 assert isinstance(variable_name
, basestring
)
371 assert is_iterable_typed(condition
, property_set
.PropertySet
)
372 assert is_iterable(values
) and all(
373 isinstance(v
, (basestring
, type(None))) for v
in values
)
374 f
= Flag(variable_name
, values
, condition
, rule_or_module
)
376 # Grab the name of the module
377 m
= __re_first_segment
.match (rule_or_module
)
381 __module_flags
.setdefault(module
, []).append(f
)
382 __flags
.setdefault(rule_or_module
, []).append(f
)
387 """Return the list of global 'toolset requirements'.
388 Those requirements will be automatically added to the requirements of any main target."""
389 return __requirements
391 def add_requirements(requirements
):
392 """Adds elements to the list of global 'toolset requirements'. The requirements
393 will be automatically added to the requirements for all main targets, as if
394 they were specified literally. For best results, all requirements added should
395 be conditional or indirect conditional."""
396 assert is_iterable_typed(requirements
, basestring
)
398 if _ignore_toolset_requirements
:
399 __requirements
.extend(requirements
)
402 # Make toolset 'toolset', defined in a module of the same name,
403 # inherit from 'base'
404 # 1. The 'init' rule from 'base' is imported into 'toolset' with full
405 # name. Another 'init' is called, which forwards to the base one.
406 # 2. All generators from 'base' are cloned. The ids are adjusted and
407 # <toolset> property in requires is adjusted too
408 # 3. All flags are inherited
409 # 4. All rules are imported.
410 def inherit(toolset
, base
):
411 assert isinstance(toolset
, basestring
)
412 assert isinstance(base
, basestring
)
413 get_manager().projects().load_module(base
, ['.']);
415 inherit_generators(toolset
, [], base
)
416 inherit_flags(toolset
, base
)
417 inherit_rules(toolset
, base
)