1"""SCons.Tool
2
3SCons tool selection.
4
5This looks for modules that define a callable object that can modify
6a construction environment as appropriate for a given tool (or tool
7chain).
8
9Note that because this subsystem just *selects* a callable that can
10modify a construction environment, it's possible for people to define
11their own "tool specification" in an arbitrary callable function.  No
12one needs to use or tie in to this subsystem in order to roll their own
13tool definition.
14"""
15
16#
17# Copyright (c) 2001 - 2016 The SCons Foundation
18#
19# Permission is hereby granted, free of charge, to any person obtaining
20# a copy of this software and associated documentation files (the
21# "Software"), to deal in the Software without restriction, including
22# without limitation the rights to use, copy, modify, merge, publish,
23# distribute, sublicense, and/or sell copies of the Software, and to
24# permit persons to whom the Software is furnished to do so, subject to
25# the following conditions:
26#
27# The above copyright notice and this permission notice shall be included
28# in all copies or substantial portions of the Software.
29#
30# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
31# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
32# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
34# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
35# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
36# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37
38__revision__ = "src/engine/SCons/Tool/__init__.py rel_2.5.0:3543:937e55cd78f7 2016/04/09 11:29:54 bdbaddog"
39
40import imp
41import sys
42import re
43import os
44import shutil
45
46import SCons.Builder
47import SCons.Errors
48import SCons.Node.FS
49import SCons.Scanner
50import SCons.Scanner.C
51import SCons.Scanner.D
52import SCons.Scanner.LaTeX
53import SCons.Scanner.Prog
54import SCons.Scanner.SWIG
55
56DefaultToolpath=[]
57
58CScanner = SCons.Scanner.C.CScanner()
59DScanner = SCons.Scanner.D.DScanner()
60LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
61PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
62ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
63SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
64SWIGScanner = SCons.Scanner.SWIG.SWIGScanner()
65
66CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
67             ".h", ".H", ".hxx", ".hpp", ".hh",
68             ".F", ".fpp", ".FPP",
69             ".m", ".mm",
70             ".S", ".spp", ".SPP", ".sx"]
71
72DSuffixes = ['.d']
73
74IDLSuffixes = [".idl", ".IDL"]
75
76LaTeXSuffixes = [".tex", ".ltx", ".latex"]
77
78SWIGSuffixes = ['.i']
79
80for suffix in CSuffixes:
81    SourceFileScanner.add_scanner(suffix, CScanner)
82
83for suffix in DSuffixes:
84    SourceFileScanner.add_scanner(suffix, DScanner)
85
86for suffix in SWIGSuffixes:
87    SourceFileScanner.add_scanner(suffix, SWIGScanner)
88
89# FIXME: what should be done here? Two scanners scan the same extensions,
90# but look for different files, e.g., "picture.eps" vs. "picture.pdf".
91# The builders for DVI and PDF explicitly reference their scanners
92# I think that means this is not needed???
93for suffix in LaTeXSuffixes:
94    SourceFileScanner.add_scanner(suffix, LaTeXScanner)
95    SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner)
96
97class Tool(object):
98    def __init__(self, name, toolpath=[], **kw):
99        self.name = name
100        self.toolpath = toolpath + DefaultToolpath
101        # remember these so we can merge them into the call
102        self.init_kw = kw
103
104        module = self._tool_module()
105        self.generate = module.generate
106        self.exists = module.exists
107        if hasattr(module, 'options'):
108            self.options = module.options
109
110    def _tool_module(self):
111        # TODO: Interchange zipimport with normal initialization for better error reporting
112        oldpythonpath = sys.path
113        sys.path = self.toolpath + sys.path
114
115        try:
116            try:
117                file, path, desc = imp.find_module(self.name, self.toolpath)
118                try:
119                    return imp.load_module(self.name, file, path, desc)
120                finally:
121                    if file:
122                        file.close()
123            except ImportError, e:
124                if str(e)!="No module named %s"%self.name:
125                    raise SCons.Errors.EnvironmentError(e)
126                try:
127                    import zipimport
128                except ImportError:
129                    pass
130                else:
131                    for aPath in self.toolpath:
132                        try:
133                            importer = zipimport.zipimporter(aPath)
134                            return importer.load_module(self.name)
135                        except ImportError, e:
136                            pass
137        finally:
138            sys.path = oldpythonpath
139
140        full_name = 'SCons.Tool.' + self.name
141        try:
142            return sys.modules[full_name]
143        except KeyError:
144            try:
145                smpath = sys.modules['SCons.Tool'].__path__
146                try:
147                    file, path, desc = imp.find_module(self.name, smpath)
148                    module = imp.load_module(full_name, file, path, desc)
149                    setattr(SCons.Tool, self.name, module)
150                    if file:
151                        file.close()
152                    return module
153                except ImportError, e:
154                    if str(e)!="No module named %s"%self.name:
155                        raise SCons.Errors.EnvironmentError(e)
156                    try:
157                        import zipimport
158                        importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] )
159                        module = importer.load_module(full_name)
160                        setattr(SCons.Tool, self.name, module)
161                        return module
162                    except ImportError, e:
163                        m = "No tool named '%s': %s" % (self.name, e)
164                        raise SCons.Errors.EnvironmentError(m)
165            except ImportError, e:
166                m = "No tool named '%s': %s" % (self.name, e)
167                raise SCons.Errors.EnvironmentError(m)
168
169    def __call__(self, env, *args, **kw):
170        if self.init_kw is not None:
171            # Merge call kws into init kws;
172            # but don't bash self.init_kw.
173            if kw is not None:
174                call_kw = kw
175                kw = self.init_kw.copy()
176                kw.update(call_kw)
177            else:
178                kw = self.init_kw
179        env.Append(TOOLS = [ self.name ])
180        if hasattr(self, 'options'):
181            import SCons.Variables
182            if 'options' not in env:
183                from SCons.Script import ARGUMENTS
184                env['options']=SCons.Variables.Variables(args=ARGUMENTS)
185            opts=env['options']
186
187            self.options(opts)
188            opts.Update(env)
189
190        self.generate(env, *args, **kw)
191
192    def __str__(self):
193        return self.name
194
195##########################################################################
196#  Create common executable program / library / object builders
197
198def createProgBuilder(env):
199    """This is a utility function that creates the Program
200    Builder in an Environment if it is not there already.
201
202    If it is already there, we return the existing one.
203    """
204
205    try:
206        program = env['BUILDERS']['Program']
207    except KeyError:
208        import SCons.Defaults
209        program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction,
210                                        emitter = '$PROGEMITTER',
211                                        prefix = '$PROGPREFIX',
212                                        suffix = '$PROGSUFFIX',
213                                        src_suffix = '$OBJSUFFIX',
214                                        src_builder = 'Object',
215                                        target_scanner = ProgramScanner)
216        env['BUILDERS']['Program'] = program
217
218    return program
219
220def createStaticLibBuilder(env):
221    """This is a utility function that creates the StaticLibrary
222    Builder in an Environment if it is not there already.
223
224    If it is already there, we return the existing one.
225    """
226
227    try:
228        static_lib = env['BUILDERS']['StaticLibrary']
229    except KeyError:
230        action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
231        if env.Detect('ranlib'):
232            ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
233            action_list.append(ranlib_action)
234
235        static_lib = SCons.Builder.Builder(action = action_list,
236                                           emitter = '$LIBEMITTER',
237                                           prefix = '$LIBPREFIX',
238                                           suffix = '$LIBSUFFIX',
239                                           src_suffix = '$OBJSUFFIX',
240                                           src_builder = 'StaticObject')
241        env['BUILDERS']['StaticLibrary'] = static_lib
242        env['BUILDERS']['Library'] = static_lib
243
244    return static_lib
245
246def _call_linker_cb(env, callback, args, result = None):
247    """Returns the result of env['LINKCALLBACKS'][callback](*args)
248    if env['LINKCALLBACKS'] is a dictionary and env['LINKCALLBACKS'][callback]
249    is callable. If these conditions are not met, return the value provided as
250    the *result* argument. This function is mainly used for generating library
251    info such as versioned suffixes, symlink maps, sonames etc. by delegating
252    the core job to callbacks configured by current linker tool"""
253
254    Verbose = False
255
256    if Verbose:
257        print '_call_linker_cb: args=%r' % args
258        print '_call_linker_cb: callback=%r' % callback
259
260    try:
261        cbfun = env['LINKCALLBACKS'][callback]
262    except (KeyError, TypeError):
263        if Verbose:
264            print '_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' % callback
265        pass
266    else:
267        if Verbose:
268            print '_call_linker_cb: env["LINKCALLBACKS"][%r] found' % callback
269            print '_call_linker_cb: env["LINKCALLBACKS"][%r]=%r' % (callback, cbfun)
270        if(callable(cbfun)):
271            if Verbose:
272                print '_call_linker_cb: env["LINKCALLBACKS"][%r] is callable' % callback
273            result = cbfun(env, *args)
274    return result
275
276def _call_env_subst(env, string, *args, **kw):
277    kw2 = {}
278    for k in ('raw', 'target', 'source', 'conv', 'executor'):
279        try: kw2[k] = kw[k]
280        except KeyError: pass
281    return env.subst(string, *args, **kw2)
282
283class _ShLibInfoSupport(object):
284    def get_libtype(self):
285        return 'ShLib'
286    def get_lib_prefix(self, env, *args, **kw):
287        return _call_env_subst(env,'$SHLIBPREFIX', *args, **kw)
288    def get_lib_suffix(self, env, *args, **kw):
289        return _call_env_subst(env,'$SHLIBSUFFIX', *args, **kw)
290    def get_lib_version(self, env, *args, **kw):
291        return _call_env_subst(env,'$SHLIBVERSION', *args, **kw)
292    def get_lib_noversionsymlinks(self, env, *args, **kw):
293        return _call_env_subst(env,'$SHLIBNOVERSIONSYMLINKS', *args, **kw)
294
295class _LdModInfoSupport(object):
296    def get_libtype(self):
297        return 'LdMod'
298    def get_lib_prefix(self, env, *args, **kw):
299        return _call_env_subst(env,'$LDMODULEPREFIX', *args, **kw)
300    def get_lib_suffix(self, env, *args, **kw):
301        return _call_env_subst(env,'$LDMODULESUFFIX', *args, **kw)
302    def get_lib_version(self, env, *args, **kw):
303        return _call_env_subst(env,'$LDMODULEVERSION', *args, **kw)
304    def get_lib_noversionsymlinks(self, env, *args, **kw):
305        return _call_env_subst(env,'$LDMODULENOVERSIONSYMLINKS', *args, **kw)
306
307class _ImpLibInfoSupport(object):
308    def get_libtype(self):
309        return 'ImpLib'
310    def get_lib_prefix(self, env, *args, **kw):
311        return _call_env_subst(env,'$IMPLIBPREFIX', *args, **kw)
312    def get_lib_suffix(self, env, *args, **kw):
313        return _call_env_subst(env,'$IMPLIBSUFFIX', *args, **kw)
314    def get_lib_version(self, env, *args, **kw):
315        version = _call_env_subst(env,'$IMPLIBVERSION', *args, **kw)
316        if not version:
317            try: lt = kw['implib_libtype']
318            except KeyError: pass
319            else:
320                if lt == 'ShLib':
321                    version = _call_env_subst(env,'$SHLIBVERSION', *args, **kw)
322                elif lt == 'LdMod':
323                    version = _call_env_subst(env,'$LDMODULEVERSION', *args, **kw)
324        return version
325    def get_lib_noversionsymlinks(self, env, *args, **kw):
326        disable = None
327        try: env['IMPLIBNOVERSIONSYMLINKS']
328        except KeyError:
329            try: lt = kw['implib_libtype']
330            except KeyError: pass
331            else:
332                if lt == 'ShLib':
333                    disable = _call_env_subst(env,'$SHLIBNOVERSIONSYMLINKS', *args, **kw)
334                elif lt == 'LdMod':
335                    disable = _call_env_subst(env,'$LDMODULENOVERSIONSYMLINKS', *args, **kw)
336        else:
337            disable = _call_env_subst(env,'$IMPLIBNOVERSIONSYMLINKS', *args, **kw)
338        return disable
339
340class _LibInfoGeneratorBase(object):
341    """Generator base class for library-related info such as suffixes for
342    versioned libraries, symlink maps, sonames etc. It handles commonities
343    of SharedLibrary and LoadableModule
344    """
345    _support_classes = { 'ShLib'  : _ShLibInfoSupport,
346                         'LdMod'  : _LdModInfoSupport,
347                         'ImpLib' : _ImpLibInfoSupport }
348    def __init__(self, libtype, infoname):
349        self.set_libtype(libtype)
350        self.set_infoname(infoname)
351
352    def set_libtype(self, libtype):
353        try:
354            support_class = self._support_classes[libtype]
355        except KeyError:
356            raise ValueError('unsupported libtype %r' % libtype)
357        self._support = support_class()
358
359    def get_libtype(self):
360        return self._support.get_libtype()
361
362    def set_infoname(self, infoname):
363        self.infoname = infoname
364
365    def get_infoname(self):
366        return self.infoname
367
368    def get_lib_prefix(self, env, *args, **kw):
369        return self._support.get_lib_prefix(env,*args,**kw)
370
371    def get_lib_suffix(self, env, *args, **kw):
372        return self._support.get_lib_suffix(env,*args,**kw)
373
374    def get_lib_version(self, env, *args, **kw):
375        return self._support.get_lib_version(env,*args,**kw)
376
377    def get_lib_noversionsymlinks(self, env, *args, **kw):
378        return self._support.get_lib_noversionsymlinks(env,*args,**kw)
379
380    # Returns name of generator linker callback that shall be used to generate
381    # our info for a versioned library. For example, if our libtype is 'ShLib'
382    # and infoname is 'Prefix', it would return 'VersionedShLibPrefix'.
383    def get_versioned_lib_info_generator(self, **kw):
384        try: libtype = kw['generator_libtype']
385        except KeyError: libtype = self.get_libtype()
386        infoname = self.get_infoname()
387        return 'Versioned%s%s' % (libtype, infoname)
388
389    def generate_versioned_lib_info(self, env, args, result = None, **kw):
390        callback = self.get_versioned_lib_info_generator(**kw)
391        return _call_linker_cb(env, callback, args, result)
392
393class _LibPrefixGenerator(_LibInfoGeneratorBase):
394    """Library prefix generator, used as target_prefix in SharedLibrary and
395    LoadableModule builders"""
396    def __init__(self, libtype):
397        super(_LibPrefixGenerator, self).__init__(libtype, 'Prefix')
398
399    def __call__(self, env, sources = None, **kw):
400        Verbose = False
401
402        if sources and 'source' not in kw:
403            kw2 = kw.copy()
404            kw2['source'] = sources
405        else:
406            kw2 = kw
407
408        prefix = self.get_lib_prefix(env,**kw2)
409        if Verbose:
410            print "_LibPrefixGenerator: input prefix=%r" % prefix
411
412        version = self.get_lib_version(env, **kw2)
413        if Verbose:
414            print "_LibPrefixGenerator: version=%r" % version
415
416        if version:
417            prefix = self.generate_versioned_lib_info(env, [prefix, version], prefix, **kw2)
418
419        if Verbose:
420            print "_LibPrefixGenerator: return prefix=%r" % prefix
421        return prefix
422
423ShLibPrefixGenerator  = _LibPrefixGenerator('ShLib')
424LdModPrefixGenerator  = _LibPrefixGenerator('LdMod')
425ImpLibPrefixGenerator = _LibPrefixGenerator('ImpLib')
426
427class _LibSuffixGenerator(_LibInfoGeneratorBase):
428    """Library suffix generator, used as target_suffix in SharedLibrary and
429    LoadableModule builders"""
430    def __init__(self, libtype):
431        super(_LibSuffixGenerator, self).__init__(libtype, 'Suffix')
432
433    def __call__(self, env, sources = None, **kw):
434        Verbose = False
435
436        if sources and 'source' not in kw:
437            kw2 = kw.copy()
438            kw2['source'] = sources
439        else:
440            kw2 = kw
441
442        suffix = self.get_lib_suffix(env, **kw2)
443        if Verbose:
444            print "_LibSuffixGenerator: input suffix=%r" % suffix
445
446        version = self.get_lib_version(env, **kw2)
447        if Verbose:
448            print "_LibSuffixGenerator: version=%r" % version
449
450        if version:
451            suffix = self.generate_versioned_lib_info(env, [suffix, version], suffix, **kw2)
452
453        if Verbose:
454            print "_LibSuffixGenerator: return suffix=%r" % suffix
455        return suffix
456
457ShLibSuffixGenerator  = _LibSuffixGenerator('ShLib')
458LdModSuffixGenerator  = _LibSuffixGenerator('LdMod')
459ImpLibSuffixGenerator = _LibSuffixGenerator('ImpLib')
460
461class _LibSymlinkGenerator(_LibInfoGeneratorBase):
462    """Library symlink map generator. It generates a list of symlinks that
463    should be created by SharedLibrary or LoadableModule builders"""
464    def __init__(self, libtype):
465        super(_LibSymlinkGenerator, self).__init__(libtype, 'Symlinks')
466
467    def __call__(self, env, libnode, **kw):
468        Verbose = False
469
470        if libnode and 'target' not in kw:
471            kw2 = kw.copy()
472            kw2['target'] = libnode
473        else:
474            kw2 = kw
475
476        if Verbose:
477            print "_LibSymLinkGenerator: libnode=%r" % libnode.get_path()
478
479        symlinks = None
480
481        version = self.get_lib_version(env, **kw2)
482        disable = self.get_lib_noversionsymlinks(env, **kw2)
483        if Verbose:
484            print '_LibSymlinkGenerator: version=%r' % version
485            print '_LibSymlinkGenerator: disable=%r' % disable
486
487        if version and not disable:
488            prefix = self.get_lib_prefix(env,**kw2)
489            suffix = self.get_lib_suffix(env,**kw2)
490            symlinks = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2)
491
492        if Verbose:
493            print '_LibSymlinkGenerator: return symlinks=%r' % StringizeLibSymlinks(symlinks)
494        return symlinks
495
496ShLibSymlinkGenerator =  _LibSymlinkGenerator('ShLib')
497LdModSymlinkGenerator =  _LibSymlinkGenerator('LdMod')
498ImpLibSymlinkGenerator = _LibSymlinkGenerator('ImpLib')
499
500class _LibNameGenerator(_LibInfoGeneratorBase):
501    """Generates "unmangled" library name from a library file node.
502
503    Generally, it's thought to revert modifications done by prefix/suffix
504    generators (_LibPrefixGenerator/_LibSuffixGenerator) used by a library
505    builder. For example, on gnulink the suffix generator used by SharedLibrary
506    builder appends $SHLIBVERSION to $SHLIBSUFFIX producing node name which
507    ends with "$SHLIBSUFFIX.$SHLIBVERSION". Correspondingly, the implementation
508    of _LibNameGenerator replaces "$SHLIBSUFFIX.$SHLIBVERSION" with
509    "$SHLIBSUFFIX" in the node's basename. So that, if $SHLIBSUFFIX is ".so",
510    $SHLIBVERSION is "0.1.2" and the node path is "/foo/bar/libfoo.so.0.1.2",
511    the _LibNameGenerator shall return "libfoo.so". Other link tools may
512    implement it's own way of library name unmangling.
513    """
514    def __init__(self, libtype):
515        super(_LibNameGenerator, self).__init__(libtype, 'Name')
516
517    def __call__(self, env, libnode, **kw):
518        """Returns "demangled" library name"""
519        Verbose = False
520
521        if libnode and 'target' not in kw:
522            kw2 = kw.copy()
523            kw2['target'] = libnode
524        else:
525            kw2 = kw
526
527        if Verbose:
528            print "_LibNameGenerator: libnode=%r" % libnode.get_path()
529
530        version = self.get_lib_version(env, **kw2)
531        if Verbose:
532            print '_LibNameGenerator: version=%r' % version
533
534        name = None
535        if version:
536            prefix = self.get_lib_prefix(env,**kw2)
537            suffix = self.get_lib_suffix(env,**kw2)
538            name = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2)
539
540        if not name:
541            name = os.path.basename(libnode.get_path())
542
543        if Verbose:
544            print '_LibNameGenerator: return name=%r' % name
545
546        return name
547
548ShLibNameGenerator =  _LibNameGenerator('ShLib')
549LdModNameGenerator =  _LibNameGenerator('LdMod')
550ImpLibNameGenerator = _LibNameGenerator('ImpLib')
551
552class _LibSonameGenerator(_LibInfoGeneratorBase):
553    """Library soname generator. Returns library soname (e.g. libfoo.so.0) for
554    a given node (e.g. /foo/bar/libfoo.so.0.1.2)"""
555    def __init__(self, libtype):
556        super(_LibSonameGenerator, self).__init__(libtype, 'Soname')
557
558    def __call__(self, env, libnode, **kw):
559        """Returns a SONAME based on a shared library's node path"""
560        Verbose = False
561
562        if libnode and 'target' not in kw:
563            kw2 = kw.copy()
564            kw2['target'] = libnode
565        else:
566            kw2 = kw
567
568        if Verbose:
569            print "_LibSonameGenerator: libnode=%r" % libnode.get_path()
570
571        soname = _call_env_subst(env, '$SONAME', **kw2)
572        if not soname:
573            version = self.get_lib_version(env,**kw2)
574            if Verbose:
575                print "_LibSonameGenerator: version=%r" % version
576            if version:
577                prefix = self.get_lib_prefix(env,**kw2)
578                suffix = self.get_lib_suffix(env,**kw2)
579                soname = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2)
580
581        if not soname:
582            # fallback to library name (as returned by appropriate _LibNameGenerator)
583            soname = _LibNameGenerator(self.get_libtype())(env, libnode)
584            if Verbose:
585                print "_LibSonameGenerator: FALLBACK: soname=%r" % soname
586
587        if Verbose:
588            print "_LibSonameGenerator: return soname=%r" % soname
589
590        return soname
591
592ShLibSonameGenerator =  _LibSonameGenerator('ShLib')
593LdModSonameGenerator =  _LibSonameGenerator('LdMod')
594
595def StringizeLibSymlinks(symlinks):
596    """Converts list with pairs of nodes to list with pairs of node paths
597    (strings). Used mainly for debugging."""
598    if SCons.Util.is_List(symlinks):
599        try:
600            return [ (k.get_path(), v.get_path()) for k,v in symlinks ]
601        except (TypeError, ValueError):
602            return symlinks
603    else:
604        return symlinks
605
606def EmitLibSymlinks(env, symlinks, libnode, **kw):
607    """Used by emitters to handle (shared/versioned) library symlinks"""
608    Verbose = False
609
610    # nodes involved in process... all symlinks + library
611    nodes = list(set([ x for x,y in symlinks ] + [libnode]))
612
613    clean_targets = kw.get('clean_targets', [])
614    if not SCons.Util.is_List(clean_targets):
615        clean_targets = [ clean_targets ]
616
617    for link, linktgt in symlinks:
618        env.SideEffect(link, linktgt)
619        if(Verbose):
620            print "EmitLibSymlinks: SideEffect(%r,%r)" % (link.get_path(), linktgt.get_path())
621        clean_list = filter(lambda x : x != linktgt, nodes)
622        env.Clean(list(set([linktgt] + clean_targets)), clean_list)
623        if(Verbose):
624            print "EmitLibSymlinks: Clean(%r,%r)" % (linktgt.get_path(), map(lambda x : x.get_path(), clean_list))
625
626def CreateLibSymlinks(env, symlinks):
627    """Physically creates symlinks. The symlinks argument must be a list in
628    form [ (link, linktarget), ... ], where link and linktarget are SCons
629    nodes.
630    """
631
632    Verbose = False
633    for link, linktgt in symlinks:
634        linktgt = link.get_dir().rel_path(linktgt)
635        link = link.get_path()
636        if(Verbose):
637            print "CreateLibSymlinks: preparing to add symlink %r -> %r" % (link, linktgt)
638        # Delete the (previously created) symlink if exists. Let only symlinks
639        # to be deleted to prevent accidental deletion of source files...
640        if env.fs.islink(link):
641            env.fs.unlink(link)
642            if(Verbose):
643                print "CreateLibSymlinks: removed old symlink %r" % link
644        # If a file or directory exists with the same name as link, an OSError
645        # will be thrown, which should be enough, I think.
646        env.fs.symlink(linktgt, link)
647        if(Verbose):
648            print "CreateLibSymlinks: add symlink %r -> %r" % (link, linktgt)
649    return 0
650
651def LibSymlinksActionFunction(target, source, env):
652    for tgt in target:
653        symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None)
654        if symlinks:
655            CreateLibSymlinks(env, symlinks)
656    return 0
657
658def LibSymlinksStrFun(target, source, env, *args):
659    cmd = None
660    for tgt in target:
661        symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None)
662        if symlinks:
663            if cmd is None: cmd = ""
664            if cmd: cmd += "\n"
665            cmd += "Create symlinks for: %r" % tgt.get_path()
666            try:
667                linkstr = ', '.join([ "%r->%r" %(k,v) for k,v in StringizeLibSymlinks(symlinks)])
668            except (KeyError, ValueError):
669                pass
670            else:
671                cmd += ": %s" % linkstr
672    return cmd
673
674
675LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun)
676
677def createSharedLibBuilder(env):
678    """This is a utility function that creates the SharedLibrary
679    Builder in an Environment if it is not there already.
680
681    If it is already there, we return the existing one.
682    """
683
684    try:
685        shared_lib = env['BUILDERS']['SharedLibrary']
686    except KeyError:
687        import SCons.Defaults
688        action_list = [ SCons.Defaults.SharedCheck,
689                        SCons.Defaults.ShLinkAction,
690                        LibSymlinksAction ]
691        shared_lib = SCons.Builder.Builder(action = action_list,
692                                           emitter = "$SHLIBEMITTER",
693                                           prefix = ShLibPrefixGenerator,
694                                           suffix = ShLibSuffixGenerator,
695                                           target_scanner = ProgramScanner,
696                                           src_suffix = '$SHOBJSUFFIX',
697                                           src_builder = 'SharedObject')
698        env['BUILDERS']['SharedLibrary'] = shared_lib
699
700    return shared_lib
701
702def createLoadableModuleBuilder(env):
703    """This is a utility function that creates the LoadableModule
704    Builder in an Environment if it is not there already.
705
706    If it is already there, we return the existing one.
707    """
708
709    try:
710        ld_module = env['BUILDERS']['LoadableModule']
711    except KeyError:
712        import SCons.Defaults
713        action_list = [ SCons.Defaults.SharedCheck,
714                        SCons.Defaults.LdModuleLinkAction,
715                        LibSymlinksAction ]
716        ld_module = SCons.Builder.Builder(action = action_list,
717                                          emitter = "$LDMODULEEMITTER",
718                                          prefix = LdModPrefixGenerator,
719                                          suffix = LdModSuffixGenerator,
720                                          target_scanner = ProgramScanner,
721                                          src_suffix = '$SHOBJSUFFIX',
722                                          src_builder = 'SharedObject')
723        env['BUILDERS']['LoadableModule'] = ld_module
724
725    return ld_module
726
727def createObjBuilders(env):
728    """This is a utility function that creates the StaticObject
729    and SharedObject Builders in an Environment if they
730    are not there already.
731
732    If they are there already, we return the existing ones.
733
734    This is a separate function because soooo many Tools
735    use this functionality.
736
737    The return is a 2-tuple of (StaticObject, SharedObject)
738    """
739
740
741    try:
742        static_obj = env['BUILDERS']['StaticObject']
743    except KeyError:
744        static_obj = SCons.Builder.Builder(action = {},
745                                           emitter = {},
746                                           prefix = '$OBJPREFIX',
747                                           suffix = '$OBJSUFFIX',
748                                           src_builder = ['CFile', 'CXXFile'],
749                                           source_scanner = SourceFileScanner,
750                                           single_source = 1)
751        env['BUILDERS']['StaticObject'] = static_obj
752        env['BUILDERS']['Object'] = static_obj
753
754    try:
755        shared_obj = env['BUILDERS']['SharedObject']
756    except KeyError:
757        shared_obj = SCons.Builder.Builder(action = {},
758                                           emitter = {},
759                                           prefix = '$SHOBJPREFIX',
760                                           suffix = '$SHOBJSUFFIX',
761                                           src_builder = ['CFile', 'CXXFile'],
762                                           source_scanner = SourceFileScanner,
763                                           single_source = 1)
764        env['BUILDERS']['SharedObject'] = shared_obj
765
766    return (static_obj, shared_obj)
767
768def createCFileBuilders(env):
769    """This is a utility function that creates the CFile/CXXFile
770    Builders in an Environment if they
771    are not there already.
772
773    If they are there already, we return the existing ones.
774
775    This is a separate function because soooo many Tools
776    use this functionality.
777
778    The return is a 2-tuple of (CFile, CXXFile)
779    """
780
781    try:
782        c_file = env['BUILDERS']['CFile']
783    except KeyError:
784        c_file = SCons.Builder.Builder(action = {},
785                                       emitter = {},
786                                       suffix = {None:'$CFILESUFFIX'})
787        env['BUILDERS']['CFile'] = c_file
788
789        env.SetDefault(CFILESUFFIX = '.c')
790
791    try:
792        cxx_file = env['BUILDERS']['CXXFile']
793    except KeyError:
794        cxx_file = SCons.Builder.Builder(action = {},
795                                         emitter = {},
796                                         suffix = {None:'$CXXFILESUFFIX'})
797        env['BUILDERS']['CXXFile'] = cxx_file
798        env.SetDefault(CXXFILESUFFIX = '.cc')
799
800    return (c_file, cxx_file)
801
802##########################################################################
803#  Create common Java builders
804
805def CreateJarBuilder(env):
806    try:
807        java_jar = env['BUILDERS']['Jar']
808    except KeyError:
809        fs = SCons.Node.FS.get_default_fs()
810        jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR')
811        java_jar = SCons.Builder.Builder(action = jar_com,
812                                         suffix = '$JARSUFFIX',
813                                         src_suffix = '$JAVACLASSSUFIX',
814                                         src_builder = 'JavaClassFile',
815                                         source_factory = fs.Entry)
816        env['BUILDERS']['Jar'] = java_jar
817    return java_jar
818
819def CreateJavaHBuilder(env):
820    try:
821        java_javah = env['BUILDERS']['JavaH']
822    except KeyError:
823        fs = SCons.Node.FS.get_default_fs()
824        java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR')
825        java_javah = SCons.Builder.Builder(action = java_javah_com,
826                                           src_suffix = '$JAVACLASSSUFFIX',
827                                           target_factory = fs.Entry,
828                                           source_factory = fs.File,
829                                           src_builder = 'JavaClassFile')
830        env['BUILDERS']['JavaH'] = java_javah
831    return java_javah
832
833def CreateJavaClassFileBuilder(env):
834    try:
835        java_class_file = env['BUILDERS']['JavaClassFile']
836    except KeyError:
837        fs = SCons.Node.FS.get_default_fs()
838        javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
839        java_class_file = SCons.Builder.Builder(action = javac_com,
840                                                emitter = {},
841                                                #suffix = '$JAVACLASSSUFFIX',
842                                                src_suffix = '$JAVASUFFIX',
843                                                src_builder = ['JavaFile'],
844                                                target_factory = fs.Entry,
845                                                source_factory = fs.File)
846        env['BUILDERS']['JavaClassFile'] = java_class_file
847    return java_class_file
848
849def CreateJavaClassDirBuilder(env):
850    try:
851        java_class_dir = env['BUILDERS']['JavaClassDir']
852    except KeyError:
853        fs = SCons.Node.FS.get_default_fs()
854        javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
855        java_class_dir = SCons.Builder.Builder(action = javac_com,
856                                               emitter = {},
857                                               target_factory = fs.Dir,
858                                               source_factory = fs.Dir)
859        env['BUILDERS']['JavaClassDir'] = java_class_dir
860    return java_class_dir
861
862def CreateJavaFileBuilder(env):
863    try:
864        java_file = env['BUILDERS']['JavaFile']
865    except KeyError:
866        java_file = SCons.Builder.Builder(action = {},
867                                          emitter = {},
868                                          suffix = {None:'$JAVASUFFIX'})
869        env['BUILDERS']['JavaFile'] = java_file
870        env['JAVASUFFIX'] = '.java'
871    return java_file
872
873class ToolInitializerMethod(object):
874    """
875    This is added to a construction environment in place of a
876    method(s) normally called for a Builder (env.Object, env.StaticObject,
877    etc.).  When called, it has its associated ToolInitializer
878    object search the specified list of tools and apply the first
879    one that exists to the construction environment.  It then calls
880    whatever builder was (presumably) added to the construction
881    environment in place of this particular instance.
882    """
883    def __init__(self, name, initializer):
884        """
885        Note:  we store the tool name as __name__ so it can be used by
886        the class that attaches this to a construction environment.
887        """
888        self.__name__ = name
889        self.initializer = initializer
890
891    def get_builder(self, env):
892        """
893	Returns the appropriate real Builder for this method name
894	after having the associated ToolInitializer object apply
895	the appropriate Tool module.
896        """
897        builder = getattr(env, self.__name__)
898
899        self.initializer.apply_tools(env)
900
901        builder = getattr(env, self.__name__)
902        if builder is self:
903            # There was no Builder added, which means no valid Tool
904            # for this name was found (or possibly there's a mismatch
905            # between the name we were called by and the Builder name
906            # added by the Tool module).
907            return None
908
909        self.initializer.remove_methods(env)
910
911        return builder
912
913    def __call__(self, env, *args, **kw):
914        """
915        """
916        builder = self.get_builder(env)
917        if builder is None:
918            return [], []
919        return builder(*args, **kw)
920
921class ToolInitializer(object):
922    """
923    A class for delayed initialization of Tools modules.
924
925    Instances of this class associate a list of Tool modules with
926    a list of Builder method names that will be added by those Tool
927    modules.  As part of instantiating this object for a particular
928    construction environment, we also add the appropriate
929    ToolInitializerMethod objects for the various Builder methods
930    that we want to use to delay Tool searches until necessary.
931    """
932    def __init__(self, env, tools, names):
933        if not SCons.Util.is_List(tools):
934            tools = [tools]
935        if not SCons.Util.is_List(names):
936            names = [names]
937        self.env = env
938        self.tools = tools
939        self.names = names
940        self.methods = {}
941        for name in names:
942            method = ToolInitializerMethod(name, self)
943            self.methods[name] = method
944            env.AddMethod(method)
945
946    def remove_methods(self, env):
947        """
948        Removes the methods that were added by the tool initialization
949        so we no longer copy and re-bind them when the construction
950        environment gets cloned.
951        """
952        for method in self.methods.values():
953            env.RemoveMethod(method)
954
955    def apply_tools(self, env):
956        """
957	Searches the list of associated Tool modules for one that
958	exists, and applies that to the construction environment.
959        """
960        for t in self.tools:
961            tool = SCons.Tool.Tool(t)
962            if tool.exists(env):
963                env.Tool(tool)
964                return
965
966	# If we fall through here, there was no tool module found.
967	# This is where we can put an informative error message
968	# about the inability to find the tool.   We'll start doing
969	# this as we cut over more pre-defined Builder+Tools to use
970	# the ToolInitializer class.
971
972def Initializers(env):
973    ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs', '_InternalInstallVersionedLib'])
974    def Install(self, *args, **kw):
975        return self._InternalInstall(*args, **kw)
976    def InstallAs(self, *args, **kw):
977        return self._InternalInstallAs(*args, **kw)
978    def InstallVersionedLib(self, *args, **kw):
979        return self._InternalInstallVersionedLib(*args, **kw)
980    env.AddMethod(Install)
981    env.AddMethod(InstallAs)
982    env.AddMethod(InstallVersionedLib)
983
984def FindTool(tools, env):
985    for tool in tools:
986        t = Tool(tool)
987        if t.exists(env):
988            return tool
989    return None
990
991def FindAllTools(tools, env):
992    def ToolExists(tool, env=env):
993        return Tool(tool).exists(env)
994    return list(filter (ToolExists, tools))
995
996def tool_list(platform, env):
997
998    other_plat_tools=[]
999    # XXX this logic about what tool to prefer on which platform
1000    #     should be moved into either the platform files or
1001    #     the tool files themselves.
1002    # The search orders here are described in the man page.  If you
1003    # change these search orders, update the man page as well.
1004    if str(platform) == 'win32':
1005        "prefer Microsoft tools on Windows"
1006        linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ]
1007        c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ]
1008        cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ]
1009        assemblers = ['masm', 'nasm', 'gas', '386asm' ]
1010        fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran']
1011        ars = ['mslib', 'ar', 'tlib']
1012        other_plat_tools = ['msvs', 'midl']
1013    elif str(platform) == 'os2':
1014        "prefer IBM tools on OS/2"
1015        linkers = ['ilink', 'gnulink', ]#'mslink']
1016        c_compilers = ['icc', 'gcc',]# 'msvc', 'cc']
1017        cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++']
1018        assemblers = ['nasm',]# 'masm', 'gas']
1019        fortran_compilers = ['ifl', 'g77']
1020        ars = ['ar',]# 'mslib']
1021    elif str(platform) == 'irix':
1022        "prefer MIPSPro on IRIX"
1023        linkers = ['sgilink', 'gnulink']
1024        c_compilers = ['sgicc', 'gcc', 'cc']
1025        cxx_compilers = ['sgic++', 'g++', 'c++']
1026        assemblers = ['as', 'gas']
1027        fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
1028        ars = ['sgiar']
1029    elif str(platform) == 'sunos':
1030        "prefer Forte tools on SunOS"
1031        linkers = ['sunlink', 'gnulink']
1032        c_compilers = ['suncc', 'gcc', 'cc']
1033        cxx_compilers = ['sunc++', 'g++', 'c++']
1034        assemblers = ['as', 'gas']
1035        fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77',
1036                             'gfortran', 'g77', 'fortran']
1037        ars = ['sunar']
1038    elif str(platform) == 'hpux':
1039        "prefer aCC tools on HP-UX"
1040        linkers = ['hplink', 'gnulink']
1041        c_compilers = ['hpcc', 'gcc', 'cc']
1042        cxx_compilers = ['hpc++', 'g++', 'c++']
1043        assemblers = ['as', 'gas']
1044        fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
1045        ars = ['ar']
1046    elif str(platform) == 'aix':
1047        "prefer AIX Visual Age tools on AIX"
1048        linkers = ['aixlink', 'gnulink']
1049        c_compilers = ['aixcc', 'gcc', 'cc']
1050        cxx_compilers = ['aixc++', 'g++', 'c++']
1051        assemblers = ['as', 'gas']
1052        fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran']
1053        ars = ['ar']
1054    elif str(platform) == 'darwin':
1055        "prefer GNU tools on Mac OS X, except for some linkers and IBM tools"
1056        linkers = ['applelink', 'gnulink']
1057        c_compilers = ['gcc', 'cc']
1058        cxx_compilers = ['g++', 'c++']
1059        assemblers = ['as']
1060        fortran_compilers = ['gfortran', 'f95', 'f90', 'g77']
1061        ars = ['ar']
1062    elif str(platform) == 'cygwin':
1063        "prefer GNU tools on Cygwin, except for a platform-specific linker"
1064        linkers = ['cyglink', 'mslink', 'ilink']
1065        c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
1066        cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
1067        assemblers = ['gas', 'nasm', 'masm']
1068        fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77']
1069        ars = ['ar', 'mslib']
1070    else:
1071        "prefer GNU tools on all other platforms"
1072        linkers = ['gnulink', 'mslink', 'ilink']
1073        c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
1074        cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
1075        assemblers = ['gas', 'nasm', 'masm']
1076        fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77']
1077        ars = ['ar', 'mslib']
1078
1079    if not str(platform) == 'win32':
1080        other_plat_tools += ['m4', 'rpm']
1081
1082    c_compiler = FindTool(c_compilers, env) or c_compilers[0]
1083
1084    # XXX this logic about what tool provides what should somehow be
1085    #     moved into the tool files themselves.
1086    if c_compiler and c_compiler == 'mingw':
1087        # MinGW contains a linker, C compiler, C++ compiler,
1088        # Fortran compiler, archiver and assembler:
1089        cxx_compiler = None
1090        linker = None
1091        assembler = None
1092        fortran_compiler = None
1093        ar = None
1094    else:
1095        # Don't use g++ if the C compiler has built-in C++ support:
1096        if c_compiler in ('msvc', 'intelc', 'icc'):
1097            cxx_compiler = None
1098        else:
1099            cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
1100        linker = FindTool(linkers, env) or linkers[0]
1101        assembler = FindTool(assemblers, env) or assemblers[0]
1102        fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0]
1103        ar = FindTool(ars, env) or ars[0]
1104
1105    d_compilers = ['dmd', 'gdc', 'ldc']
1106    d_compiler = FindTool(d_compilers, env) or d_compilers[0]
1107
1108    other_tools = FindAllTools(other_plat_tools + [
1109                               #TODO: merge 'install' into 'filesystem' and
1110                               # make 'filesystem' the default
1111                               'filesystem',
1112                               'wix', #'midl', 'msvs',
1113                               # Parser generators
1114                               'lex', 'yacc',
1115                               # Foreign function interface
1116                               'rpcgen', 'swig',
1117                               # Java
1118                               'jar', 'javac', 'javah', 'rmic',
1119                               # TeX
1120                               'dvipdf', 'dvips', 'gs',
1121                               'tex', 'latex', 'pdflatex', 'pdftex',
1122                               # Archivers
1123                               'tar', 'zip',
1124                               # SourceCode factories
1125                               'BitKeeper', 'CVS', 'Perforce',
1126                               'RCS', 'SCCS', # 'Subversion',
1127                               ], env)
1128
1129    tools = ([linker, c_compiler, cxx_compiler,
1130              fortran_compiler, assembler, ar, d_compiler]
1131             + other_tools)
1132
1133    return [x for x in tools if x]
1134
1135# Local Variables:
1136# tab-width:4
1137# indent-tabs-mode:nil
1138# End:
1139# vim: set expandtab tabstop=4 shiftwidth=4:
1140
1141