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