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
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 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', ['on', 'off'], ['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('c++-template-depth',
201        [str(i) for i in range(64,1024+1,64)] +
202        [str(i) for i in range(20,1000+1,10)] +
203        #   Maximum template instantiation depth guaranteed for ANSI/ISO C++
204        # conforming programs.
205        ['17'],
206        ['incidental', 'optional', 'propagated'])
207
208    feature.feature ('source', [], ['free', 'dependency', 'incidental'])
209    feature.feature ('library', [], ['free', 'dependency', 'incidental'])
210    feature.feature ('file', [], ['free', 'dependency', 'incidental'])
211    feature.feature ('find-shared-library', [], ['free']) #order-sensitive ;
212    feature.feature ('find-static-library', [], ['free']) #order-sensitive ;
213    feature.feature ('library-path', [], ['free', 'path']) #order-sensitive ;
214    # Internal feature.
215    feature.feature ('library-file', [], ['free', 'dependency'])
216
217    feature.feature ('name', [], ['free'])
218    feature.feature ('tag', [], ['free'])
219    feature.feature ('search', [], ['free', 'path']) #order-sensitive ;
220    feature.feature ('location', [], ['free', 'path'])
221
222    feature.feature ('dll-path', [], ['free', 'path'])
223    feature.feature ('hardcode-dll-paths', ['true', 'false'], ['incidental'])
224
225
226    # This is internal feature which holds the paths of all dependency
227    # dynamic libraries. On Windows, it's needed so that we can all
228    # those paths to PATH, when running applications.
229    # On Linux, it's needed to add proper -rpath-link command line options.
230    feature.feature ('xdll-path', [], ['free', 'path'])
231
232    #provides means to specify def-file for windows dlls.
233    feature.feature ('def-file', [], ['free', 'dependency'])
234
235    # This feature is used to allow specific generators to run.
236    # For example, QT tools can only be invoked when QT library
237    # is used. In that case, <allow>qt will be in usage requirement
238    # of the library.
239    feature.feature ('allow', [], ['free'])
240
241    # The addressing model to generate code for. Currently a limited set only
242    # specifying the bit size of pointers.
243    feature.feature('address-model', ['16', '32', '64'], ['propagated', 'optional'])
244
245    # Type of CPU architecture to compile for.
246    feature.feature('architecture', [
247        # x86 and x86-64
248        'x86',
249
250        # ia64
251        'ia64',
252
253        # Sparc
254        'sparc',
255
256        # RS/6000 & PowerPC
257        'power',
258
259        # MIPS/SGI
260        'mips1', 'mips2', 'mips3', 'mips4', 'mips32', 'mips32r2', 'mips64',
261
262        # HP/PA-RISC
263        'parisc',
264
265        # Advanced RISC Machines
266        'arm',
267
268        # Combined architectures for platforms/toolsets that support building for
269        # multiple architectures at once. "combined" would be the default multi-arch
270        # for the toolset.
271        'combined',
272        'combined-x86-power'],
273
274        ['propagated', 'optional'])
275
276    # The specific instruction set in an architecture to compile.
277    feature.feature('instruction-set', [
278        # x86 and x86-64
279        'native', 'i486', 'i586', 'i686', 'pentium', 'pentium-mmx', 'pentiumpro', 'pentium2', 'pentium3',
280        'pentium3m', 'pentium-m', 'pentium4', 'pentium4m', 'prescott', 'nocona', 'core2', 'corei7', 'corei7-avx', 'core-avx-i',
281        'conroe', 'conroe-xe', 'conroe-l', 'allendale', 'merom', 'merom-xe', 'kentsfield', 'kentsfield-xe', 'penryn', 'wolfdale',
282        'yorksfield', 'nehalem', 'sandy-bridge', 'ivy-bridge', 'haswell', 'k6', 'k6-2', 'k6-3', 'athlon', 'athlon-tbird', 'athlon-4', 'athlon-xp',
283        'athlon-mp', 'k8', 'opteron', 'athlon64', 'athlon-fx', 'k8-sse3', 'opteron-sse3', 'athlon64-sse3', 'amdfam10', 'barcelona',
284        'bdver1', 'bdver2', 'bdver3', 'btver1', 'btver2', 'winchip-c6', 'winchip2', 'c3', 'c3-2', 'atom',
285
286        # ia64
287        'itanium', 'itanium1', 'merced', 'itanium2', 'mckinley',
288
289        # Sparc
290        'v7', 'cypress', 'v8', 'supersparc', 'sparclite', 'hypersparc', 'sparclite86x', 'f930', 'f934',
291        'sparclet', 'tsc701', 'v9', 'ultrasparc', 'ultrasparc3',
292
293        # RS/6000 & PowerPC
294        '401', '403', '405', '405fp', '440', '440fp', '505', '601', '602',
295        '603', '603e', '604', '604e', '620', '630', '740', '7400',
296        '7450', '750', '801', '821', '823', '860', '970', '8540',
297        'power-common', 'ec603e', 'g3', 'g4', 'g5', 'power', 'power2',
298        'power3', 'power4', 'power5', 'powerpc', 'powerpc64', 'rios',
299        'rios1', 'rsc', 'rios2', 'rs64a',
300
301        # MIPS
302        '4kc', '4kp', '5kc', '20kc', 'm4k', 'r2000', 'r3000', 'r3900', 'r4000',
303        'r4100', 'r4300', 'r4400', 'r4600', 'r4650',
304        'r6000', 'r8000', 'rm7000', 'rm9000', 'orion', 'sb1', 'vr4100',
305        'vr4111', 'vr4120', 'vr4130', 'vr4300',
306        'vr5000', 'vr5400', 'vr5500',
307
308        # HP/PA-RISC
309        '700', '7100', '7100lc', '7200', '7300', '8000',
310
311        # Advanced RISC Machines
312        'armv2', 'armv2a', 'armv3', 'armv3m', 'armv4', 'armv4t', 'armv5',
313        'armv5t', 'armv5te', 'armv6', 'armv6j', 'iwmmxt', 'ep9312'],
314
315        ['propagated', 'optional'])
316
317    feature.feature('conditional', [], ['incidental', 'free'])
318
319    # The value of 'no' prevents building of a target.
320    feature.feature('build', ['yes', 'no'], ['optional'])
321
322    # Windows-specific features
323    feature.feature ('user-interface', ['console', 'gui', 'wince', 'native', 'auto'], [])
324    feature.feature ('variant', [], ['implicit', 'composite', 'propagated', 'symmetric'])
325
326
327    variant ('debug', ['<optimization>off', '<debug-symbols>on', '<inlining>off', '<runtime-debugging>on'])
328    variant ('release', ['<optimization>speed', '<debug-symbols>off', '<inlining>full',
329                         '<runtime-debugging>off', '<define>NDEBUG'])
330    variant ('profile', ['release'], ['<profiling>on', '<debug-symbols>on'])
331
332
333reset ()
334register_globals ()
335
336class SearchedLibTarget (virtual_target.AbstractFileTarget):
337    def __init__ (self, name, project, shared, search, action):
338        virtual_target.AbstractFileTarget.__init__ (self, name, 'SEARCHED_LIB', project, action)
339
340        self.shared_ = shared
341        self.search_ = search
342
343    def shared (self):
344        return self.shared_
345
346    def search (self):
347        return self.search_
348
349    def actualize_location (self, target):
350        bjam.call("NOTFILE", target)
351
352    def path (self):
353        #FIXME: several functions rely on this not being None
354        return ""
355
356
357class CScanner (scanner.Scanner):
358    def __init__ (self, includes):
359        scanner.Scanner.__init__ (self)
360
361        self.includes_ = []
362
363        for i in includes:
364            self.includes_.extend(i.split("&&"))
365
366    def pattern (self):
367        return r'#[ \t]*include[ ]*(<(.*)>|"(.*)")'
368
369    def process (self, target, matches, binding):
370
371        angle = regex.transform (matches, "<(.*)>")
372        quoted = regex.transform (matches, '"(.*)"')
373
374        g = str(id(self))
375        b = os.path.normpath(os.path.dirname(binding[0]))
376
377        # Attach binding of including file to included targets.
378        # When target is directly created from virtual target
379        # this extra information is unnecessary. But in other
380        # cases, it allows to distinguish between two headers of the
381        # same name included from different places.
382        # We don't need this extra information for angle includes,
383        # since they should not depend on including file (we can't
384        # get literal "." in include path).
385        g2 = g + "#" + b
386
387        g = "<" + g + ">"
388        g2 = "<" + g2 + ">"
389        angle = [g + x for x in angle]
390        quoted = [g2 + x for x in quoted]
391
392        all = angle + quoted
393        bjam.call("mark-included", target, all)
394
395        engine = get_manager().engine()
396        engine.set_target_variable(angle, "SEARCH", get_value(self.includes_))
397        engine.set_target_variable(quoted, "SEARCH", [b] + get_value(self.includes_))
398
399        # Just propagate current scanner to includes, in a hope
400        # that includes do not change scanners.
401        get_manager().scanners().propagate(self, angle + quoted)
402
403scanner.register (CScanner, 'include')
404type.set_scanner ('CPP', CScanner)
405type.set_scanner ('C', CScanner)
406
407# Ported to trunk@47077
408class LibGenerator (generators.Generator):
409    """ The generator class for libraries (target type LIB). Depending on properties it will
410        request building of the approapriate specific type -- SHARED_LIB, STATIC_LIB or
411        SHARED_LIB.
412    """
413
414    def __init__(self, id, composing = True, source_types = [], target_types_and_names = ['LIB'], requirements = []):
415        generators.Generator.__init__(self, id, composing, source_types, target_types_and_names, requirements)
416
417    def run(self, project, name, prop_set, sources):
418
419        # The lib generator is composing, and can be only invoked with
420        # explicit name. This check is present in generator.run (and so in
421        # builtin.LinkingGenerator), but duplicate it here to avoid doing
422        # extra work.
423        if name:
424            properties = prop_set.raw()
425            # Determine the needed target type
426            actual_type = None
427            properties_grist = get_grist(properties)
428            if '<source>' not in properties_grist  and \
429               ('<search>' in properties_grist or '<name>' in properties_grist):
430                actual_type = 'SEARCHED_LIB'
431            elif '<file>' in properties_grist:
432                # The generator for
433                actual_type = 'LIB'
434            elif '<link>shared' in properties:
435                actual_type = 'SHARED_LIB'
436            else:
437                actual_type = 'STATIC_LIB'
438
439            prop_set = prop_set.add_raw(['<main-target-type>LIB'])
440
441            # Construct the target.
442            return generators.construct(project, name, actual_type, prop_set, sources)
443
444    def viable_source_types(self):
445        return ['*']
446
447generators.register(LibGenerator("builtin.lib-generator"))
448
449generators.override("builtin.prebuilt", "builtin.lib-generator")
450
451def lib(names, sources=[], requirements=[], default_build=[], usage_requirements=[]):
452    """The implementation of the 'lib' rule. Beyond standard syntax that rule allows
453    simplified: 'lib a b c ;'."""
454
455    if len(names) > 1:
456        if any(r.startswith('<name>') for r in requirements):
457            get_manager().errors()("When several names are given to the 'lib' rule\n" +
458                                   "it is not allowed to specify the <name> feature.")
459
460        if sources:
461            get_manager().errors()("When several names are given to the 'lib' rule\n" +
462                                   "it is not allowed to specify sources.")
463
464    project = get_manager().projects().current()
465    result = []
466
467    for name in names:
468        r = requirements[:]
469
470        # Support " lib a ; " and " lib a b c ; " syntax.
471        if not sources and not any(r.startswith("<name>") for r in requirements) \
472           and not any(r.startswith("<file") for r in requirements):
473            r.append("<name>" + name)
474
475        result.append(targets.create_typed_metatarget(name, "LIB", sources,
476                                                      r,
477                                                      default_build,
478                                                      usage_requirements))
479    return result
480
481get_manager().projects().add_rule("lib", lib)
482
483
484# Updated to trunk@47077
485class SearchedLibGenerator (generators.Generator):
486    def __init__ (self, id = 'SearchedLibGenerator', composing = False, source_types = [], target_types_and_names = ['SEARCHED_LIB'], requirements = []):
487        # TODO: the comment below looks strange. There are no requirements!
488        # The requirements cause the generators to be tried *only* when we're building
489        # lib target and there's 'search' feature. This seems ugly --- all we want
490        # is make sure SearchedLibGenerator is not invoked deep in transformation
491        # search.
492        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
493
494    def run(self, project, name, prop_set, sources):
495
496        if not name:
497            return None
498
499        # If name is empty, it means we're called not from top-level.
500        # In this case, we just fail immediately, because SearchedLibGenerator
501        # cannot be used to produce intermediate targets.
502
503        properties = prop_set.raw ()
504        shared = '<link>shared' in properties
505
506        a = virtual_target.NullAction (project.manager(), prop_set)
507
508        real_name = feature.get_values ('<name>', properties)
509        if real_name:
510            real_name = real_name[0]
511        else:
512            real_name = name
513        search = feature.get_values('<search>', properties)
514        usage_requirements = property_set.create(['<xdll-path>' + p for p in search])
515        t = SearchedLibTarget(real_name, project, shared, search, a)
516
517        # We return sources for a simple reason. If there's
518        #    lib png : z : <name>png ;
519        # the 'z' target should be returned, so that apps linking to
520        # 'png' will link to 'z', too.
521        return(usage_requirements, [b2.manager.get_manager().virtual_targets().register(t)] + sources)
522
523generators.register (SearchedLibGenerator ())
524
525class PrebuiltLibGenerator(generators.Generator):
526
527    def __init__(self, id, composing, source_types, target_types_and_names, requirements):
528        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
529
530    def run(self, project, name, properties, sources):
531        f = properties.get("file")
532        return f + sources
533
534generators.register(PrebuiltLibGenerator("builtin.prebuilt", False, [],
535                                         ["LIB"], ["<file>"]))
536
537generators.override("builtin.prebuilt", "builtin.lib-generator")
538
539
540class CompileAction (virtual_target.Action):
541    def __init__ (self, manager, sources, action_name, prop_set):
542        virtual_target.Action.__init__ (self, manager, sources, action_name, prop_set)
543
544    def adjust_properties (self, prop_set):
545        """ For all virtual targets for the same dependency graph as self,
546            i.e. which belong to the same main target, add their directories
547            to include path.
548        """
549        s = self.targets () [0].creating_subvariant ()
550
551        return prop_set.add_raw (s.implicit_includes ('include', 'H'))
552
553class CCompilingGenerator (generators.Generator):
554    """ Declare a special compiler generator.
555        The only thing it does is changing the type used to represent
556        'action' in the constructed dependency graph to 'CompileAction'.
557        That class in turn adds additional include paths to handle a case
558        when a source file includes headers which are generated themselfs.
559    """
560    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
561        # TODO: (PF) What to do with optional_properties? It seemed that, in the bjam version, the arguments are wrong.
562        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
563
564    def action_class (self):
565        return CompileAction
566
567def register_c_compiler (id, source_types, target_types, requirements, optional_properties = []):
568    g = CCompilingGenerator (id, False, source_types, target_types, requirements + optional_properties)
569    return generators.register (g)
570
571
572class LinkingGenerator (generators.Generator):
573    """ The generator class for handling EXE and SHARED_LIB creation.
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, prop_set, sources):
579
580        sources.extend(prop_set.get('<library>'))
581
582        # Add <library-path> properties for all searched libraries
583        extra = []
584        for s in sources:
585            if s.type () == 'SEARCHED_LIB':
586                search = s.search()
587                extra.extend(property.Property('<library-path>', sp) for sp in search)
588
589        # It's possible that we have libraries in sources which did not came
590        # from 'lib' target. For example, libraries which are specified
591        # just as filenames as sources. We don't have xdll-path properties
592        # for such target, but still need to add proper dll-path properties.
593        extra_xdll_path = []
594        for s in sources:
595                if type.is_derived (s.type (), 'SHARED_LIB') and not s.action ():
596                    # Unfortunately, we don't have a good way to find the path
597                    # to a file, so use this nasty approach.
598                    p = s.project()
599                    location = path.root(s.name(), p.get('source-location')[0])
600                    extra_xdll_path.append(os.path.dirname(location))
601
602        # Hardcode DLL paths only when linking executables.
603        # Pros: do not need to relink libraries when installing.
604        # Cons: "standalone" libraries (plugins, python extensions) can not
605        # hardcode paths to dependent libraries.
606        if prop_set.get('<hardcode-dll-paths>') == ['true'] \
607              and type.is_derived(self.target_types_ [0], 'EXE'):
608                xdll_path = prop_set.get('<xdll-path>')
609                extra.extend(property.Property('<dll-path>', sp) \
610                     for sp in extra_xdll_path)
611                extra.extend(property.Property('<dll-path>', sp) \
612                     for sp in xdll_path)
613
614        if extra:
615            prop_set = prop_set.add_raw (extra)
616        result = generators.Generator.run(self, project, name, prop_set, sources)
617
618        if result:
619            ur = self.extra_usage_requirements(result, prop_set)
620            ur = ur.add(property_set.create(['<xdll-path>' + p for p in extra_xdll_path]))
621        else:
622            return None
623        return (ur, result)
624
625    def extra_usage_requirements (self, created_targets, prop_set):
626
627        result = property_set.empty ()
628        extra = []
629
630        # Add appropriate <xdll-path> usage requirements.
631        raw = prop_set.raw ()
632        if '<link>shared' in raw:
633            paths = []
634
635            # TODO: is it safe to use the current directory? I think we should use
636            # another mechanism to allow this to be run from anywhere.
637            pwd = os.getcwd()
638
639            for t in created_targets:
640                if type.is_derived(t.type(), 'SHARED_LIB'):
641                    paths.append(path.root(path.make(t.path()), pwd))
642
643            extra += replace_grist(paths, '<xdll-path>')
644
645        # We need to pass <xdll-path> features that we've got from sources,
646        # because if shared library is built, exe which uses it must know paths
647        # to other shared libraries this one depends on, to be able to find them
648        # all at runtime.
649
650        # Just pass all features in property_set, it's theorically possible
651        # that we'll propagate <xdll-path> features explicitly specified by
652        # the user, but then the user's to blaim for using internal feature.
653        values = prop_set.get('<xdll-path>')
654        extra += replace_grist(values, '<xdll-path>')
655
656        if extra:
657            result = property_set.create(extra)
658
659        return result
660
661    def generated_targets (self, sources, prop_set, project, name):
662
663        # sources to pass to inherited rule
664        sources2 = []
665        # sources which are libraries
666        libraries  = []
667
668        # Searched libraries are not passed as argument to linker
669        # but via some option. So, we pass them to the action
670        # via property.
671        fsa = []
672        fst = []
673        for s in sources:
674            if type.is_derived(s.type(), 'SEARCHED_LIB'):
675                n = s.name()
676                if s.shared():
677                    fsa.append(n)
678
679                else:
680                    fst.append(n)
681
682            else:
683                sources2.append(s)
684
685        add = []
686        if fsa:
687            add.append("<find-shared-library>" + '&&'.join(fsa))
688        if fst:
689            add.append("<find-static-library>" + '&&'.join(fst))
690
691        spawn = generators.Generator.generated_targets(self, sources2, prop_set.add_raw(add), project, name)
692        return spawn
693
694
695def register_linker(id, source_types, target_types, requirements):
696    g = LinkingGenerator(id, True, source_types, target_types, requirements)
697    generators.register(g)
698
699class ArchiveGenerator (generators.Generator):
700    """ The generator class for handling STATIC_LIB creation.
701    """
702    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
703        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
704
705    def run (self, project, name, prop_set, sources):
706        sources += prop_set.get ('<library>')
707
708        result = generators.Generator.run (self, project, name, prop_set, sources)
709
710        return result
711
712
713def register_archiver(id, source_types, target_types, requirements):
714    g = ArchiveGenerator(id, True, source_types, target_types, requirements)
715    generators.register(g)
716
717class DummyGenerator(generators.Generator):
718     """Generator that accepts everything and produces nothing. Useful as a general
719     fallback for toolset-specific actions like PCH generation.
720     """
721     def run (self, project, name, prop_set, sources):
722       return (property_set.empty(), [])
723
724
725get_manager().projects().add_rule("variant", variant)
726
727import stage
728import symlink
729import message
730