]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/tools/common.py
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / tools / build / src / tools / common.py
1 # Status: being ported by Steven Watanabe
2 # Base revision: 47174
3 #
4 # Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
5 # distribute this software is granted provided this copyright notice appears in
6 # all copies. This software is provided "as is" without express or implied
7 # warranty, and with no claim as to its suitability for any purpose.
8
9 """ Provides actions common to all toolsets, such as creating directories and
10 removing files.
11 """
12
13 import re
14 import bjam
15 import os
16 import os.path
17 import sys
18
19 # for some reason this fails on Python 2.7(r27:82525)
20 # from b2.build import virtual_target
21 import b2.build.virtual_target
22 from b2.build import feature, type
23 from b2.util.utility import *
24 from b2.util import path, is_iterable_typed
25
26 __re__before_first_dash = re.compile ('([^-]*)-')
27
28 def reset ():
29 """ Clear the module state. This is mainly for testing purposes.
30 Note that this must be called _after_ resetting the module 'feature'.
31 """
32 global __had_unspecified_value, __had_value, __declared_subfeature
33 global __init_loc
34 global __all_signatures, __debug_configuration, __show_configuration
35
36 # Stores toolsets without specified initialization values.
37 __had_unspecified_value = {}
38
39 # Stores toolsets with specified initialization values.
40 __had_value = {}
41
42 # Stores toolsets with declared subfeatures.
43 __declared_subfeature = {}
44
45 # Stores all signatures of the toolsets.
46 __all_signatures = {}
47
48 # Stores the initialization locations of each toolset
49 __init_loc = {}
50
51 __debug_configuration = '--debug-configuration' in bjam.variable('ARGV')
52 __show_configuration = '--show-configuration' in bjam.variable('ARGV')
53
54 global __executable_path_variable
55 OS = bjam.call("peek", [], "OS")[0]
56 if OS == "NT":
57 # On Windows the case and capitalization of PATH is not always predictable, so
58 # let's find out what variable name was really set.
59 for n in os.environ:
60 if n.lower() == "path":
61 __executable_path_variable = n
62 break
63 else:
64 __executable_path_variable = "PATH"
65
66 m = {"NT": __executable_path_variable,
67 "CYGWIN": "PATH",
68 "MACOSX": "DYLD_LIBRARY_PATH",
69 "AIX": "LIBPATH",
70 "HAIKU": "LIBRARY_PATH"}
71 global __shared_library_path_variable
72 __shared_library_path_variable = m.get(OS, "LD_LIBRARY_PATH")
73
74 reset()
75
76 def shared_library_path_variable():
77 return __shared_library_path_variable
78
79 # ported from trunk@47174
80 class Configurations(object):
81 """
82 This class helps to manage toolset configurations. Each configuration
83 has a unique ID and one or more parameters. A typical example of a unique ID
84 is a condition generated by 'common.check-init-parameters' rule. Other kinds
85 of IDs can be used. Parameters may include any details about the configuration
86 like 'command', 'path', etc.
87
88 A toolset configuration may be in one of the following states:
89
90 - registered
91 Configuration has been registered (e.g. by autodetection code) but has
92 not yet been marked as used, i.e. 'toolset.using' rule has not yet been
93 called for it.
94 - used
95 Once called 'toolset.using' rule marks the configuration as 'used'.
96
97 The main difference between the states above is that while a configuration is
98 'registered' its options can be freely changed. This is useful in particular
99 for autodetection code - all detected configurations may be safely overwritten
100 by user code.
101 """
102
103 def __init__(self):
104 self.used_ = set()
105 self.all_ = set()
106 self.params_ = {}
107
108 def register(self, id):
109 """
110 Registers a configuration.
111
112 Returns True if the configuration has been added and False if
113 it already exists. Reports an error if the configuration is 'used'.
114 """
115 assert isinstance(id, basestring)
116 if id in self.used_:
117 #FIXME
118 errors.error("common: the configuration '$(id)' is in use")
119
120 if id not in self.all_:
121 self.all_.add(id)
122
123 # Indicate that a new configuration has been added.
124 return True
125 else:
126 return False
127
128 def use(self, id):
129 """
130 Mark a configuration as 'used'.
131
132 Returns True if the state of the configuration has been changed to
133 'used' and False if it the state wasn't changed. Reports an error
134 if the configuration isn't known.
135 """
136 assert isinstance(id, basestring)
137 if id not in self.all_:
138 #FIXME:
139 errors.error("common: the configuration '$(id)' is not known")
140
141 if id not in self.used_:
142 self.used_.add(id)
143
144 # indicate that the configuration has been marked as 'used'
145 return True
146 else:
147 return False
148
149 def all(self):
150 """ Return all registered configurations. """
151 return self.all_
152
153 def used(self):
154 """ Return all used configurations. """
155 return self.used_
156
157 def get(self, id, param):
158 """ Returns the value of a configuration parameter. """
159 assert isinstance(id, basestring)
160 assert isinstance(param, basestring)
161 return self.params_.get(param, {}).get(id)
162
163 def set (self, id, param, value):
164 """ Sets the value of a configuration parameter. """
165 assert isinstance(id, basestring)
166 assert isinstance(param, basestring)
167 assert is_iterable_typed(value, basestring)
168 self.params_.setdefault(param, {})[id] = value
169
170 # Ported from trunk@47174
171 def check_init_parameters(toolset, requirement, *args):
172 """ The rule for checking toolset parameters. Trailing parameters should all be
173 parameter name/value pairs. The rule will check that each parameter either has
174 a value in each invocation or has no value in each invocation. Also, the rule
175 will check that the combination of all parameter values is unique in all
176 invocations.
177
178 Each parameter name corresponds to a subfeature. This rule will declare a
179 subfeature the first time a non-empty parameter value is passed and will
180 extend it with all the values.
181
182 The return value from this rule is a condition to be used for flags settings.
183 """
184 assert isinstance(toolset, basestring)
185 assert is_iterable_typed(requirement, basestring) or requirement is None
186 from b2.build import toolset as b2_toolset
187 if requirement is None:
188 requirement = []
189 sig = toolset
190 condition = replace_grist(toolset, '<toolset>')
191 subcondition = []
192
193 for arg in args:
194 assert(isinstance(arg, tuple))
195 assert(len(arg) == 2)
196 name = arg[0]
197 value = arg[1]
198 assert(isinstance(name, str))
199 assert(isinstance(value, str) or value is None)
200
201 str_toolset_name = str((toolset, name))
202
203 # FIXME: is this the correct translation?
204 ### if $(value)-is-not-empty
205 if value is not None:
206 condition = condition + '-' + value
207 if str_toolset_name in __had_unspecified_value:
208 raise BaseException("'%s' initialization: parameter '%s' inconsistent\n" \
209 "no value was specified in earlier initialization\n" \
210 "an explicit value is specified now" % (toolset, name))
211
212 # The logic below is for intel compiler. It calls this rule
213 # with 'intel-linux' and 'intel-win' as toolset, so we need to
214 # get the base part of toolset name.
215 # We can't pass 'intel' as toolset, because it that case it will
216 # be impossible to register versionles intel-linux and
217 # intel-win of specific version.
218 t = toolset
219 m = __re__before_first_dash.match(toolset)
220 if m:
221 t = m.group(1)
222
223 if str_toolset_name not in __had_value:
224 if str((t, name)) not in __declared_subfeature:
225 feature.subfeature('toolset', t, name, [], ['propagated'])
226 __declared_subfeature[str((t, name))] = True
227
228 __had_value[str_toolset_name] = True
229
230 feature.extend_subfeature('toolset', t, name, [value])
231 subcondition += ['<toolset-' + t + ':' + name + '>' + value ]
232
233 else:
234 if str_toolset_name in __had_value:
235 raise BaseException ("'%s' initialization: parameter '%s' inconsistent\n" \
236 "an explicit value was specified in an earlier initialization\n" \
237 "no value is specified now" % (toolset, name))
238
239 __had_unspecified_value[str_toolset_name] = True
240
241 if value == None: value = ''
242
243 sig = sig + value + '-'
244
245 # if a requirement is specified, the signature should be unique
246 # with that requirement
247 if requirement:
248 sig += '-' + '-'.join(requirement)
249
250 if sig in __all_signatures:
251 message = "duplicate initialization of '%s' with the following parameters: " % toolset
252
253 for arg in args:
254 name = arg[0]
255 value = arg[1]
256 if value == None: value = '<unspecified>'
257
258 message += "'%s' = '%s'\n" % (name, value)
259
260 raise BaseException(message)
261
262 __all_signatures[sig] = True
263 # FIXME
264 __init_loc[sig] = "User location unknown" #[ errors.nearest-user-location ] ;
265
266 # If we have a requirement, this version should only be applied under that
267 # condition. To accomplish this we add a toolset requirement that imposes
268 # the toolset subcondition, which encodes the version.
269 if requirement:
270 r = ['<toolset>' + toolset] + requirement
271 r = ','.join(r)
272 b2_toolset.add_requirements([r + ':' + c for c in subcondition])
273
274 # We add the requirements, if any, to the condition to scope the toolset
275 # variables and options to this specific version.
276 condition = [condition]
277 if requirement:
278 condition += requirement
279
280 if __show_configuration:
281 print "notice:", condition
282 return ['/'.join(condition)]
283
284 # Ported from trunk@47077
285 def get_invocation_command_nodefault(
286 toolset, tool, user_provided_command=[], additional_paths=[], path_last=False):
287 """
288 A helper rule to get the command to invoke some tool. If
289 'user-provided-command' is not given, tries to find binary named 'tool' in
290 PATH and in the passed 'additional-path'. Otherwise, verifies that the first
291 element of 'user-provided-command' is an existing program.
292
293 This rule returns the command to be used when invoking the tool. If we can't
294 find the tool, a warning is issued. If 'path-last' is specified, PATH is
295 checked after 'additional-paths' when searching for 'tool'.
296 """
297 assert isinstance(toolset, basestring)
298 assert isinstance(tool, basestring)
299 assert is_iterable_typed(user_provided_command, basestring)
300 assert is_iterable_typed(additional_paths, basestring) or additional_paths is None
301 assert isinstance(path_last, (int, bool))
302
303 if not user_provided_command:
304 command = find_tool(tool, additional_paths, path_last)
305 if not command and __debug_configuration:
306 print "warning: toolset", toolset, "initialization: can't find tool, tool"
307 #FIXME
308 #print "warning: initialized from" [ errors.nearest-user-location ] ;
309 else:
310 command = check_tool(user_provided_command)
311 if not command and __debug_configuration:
312 print "warning: toolset", toolset, "initialization:"
313 print "warning: can't find user-provided command", user_provided_command
314 #FIXME
315 #ECHO "warning: initialized from" [ errors.nearest-user-location ]
316 command = []
317 if command:
318 command = ' '.join(command)
319
320 return command
321
322 # ported from trunk@47174
323 def get_invocation_command(toolset, tool, user_provided_command = [],
324 additional_paths = [], path_last = False):
325 """ Same as get_invocation_command_nodefault, except that if no tool is found,
326 returns either the user-provided-command, if present, or the 'tool' parameter.
327 """
328 assert isinstance(toolset, basestring)
329 assert isinstance(tool, basestring)
330 assert is_iterable_typed(user_provided_command, basestring)
331 assert is_iterable_typed(additional_paths, basestring) or additional_paths is None
332 assert isinstance(path_last, (int, bool))
333
334 result = get_invocation_command_nodefault(toolset, tool,
335 user_provided_command,
336 additional_paths,
337 path_last)
338
339 if not result:
340 if user_provided_command:
341 result = user_provided_command[0]
342 else:
343 result = tool
344
345 assert(isinstance(result, str))
346
347 return result
348
349 # ported from trunk@47281
350 def get_absolute_tool_path(command):
351 """
352 Given an invocation command,
353 return the absolute path to the command. This works even if commnad
354 has not path element and is present in PATH.
355 """
356 assert isinstance(command, basestring)
357 if os.path.dirname(command):
358 return os.path.dirname(command)
359 else:
360 programs = path.programs_path()
361 m = path.glob(programs, [command, command + '.exe' ])
362 if not len(m):
363 if __debug_configuration:
364 print "Could not find:", command, "in", programs
365 return None
366 return os.path.dirname(m[0])
367
368 # ported from trunk@47174
369 def find_tool(name, additional_paths = [], path_last = False):
370 """ Attempts to find tool (binary) named 'name' in PATH and in
371 'additional-paths'. If found in path, returns 'name'. If
372 found in additional paths, returns full name. If the tool
373 is found in several directories, returns the first path found.
374 Otherwise, returns the empty string. If 'path_last' is specified,
375 path is checked after 'additional_paths'.
376 """
377 assert isinstance(name, basestring)
378 assert is_iterable_typed(additional_paths, basestring)
379 assert isinstance(path_last, (int, bool))
380
381 programs = path.programs_path()
382 match = path.glob(programs, [name, name + '.exe'])
383 additional_match = path.glob(additional_paths, [name, name + '.exe'])
384
385 result = []
386 if path_last:
387 result = additional_match
388 if not result and match:
389 result = match
390
391 else:
392 if match:
393 result = match
394
395 elif additional_match:
396 result = additional_match
397
398 if result:
399 return path.native(result[0])
400 else:
401 return ''
402
403 #ported from trunk@47281
404 def check_tool_aux(command):
405 """ Checks if 'command' can be found either in path
406 or is a full name to an existing file.
407 """
408 assert isinstance(command, basestring)
409 dirname = os.path.dirname(command)
410 if dirname:
411 if os.path.exists(command):
412 return command
413 # Both NT and Cygwin will run .exe files by their unqualified names.
414 elif on_windows() and os.path.exists(command + '.exe'):
415 return command
416 # Only NT will run .bat files by their unqualified names.
417 elif os_name() == 'NT' and os.path.exists(command + '.bat'):
418 return command
419 else:
420 paths = path.programs_path()
421 if path.glob(paths, [command]):
422 return command
423
424 # ported from trunk@47281
425 def check_tool(command):
426 """ Checks that a tool can be invoked by 'command'.
427 If command is not an absolute path, checks if it can be found in 'path'.
428 If comand is absolute path, check that it exists. Returns 'command'
429 if ok and empty string otherwise.
430 """
431 assert is_iterable_typed(command, basestring)
432 #FIXME: why do we check the first and last elements????
433 if check_tool_aux(command[0]) or check_tool_aux(command[-1]):
434 return command
435
436 # ported from trunk@47281
437 def handle_options(tool, condition, command, options):
438 """ Handle common options for toolset, specifically sets the following
439 flag variables:
440 - CONFIG_COMMAND to 'command'
441 - OPTIOns for compile to the value of <compileflags> in options
442 - OPTIONS for compile.c to the value of <cflags> in options
443 - OPTIONS for compile.c++ to the value of <cxxflags> in options
444 - OPTIONS for compile.fortran to the value of <fflags> in options
445 - OPTIONs for link to the value of <linkflags> in options
446 """
447 from b2.build import toolset
448
449 assert isinstance(tool, basestring)
450 assert is_iterable_typed(condition, basestring)
451 assert command and isinstance(command, basestring)
452 assert is_iterable_typed(options, basestring)
453 toolset.flags(tool, 'CONFIG_COMMAND', condition, [command])
454 toolset.flags(tool + '.compile', 'OPTIONS', condition, feature.get_values('<compileflags>', options))
455 toolset.flags(tool + '.compile.c', 'OPTIONS', condition, feature.get_values('<cflags>', options))
456 toolset.flags(tool + '.compile.c++', 'OPTIONS', condition, feature.get_values('<cxxflags>', options))
457 toolset.flags(tool + '.compile.fortran', 'OPTIONS', condition, feature.get_values('<fflags>', options))
458 toolset.flags(tool + '.link', 'OPTIONS', condition, feature.get_values('<linkflags>', options))
459
460 # ported from trunk@47281
461 def get_program_files_dir():
462 """ returns the location of the "program files" directory on a windows
463 platform
464 """
465 ProgramFiles = bjam.variable("ProgramFiles")
466 if ProgramFiles:
467 ProgramFiles = ' '.join(ProgramFiles)
468 else:
469 ProgramFiles = "c:\\Program Files"
470 return ProgramFiles
471
472 # ported from trunk@47281
473 def rm_command():
474 return __RM
475
476 # ported from trunk@47281
477 def copy_command():
478 return __CP
479
480 # ported from trunk@47281
481 def variable_setting_command(variable, value):
482 """
483 Returns the command needed to set an environment variable on the current
484 platform. The variable setting persists through all following commands and is
485 visible in the environment seen by subsequently executed commands. In other
486 words, on Unix systems, the variable is exported, which is consistent with the
487 only possible behavior on Windows systems.
488 """
489 assert isinstance(variable, basestring)
490 assert isinstance(value, basestring)
491
492 if os_name() == 'NT':
493 return "set " + variable + "=" + value + os.linesep
494 else:
495 # (todo)
496 # The following does not work on CYGWIN and needs to be fixed. On
497 # CYGWIN the $(nl) variable holds a Windows new-line \r\n sequence that
498 # messes up the executed export command which then reports that the
499 # passed variable name is incorrect. This is most likely due to the
500 # extra \r character getting interpreted as a part of the variable name.
501 #
502 # Several ideas pop to mind on how to fix this:
503 # * One way would be to separate the commands using the ; shell
504 # command separator. This seems like the quickest possible
505 # solution but I do not know whether this would break code on any
506 # platforms I I have no access to.
507 # * Another would be to not use the terminating $(nl) but that would
508 # require updating all the using code so it does not simply
509 # prepend this variable to its own commands.
510 # * I guess the cleanest solution would be to update Boost Jam to
511 # allow explicitly specifying \n & \r characters in its scripts
512 # instead of always relying only on the 'current OS native newline
513 # sequence'.
514 #
515 # Some code found to depend on this behaviour:
516 # * This Boost Build module.
517 # * __test__ rule.
518 # * path-variable-setting-command rule.
519 # * python.jam toolset.
520 # * xsltproc.jam toolset.
521 # * fop.jam toolset.
522 # (todo) (07.07.2008.) (Jurko)
523 #
524 # I think that this works correctly in python -- Steven Watanabe
525 return variable + "=" + value + os.linesep + "export " + variable + os.linesep
526
527 def path_variable_setting_command(variable, paths):
528 """
529 Returns a command to sets a named shell path variable to the given NATIVE
530 paths on the current platform.
531 """
532 assert isinstance(variable, basestring)
533 assert is_iterable_typed(paths, basestring)
534 sep = os.path.pathsep
535 return variable_setting_command(variable, sep.join(paths))
536
537 def prepend_path_variable_command(variable, paths):
538 """
539 Returns a command that prepends the given paths to the named path variable on
540 the current platform.
541 """
542 assert isinstance(variable, basestring)
543 assert is_iterable_typed(paths, basestring)
544 return path_variable_setting_command(
545 variable, paths + [expand_variable(variable)])
546
547
548 def expand_variable(variable):
549 """Produce a string that expands the shell variable."""
550 if os.name == 'nt':
551 return '%{}%'.format(variable)
552 return '${%s}' % variable
553
554
555 def file_creation_command():
556 """
557 Return a command which can create a file. If 'r' is result of invocation, then
558 'r foobar' will create foobar with unspecified content. What happens if file
559 already exists is unspecified.
560 """
561 if os_name() == 'NT':
562 return "echo. > "
563 else:
564 return "touch "
565
566 #FIXME: global variable
567 __mkdir_set = set()
568 __re_windows_drive = re.compile(r'^.*:\$')
569
570 def mkdir(engine, target):
571 assert isinstance(target, basestring)
572 # If dir exists, do not update it. Do this even for $(DOT).
573 bjam.call('NOUPDATE', target)
574
575 global __mkdir_set
576
577 # FIXME: Where is DOT defined?
578 #if $(<) != $(DOT) && ! $($(<)-mkdir):
579 if target != '.' and target not in __mkdir_set:
580 # Cheesy gate to prevent multiple invocations on same dir.
581 __mkdir_set.add(target)
582
583 # Schedule the mkdir build action.
584 engine.set_update_action("common.MkDir", target, [])
585
586 # Prepare a Jam 'dirs' target that can be used to make the build only
587 # construct all the target directories.
588 engine.add_dependency('dirs', target)
589
590 # Recursively create parent directories. $(<:P) = $(<)'s parent & we
591 # recurse until root.
592
593 s = os.path.dirname(target)
594 if os_name() == 'NT':
595 if(__re_windows_drive.match(s)):
596 s = ''
597
598 if s:
599 if s != target:
600 engine.add_dependency(target, s)
601 mkdir(engine, s)
602 else:
603 bjam.call('NOTFILE', s)
604
605 __re_version = re.compile(r'^([^.]+)[.]([^.]+)[.]?([^.]*)')
606
607 def format_name(format, name, target_type, prop_set):
608 """ Given a target, as given to a custom tag rule, returns a string formatted
609 according to the passed format. Format is a list of properties that is
610 represented in the result. For each element of format the corresponding target
611 information is obtained and added to the result string. For all, but the
612 literal, the format value is taken as the as string to prepend to the output
613 to join the item to the rest of the result. If not given "-" is used as a
614 joiner.
615
616 The format options can be:
617
618 <base>[joiner]
619 :: The basename of the target name.
620 <toolset>[joiner]
621 :: The abbreviated toolset tag being used to build the target.
622 <threading>[joiner]
623 :: Indication of a multi-threaded build.
624 <runtime>[joiner]
625 :: Collective tag of the build runtime.
626 <version:/version-feature | X.Y[.Z]/>[joiner]
627 :: Short version tag taken from the given "version-feature"
628 in the build properties. Or if not present, the literal
629 value as the version number.
630 <property:/property-name/>[joiner]
631 :: Direct lookup of the given property-name value in the
632 build properties. /property-name/ is a regular expression.
633 e.g. <property:toolset-.*:flavor> will match every toolset.
634 /otherwise/
635 :: The literal value of the format argument.
636
637 For example this format:
638
639 boost_ <base> <toolset> <threading> <runtime> <version:boost-version>
640
641 Might return:
642
643 boost_thread-vc80-mt-gd-1_33.dll, or
644 boost_regex-vc80-gd-1_33.dll
645
646 The returned name also has the target type specific prefix and suffix which
647 puts it in a ready form to use as the value from a custom tag rule.
648 """
649 if __debug__:
650 from ..build.property_set import PropertySet
651 assert is_iterable_typed(format, basestring)
652 assert isinstance(name, basestring)
653 assert isinstance(target_type, basestring)
654 assert isinstance(prop_set, PropertySet)
655 # assert(isinstance(prop_set, property_set.PropertySet))
656 if type.is_derived(target_type, 'LIB'):
657 result = "" ;
658 for f in format:
659 grist = get_grist(f)
660 if grist == '<base>':
661 result += os.path.basename(name)
662 elif grist == '<toolset>':
663 result += join_tag(get_value(f),
664 toolset_tag(name, target_type, prop_set))
665 elif grist == '<threading>':
666 result += join_tag(get_value(f),
667 threading_tag(name, target_type, prop_set))
668 elif grist == '<runtime>':
669 result += join_tag(get_value(f),
670 runtime_tag(name, target_type, prop_set))
671 elif grist.startswith('<version:'):
672 key = grist[len('<version:'):-1]
673 version = prop_set.get('<' + key + '>')
674 if not version:
675 version = key
676 version = __re_version.match(version)
677 result += join_tag(get_value(f), version[1] + '_' + version[2])
678 elif grist.startswith('<property:'):
679 key = grist[len('<property:'):-1]
680 property_re = re.compile('<(' + key + ')>')
681 p0 = None
682 for prop in prop_set.raw():
683 match = property_re.match(prop)
684 if match:
685 p0 = match[1]
686 break
687 if p0:
688 p = prop_set.get('<' + p0 + '>')
689 if p:
690 assert(len(p) == 1)
691 result += join_tag(ungrist(f), p)
692 else:
693 result += f
694
695 result = b2.build.virtual_target.add_prefix_and_suffix(
696 ''.join(result), target_type, prop_set)
697 return result
698
699 def join_tag(joiner, tag):
700 assert isinstance(joiner, basestring)
701 assert isinstance(tag, basestring)
702 if tag:
703 if not joiner: joiner = '-'
704 return joiner + tag
705 return ''
706
707 __re_toolset_version = re.compile(r"<toolset.*version>(\d+)[.](\d*)")
708
709 def toolset_tag(name, target_type, prop_set):
710 if __debug__:
711 from ..build.property_set import PropertySet
712 assert isinstance(name, basestring)
713 assert isinstance(target_type, basestring)
714 assert isinstance(prop_set, PropertySet)
715 tag = ''
716
717 properties = prop_set.raw()
718 tools = prop_set.get('<toolset>')
719 assert(len(tools) == 1)
720 tools = tools[0]
721 if tools.startswith('borland'): tag += 'bcb'
722 elif tools.startswith('como'): tag += 'como'
723 elif tools.startswith('cw'): tag += 'cw'
724 elif tools.startswith('darwin'): tag += 'xgcc'
725 elif tools.startswith('edg'): tag += 'edg'
726 elif tools.startswith('gcc'):
727 flavor = prop_set.get('<toolset-gcc:flavor>')
728 ''.find
729 if flavor.find('mingw') != -1:
730 tag += 'mgw'
731 else:
732 tag += 'gcc'
733 elif tools == 'intel':
734 if prop_set.get('<toolset-intel:platform>') == ['win']:
735 tag += 'iw'
736 else:
737 tag += 'il'
738 elif tools.startswith('kcc'): tag += 'kcc'
739 elif tools.startswith('kylix'): tag += 'bck'
740 #case metrowerks* : tag += cw ;
741 #case mingw* : tag += mgw ;
742 elif tools.startswith('mipspro'): tag += 'mp'
743 elif tools.startswith('msvc'): tag += 'vc'
744 elif tools.startswith('sun'): tag += 'sw'
745 elif tools.startswith('tru64cxx'): tag += 'tru'
746 elif tools.startswith('vacpp'): tag += 'xlc'
747
748 for prop in properties:
749 match = __re_toolset_version.match(prop)
750 if(match):
751 version = match
752 break
753 version_string = None
754 # For historical reasons, vc6.0 and vc7.0 use different naming.
755 if tag == 'vc':
756 if version.group(1) == '6':
757 # Cancel minor version.
758 version_string = '6'
759 elif version.group(1) == '7' and version.group(2) == '0':
760 version_string = '7'
761
762 # On intel, version is not added, because it does not matter and it's the
763 # version of vc used as backend that matters. Ideally, we'd encode the
764 # backend version but that would break compatibility with V1.
765 elif tag == 'iw':
766 version_string = ''
767
768 # On borland, version is not added for compatibility with V1.
769 elif tag == 'bcb':
770 version_string = ''
771
772 if version_string is None:
773 version = version.group(1) + version.group(2)
774
775 tag += version
776
777 return tag
778
779
780 def threading_tag(name, target_type, prop_set):
781 if __debug__:
782 from ..build.property_set import PropertySet
783 assert isinstance(name, basestring)
784 assert isinstance(target_type, basestring)
785 assert isinstance(prop_set, PropertySet)
786 tag = ''
787 properties = prop_set.raw()
788 if '<threading>multi' in properties: tag = 'mt'
789
790 return tag
791
792
793 def runtime_tag(name, target_type, prop_set ):
794 if __debug__:
795 from ..build.property_set import PropertySet
796 assert isinstance(name, basestring)
797 assert isinstance(target_type, basestring)
798 assert isinstance(prop_set, PropertySet)
799 tag = ''
800
801 properties = prop_set.raw()
802 if '<runtime-link>static' in properties: tag += 's'
803
804 # This is an ugly thing. In V1, there's a code to automatically detect which
805 # properties affect a target. So, if <runtime-debugging> does not affect gcc
806 # toolset, the tag rules won't even see <runtime-debugging>. Similar
807 # functionality in V2 is not implemented yet, so we just check for toolsets
808 # which are known to care about runtime debug.
809 if '<toolset>msvc' in properties \
810 or '<stdlib>stlport' in properties \
811 or '<toolset-intel:platform>win' in properties:
812 if '<runtime-debugging>on' in properties: tag += 'g'
813
814 if '<python-debugging>on' in properties: tag += 'y'
815 if '<variant>debug' in properties: tag += 'd'
816 if '<stdlib>stlport' in properties: tag += 'p'
817 if '<stdlib-stlport:iostream>hostios' in properties: tag += 'n'
818
819 return tag
820
821
822 def init(manager):
823 global __RM, __CP, __IGNORE, __LN
824 engine = manager.engine()
825
826 # register the make() and alias() rules globally
827 import b2.tools.make
828 import b2.build.alias
829
830 windows_hack = ''
831 # ported from trunk@47281
832 if os_name() == 'NT':
833 __RM = 'del /f /q'
834 __CP = 'copy /b'
835 windows_hack = '+ this-file-does-not-exist-A698EE7806899E69'
836 __IGNORE = '2>nul >nul & setlocal'
837 __LN = __CP
838 #if not __LN:
839 # __LN = CP
840 MKDIR = 'if not exist "$(<)\\" mkdir "$(<)"'
841 else:
842 __RM = 'rm -f'
843 __CP = 'cp'
844 __IGNORE = ''
845 __LN = 'ln'
846 MKDIR = 'mkdir -p "$(<)"'
847
848 engine.register_action("common.MkDir", MKDIR + __IGNORE)
849
850 engine.register_action(
851 "common.Clean", __RM + ' "$(>)"', flags=['piecemeal', 'together', 'existing'])
852 engine.register_action("common.copy", '{} "$(>)" {} "$(<)"'.format(__CP, windows_hack))
853 engine.register_action("common.RmTemps", __RM + ' "$(>)" ' + __IGNORE,
854 flags=['quietly', 'updated', 'piecemeal', 'together'])
855
856 engine.register_action("common.hard-link",
857 __RM + ' "$(<)" 2$(NULL_OUT) $(NULL_OUT)' + os.linesep +
858 __LN + ' "$(>)" "$(<)" $(NULL_OUT)')