]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/build/toolset.py
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / tools / build / src / build / toolset.py
1 # Status: being ported by Vladimir Prus
2 # Base revision: 40958
3 #
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)
9
10 """ Support for toolset definition.
11 """
12 import sys
13
14 import feature, property, generators, property_set
15 import b2.util.set
16 import bjam
17
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
22
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
28
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.
33 #
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
37 # for,
38 class Flag:
39
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
47 self.values = values
48 self.condition = condition
49 self.rule = rule
50
51 def __str__(self):
52 return("Flag(" + str(self.variable_name) + ", " + str(self.values) +\
53 ", " + str(self.condition) + ", " + str(self.rule) + ")")
54
55 def reset ():
56 """ Clear the module state. This is mainly for testing purposes.
57 """
58 global __module_flags, __flags, __stv
59
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.
65 __module_flags = {}
66
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.
72 __flags = {}
73
74 # A cache for variable settings. The key is generated from the rule name and the properties.
75 __stv = {}
76
77 reset ()
78
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)
85
86 # FIXME push-checking-for-flags-module ....
87 # FIXME: investigate existing uses of 'hack-hack' parameter
88 # in jam code.
89
90 @bjam_signature((["rule_or_module", "variable_name", "condition", "*"],
91 ["values", "*"]))
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.
98
99 If does not contain dot, should be a module name.
100 The flags will be applied for all rules in that
101 module.
102 If module for rule is different from the calling
103 module, an error is issued.
104
105 variable_name: Variable that should be set on target
106
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
115 always used.
116
117 Property sets may use value-less properties
118 ('<a>' vs. '<a>value') to match absent
119 properties. This allows to separately match
120
121 <architecture>/<address-model>64
122 <architecture>ia64/<address-model>
123
124 Where both features are optional. Without this
125 syntax we'd be forced to define "default" value.
126
127 values: The value to add to variable. If <feature>
128 is specified, then the value of 'feature'
129 will be added.
130 """
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
141 # 'flags-on-module'.
142 rule_or_module = qualify_jam_action(rule_or_module, caller)
143 else:
144 # FIXME: revive checking that we don't set flags for a different
145 # module unintentionally
146 pass
147
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:
151 #
152 # flags gcc.link RPATH <dll-path> ;
153 # for compatibility, convert it to
154 # flags gcc.link RPATH : <dll-path> ;
155 values = [ condition ]
156 condition = None
157
158 if condition:
159 transformed = []
160 for c in 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
166
167 property.validate_property_sets(condition)
168
169 __add_flag (rule_or_module, variable_name, condition, values)
170
171 def set_target_variables (manager, rule_or_module, targets, ps):
172 """
173 """
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)
178
179 if settings:
180 for s in settings:
181 for target in targets:
182 manager.engine ().set_target_variable (target, s [0], s[1], True)
183
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)
189
190 for condition in conditions:
191
192 found_all = True
193 for i in condition.all():
194
195 if i.value:
196 found = i.value in ps.get(i.feature)
197 else:
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
202 # example below:
203 #
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)
210
211 found_all = found_all and found
212
213 if found_all:
214 return condition
215
216 return None
217
218
219 def register (toolset):
220 """ Registers a new toolset.
221 """
222 assert isinstance(toolset, basestring)
223 feature.extend('toolset', [toolset])
224
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)
230 if not properties:
231 properties = [replace_grist (toolset, '<toolset>')]
232
233 base_generators = generators.generators_for_toolset(base)
234
235 for g in base_generators:
236 id = g.id()
237
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)
246
247 new_id = toolset + '.' + suffix
248
249 generators.register(g.clone(new_id, properties))
250
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.
257
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, []):
266
267 if not f.condition or b2.util.set.difference(f.condition, prohibited_properties):
268 match = __re_first_group.match(f.rule)
269 rule_ = None
270 if match:
271 rule_ = match.group(1)
272
273 new_rule_or_module = ''
274
275 if rule_:
276 new_rule_or_module = toolset + '.' + rule_
277 else:
278 new_rule_or_module = toolset
279
280 __add_flag (new_rule_or_module, f.variable_name, f.condition, f.values)
281
282
283 def inherit_rules(toolset, base):
284 engine = get_manager().engine()
285 new_actions = {}
286 for action_name, action in engine.actions.iteritems():
287 module, id = split_action_id(action_name)
288 if module == base:
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
294
295 engine.actions.update(new_actions)
296
297 ######################################################################################
298 # Private functions
299
300 @cached
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.
305 """
306 assert isinstance(rule_or_module, basestring)
307 assert isinstance(ps, property_set.PropertySet)
308 result = []
309
310 for f in __flags.get(rule_or_module, []):
311
312 if not f.condition or find_satisfied_condition (f.condition, ps):
313 processed = []
314 for v in f.values:
315 # The value might be <feature-name> so needs special
316 # treatment.
317 processed += __handle_flag_value (manager, v, ps)
318
319 for r in processed:
320 result.append ((f.variable_name, r))
321
322 # strip away last dot separated part and recurse.
323 next = __re_split_last_segment.match(rule_or_module)
324
325 if next:
326 result.extend(__set_target_variables_aux(
327 manager, next.group(1), ps))
328
329 return result
330
331 def __handle_flag_value (manager, value, ps):
332 assert isinstance(value, basestring)
333 assert isinstance(ps, property_set.PropertySet)
334 result = []
335
336 if get_grist (value):
337 f = feature.get(value)
338 values = ps.get(f)
339
340 for value in values:
341
342 if f.dependency:
343 # the value of a dependency feature is a target
344 # and must be actualized
345 result.append(value.actualize())
346
347 elif f.path or f.free:
348
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):
354 result.append(value)
355
356 else:
357 result.extend(value.split ('&&'))
358 else:
359 result.append (value)
360 else:
361 result.append (value)
362
363 return sequence.unique(result, stable=True)
364
365 def __add_flag (rule_or_module, variable_name, condition, values):
366 """ Adds a new flag setting with the specified values.
367 Does no checking.
368 """
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)
375
376 # Grab the name of the module
377 m = __re_first_segment.match (rule_or_module)
378 assert m
379 module = m.group(1)
380
381 __module_flags.setdefault(module, []).append(f)
382 __flags.setdefault(rule_or_module, []).append(f)
383
384 __requirements = []
385
386 def requirements():
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
390
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)
397
398 if _ignore_toolset_requirements:
399 __requirements.extend(requirements)
400
401
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, ['.']);
414
415 inherit_generators(toolset, [], base)
416 inherit_flags(toolset, base)
417 inherit_rules(toolset, base)