1# Status: minor updates by Steven Watanabe to make gcc work 2# 3# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and 4# distribute this software is granted provided this copyright notice appears in 5# all copies. This software is provided "as is" without express or implied 6# warranty, and with no claim as to its suitability for any purpose. 7 8""" Defines standard features and rules. 9""" 10 11import b2.build.targets as targets 12 13import sys 14from b2.build import feature, property, virtual_target, generators, type, property_set, scanner 15from b2.util.utility import * 16from b2.util import path, regex, bjam_signature, is_iterable_typed 17import b2.tools.types 18from b2.manager import get_manager 19 20 21# Records explicit properties for a variant. 22# The key is the variant name. 23__variant_explicit_properties = {} 24 25def reset (): 26 """ Clear the module state. This is mainly for testing purposes. 27 """ 28 global __variant_explicit_properties 29 30 __variant_explicit_properties = {} 31 32@bjam_signature((["name"], ["parents_or_properties", "*"], ["explicit_properties", "*"])) 33def variant (name, parents_or_properties, explicit_properties = []): 34 """ Declares a new variant. 35 First determines explicit properties for this variant, by 36 refining parents' explicit properties with the passed explicit 37 properties. The result is remembered and will be used if 38 this variant is used as parent. 39 40 Second, determines the full property set for this variant by 41 adding to the explicit properties default values for all properties 42 which neither present nor are symmetric. 43 44 Lastly, makes appropriate value of 'variant' property expand 45 to the full property set. 46 name: Name of the variant 47 parents_or_properties: Specifies parent variants, if 48 'explicit_properties' are given, 49 and explicit_properties otherwise. 50 explicit_properties: Explicit properties. 51 """ 52 parents = [] 53 if not explicit_properties: 54 explicit_properties = parents_or_properties 55 else: 56 parents = parents_or_properties 57 58 inherited = property_set.empty() 59 if parents: 60 61 # If we allow multiple parents, we'd have to to check for conflicts 62 # between base variants, and there was no demand for so to bother. 63 if len (parents) > 1: 64 raise BaseException ("Multiple base variants are not yet supported") 65 66 p = parents[0] 67 # TODO: the check may be stricter 68 if not feature.is_implicit_value (p): 69 raise BaseException ("Invalid base variant '%s'" % p) 70 71 inherited = __variant_explicit_properties[p] 72 73 explicit_properties = property_set.create_with_validation(explicit_properties) 74 explicit_properties = inherited.refine(explicit_properties) 75 76 # Record explicitly specified properties for this variant 77 # We do this after inheriting parents' properties, so that 78 # they affect other variants, derived from this one. 79 __variant_explicit_properties[name] = explicit_properties 80 81 feature.extend('variant', [name]) 82 feature.compose ("<variant>" + name, explicit_properties.all()) 83 84__os_names = """ 85 amiga aix appletv bsd cygwin darwin dos emx freebsd hpux iphone linux netbsd 86 openbsd osf qnx qnxnto sgi solaris sun sunos svr4 sysv ultrix unix unixware 87 vms windows 88""".split() 89 90# Translates from bjam current OS to the os tags used in host-os and target-os, 91# i.e. returns the running host-os. 92# 93def default_host_os(): 94 host_os = os_name() 95 if host_os not in (x.upper() for x in __os_names): 96 if host_os == 'NT': host_os = 'windows' 97 elif host_os == 'AS400': host_os = 'unix' 98 elif host_os == 'MINGW': host_os = 'windows' 99 elif host_os == 'BSDI': host_os = 'bsd' 100 elif host_os == 'COHERENT': host_os = 'unix' 101 elif host_os == 'DRAGONFLYBSD': host_os = 'bsd' 102 elif host_os == 'IRIX': host_os = 'sgi' 103 elif host_os == 'MACOSX': host_os = 'darwin' 104 elif host_os == 'KFREEBSD': host_os = 'freebsd' 105 elif host_os == 'LINUX': host_os = 'linux' 106 elif host_os == 'HAIKU': host_os = 'haiku' 107 else: host_os = 'unix' 108 return host_os.lower() 109 110def register_globals (): 111 """ Registers all features and variants declared by this module. 112 """ 113 114 # This feature is used to determine which OS we're on. 115 # In future, this may become <target-os> and <host-os> 116 # TODO: check this. Compatibility with bjam names? Subfeature for version? 117 os = sys.platform 118 feature.feature ('os', [os], ['propagated', 'link-incompatible']) 119 120 121 # The two OS features define a known set of abstract OS names. The host-os is 122 # the OS under which bjam is running. Even though this should really be a fixed 123 # property we need to list all the values to prevent unknown value errors. Both 124 # set the default value to the current OS to account for the default use case of 125 # building on the target OS. 126 feature.feature('host-os', __os_names) 127 feature.set_default('host-os', default_host_os()) 128 129 feature.feature('target-os', __os_names, ['propagated', 'link-incompatible']) 130 feature.set_default('target-os', default_host_os()) 131 132 feature.feature ('toolset', [], ['implicit', 'propagated' ,'symmetric']) 133 134 feature.feature ('stdlib', ['native'], ['propagated', 'composite']) 135 136 feature.feature ('link', ['shared', 'static'], ['propagated']) 137 feature.feature ('runtime-link', ['shared', 'static'], ['propagated']) 138 feature.feature ('runtime-debugging', ['on', 'off'], ['propagated']) 139 140 141 feature.feature ('optimization', ['off', 'speed', 'space'], ['propagated']) 142 feature.feature ('profiling', ['off', 'on'], ['propagated']) 143 feature.feature ('inlining', ['off', 'on', 'full'], ['propagated']) 144 145 feature.feature ('threading', ['single', 'multi'], ['propagated']) 146 feature.feature ('rtti', ['on', 'off'], ['propagated']) 147 feature.feature ('exception-handling', ['on', 'off'], ['propagated']) 148 149 # Whether there is support for asynchronous EH (e.g. catching SEGVs). 150 feature.feature ('asynch-exceptions', ['off', 'on'], ['propagated']) 151 152 # Whether all extern "C" functions are considered nothrow by default. 153 feature.feature ('extern-c-nothrow', ['off', 'on'], ['propagated']) 154 155 feature.feature ('debug-symbols', ['on', 'off'], ['propagated']) 156 feature.feature ('define', [], ['free']) 157 feature.feature ('undef', [], ['free']) 158 feature.feature ('include', [], ['free', 'path']) #order-sensitive 159 feature.feature ('cflags', [], ['free']) 160 feature.feature ('cxxflags', [], ['free']) 161 feature.feature ('asmflags', [], ['free']) 162 feature.feature ('linkflags', [], ['free']) 163 feature.feature ('archiveflags', [], ['free']) 164 feature.feature ('version', [], ['free']) 165 166 feature.feature ('location-prefix', [], ['free']) 167 168 feature.feature ('action', [], ['free']) 169 170 171 # The following features are incidental, since 172 # in themself they have no effect on build products. 173 # Not making them incidental will result in problems in corner 174 # cases, for example: 175 # 176 # unit-test a : a.cpp : <use>b ; 177 # lib b : a.cpp b ; 178 # 179 # Here, if <use> is not incidental, we'll decide we have two 180 # targets for a.obj with different properties, and will complain. 181 # 182 # Note that making feature incidental does not mean it's ignored. It may 183 # be ignored when creating the virtual target, but the rest of build process 184 # will use them. 185 feature.feature ('use', [], ['free', 'dependency', 'incidental']) 186 feature.feature ('dependency', [], ['free', 'dependency', 'incidental']) 187 feature.feature ('implicit-dependency', [], ['free', 'dependency', 'incidental']) 188 189 feature.feature('warnings', [ 190 'on', # Enable default/"reasonable" warning level for the tool. 191 'all', # Enable all possible warnings issued by the tool. 192 'off'], # Disable all warnings issued by the tool. 193 ['incidental', 'propagated']) 194 195 feature.feature('warnings-as-errors', [ 196 'off', # Do not fail the compilation if there are warnings. 197 'on'], # Fail the compilation if there are warnings. 198 ['incidental', 'propagated']) 199 200 feature.feature('coverage', [ 201 'off', # Disable coverage generation for the tool. 202 'on'], # Enable coverage generation for the tool. 203 ['incidental', 'propagated']) 204 205 feature.feature('c++-template-depth', 206 [str(i) for i in range(64,1024+1,64)] + 207 [str(i) for i in range(20,1000+1,10)] + 208 # Maximum template instantiation depth guaranteed for ANSI/ISO C++ 209 # conforming programs. 210 ['17'], 211 ['incidental', 'optional', 'propagated']) 212 213 feature.feature ('source', [], ['free', 'dependency', 'incidental']) 214 feature.feature ('library', [], ['free', 'dependency', 'incidental']) 215 feature.feature ('file', [], ['free', 'dependency', 'incidental']) 216 feature.feature ('find-shared-library', [], ['free']) #order-sensitive ; 217 feature.feature ('find-static-library', [], ['free']) #order-sensitive ; 218 feature.feature ('library-path', [], ['free', 'path']) #order-sensitive ; 219 # Internal feature. 220 feature.feature ('library-file', [], ['free', 'dependency']) 221 222 feature.feature ('name', [], ['free']) 223 feature.feature ('tag', [], ['free']) 224 feature.feature ('search', [], ['free', 'path']) #order-sensitive ; 225 feature.feature ('location', [], ['free', 'path']) 226 227 feature.feature ('dll-path', [], ['free', 'path']) 228 feature.feature ('hardcode-dll-paths', ['true', 'false'], ['incidental']) 229 230 231 # This is internal feature which holds the paths of all dependency 232 # dynamic libraries. On Windows, it's needed so that we can all 233 # those paths to PATH, when running applications. 234 # On Linux, it's needed to add proper -rpath-link command line options. 235 feature.feature ('xdll-path', [], ['free', 'path']) 236 237 #provides means to specify def-file for windows dlls. 238 feature.feature ('def-file', [], ['free', 'dependency']) 239 240 # This feature is used to allow specific generators to run. 241 # For example, QT tools can only be invoked when QT library 242 # is used. In that case, <allow>qt will be in usage requirement 243 # of the library. 244 feature.feature ('allow', [], ['free']) 245 246 # The addressing model to generate code for. Currently a limited set only 247 # specifying the bit size of pointers. 248 feature.feature('address-model', ['16', '32', '64'], ['propagated', 'optional']) 249 250 # Type of CPU architecture to compile for. 251 feature.feature('architecture', [ 252 # x86 and x86-64 253 'x86', 254 255 # ia64 256 'ia64', 257 258 # Sparc 259 'sparc', 260 261 # RS/6000 & PowerPC 262 'power', 263 264 # MIPS/SGI 265 'mips1', 'mips2', 'mips3', 'mips4', 'mips32', 'mips32r2', 'mips64', 266 267 # HP/PA-RISC 268 'parisc', 269 270 # Advanced RISC Machines 271 'arm', 272 273 # z Systems (aka s390x) 274 's390x', 275 276 # Combined architectures for platforms/toolsets that support building for 277 # multiple architectures at once. "combined" would be the default multi-arch 278 # for the toolset. 279 'combined', 280 'combined-x86-power'], 281 282 ['propagated', 'optional']) 283 284 # The specific instruction set in an architecture to compile. 285 feature.feature('instruction-set', [ 286 # x86 and x86-64 287 'native', 'i486', 'i586', 'i686', 'pentium', 'pentium-mmx', 'pentiumpro', 'pentium2', 'pentium3', 288 'pentium3m', 'pentium-m', 'pentium4', 'pentium4m', 'prescott', 'nocona', 'core2', 'corei7', 'corei7-avx', 'core-avx-i', 289 'conroe', 'conroe-xe', 'conroe-l', 'allendale', 'merom', 'merom-xe', 'kentsfield', 'kentsfield-xe', 'penryn', 'wolfdale', 290 'yorksfield', 'nehalem', 'sandy-bridge', 'ivy-bridge', 'haswell', 'broadwell', 'skylake', 'skylake-avx512', 'cannonlake', 291 'icelake-client', 'icelake-server', 'cascadelake', 'cooperlake', 'tigerlake', 292 'atom', 293 'k6', 'k6-2', 'k6-3', 'athlon', 'athlon-tbird', 'athlon-4', 'athlon-xp', 'athlon-mp', 'k8', 'opteron', 'athlon64', 'athlon-fx', 294 'k8-sse3', 'opteron-sse3', 'athlon64-sse3', 'amdfam10', 'barcelona', 'bdver1', 'bdver2', 'bdver3', 'btver1', 295 'btver2', 'znver1', 'znver2', 296 'winchip-c6', 'winchip2', 297 'c3', 'c3-2', 'c7', 298 299 # ia64 300 'itanium', 'itanium1', 'merced', 'itanium2', 'mckinley', 301 302 # Sparc 303 'v7', 'cypress', 'v8', 'supersparc', 'sparclite', 'hypersparc', 'sparclite86x', 'f930', 'f934', 304 'sparclet', 'tsc701', 'v9', 'ultrasparc', 'ultrasparc3', 305 306 # RS/6000 & PowerPC 307 '401', '403', '405', '405fp', '440', '440fp', '505', '601', '602', 308 '603', '603e', '604', '604e', '620', '630', '740', '7400', 309 '7450', '750', '801', '821', '823', '860', '970', '8540', 310 'power-common', 'ec603e', 'g3', 'g4', 'g5', 'power', 'power2', 311 'power3', 'power4', 'power5', 'powerpc', 'powerpc64', 'rios', 312 'rios1', 'rsc', 'rios2', 'rs64a', 313 314 # MIPS 315 '4kc', '4kp', '5kc', '20kc', 'm4k', 'r2000', 'r3000', 'r3900', 'r4000', 316 'r4100', 'r4300', 'r4400', 'r4600', 'r4650', 317 'r6000', 'r8000', 'rm7000', 'rm9000', 'orion', 'sb1', 'vr4100', 318 'vr4111', 'vr4120', 'vr4130', 'vr4300', 319 'vr5000', 'vr5400', 'vr5500', 320 321 # HP/PA-RISC 322 '700', '7100', '7100lc', '7200', '7300', '8000', 323 324 # Advanced RISC Machines 325 'armv2', 'armv2a', 'armv3', 'armv3m', 'armv4', 'armv4t', 'armv5', 326 'armv5t', 'armv5te', 'armv6', 'armv6j', 'iwmmxt', 'ep9312', 327 328 # z Systems (aka s390x) 329 'z196', 'zEC12', 'z13', 'z13', 'z14', 'z15'], 330 331 ['propagated', 'optional']) 332 333 feature.feature('conditional', [], ['incidental', 'free']) 334 335 # The value of 'no' prevents building of a target. 336 feature.feature('build', ['yes', 'no'], ['optional']) 337 338 # Windows-specific features 339 feature.feature ('user-interface', ['console', 'gui', 'wince', 'native', 'auto'], []) 340 feature.feature ('variant', [], ['implicit', 'composite', 'propagated', 'symmetric']) 341 342 343 variant ('debug', ['<optimization>off', '<debug-symbols>on', '<inlining>off', '<runtime-debugging>on']) 344 variant ('release', ['<optimization>speed', '<debug-symbols>off', '<inlining>full', 345 '<runtime-debugging>off', '<define>NDEBUG']) 346 variant ('profile', ['release'], ['<profiling>on', '<debug-symbols>on']) 347 348 349reset () 350register_globals () 351 352class SearchedLibTarget (virtual_target.AbstractFileTarget): 353 def __init__ (self, name, project, shared, search, action): 354 virtual_target.AbstractFileTarget.__init__ (self, name, 'SEARCHED_LIB', project, action) 355 356 self.shared_ = shared 357 self.search_ = search 358 359 def shared (self): 360 return self.shared_ 361 362 def search (self): 363 return self.search_ 364 365 def actualize_location (self, target): 366 bjam.call("NOTFILE", target) 367 368 def path (self): 369 #FIXME: several functions rely on this not being None 370 return "" 371 372 373class CScanner (scanner.Scanner): 374 def __init__ (self, includes): 375 scanner.Scanner.__init__ (self) 376 377 self.includes_ = [] 378 379 for i in includes: 380 self.includes_.extend(i.split("&&")) 381 382 def pattern (self): 383 return r'#[ \t]*include[ ]*(<(.*)>|"(.*)")' 384 385 def process (self, target, matches, binding): 386 # since it's possible for this function to be called 387 # thousands to millions of times (depending on how many 388 # header files there are), as such, there are some 389 # optimizations that have been used here. Anything that 390 # is slightly out of the ordinary for Python code 391 # has been commented. 392 angle = [] 393 quoted = [] 394 for match in matches: 395 if '<' in match: 396 angle.append(match.strip('<>')) 397 elif '"' in match: 398 quoted.append(match.strip('"')) 399 400 g = id(self) 401 b = os.path.normpath(os.path.dirname(binding[0])) 402 403 # Attach binding of including file to included targets. 404 # When target is directly created from virtual target 405 # this extra information is unnecessary. But in other 406 # cases, it allows to distinguish between two headers of the 407 # same name included from different places. 408 # We don't need this extra information for angle includes, 409 # since they should not depend on including file (we can't 410 # get literal "." in include path). 411 # Note: string interpolation is slightly faster 412 # than .format() 413 g2 = '<%s#%s>' % (g, b) 414 g = "<%s>" % g 415 416 angle = [g + x for x in angle] 417 quoted = [g2 + x for x in quoted] 418 419 all = angle + quoted 420 bjam.call("mark-included", target, all) 421 422 # each include in self.includes_ looks something like this: 423 # <include>path/to/somewhere 424 # calling get_value(include) is super slow, 425 # calling .replace('<include>', '') is much faster 426 # however, i[9:] is the fastest way of stripping off the "<include>" 427 # substring. 428 include_paths = [i[9:] for i in self.includes_] 429 430 engine = get_manager().engine() 431 engine.set_target_variable(angle, "SEARCH", include_paths) 432 engine.set_target_variable(quoted, "SEARCH", [b] + include_paths) 433 434 # Just propagate current scanner to includes, in a hope 435 # that includes do not change scanners. 436 get_manager().scanners().propagate(self, all) 437 438scanner.register (CScanner, 'include') 439type.set_scanner ('CPP', CScanner) 440type.set_scanner ('C', CScanner) 441type.set_scanner('H', CScanner) 442type.set_scanner('HPP', CScanner) 443 444# Ported to trunk@47077 445class LibGenerator (generators.Generator): 446 """ The generator class for libraries (target type LIB). Depending on properties it will 447 request building of the approapriate specific type -- SHARED_LIB, STATIC_LIB or 448 SHARED_LIB. 449 """ 450 451 def __init__(self, id, composing = True, source_types = [], target_types_and_names = ['LIB'], requirements = []): 452 generators.Generator.__init__(self, id, composing, source_types, target_types_and_names, requirements) 453 454 def run(self, project, name, prop_set, sources): 455 assert isinstance(project, targets.ProjectTarget) 456 assert isinstance(name, basestring) or name is None 457 assert isinstance(prop_set, property_set.PropertySet) 458 assert is_iterable_typed(sources, virtual_target.VirtualTarget) 459 # The lib generator is composing, and can be only invoked with 460 # explicit name. This check is present in generator.run (and so in 461 # builtin.LinkingGenerator), but duplicate it here to avoid doing 462 # extra work. 463 if name: 464 properties = prop_set.raw() 465 # Determine the needed target type 466 actual_type = None 467 properties_grist = get_grist(properties) 468 if '<source>' not in properties_grist and \ 469 ('<search>' in properties_grist or '<name>' in properties_grist): 470 actual_type = 'SEARCHED_LIB' 471 elif '<file>' in properties_grist: 472 # The generator for 473 actual_type = 'LIB' 474 elif '<link>shared' in properties: 475 actual_type = 'SHARED_LIB' 476 else: 477 actual_type = 'STATIC_LIB' 478 479 prop_set = prop_set.add_raw(['<main-target-type>LIB']) 480 481 # Construct the target. 482 return generators.construct(project, name, actual_type, prop_set, sources) 483 484 def viable_source_types(self): 485 return ['*'] 486 487generators.register(LibGenerator("builtin.lib-generator")) 488 489generators.override("builtin.prebuilt", "builtin.lib-generator") 490 491def lib(names, sources=[], requirements=[], default_build=[], usage_requirements=[]): 492 """The implementation of the 'lib' rule. Beyond standard syntax that rule allows 493 simplified: 'lib a b c ;'.""" 494 assert is_iterable_typed(names, basestring) 495 assert is_iterable_typed(sources, basestring) 496 assert is_iterable_typed(requirements, basestring) 497 assert is_iterable_typed(default_build, basestring) 498 assert is_iterable_typed(usage_requirements, basestring) 499 if len(names) > 1: 500 if any(r.startswith('<name>') for r in requirements): 501 get_manager().errors()("When several names are given to the 'lib' rule\n" + 502 "it is not allowed to specify the <name> feature.") 503 504 if sources: 505 get_manager().errors()("When several names are given to the 'lib' rule\n" + 506 "it is not allowed to specify sources.") 507 508 project = get_manager().projects().current() 509 result = [] 510 511 for name in names: 512 r = requirements[:] 513 514 # Support " lib a ; " and " lib a b c ; " syntax. 515 if not sources and not any(r.startswith("<name>") for r in requirements) \ 516 and not any(r.startswith("<file") for r in requirements): 517 r.append("<name>" + name) 518 519 result.append(targets.create_typed_metatarget(name, "LIB", sources, 520 r, 521 default_build, 522 usage_requirements)) 523 return result 524 525get_manager().projects().add_rule("lib", lib) 526 527 528# Updated to trunk@47077 529class SearchedLibGenerator (generators.Generator): 530 def __init__ (self, id = 'SearchedLibGenerator', composing = False, source_types = [], target_types_and_names = ['SEARCHED_LIB'], requirements = []): 531 # TODO: the comment below looks strange. There are no requirements! 532 # The requirements cause the generators to be tried *only* when we're building 533 # lib target and there's 'search' feature. This seems ugly --- all we want 534 # is make sure SearchedLibGenerator is not invoked deep in transformation 535 # search. 536 generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) 537 538 def run(self, project, name, prop_set, sources): 539 assert isinstance(project, targets.ProjectTarget) 540 assert isinstance(name, basestring) or name is None 541 assert isinstance(prop_set, property_set.PropertySet) 542 assert is_iterable_typed(sources, virtual_target.VirtualTarget) 543 544 if not name: 545 return None 546 547 # If name is empty, it means we're called not from top-level. 548 # In this case, we just fail immediately, because SearchedLibGenerator 549 # cannot be used to produce intermediate targets. 550 551 properties = prop_set.raw () 552 shared = '<link>shared' in properties 553 554 a = virtual_target.NullAction (project.manager(), prop_set) 555 556 real_name = feature.get_values ('<name>', properties) 557 if real_name: 558 real_name = real_name[0] 559 else: 560 real_name = name 561 search = feature.get_values('<search>', properties) 562 usage_requirements = property_set.create(['<xdll-path>' + p for p in search]) 563 t = SearchedLibTarget(real_name, project, shared, search, a) 564 565 # We return sources for a simple reason. If there's 566 # lib png : z : <name>png ; 567 # the 'z' target should be returned, so that apps linking to 568 # 'png' will link to 'z', too. 569 return(usage_requirements, [b2.manager.get_manager().virtual_targets().register(t)] + sources) 570 571generators.register (SearchedLibGenerator ()) 572 573class PrebuiltLibGenerator(generators.Generator): 574 575 def __init__(self, id, composing, source_types, target_types_and_names, requirements): 576 generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) 577 578 def run(self, project, name, properties, sources): 579 assert isinstance(project, targets.ProjectTarget) 580 assert isinstance(name, basestring) 581 assert isinstance(properties, property_set.PropertySet) 582 assert is_iterable_typed(sources, virtual_target.VirtualTarget) 583 584 f = properties.get("file") 585 return f + sources 586 587generators.register(PrebuiltLibGenerator("builtin.prebuilt", False, [], 588 ["LIB"], ["<file>"])) 589 590generators.override("builtin.prebuilt", "builtin.lib-generator") 591 592 593class CompileAction (virtual_target.Action): 594 def __init__ (self, manager, sources, action_name, prop_set): 595 virtual_target.Action.__init__ (self, manager, sources, action_name, prop_set) 596 597 def adjust_properties (self, prop_set): 598 """ For all virtual targets for the same dependency graph as self, 599 i.e. which belong to the same main target, add their directories 600 to include path. 601 """ 602 assert isinstance(prop_set, property_set.PropertySet) 603 s = self.targets () [0].creating_subvariant () 604 605 return prop_set.add_raw (s.implicit_includes ('include', 'H')) 606 607class CCompilingGenerator (generators.Generator): 608 """ Declare a special compiler generator. 609 The only thing it does is changing the type used to represent 610 'action' in the constructed dependency graph to 'CompileAction'. 611 That class in turn adds additional include paths to handle a case 612 when a source file includes headers which are generated themselves. 613 """ 614 def __init__ (self, id, composing, source_types, target_types_and_names, requirements): 615 # TODO: (PF) What to do with optional_properties? It seemed that, in the bjam version, the arguments are wrong. 616 generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) 617 618 def action_class (self): 619 return CompileAction 620 621def register_c_compiler (id, source_types, target_types, requirements, optional_properties = []): 622 g = CCompilingGenerator (id, False, source_types, target_types, requirements + optional_properties) 623 return generators.register (g) 624 625 626class LinkingGenerator (generators.Generator): 627 """ The generator class for handling EXE and SHARED_LIB creation. 628 """ 629 def __init__ (self, id, composing, source_types, target_types_and_names, requirements): 630 generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) 631 632 def run (self, project, name, prop_set, sources): 633 assert isinstance(project, targets.ProjectTarget) 634 assert isinstance(name, basestring) or name is None 635 assert isinstance(prop_set, property_set.PropertySet) 636 assert is_iterable_typed(sources, virtual_target.VirtualTarget) 637 638 # create a copy since sources is being modified 639 sources = list(sources) 640 sources.extend(prop_set.get('<library>')) 641 642 # Add <library-path> properties for all searched libraries 643 extra = [] 644 for s in sources: 645 if s.type () == 'SEARCHED_LIB': 646 search = s.search() 647 extra.extend(property.Property('<library-path>', sp) for sp in search) 648 649 # It's possible that we have libraries in sources which did not came 650 # from 'lib' target. For example, libraries which are specified 651 # just as filenames as sources. We don't have xdll-path properties 652 # for such target, but still need to add proper dll-path properties. 653 extra_xdll_path = [] 654 for s in sources: 655 if type.is_derived (s.type (), 'SHARED_LIB') and not s.action (): 656 # Unfortunately, we don't have a good way to find the path 657 # to a file, so use this nasty approach. 658 p = s.project() 659 location = path.root(s.name(), p.get('source-location')[0]) 660 extra_xdll_path.append(os.path.dirname(location)) 661 662 # Hardcode DLL paths only when linking executables. 663 # Pros: do not need to relink libraries when installing. 664 # Cons: "standalone" libraries (plugins, python extensions) can not 665 # hardcode paths to dependent libraries. 666 if prop_set.get('<hardcode-dll-paths>') == ['true'] \ 667 and type.is_derived(self.target_types_ [0], 'EXE'): 668 xdll_path = prop_set.get('<xdll-path>') 669 extra.extend(property.Property('<dll-path>', sp) \ 670 for sp in extra_xdll_path) 671 extra.extend(property.Property('<dll-path>', sp) \ 672 for sp in xdll_path) 673 674 if extra: 675 prop_set = prop_set.add_raw (extra) 676 result = generators.Generator.run(self, project, name, prop_set, sources) 677 678 if result: 679 ur = self.extra_usage_requirements(result, prop_set) 680 ur = ur.add(property_set.create(['<xdll-path>' + p for p in extra_xdll_path])) 681 else: 682 return None 683 return (ur, result) 684 685 def extra_usage_requirements (self, created_targets, prop_set): 686 assert is_iterable_typed(created_targets, virtual_target.VirtualTarget) 687 assert isinstance(prop_set, property_set.PropertySet) 688 689 result = property_set.empty () 690 extra = [] 691 692 # Add appropriate <xdll-path> usage requirements. 693 raw = prop_set.raw () 694 if '<link>shared' in raw: 695 paths = [] 696 697 # TODO: is it safe to use the current directory? I think we should use 698 # another mechanism to allow this to be run from anywhere. 699 pwd = os.getcwd() 700 701 for t in created_targets: 702 if type.is_derived(t.type(), 'SHARED_LIB'): 703 paths.append(path.root(path.make(t.path()), pwd)) 704 705 extra += replace_grist(paths, '<xdll-path>') 706 707 # We need to pass <xdll-path> features that we've got from sources, 708 # because if shared library is built, exe which uses it must know paths 709 # to other shared libraries this one depends on, to be able to find them 710 # all at runtime. 711 712 # Just pass all features in property_set, it's theorically possible 713 # that we'll propagate <xdll-path> features explicitly specified by 714 # the user, but then the user's to blame for using internal feature. 715 values = prop_set.get('<xdll-path>') 716 extra += replace_grist(values, '<xdll-path>') 717 718 if extra: 719 result = property_set.create(extra) 720 721 return result 722 723 def generated_targets (self, sources, prop_set, project, name): 724 assert is_iterable_typed(sources, virtual_target.VirtualTarget) 725 assert isinstance(prop_set, property_set.PropertySet) 726 assert isinstance(project, targets.ProjectTarget) 727 assert isinstance(name, basestring) 728 # sources to pass to inherited rule 729 sources2 = [] 730 # sources which are libraries 731 libraries = [] 732 733 # Searched libraries are not passed as argument to linker 734 # but via some option. So, we pass them to the action 735 # via property. 736 fsa = [] 737 fst = [] 738 for s in sources: 739 if type.is_derived(s.type(), 'SEARCHED_LIB'): 740 n = s.name() 741 if s.shared(): 742 fsa.append(n) 743 744 else: 745 fst.append(n) 746 747 else: 748 sources2.append(s) 749 750 add = [] 751 if fsa: 752 add.append("<find-shared-library>" + '&&'.join(fsa)) 753 if fst: 754 add.append("<find-static-library>" + '&&'.join(fst)) 755 756 spawn = generators.Generator.generated_targets(self, sources2, prop_set.add_raw(add), project, name) 757 return spawn 758 759 760def register_linker(id, source_types, target_types, requirements): 761 g = LinkingGenerator(id, True, source_types, target_types, requirements) 762 generators.register(g) 763 764class ArchiveGenerator (generators.Generator): 765 """ The generator class for handling STATIC_LIB creation. 766 """ 767 def __init__ (self, id, composing, source_types, target_types_and_names, requirements): 768 generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) 769 770 def run (self, project, name, prop_set, sources): 771 assert isinstance(project, targets.ProjectTarget) 772 assert isinstance(name, basestring) or name is None 773 assert isinstance(prop_set, property_set.PropertySet) 774 assert is_iterable_typed(sources, virtual_target.VirtualTarget) 775 776 # create a copy since this modifies the sources list 777 sources = list(sources) 778 sources.extend(prop_set.get('<library>')) 779 780 result = generators.Generator.run (self, project, name, prop_set, sources) 781 782 usage_requirements = [] 783 link = prop_set.get('<link>') 784 if 'static' in link: 785 for t in sources: 786 if type.is_derived(t.type(), 'LIB'): 787 usage_requirements.append(property.Property('<library>', t)) 788 789 usage_requirements = property_set.create(usage_requirements) 790 791 return usage_requirements, result 792 793 794def register_archiver(id, source_types, target_types, requirements): 795 g = ArchiveGenerator(id, True, source_types, target_types, requirements) 796 generators.register(g) 797 798class DummyGenerator(generators.Generator): 799 """Generator that accepts everything and produces nothing. Useful as a general 800 fallback for toolset-specific actions like PCH generation. 801 """ 802 def run (self, project, name, prop_set, sources): 803 return (property_set.empty(), []) 804 805 806get_manager().projects().add_rule("variant", variant) 807 808import stage 809import symlink 810import message 811