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 13import re 14import bjam 15import os 16import os.path 17import sys 18 19# for some reason this fails on Python 2.7(r27:82525) 20# from b2.build import virtual_target 21import b2.build.virtual_target 22from b2.build import feature, type 23from b2.util.utility import * 24from b2.util import path, is_iterable_typed 25 26__re__before_first_dash = re.compile ('([^-]*)-') 27 28def 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 74reset() 75 76def shared_library_path_variable(): 77 return __shared_library_path_variable 78 79# ported from trunk@47174 80class 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 171def 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 285def 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 323def 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 350def get_absolute_tool_path(command): 351 """ 352 Given an invocation command, 353 return the absolute path to the command. This works even if command 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 369def 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 404def 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 425def 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 command 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 437def 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.asm to the value of <asmflags> in options 445 - OPTIONS for compile.fortran to the value of <fflags> in options 446 - OPTIONs for link to the value of <linkflags> in options 447 """ 448 from b2.build import toolset 449 450 assert isinstance(tool, basestring) 451 assert is_iterable_typed(condition, basestring) 452 assert command and isinstance(command, basestring) 453 assert is_iterable_typed(options, basestring) 454 toolset.flags(tool, 'CONFIG_COMMAND', condition, [command]) 455 toolset.flags(tool + '.compile', 'OPTIONS', condition, feature.get_values('<compileflags>', options)) 456 toolset.flags(tool + '.compile.c', 'OPTIONS', condition, feature.get_values('<cflags>', options)) 457 toolset.flags(tool + '.compile.c++', 'OPTIONS', condition, feature.get_values('<cxxflags>', options)) 458 toolset.flags(tool + '.compile.asm', 'OPTIONS', condition, feature.get_values('<asmflags>', options)) 459 toolset.flags(tool + '.compile.fortran', 'OPTIONS', condition, feature.get_values('<fflags>', options)) 460 toolset.flags(tool + '.link', 'OPTIONS', condition, feature.get_values('<linkflags>', options)) 461 462# ported from trunk@47281 463def get_program_files_dir(): 464 """ returns the location of the "program files" directory on a windows 465 platform 466 """ 467 ProgramFiles = bjam.variable("ProgramFiles") 468 if ProgramFiles: 469 ProgramFiles = ' '.join(ProgramFiles) 470 else: 471 ProgramFiles = "c:\\Program Files" 472 return ProgramFiles 473 474# ported from trunk@47281 475def rm_command(): 476 return __RM 477 478# ported from trunk@47281 479def copy_command(): 480 return __CP 481 482# ported from trunk@47281 483def variable_setting_command(variable, value): 484 """ 485 Returns the command needed to set an environment variable on the current 486 platform. The variable setting persists through all following commands and is 487 visible in the environment seen by subsequently executed commands. In other 488 words, on Unix systems, the variable is exported, which is consistent with the 489 only possible behavior on Windows systems. 490 """ 491 assert isinstance(variable, basestring) 492 assert isinstance(value, basestring) 493 494 if os_name() == 'NT': 495 return "set " + variable + "=" + value + os.linesep 496 else: 497 # (todo) 498 # The following does not work on CYGWIN and needs to be fixed. On 499 # CYGWIN the $(nl) variable holds a Windows new-line \r\n sequence that 500 # messes up the executed export command which then reports that the 501 # passed variable name is incorrect. This is most likely due to the 502 # extra \r character getting interpreted as a part of the variable name. 503 # 504 # Several ideas pop to mind on how to fix this: 505 # * One way would be to separate the commands using the ; shell 506 # command separator. This seems like the quickest possible 507 # solution but I do not know whether this would break code on any 508 # platforms I I have no access to. 509 # * Another would be to not use the terminating $(nl) but that would 510 # require updating all the using code so it does not simply 511 # prepend this variable to its own commands. 512 # * I guess the cleanest solution would be to update Boost Jam to 513 # allow explicitly specifying \n & \r characters in its scripts 514 # instead of always relying only on the 'current OS native newline 515 # sequence'. 516 # 517 # Some code found to depend on this behaviour: 518 # * This Boost Build module. 519 # * __test__ rule. 520 # * path-variable-setting-command rule. 521 # * python.jam toolset. 522 # * xsltproc.jam toolset. 523 # * fop.jam toolset. 524 # (todo) (07.07.2008.) (Jurko) 525 # 526 # I think that this works correctly in python -- Steven Watanabe 527 return variable + "=" + value + os.linesep + "export " + variable + os.linesep 528 529def path_variable_setting_command(variable, paths): 530 """ 531 Returns a command to sets a named shell path variable to the given NATIVE 532 paths on the current platform. 533 """ 534 assert isinstance(variable, basestring) 535 assert is_iterable_typed(paths, basestring) 536 sep = os.path.pathsep 537 return variable_setting_command(variable, sep.join(paths)) 538 539def prepend_path_variable_command(variable, paths): 540 """ 541 Returns a command that prepends the given paths to the named path variable on 542 the current platform. 543 """ 544 assert isinstance(variable, basestring) 545 assert is_iterable_typed(paths, basestring) 546 return path_variable_setting_command( 547 variable, paths + [expand_variable(variable)]) 548 549 550def expand_variable(variable): 551 """Produce a string that expands the shell variable.""" 552 if os.name == 'nt': 553 return '%{}%'.format(variable) 554 return '${%s}' % variable 555 556 557def file_creation_command(): 558 """ 559 Return a command which can create a file. If 'r' is result of invocation, then 560 'r foobar' will create foobar with unspecified content. What happens if file 561 already exists is unspecified. 562 """ 563 if os_name() == 'NT': 564 return "echo. > " 565 else: 566 return "touch " 567 568#FIXME: global variable 569__mkdir_set = set() 570__re_windows_drive = re.compile(r'^.*:\$') 571 572def mkdir(engine, target): 573 assert isinstance(target, basestring) 574 # If dir exists, do not update it. Do this even for $(DOT). 575 bjam.call('NOUPDATE', target) 576 577 global __mkdir_set 578 579 # FIXME: Where is DOT defined? 580 #if $(<) != $(DOT) && ! $($(<)-mkdir): 581 if target != '.' and target not in __mkdir_set: 582 # Cheesy gate to prevent multiple invocations on same dir. 583 __mkdir_set.add(target) 584 585 # Schedule the mkdir build action. 586 engine.set_update_action("common.MkDir", target, []) 587 588 # Prepare a Jam 'dirs' target that can be used to make the build only 589 # construct all the target directories. 590 engine.add_dependency('dirs', target) 591 592 # Recursively create parent directories. $(<:P) = $(<)'s parent & we 593 # recurse until root. 594 595 s = os.path.dirname(target) 596 if os_name() == 'NT': 597 if(__re_windows_drive.match(s)): 598 s = '' 599 600 if s: 601 if s != target: 602 engine.add_dependency(target, s) 603 mkdir(engine, s) 604 else: 605 bjam.call('NOTFILE', s) 606 607__re_version = re.compile(r'^([^.]+)[.]([^.]+)[.]?([^.]*)') 608 609def format_name(format, name, target_type, prop_set): 610 """ Given a target, as given to a custom tag rule, returns a string formatted 611 according to the passed format. Format is a list of properties that is 612 represented in the result. For each element of format the corresponding target 613 information is obtained and added to the result string. For all, but the 614 literal, the format value is taken as the as string to prepend to the output 615 to join the item to the rest of the result. If not given "-" is used as a 616 joiner. 617 618 The format options can be: 619 620 <base>[joiner] 621 :: The basename of the target name. 622 <toolset>[joiner] 623 :: The abbreviated toolset tag being used to build the target. 624 <threading>[joiner] 625 :: Indication of a multi-threaded build. 626 <runtime>[joiner] 627 :: Collective tag of the build runtime. 628 <version:/version-feature | X.Y[.Z]/>[joiner] 629 :: Short version tag taken from the given "version-feature" 630 in the build properties. Or if not present, the literal 631 value as the version number. 632 <property:/property-name/>[joiner] 633 :: Direct lookup of the given property-name value in the 634 build properties. /property-name/ is a regular expression. 635 e.g. <property:toolset-.*:flavor> will match every toolset. 636 /otherwise/ 637 :: The literal value of the format argument. 638 639 For example this format: 640 641 boost_ <base> <toolset> <threading> <runtime> <version:boost-version> 642 643 Might return: 644 645 boost_thread-vc80-mt-gd-1_33.dll, or 646 boost_regex-vc80-gd-1_33.dll 647 648 The returned name also has the target type specific prefix and suffix which 649 puts it in a ready form to use as the value from a custom tag rule. 650 """ 651 if __debug__: 652 from ..build.property_set import PropertySet 653 assert is_iterable_typed(format, basestring) 654 assert isinstance(name, basestring) 655 assert isinstance(target_type, basestring) 656 assert isinstance(prop_set, PropertySet) 657 # assert(isinstance(prop_set, property_set.PropertySet)) 658 if type.is_derived(target_type, 'LIB'): 659 result = "" ; 660 for f in format: 661 grist = get_grist(f) 662 if grist == '<base>': 663 result += os.path.basename(name) 664 elif grist == '<toolset>': 665 result += join_tag(get_value(f), 666 toolset_tag(name, target_type, prop_set)) 667 elif grist == '<threading>': 668 result += join_tag(get_value(f), 669 threading_tag(name, target_type, prop_set)) 670 elif grist == '<runtime>': 671 result += join_tag(get_value(f), 672 runtime_tag(name, target_type, prop_set)) 673 elif grist.startswith('<version:'): 674 key = grist[len('<version:'):-1] 675 version = prop_set.get('<' + key + '>') 676 if not version: 677 version = key 678 version = __re_version.match(version) 679 result += join_tag(get_value(f), version[1] + '_' + version[2]) 680 elif grist.startswith('<property:'): 681 key = grist[len('<property:'):-1] 682 property_re = re.compile('<(' + key + ')>') 683 p0 = None 684 for prop in prop_set.raw(): 685 match = property_re.match(prop) 686 if match: 687 p0 = match[1] 688 break 689 if p0: 690 p = prop_set.get('<' + p0 + '>') 691 if p: 692 assert(len(p) == 1) 693 result += join_tag(ungrist(f), p) 694 else: 695 result += f 696 697 result = b2.build.virtual_target.add_prefix_and_suffix( 698 ''.join(result), target_type, prop_set) 699 return result 700 701def join_tag(joiner, tag): 702 assert isinstance(joiner, basestring) 703 assert isinstance(tag, basestring) 704 if tag: 705 if not joiner: joiner = '-' 706 return joiner + tag 707 return '' 708 709__re_toolset_version = re.compile(r"<toolset.*version>(\d+)[.](\d*)") 710 711def toolset_tag(name, target_type, prop_set): 712 if __debug__: 713 from ..build.property_set import PropertySet 714 assert isinstance(name, basestring) 715 assert isinstance(target_type, basestring) 716 assert isinstance(prop_set, PropertySet) 717 tag = '' 718 719 properties = prop_set.raw() 720 tools = prop_set.get('<toolset>') 721 assert(len(tools) == 1) 722 tools = tools[0] 723 if tools.startswith('borland'): tag += 'bcb' 724 elif tools.startswith('como'): tag += 'como' 725 elif tools.startswith('cw'): tag += 'cw' 726 elif tools.startswith('darwin'): tag += 'xgcc' 727 elif tools.startswith('edg'): tag += 'edg' 728 elif tools.startswith('gcc'): 729 flavor = prop_set.get('<toolset-gcc:flavor>') 730 ''.find 731 if flavor.find('mingw') != -1: 732 tag += 'mgw' 733 else: 734 tag += 'gcc' 735 elif tools == 'intel': 736 if prop_set.get('<toolset-intel:platform>') == ['win']: 737 tag += 'iw' 738 else: 739 tag += 'il' 740 elif tools.startswith('kcc'): tag += 'kcc' 741 elif tools.startswith('kylix'): tag += 'bck' 742 #case metrowerks* : tag += cw ; 743 #case mingw* : tag += mgw ; 744 elif tools.startswith('mipspro'): tag += 'mp' 745 elif tools.startswith('msvc'): tag += 'vc' 746 elif tools.startswith('sun'): tag += 'sw' 747 elif tools.startswith('tru64cxx'): tag += 'tru' 748 elif tools.startswith('vacpp'): tag += 'xlc' 749 750 for prop in properties: 751 match = __re_toolset_version.match(prop) 752 if(match): 753 version = match 754 break 755 version_string = None 756 # For historical reasons, vc6.0 and vc7.0 use different naming. 757 if tag == 'vc': 758 if version.group(1) == '6': 759 # Cancel minor version. 760 version_string = '6' 761 elif version.group(1) == '7' and version.group(2) == '0': 762 version_string = '7' 763 764 # On intel, version is not added, because it does not matter and it's the 765 # version of vc used as backend that matters. Ideally, we'd encode the 766 # backend version but that would break compatibility with V1. 767 elif tag == 'iw': 768 version_string = '' 769 770 # On borland, version is not added for compatibility with V1. 771 elif tag == 'bcb': 772 version_string = '' 773 774 if version_string is None: 775 version = version.group(1) + version.group(2) 776 777 tag += version 778 779 return tag 780 781 782def threading_tag(name, target_type, prop_set): 783 if __debug__: 784 from ..build.property_set import PropertySet 785 assert isinstance(name, basestring) 786 assert isinstance(target_type, basestring) 787 assert isinstance(prop_set, PropertySet) 788 tag = '' 789 properties = prop_set.raw() 790 if '<threading>multi' in properties: tag = 'mt' 791 792 return tag 793 794 795def runtime_tag(name, target_type, prop_set ): 796 if __debug__: 797 from ..build.property_set import PropertySet 798 assert isinstance(name, basestring) 799 assert isinstance(target_type, basestring) 800 assert isinstance(prop_set, PropertySet) 801 tag = '' 802 803 properties = prop_set.raw() 804 if '<runtime-link>static' in properties: tag += 's' 805 806 # This is an ugly thing. In V1, there's a code to automatically detect which 807 # properties affect a target. So, if <runtime-debugging> does not affect gcc 808 # toolset, the tag rules won't even see <runtime-debugging>. Similar 809 # functionality in V2 is not implemented yet, so we just check for toolsets 810 # which are known to care about runtime debug. 811 if '<toolset>msvc' in properties \ 812 or '<stdlib>stlport' in properties \ 813 or '<toolset-intel:platform>win' in properties: 814 if '<runtime-debugging>on' in properties: tag += 'g' 815 816 if '<python-debugging>on' in properties: tag += 'y' 817 if '<variant>debug' in properties: tag += 'd' 818 if '<stdlib>stlport' in properties: tag += 'p' 819 if '<stdlib-stlport:iostream>hostios' in properties: tag += 'n' 820 821 return tag 822 823 824def init(manager): 825 global __RM, __CP, __IGNORE, __LN 826 engine = manager.engine() 827 828 # register the make() and alias() rules globally 829 import b2.tools.make 830 import b2.build.alias 831 832 windows_hack = '' 833 # ported from trunk@47281 834 if os_name() == 'NT': 835 __RM = 'del /f /q' 836 __CP = 'copy /b' 837 windows_hack = '+ this-file-does-not-exist-A698EE7806899E69' 838 __IGNORE = '2>nul >nul & setlocal' 839 __LN = __CP 840 #if not __LN: 841 # __LN = CP 842 MKDIR = 'if not exist "$(<)\\" mkdir "$(<)"' 843 else: 844 __RM = 'rm -f' 845 __CP = 'cp' 846 __IGNORE = '' 847 __LN = 'ln' 848 MKDIR = 'mkdir -p "$(<)"' 849 850 engine.register_action("common.MkDir", MKDIR + __IGNORE) 851 852 engine.register_action( 853 "common.Clean", __RM + ' "$(>)"', flags=['piecemeal', 'together', 'existing']) 854 engine.register_action("common.copy", '{} "$(>)" {} "$(<)"'.format(__CP, windows_hack)) 855 engine.register_action("common.RmTemps", __RM + ' "$(>)" ' + __IGNORE, 856 flags=['quietly', 'updated', 'piecemeal', 'together']) 857 858 engine.register_action("common.hard-link", 859 __RM + ' "$(<)" 2$(NULL_OUT) $(NULL_OUT)' + os.linesep + 860 __LN + ' "$(>)" "$(<)" $(NULL_OUT)') 861