1"""SCons.Environment
2
3Base class for construction Environments.  These are
4the primary objects used to communicate dependency and
5construction information to the build engine.
6
7Keyword arguments supplied when the construction Environment
8is created are construction variables used to initialize the
9Environment
10"""
11
12#
13# Copyright (c) 2001 - 2014 The SCons Foundation
14#
15# Permission is hereby granted, free of charge, to any person obtaining
16# a copy of this software and associated documentation files (the
17# "Software"), to deal in the Software without restriction, including
18# without limitation the rights to use, copy, modify, merge, publish,
19# distribute, sublicense, and/or sell copies of the Software, and to
20# permit persons to whom the Software is furnished to do so, subject to
21# the following conditions:
22#
23# The above copyright notice and this permission notice shall be included
24# in all copies or substantial portions of the Software.
25#
26# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
34__revision__ = "src/engine/SCons/Environment.py  2014/07/05 09:42:21 garyo"
35
36
37import copy
38import os
39import sys
40import re
41import shlex
42from collections import UserDict
43
44import SCons.Action
45import SCons.Builder
46import SCons.Debug
47from SCons.Debug import logInstanceCreation
48import SCons.Defaults
49import SCons.Errors
50import SCons.Memoize
51import SCons.Node
52import SCons.Node.Alias
53import SCons.Node.FS
54import SCons.Node.Python
55import SCons.Platform
56import SCons.SConf
57import SCons.SConsign
58import SCons.Subst
59import SCons.Tool
60import SCons.Util
61import SCons.Warnings
62
63class _Null(object):
64    pass
65
66_null = _Null
67
68_warn_copy_deprecated = True
69_warn_source_signatures_deprecated = True
70_warn_target_signatures_deprecated = True
71
72CleanTargets = {}
73CalculatorArgs = {}
74
75semi_deepcopy = SCons.Util.semi_deepcopy
76semi_deepcopy_dict = SCons.Util.semi_deepcopy_dict
77
78# Pull UserError into the global name space for the benefit of
79# Environment().SourceSignatures(), which has some import statements
80# which seem to mess up its ability to reference SCons directly.
81UserError = SCons.Errors.UserError
82
83def alias_builder(env, target, source):
84    pass
85
86AliasBuilder = SCons.Builder.Builder(action = alias_builder,
87                                     target_factory = SCons.Node.Alias.default_ans.Alias,
88                                     source_factory = SCons.Node.FS.Entry,
89                                     multi = 1,
90                                     is_explicit = None,
91                                     name='AliasBuilder')
92
93def apply_tools(env, tools, toolpath):
94    # Store the toolpath in the Environment.
95    if toolpath is not None:
96        env['toolpath'] = toolpath
97
98    if not tools:
99        return
100    # Filter out null tools from the list.
101    for tool in [_f for _f in tools if _f]:
102        if SCons.Util.is_List(tool) or isinstance(tool, tuple):
103            toolname = tool[0]
104            toolargs = tool[1] # should be a dict of kw args
105            tool = env.Tool(toolname, **toolargs)
106        else:
107            env.Tool(tool)
108
109# These names are (or will be) controlled by SCons; users should never
110# set or override them.  This warning can optionally be turned off,
111# but scons will still ignore the illegal variable names even if it's off.
112reserved_construction_var_names = [
113    'CHANGED_SOURCES',
114    'CHANGED_TARGETS',
115    'SOURCE',
116    'SOURCES',
117    'TARGET',
118    'TARGETS',
119    'UNCHANGED_SOURCES',
120    'UNCHANGED_TARGETS',
121]
122
123future_reserved_construction_var_names = [
124    #'HOST_OS',
125    #'HOST_ARCH',
126    #'HOST_CPU',
127    ]
128
129def copy_non_reserved_keywords(dict):
130    result = semi_deepcopy(dict)
131    for k in result.keys():
132        if k in reserved_construction_var_names:
133            msg = "Ignoring attempt to set reserved variable `$%s'"
134            SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k)
135            del result[k]
136    return result
137
138def _set_reserved(env, key, value):
139    msg = "Ignoring attempt to set reserved variable `$%s'"
140    SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
141
142def _set_future_reserved(env, key, value):
143    env._dict[key] = value
144    msg = "`$%s' will be reserved in a future release and setting it will become ignored"
145    SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
146
147def _set_BUILDERS(env, key, value):
148    try:
149        bd = env._dict[key]
150        for k in bd.keys():
151            del bd[k]
152    except KeyError:
153        bd = BuilderDict(kwbd, env)
154        env._dict[key] = bd
155    for k, v in value.items():
156        if not SCons.Builder.is_a_Builder(v):
157            raise SCons.Errors.UserError('%s is not a Builder.' % repr(v))
158    bd.update(value)
159
160def _del_SCANNERS(env, key):
161    del env._dict[key]
162    env.scanner_map_delete()
163
164def _set_SCANNERS(env, key, value):
165    env._dict[key] = value
166    env.scanner_map_delete()
167
168def _delete_duplicates(l, keep_last):
169    """Delete duplicates from a sequence, keeping the first or last."""
170    seen={}
171    result=[]
172    if keep_last:           # reverse in & out, then keep first
173        l.reverse()
174    for i in l:
175        try:
176            if i not in seen:
177                result.append(i)
178                seen[i]=1
179        except TypeError:
180            # probably unhashable.  Just keep it.
181            result.append(i)
182    if keep_last:
183        result.reverse()
184    return result
185
186
187
188# The following is partly based on code in a comment added by Peter
189# Shannon at the following page (there called the "transplant" class):
190#
191# ASPN : Python Cookbook : Dynamically added methods to a class
192# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
193#
194# We had independently been using the idiom as BuilderWrapper, but
195# factoring out the common parts into this base class, and making
196# BuilderWrapper a subclass that overrides __call__() to enforce specific
197# Builder calling conventions, simplified some of our higher-layer code.
198
199class MethodWrapper(object):
200    """
201    A generic Wrapper class that associates a method (which can
202    actually be any callable) with an object.  As part of creating this
203    MethodWrapper object an attribute with the specified (by default,
204    the name of the supplied method) is added to the underlying object.
205    When that new "method" is called, our __call__() method adds the
206    object as the first argument, simulating the Python behavior of
207    supplying "self" on method calls.
208
209    We hang on to the name by which the method was added to the underlying
210    base class so that we can provide a method to "clone" ourselves onto
211    a new underlying object being copied (without which we wouldn't need
212    to save that info).
213    """
214    def __init__(self, object, method, name=None):
215        if name is None:
216            name = method.__name__
217        self.object = object
218        self.method = method
219        self.name = name
220        setattr(self.object, name, self)
221
222    def __call__(self, *args, **kwargs):
223        nargs = (self.object,) + args
224        return self.method(*nargs, **kwargs)
225
226    def clone(self, new_object):
227        """
228        Returns an object that re-binds the underlying "method" to
229        the specified new object.
230        """
231        return self.__class__(new_object, self.method, self.name)
232
233class BuilderWrapper(MethodWrapper):
234    """
235    A MethodWrapper subclass that that associates an environment with
236    a Builder.
237
238    This mainly exists to wrap the __call__() function so that all calls
239    to Builders can have their argument lists massaged in the same way
240    (treat a lone argument as the source, treat two arguments as target
241    then source, make sure both target and source are lists) without
242    having to have cut-and-paste code to do it.
243
244    As a bit of obsessive backwards compatibility, we also intercept
245    attempts to get or set the "env" or "builder" attributes, which were
246    the names we used before we put the common functionality into the
247    MethodWrapper base class.  We'll keep this around for a while in case
248    people shipped Tool modules that reached into the wrapper (like the
249    Tool/qt.py module does, or did).  There shouldn't be a lot attribute
250    fetching or setting on these, so a little extra work shouldn't hurt.
251    """
252    def __call__(self, target=None, source=_null, *args, **kw):
253        if source is _null:
254            source = target
255            target = None
256        if target is not None and not SCons.Util.is_List(target):
257            target = [target]
258        if source is not None and not SCons.Util.is_List(source):
259            source = [source]
260        return MethodWrapper.__call__(self, target, source, *args, **kw)
261
262    def __repr__(self):
263        return '<BuilderWrapper %s>' % repr(self.name)
264
265    def __str__(self):
266        return self.__repr__()
267
268    def __getattr__(self, name):
269        if name == 'env':
270            return self.object
271        elif name == 'builder':
272            return self.method
273        else:
274            raise AttributeError(name)
275
276    def __setattr__(self, name, value):
277        if name == 'env':
278            self.object = value
279        elif name == 'builder':
280            self.method = value
281        else:
282            self.__dict__[name] = value
283
284    # This allows a Builder to be executed directly
285    # through the Environment to which it's attached.
286    # In practice, we shouldn't need this, because
287    # builders actually get executed through a Node.
288    # But we do have a unit test for this, and can't
289    # yet rule out that it would be useful in the
290    # future, so leave it for now.
291    #def execute(self, **kw):
292    #    kw['env'] = self.env
293    #    self.builder.execute(**kw)
294
295class BuilderDict(UserDict):
296    """This is a dictionary-like class used by an Environment to hold
297    the Builders.  We need to do this because every time someone changes
298    the Builders in the Environment's BUILDERS dictionary, we must
299    update the Environment's attributes."""
300    def __init__(self, dict, env):
301        # Set self.env before calling the superclass initialization,
302        # because it will end up calling our other methods, which will
303        # need to point the values in this dictionary to self.env.
304        self.env = env
305        UserDict.__init__(self, dict)
306
307    def __semi_deepcopy__(self):
308        # These cannot be copied since they would both modify the same builder object, and indeed
309        # just copying would modify the original builder
310        raise TypeError( 'cannot semi_deepcopy a BuilderDict' )
311
312    def __setitem__(self, item, val):
313        try:
314            method = getattr(self.env, item).method
315        except AttributeError:
316            pass
317        else:
318            self.env.RemoveMethod(method)
319        UserDict.__setitem__(self, item, val)
320        BuilderWrapper(self.env, val, item)
321
322    def __delitem__(self, item):
323        UserDict.__delitem__(self, item)
324        delattr(self.env, item)
325
326    def update(self, dict):
327        for i, v in dict.items():
328            self.__setitem__(i, v)
329
330
331
332_is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
333
334def is_valid_construction_var(varstr):
335    """Return if the specified string is a legitimate construction
336    variable.
337    """
338    return _is_valid_var.match(varstr)
339
340
341
342class SubstitutionEnvironment(object):
343    """Base class for different flavors of construction environments.
344
345    This class contains a minimal set of methods that handle contruction
346    variable expansion and conversion of strings to Nodes, which may or
347    may not be actually useful as a stand-alone class.  Which methods
348    ended up in this class is pretty arbitrary right now.  They're
349    basically the ones which we've empirically determined are common to
350    the different construction environment subclasses, and most of the
351    others that use or touch the underlying dictionary of construction
352    variables.
353
354    Eventually, this class should contain all the methods that we
355    determine are necessary for a "minimal" interface to the build engine.
356    A full "native Python" SCons environment has gotten pretty heavyweight
357    with all of the methods and Tools and construction variables we've
358    jammed in there, so it would be nice to have a lighter weight
359    alternative for interfaces that don't need all of the bells and
360    whistles.  (At some point, we'll also probably rename this class
361    "Base," since that more reflects what we want this class to become,
362    but because we've released comments that tell people to subclass
363    Environment.Base to create their own flavors of construction
364    environment, we'll save that for a future refactoring when this
365    class actually becomes useful.)
366    """
367
368    if SCons.Memoize.use_memoizer:
369        __metaclass__ = SCons.Memoize.Memoized_Metaclass
370
371    def __init__(self, **kw):
372        """Initialization of an underlying SubstitutionEnvironment class.
373        """
374        if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
375        self.fs = SCons.Node.FS.get_default_fs()
376        self.ans = SCons.Node.Alias.default_ans
377        self.lookup_list = SCons.Node.arg2nodes_lookups
378        self._dict = kw.copy()
379        self._init_special()
380        self.added_methods = []
381        #self._memo = {}
382
383    def _init_special(self):
384        """Initial the dispatch tables for special handling of
385        special construction variables."""
386        self._special_del = {}
387        self._special_del['SCANNERS'] = _del_SCANNERS
388
389        self._special_set = {}
390        for key in reserved_construction_var_names:
391            self._special_set[key] = _set_reserved
392        for key in future_reserved_construction_var_names:
393            self._special_set[key] = _set_future_reserved
394        self._special_set['BUILDERS'] = _set_BUILDERS
395        self._special_set['SCANNERS'] = _set_SCANNERS
396
397        # Freeze the keys of self._special_set in a list for use by
398        # methods that need to check.  (Empirically, list scanning has
399        # gotten better than dict.has_key() in Python 2.5.)
400        self._special_set_keys = list(self._special_set.keys())
401
402    def __cmp__(self, other):
403        return cmp(self._dict, other._dict)
404
405    def __delitem__(self, key):
406        special = self._special_del.get(key)
407        if special:
408            special(self, key)
409        else:
410            del self._dict[key]
411
412    def __getitem__(self, key):
413        return self._dict[key]
414
415    def __setitem__(self, key, value):
416        # This is heavily used.  This implementation is the best we have
417        # according to the timings in bench/env.__setitem__.py.
418        #
419        # The "key in self._special_set_keys" test here seems to perform
420        # pretty well for the number of keys we have.  A hard-coded
421        # list works a little better in Python 2.5, but that has the
422        # disadvantage of maybe getting out of sync if we ever add more
423        # variable names.  Using self._special_set.has_key() works a
424        # little better in Python 2.4, but is worse than this test.
425        # So right now it seems like a good trade-off, but feel free to
426        # revisit this with bench/env.__setitem__.py as needed (and
427        # as newer versions of Python come out).
428        if key in self._special_set_keys:
429            self._special_set[key](self, key, value)
430        else:
431            # If we already have the entry, then it's obviously a valid
432            # key and we don't need to check.  If we do check, using a
433            # global, pre-compiled regular expression directly is more
434            # efficient than calling another function or a method.
435            if key not in self._dict \
436               and not _is_valid_var.match(key):
437                    raise SCons.Errors.UserError("Illegal construction variable `%s'" % key)
438            self._dict[key] = value
439
440    def get(self, key, default=None):
441        """Emulates the get() method of dictionaries."""
442        return self._dict.get(key, default)
443
444    def has_key(self, key):
445        return key in self._dict
446
447    def __contains__(self, key):
448        return self._dict.__contains__(key)
449
450    def items(self):
451        return list(self._dict.items())
452
453    def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
454        if node_factory is _null:
455            node_factory = self.fs.File
456        if lookup_list is _null:
457            lookup_list = self.lookup_list
458
459        if not args:
460            return []
461
462        args = SCons.Util.flatten(args)
463
464        nodes = []
465        for v in args:
466            if SCons.Util.is_String(v):
467                n = None
468                for l in lookup_list:
469                    n = l(v)
470                    if n is not None:
471                        break
472                if n is not None:
473                    if SCons.Util.is_String(n):
474                        # n = self.subst(n, raw=1, **kw)
475                        kw['raw'] = 1
476                        n = self.subst(n, **kw)
477                        if node_factory:
478                            n = node_factory(n)
479                    if SCons.Util.is_List(n):
480                        nodes.extend(n)
481                    else:
482                        nodes.append(n)
483                elif node_factory:
484                    # v = node_factory(self.subst(v, raw=1, **kw))
485                    kw['raw'] = 1
486                    v = node_factory(self.subst(v, **kw))
487                    if SCons.Util.is_List(v):
488                        nodes.extend(v)
489                    else:
490                        nodes.append(v)
491            else:
492                nodes.append(v)
493
494        return nodes
495
496    def gvars(self):
497        return self._dict
498
499    def lvars(self):
500        return {}
501
502    def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None):
503        """Recursively interpolates construction variables from the
504        Environment into the specified string, returning the expanded
505        result.  Construction variables are specified by a $ prefix
506        in the string and begin with an initial underscore or
507        alphabetic character followed by any number of underscores
508        or alphanumeric characters.  The construction variable names
509        may be surrounded by curly braces to separate the name from
510        trailing characters.
511        """
512        gvars = self.gvars()
513        lvars = self.lvars()
514        lvars['__env__'] = self
515        if executor:
516            lvars.update(executor.get_lvars())
517        return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
518
519    def subst_kw(self, kw, raw=0, target=None, source=None):
520        nkw = {}
521        for k, v in kw.items():
522            k = self.subst(k, raw, target, source)
523            if SCons.Util.is_String(v):
524                v = self.subst(v, raw, target, source)
525            nkw[k] = v
526        return nkw
527
528    def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None):
529        """Calls through to SCons.Subst.scons_subst_list().  See
530        the documentation for that function."""
531        gvars = self.gvars()
532        lvars = self.lvars()
533        lvars['__env__'] = self
534        if executor:
535            lvars.update(executor.get_lvars())
536        return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
537
538    def subst_path(self, path, target=None, source=None):
539        """Substitute a path list, turning EntryProxies into Nodes
540        and leaving Nodes (and other objects) as-is."""
541
542        if not SCons.Util.is_List(path):
543            path = [path]
544
545        def s(obj):
546            """This is the "string conversion" routine that we have our
547            substitutions use to return Nodes, not strings.  This relies
548            on the fact that an EntryProxy object has a get() method that
549            returns the underlying Node that it wraps, which is a bit of
550            architectural dependence that we might need to break or modify
551            in the future in response to additional requirements."""
552            try:
553                get = obj.get
554            except AttributeError:
555                obj = SCons.Util.to_String_for_subst(obj)
556            else:
557                obj = get()
558            return obj
559
560        r = []
561        for p in path:
562            if SCons.Util.is_String(p):
563                p = self.subst(p, target=target, source=source, conv=s)
564                if SCons.Util.is_List(p):
565                    if len(p) == 1:
566                        p = p[0]
567                    else:
568                        # We have an object plus a string, or multiple
569                        # objects that we need to smush together.  No choice
570                        # but to make them into a string.
571                        p = ''.join(map(SCons.Util.to_String_for_subst, p))
572            else:
573                p = s(p)
574            r.append(p)
575        return r
576
577    subst_target_source = subst
578
579    def backtick(self, command):
580        import subprocess
581        # common arguments
582        kw = { 'stdin' : 'devnull',
583               'stdout' : subprocess.PIPE,
584               'stderr' : subprocess.PIPE,
585               'universal_newlines' : True,
586             }
587        # if the command is a list, assume it's been quoted
588        # othewise force a shell
589        if not SCons.Util.is_List(command): kw['shell'] = True
590        # run constructed command
591        p = SCons.Action._subproc(self, command, **kw)
592        out,err = p.communicate()
593        status = p.wait()
594        if err:
595            sys.stderr.write(unicode(err))
596        if status:
597            raise OSError("'%s' exited %d" % (command, status))
598        return out
599
600    def AddMethod(self, function, name=None):
601        """
602        Adds the specified function as a method of this construction
603        environment with the specified name.  If the name is omitted,
604        the default name is the name of the function itself.
605        """
606        method = MethodWrapper(self, function, name)
607        self.added_methods.append(method)
608
609    def RemoveMethod(self, function):
610        """
611        Removes the specified function's MethodWrapper from the
612        added_methods list, so we don't re-bind it when making a clone.
613        """
614        self.added_methods = [dm for dm in self.added_methods if not dm.method is function]
615
616    def Override(self, overrides):
617        """
618        Produce a modified environment whose variables are overriden by
619        the overrides dictionaries.  "overrides" is a dictionary that
620        will override the variables of this environment.
621
622        This function is much more efficient than Clone() or creating
623        a new Environment because it doesn't copy the construction
624        environment dictionary, it just wraps the underlying construction
625        environment, and doesn't even create a wrapper object if there
626        are no overrides.
627        """
628        if not overrides: return self
629        o = copy_non_reserved_keywords(overrides)
630        if not o: return self
631        overrides = {}
632        merges = None
633        for key, value in o.items():
634            if key == 'parse_flags':
635                merges = value
636            else:
637                overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
638        env = OverrideEnvironment(self, overrides)
639        if merges: env.MergeFlags(merges)
640        return env
641
642    def ParseFlags(self, *flags):
643        """
644        Parse the set of flags and return a dict with the flags placed
645        in the appropriate entry.  The flags are treated as a typical
646        set of command-line flags for a GNU-like toolchain and used to
647        populate the entries in the dict immediately below.  If one of
648        the flag strings begins with a bang (exclamation mark), it is
649        assumed to be a command and the rest of the string is executed;
650        the result of that evaluation is then added to the dict.
651        """
652        dict = {
653            'ASFLAGS'       : SCons.Util.CLVar(''),
654            'CFLAGS'        : SCons.Util.CLVar(''),
655            'CCFLAGS'       : SCons.Util.CLVar(''),
656            'CXXFLAGS'      : SCons.Util.CLVar(''),
657            'CPPDEFINES'    : [],
658            'CPPFLAGS'      : SCons.Util.CLVar(''),
659            'CPPPATH'       : [],
660            'FRAMEWORKPATH' : SCons.Util.CLVar(''),
661            'FRAMEWORKS'    : SCons.Util.CLVar(''),
662            'LIBPATH'       : [],
663            'LIBS'          : [],
664            'LINKFLAGS'     : SCons.Util.CLVar(''),
665            'RPATH'         : [],
666        }
667
668        def do_parse(arg):
669            # if arg is a sequence, recurse with each element
670            if not arg:
671                return
672
673            if not SCons.Util.is_String(arg):
674                for t in arg: do_parse(t)
675                return
676
677            # if arg is a command, execute it
678            if arg[0] == '!':
679                arg = self.backtick(arg[1:])
680
681            # utility function to deal with -D option
682            def append_define(name, dict = dict):
683                t = name.split('=')
684                if len(t) == 1:
685                    dict['CPPDEFINES'].append(name)
686                else:
687                    dict['CPPDEFINES'].append([t[0], '='.join(t[1:])])
688
689            # Loop through the flags and add them to the appropriate option.
690            # This tries to strike a balance between checking for all possible
691            # flags and keeping the logic to a finite size, so it doesn't
692            # check for some that don't occur often.  It particular, if the
693            # flag is not known to occur in a config script and there's a way
694            # of passing the flag to the right place (by wrapping it in a -W
695            # flag, for example) we don't check for it.  Note that most
696            # preprocessor options are not handled, since unhandled options
697            # are placed in CCFLAGS, so unless the preprocessor is invoked
698            # separately, these flags will still get to the preprocessor.
699            # Other options not currently handled:
700            #  -iqoutedir      (preprocessor search path)
701            #  -u symbol       (linker undefined symbol)
702            #  -s              (linker strip files)
703            #  -static*        (linker static binding)
704            #  -shared*        (linker dynamic binding)
705            #  -symbolic       (linker global binding)
706            #  -R dir          (deprecated linker rpath)
707            # IBM compilers may also accept -qframeworkdir=foo
708
709            params = shlex.split(arg)
710            append_next_arg_to = None   # for multi-word args
711            for arg in params:
712                if append_next_arg_to:
713                   if append_next_arg_to == 'CPPDEFINES':
714                       append_define(arg)
715                   elif append_next_arg_to == '-include':
716                       t = ('-include', self.fs.File(arg))
717                       dict['CCFLAGS'].append(t)
718                   elif append_next_arg_to == '-isysroot':
719                       t = ('-isysroot', arg)
720                       dict['CCFLAGS'].append(t)
721                       dict['LINKFLAGS'].append(t)
722                   elif append_next_arg_to == '-arch':
723                       t = ('-arch', arg)
724                       dict['CCFLAGS'].append(t)
725                       dict['LINKFLAGS'].append(t)
726                   else:
727                       dict[append_next_arg_to].append(arg)
728                   append_next_arg_to = None
729                elif not arg[0] in ['-', '+']:
730                    dict['LIBS'].append(self.fs.File(arg))
731                elif arg == '-dylib_file':
732                    dict['LINKFLAGS'].append(arg)
733                    append_next_arg_to = 'LINKFLAGS'
734                elif arg[:2] == '-L':
735                    if arg[2:]:
736                        dict['LIBPATH'].append(arg[2:])
737                    else:
738                        append_next_arg_to = 'LIBPATH'
739                elif arg[:2] == '-l':
740                    if arg[2:]:
741                        dict['LIBS'].append(arg[2:])
742                    else:
743                        append_next_arg_to = 'LIBS'
744                elif arg[:2] == '-I':
745                    if arg[2:]:
746                        dict['CPPPATH'].append(arg[2:])
747                    else:
748                        append_next_arg_to = 'CPPPATH'
749                elif arg[:4] == '-Wa,':
750                    dict['ASFLAGS'].append(arg[4:])
751                    dict['CCFLAGS'].append(arg)
752                elif arg[:4] == '-Wl,':
753                    if arg[:11] == '-Wl,-rpath=':
754                        dict['RPATH'].append(arg[11:])
755                    elif arg[:7] == '-Wl,-R,':
756                        dict['RPATH'].append(arg[7:])
757                    elif arg[:6] == '-Wl,-R':
758                        dict['RPATH'].append(arg[6:])
759                    else:
760                        dict['LINKFLAGS'].append(arg)
761                elif arg[:4] == '-Wp,':
762                    dict['CPPFLAGS'].append(arg)
763                elif arg[:2] == '-D':
764                    if arg[2:]:
765                        append_define(arg[2:])
766                    else:
767                        append_next_arg_to = 'CPPDEFINES'
768                elif arg == '-framework':
769                    append_next_arg_to = 'FRAMEWORKS'
770                elif arg[:14] == '-frameworkdir=':
771                    dict['FRAMEWORKPATH'].append(arg[14:])
772                elif arg[:2] == '-F':
773                    if arg[2:]:
774                        dict['FRAMEWORKPATH'].append(arg[2:])
775                    else:
776                        append_next_arg_to = 'FRAMEWORKPATH'
777                elif arg in ['-mno-cygwin',
778                             '-pthread',
779                             '-openmp',
780                             '-fopenmp']:
781                    dict['CCFLAGS'].append(arg)
782                    dict['LINKFLAGS'].append(arg)
783                elif arg == '-mwindows':
784                    dict['LINKFLAGS'].append(arg)
785                elif arg[:5] == '-std=':
786                    if arg[5:].find('++')!=-1:
787                        key='CXXFLAGS'
788                    else:
789                        key='CFLAGS'
790                    dict[key].append(arg)
791                elif arg[0] == '+':
792                    dict['CCFLAGS'].append(arg)
793                    dict['LINKFLAGS'].append(arg)
794                elif arg in ['-include', '-isysroot', '-arch']:
795                    append_next_arg_to = arg
796                else:
797                    dict['CCFLAGS'].append(arg)
798
799        for arg in flags:
800            do_parse(arg)
801        return dict
802
803    def MergeFlags(self, args, unique=1, dict=None):
804        """
805        Merge the dict in args into the construction variables of this
806        env, or the passed-in dict.  If args is not a dict, it is
807        converted into a dict using ParseFlags.  If unique is not set,
808        the flags are appended rather than merged.
809        """
810
811        if dict is None:
812            dict = self
813        if not SCons.Util.is_Dict(args):
814            args = self.ParseFlags(args)
815        if not unique:
816            self.Append(**args)
817            return self
818        for key, value in args.items():
819            if not value:
820                continue
821            try:
822                orig = self[key]
823            except KeyError:
824                orig = value
825            else:
826                if not orig:
827                    orig = value
828                elif value:
829                    # Add orig and value.  The logic here was lifted from
830                    # part of env.Append() (see there for a lot of comments
831                    # about the order in which things are tried) and is
832                    # used mainly to handle coercion of strings to CLVar to
833                    # "do the right thing" given (e.g.) an original CCFLAGS
834                    # string variable like '-pipe -Wall'.
835                    try:
836                        orig = orig + value
837                    except (KeyError, TypeError):
838                        try:
839                            add_to_orig = orig.append
840                        except AttributeError:
841                            value.insert(0, orig)
842                            orig = value
843                        else:
844                            add_to_orig(value)
845            t = []
846            if key[-4:] == 'PATH':
847                ### keep left-most occurence
848                for v in orig:
849                    if v not in t:
850                        t.append(v)
851            else:
852                ### keep right-most occurence
853                orig.reverse()
854                for v in orig:
855                    if v not in t:
856                        t.insert(0, v)
857            self[key] = t
858        return self
859
860#     def MergeShellPaths(self, args, prepend=1):
861#         """
862#         Merge the dict in args into the shell environment in env['ENV'].
863#         Shell path elements are appended or prepended according to prepend.
864
865#         Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
866
867#         Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
868#         prepends /usr/local/lib to env['ENV']['LIBPATH'].
869#         """
870
871#         for pathname, pathval in args.items():
872#             if not pathval:
873#                 continue
874#             if prepend:
875#                 self.PrependENVPath(pathname, pathval)
876#             else:
877#                 self.AppendENVPath(pathname, pathval)
878
879
880def default_decide_source(dependency, target, prev_ni):
881    f = SCons.Defaults.DefaultEnvironment().decide_source
882    return f(dependency, target, prev_ni)
883
884def default_decide_target(dependency, target, prev_ni):
885    f = SCons.Defaults.DefaultEnvironment().decide_target
886    return f(dependency, target, prev_ni)
887
888def default_copy_from_cache(src, dst):
889    f = SCons.Defaults.DefaultEnvironment().copy_from_cache
890    return f(src, dst)
891
892class Base(SubstitutionEnvironment):
893    """Base class for "real" construction Environments.  These are the
894    primary objects used to communicate dependency and construction
895    information to the build engine.
896
897    Keyword arguments supplied when the construction Environment
898    is created are construction variables used to initialize the
899    Environment.
900    """
901
902    memoizer_counters = []
903
904    #######################################################################
905    # This is THE class for interacting with the SCons build engine,
906    # and it contains a lot of stuff, so we're going to try to keep this
907    # a little organized by grouping the methods.
908    #######################################################################
909
910    #######################################################################
911    # Methods that make an Environment act like a dictionary.  These have
912    # the expected standard names for Python mapping objects.  Note that
913    # we don't actually make an Environment a subclass of UserDict for
914    # performance reasons.  Note also that we only supply methods for
915    # dictionary functionality that we actually need and use.
916    #######################################################################
917
918    def __init__(self,
919                 platform=None,
920                 tools=None,
921                 toolpath=None,
922                 variables=None,
923                 parse_flags = None,
924                 **kw):
925        """
926        Initialization of a basic SCons construction environment,
927        including setting up special construction variables like BUILDER,
928        PLATFORM, etc., and searching for and applying available Tools.
929
930        Note that we do *not* call the underlying base class
931        (SubsitutionEnvironment) initialization, because we need to
932        initialize things in a very specific order that doesn't work
933        with the much simpler base class initialization.
934        """
935        if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.Base')
936        self._memo = {}
937        self.fs = SCons.Node.FS.get_default_fs()
938        self.ans = SCons.Node.Alias.default_ans
939        self.lookup_list = SCons.Node.arg2nodes_lookups
940        self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
941        self._init_special()
942        self.added_methods = []
943
944        # We don't use AddMethod, or define these as methods in this
945        # class, because we *don't* want these functions to be bound
946        # methods.  They need to operate independently so that the
947        # settings will work properly regardless of whether a given
948        # target ends up being built with a Base environment or an
949        # OverrideEnvironment or what have you.
950        self.decide_target = default_decide_target
951        self.decide_source = default_decide_source
952
953        self.copy_from_cache = default_copy_from_cache
954
955        self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
956
957        if platform is None:
958            platform = self._dict.get('PLATFORM', None)
959            if platform is None:
960                platform = SCons.Platform.Platform()
961        if SCons.Util.is_String(platform):
962            platform = SCons.Platform.Platform(platform)
963        self._dict['PLATFORM'] = str(platform)
964        platform(self)
965
966        self._dict['HOST_OS']      = self._dict.get('HOST_OS',None)
967        self._dict['HOST_ARCH']    = self._dict.get('HOST_ARCH',None)
968
969        # Now set defaults for TARGET_{OS|ARCH}
970        self._dict['TARGET_OS']      = self._dict.get('TARGET_OS',None)
971        self._dict['TARGET_ARCH']    = self._dict.get('TARGET_ARCH',None)
972
973
974        # Apply the passed-in and customizable variables to the
975        # environment before calling the tools, because they may use
976        # some of them during initialization.
977        if 'options' in kw:
978            # Backwards compatibility:  they may stll be using the
979            # old "options" keyword.
980            variables = kw['options']
981            del kw['options']
982        self.Replace(**kw)
983        keys = list(kw.keys())
984        if variables:
985            keys = keys + list(variables.keys())
986            variables.Update(self)
987
988        save = {}
989        for k in keys:
990            try:
991                save[k] = self._dict[k]
992            except KeyError:
993                # No value may have been set if they tried to pass in a
994                # reserved variable name like TARGETS.
995                pass
996
997        SCons.Tool.Initializers(self)
998
999        if tools is None:
1000            tools = self._dict.get('TOOLS', None)
1001            if tools is None:
1002                tools = ['default']
1003        apply_tools(self, tools, toolpath)
1004
1005        # Now restore the passed-in and customized variables
1006        # to the environment, since the values the user set explicitly
1007        # should override any values set by the tools.
1008        for key, val in save.items():
1009            self._dict[key] = val
1010
1011        # Finally, apply any flags to be merged in
1012        if parse_flags: self.MergeFlags(parse_flags)
1013
1014    #######################################################################
1015    # Utility methods that are primarily for internal use by SCons.
1016    # These begin with lower-case letters.
1017    #######################################################################
1018
1019    def get_builder(self, name):
1020        """Fetch the builder with the specified name from the environment.
1021        """
1022        try:
1023            return self._dict['BUILDERS'][name]
1024        except KeyError:
1025            return None
1026
1027    def get_CacheDir(self):
1028        try:
1029            path = self._CacheDir_path
1030        except AttributeError:
1031            path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
1032        try:
1033            if path == self._last_CacheDir_path:
1034                return self._last_CacheDir
1035        except AttributeError:
1036            pass
1037        cd = SCons.CacheDir.CacheDir(path)
1038        self._last_CacheDir_path = path
1039        self._last_CacheDir = cd
1040        return cd
1041
1042    def get_factory(self, factory, default='File'):
1043        """Return a factory function for creating Nodes for this
1044        construction environment.
1045        """
1046        name = default
1047        try:
1048            is_node = issubclass(factory, SCons.Node.FS.Base)
1049        except TypeError:
1050            # The specified factory isn't a Node itself--it's
1051            # most likely None, or possibly a callable.
1052            pass
1053        else:
1054            if is_node:
1055                # The specified factory is a Node (sub)class.  Try to
1056                # return the FS method that corresponds to the Node's
1057                # name--that is, we return self.fs.Dir if they want a Dir,
1058                # self.fs.File for a File, etc.
1059                try: name = factory.__name__
1060                except AttributeError: pass
1061                else: factory = None
1062        if not factory:
1063            # They passed us None, or we picked up a name from a specified
1064            # class, so return the FS method.  (Note that we *don't*
1065            # use our own self.{Dir,File} methods because that would
1066            # cause env.subst() to be called twice on the file name,
1067            # interfering with files that have $$ in them.)
1068            factory = getattr(self.fs, name)
1069        return factory
1070
1071    memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
1072
1073    def _gsm(self):
1074        try:
1075            return self._memo['_gsm']
1076        except KeyError:
1077            pass
1078
1079        result = {}
1080
1081        try:
1082            scanners = self._dict['SCANNERS']
1083        except KeyError:
1084            pass
1085        else:
1086            # Reverse the scanner list so that, if multiple scanners
1087            # claim they can scan the same suffix, earlier scanners
1088            # in the list will overwrite later scanners, so that
1089            # the result looks like a "first match" to the user.
1090            if not SCons.Util.is_List(scanners):
1091                scanners = [scanners]
1092            else:
1093                scanners = scanners[:] # copy so reverse() doesn't mod original
1094            scanners.reverse()
1095            for scanner in scanners:
1096                for k in scanner.get_skeys(self):
1097                    if k and self['PLATFORM'] == 'win32':
1098                        k = k.lower()
1099                    result[k] = scanner
1100
1101        self._memo['_gsm'] = result
1102
1103        return result
1104
1105    def get_scanner(self, skey):
1106        """Find the appropriate scanner given a key (usually a file suffix).
1107        """
1108        if skey and self['PLATFORM'] == 'win32':
1109            skey = skey.lower()
1110        return self._gsm().get(skey)
1111
1112    def scanner_map_delete(self, kw=None):
1113        """Delete the cached scanner map (if we need to).
1114        """
1115        try:
1116            del self._memo['_gsm']
1117        except KeyError:
1118            pass
1119
1120    def _update(self, dict):
1121        """Update an environment's values directly, bypassing the normal
1122        checks that occur when users try to set items.
1123        """
1124        self._dict.update(dict)
1125
1126    def get_src_sig_type(self):
1127        try:
1128            return self.src_sig_type
1129        except AttributeError:
1130            t = SCons.Defaults.DefaultEnvironment().src_sig_type
1131            self.src_sig_type = t
1132            return t
1133
1134    def get_tgt_sig_type(self):
1135        try:
1136            return self.tgt_sig_type
1137        except AttributeError:
1138            t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
1139            self.tgt_sig_type = t
1140            return t
1141
1142    #######################################################################
1143    # Public methods for manipulating an Environment.  These begin with
1144    # upper-case letters.  The essential characteristic of methods in
1145    # this section is that they do *not* have corresponding same-named
1146    # global functions.  For example, a stand-alone Append() function
1147    # makes no sense, because Append() is all about appending values to
1148    # an Environment's construction variables.
1149    #######################################################################
1150
1151    def Append(self, **kw):
1152        """Append values to existing construction variables
1153        in an Environment.
1154        """
1155        kw = copy_non_reserved_keywords(kw)
1156        for key, val in kw.items():
1157            # It would be easier on the eyes to write this using
1158            # "continue" statements whenever we finish processing an item,
1159            # but Python 1.5.2 apparently doesn't let you use "continue"
1160            # within try:-except: blocks, so we have to nest our code.
1161            try:
1162                if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]):
1163                    self._dict[key] = [self._dict[key]]
1164                orig = self._dict[key]
1165            except KeyError:
1166                # No existing variable in the environment, so just set
1167                # it to the new value.
1168                if key == 'CPPDEFINES' and SCons.Util.is_String(val):
1169                    self._dict[key] = [val]
1170                else:
1171                    self._dict[key] = val
1172            else:
1173                try:
1174                    # Check if the original looks like a dictionary.
1175                    # If it is, we can't just try adding the value because
1176                    # dictionaries don't have __add__() methods, and
1177                    # things like UserList will incorrectly coerce the
1178                    # original dict to a list (which we don't want).
1179                    update_dict = orig.update
1180                except AttributeError:
1181                    try:
1182                        # Most straightforward:  just try to add them
1183                        # together.  This will work in most cases, when the
1184                        # original and new values are of compatible types.
1185                        self._dict[key] = orig + val
1186                    except (KeyError, TypeError):
1187                        try:
1188                            # Check if the original is a list.
1189                            add_to_orig = orig.append
1190                        except AttributeError:
1191                            # The original isn't a list, but the new
1192                            # value is (by process of elimination),
1193                            # so insert the original in the new value
1194                            # (if there's one to insert) and replace
1195                            # the variable with it.
1196                            if orig:
1197                                val.insert(0, orig)
1198                            self._dict[key] = val
1199                        else:
1200                            # The original is a list, so append the new
1201                            # value to it (if there's a value to append).
1202                            if val:
1203                                add_to_orig(val)
1204                else:
1205                    # The original looks like a dictionary, so update it
1206                    # based on what we think the value looks like.
1207                    if SCons.Util.is_List(val):
1208                        if key == 'CPPDEFINES':
1209                            orig = orig.items()
1210                            orig += val
1211                            self._dict[key] = orig
1212                        else:
1213                            for v in val:
1214                                orig[v] = None
1215                    else:
1216                        try:
1217                            update_dict(val)
1218                        except (AttributeError, TypeError, ValueError):
1219                            if SCons.Util.is_Dict(val):
1220                                for k, v in val.items():
1221                                    orig[k] = v
1222                            else:
1223                                orig[val] = None
1224        self.scanner_map_delete(kw)
1225
1226    # allow Dirs and strings beginning with # for top-relative
1227    # Note this uses the current env's fs (in self).
1228    def _canonicalize(self, path):
1229        if not SCons.Util.is_String(path): # typically a Dir
1230            path = str(path)
1231        if path and path[0] == '#':
1232            path = str(self.fs.Dir(path))
1233        return path
1234
1235    def AppendENVPath(self, name, newpath, envname = 'ENV',
1236                      sep = os.pathsep, delete_existing=1):
1237        """Append path elements to the path 'name' in the 'ENV'
1238        dictionary for this environment.  Will only add any particular
1239        path once, and will normpath and normcase all paths to help
1240        assure this.  This can also handle the case where the env
1241        variable is a list instead of a string.
1242
1243        If delete_existing is 0, a newpath which is already in the path
1244        will not be moved to the end (it will be left where it is).
1245        """
1246
1247        orig = ''
1248        if envname in self._dict and name in self._dict[envname]:
1249            orig = self._dict[envname][name]
1250
1251        nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing,
1252                                   canonicalize=self._canonicalize)
1253
1254        if envname not in self._dict:
1255            self._dict[envname] = {}
1256
1257        self._dict[envname][name] = nv
1258
1259    def AppendUnique(self, delete_existing=0, **kw):
1260        """Append values to existing construction variables
1261        in an Environment, if they're not already there.
1262        If delete_existing is 1, removes existing values first, so
1263        values move to end.
1264        """
1265        kw = copy_non_reserved_keywords(kw)
1266        for key, val in kw.items():
1267            if SCons.Util.is_List(val):
1268                val = _delete_duplicates(val, delete_existing)
1269            if key not in self._dict or self._dict[key] in ('', None):
1270                self._dict[key] = val
1271            elif SCons.Util.is_Dict(self._dict[key]) and \
1272                 SCons.Util.is_Dict(val):
1273                self._dict[key].update(val)
1274            elif SCons.Util.is_List(val):
1275                dk = self._dict[key]
1276                if key == 'CPPDEFINES':
1277                    tmp = []
1278                    for i in val:
1279                        if SCons.Util.is_List(i):
1280                            if len(i) >= 2:
1281                                tmp.append((i[0], i[1]))
1282                            else:
1283                                tmp.append((i[0],))
1284                        elif SCons.Util.is_Tuple(i):
1285                            tmp.append(i)
1286                        else:
1287                            tmp.append((i,))
1288                    val = tmp
1289                    if SCons.Util.is_Dict(dk):
1290                        dk = dk.items()
1291                    elif SCons.Util.is_String(dk):
1292                        dk = [(dk,)]
1293                    else:
1294                        tmp = []
1295                        for i in dk:
1296                            if SCons.Util.is_List(i):
1297                                if len(i) >= 2:
1298                                    tmp.append((i[0], i[1]))
1299                                else:
1300                                    tmp.append((i[0],))
1301                            elif SCons.Util.is_Tuple(i):
1302                                tmp.append(i)
1303                            else:
1304                                tmp.append((i,))
1305                        dk = tmp
1306                else:
1307                    if not SCons.Util.is_List(dk):
1308                        dk = [dk]
1309                if delete_existing:
1310                    dk = [x for x in dk if x not in val]
1311                else:
1312                    val = [x for x in val if x not in dk]
1313                self._dict[key] = dk + val
1314            else:
1315                dk = self._dict[key]
1316                if SCons.Util.is_List(dk):
1317                    if key == 'CPPDEFINES':
1318                        tmp = []
1319                        for i in dk:
1320                            if SCons.Util.is_List(i):
1321                                if len(i) >= 2:
1322                                    tmp.append((i[0], i[1]))
1323                                else:
1324                                    tmp.append((i[0],))
1325                            elif SCons.Util.is_Tuple(i):
1326                                tmp.append(i)
1327                            else:
1328                                tmp.append((i,))
1329                        dk = tmp
1330                        if SCons.Util.is_Dict(val):
1331                            val = val.items()
1332                        elif SCons.Util.is_String(val):
1333                            val = [(val,)]
1334                        if delete_existing:
1335                            dk = filter(lambda x, val=val: x not in val, dk)
1336                            self._dict[key] = dk + val
1337                        else:
1338                            dk = [x for x in dk if x not in val]
1339                            self._dict[key] = dk + val
1340                    else:
1341                        # By elimination, val is not a list.  Since dk is a
1342                        # list, wrap val in a list first.
1343                        if delete_existing:
1344                            dk = filter(lambda x, val=val: x not in val, dk)
1345                            self._dict[key] = dk + [val]
1346                        else:
1347                            if not val in dk:
1348                                self._dict[key] = dk + [val]
1349                else:
1350                    if key == 'CPPDEFINES':
1351                        if SCons.Util.is_String(dk):
1352                            dk = [dk]
1353                        elif SCons.Util.is_Dict(dk):
1354                            dk = dk.items()
1355                        if SCons.Util.is_String(val):
1356                            if val in dk:
1357                                val = []
1358                            else:
1359                                val = [val]
1360                        elif SCons.Util.is_Dict(val):
1361                            tmp = []
1362                            for i,j in val.iteritems():
1363                                if j is not None:
1364                                    tmp.append((i,j))
1365                                else:
1366                                    tmp.append(i)
1367                            val = tmp
1368                    if delete_existing:
1369                        dk = [x for x in dk if x not in val]
1370                    self._dict[key] = dk + val
1371        self.scanner_map_delete(kw)
1372
1373    def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1374        """Return a copy of a construction Environment.  The
1375        copy is like a Python "deep copy"--that is, independent
1376        copies are made recursively of each objects--except that
1377        a reference is copied when an object is not deep-copyable
1378        (like a function).  There are no references to any mutable
1379        objects in the original Environment.
1380        """
1381        try:
1382            builders = self._dict['BUILDERS']
1383        except KeyError:
1384            pass
1385
1386        clone = copy.copy(self)
1387        # BUILDERS is not safe to do a simple copy
1388        clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS'])
1389        clone._dict['BUILDERS'] = BuilderDict(builders, clone)
1390
1391        # Check the methods added via AddMethod() and re-bind them to
1392        # the cloned environment.  Only do this if the attribute hasn't
1393        # been overwritten by the user explicitly and still points to
1394        # the added method.
1395        clone.added_methods = []
1396        for mw in self.added_methods:
1397            if mw == getattr(self, mw.name):
1398                clone.added_methods.append(mw.clone(clone))
1399
1400        clone._memo = {}
1401
1402        # Apply passed-in variables before the tools
1403        # so the tools can use the new variables
1404        kw = copy_non_reserved_keywords(kw)
1405        new = {}
1406        for key, value in kw.items():
1407            new[key] = SCons.Subst.scons_subst_once(value, self, key)
1408        clone.Replace(**new)
1409
1410        apply_tools(clone, tools, toolpath)
1411
1412        # apply them again in case the tools overwrote them
1413        clone.Replace(**new)
1414
1415        # Finally, apply any flags to be merged in
1416        if parse_flags: clone.MergeFlags(parse_flags)
1417
1418        if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.EnvironmentClone')
1419        return clone
1420
1421    def Copy(self, *args, **kw):
1422        global _warn_copy_deprecated
1423        if _warn_copy_deprecated:
1424            msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
1425            SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
1426            _warn_copy_deprecated = False
1427        return self.Clone(*args, **kw)
1428
1429    def _changed_build(self, dependency, target, prev_ni):
1430        if dependency.changed_state(target, prev_ni):
1431            return 1
1432        return self.decide_source(dependency, target, prev_ni)
1433
1434    def _changed_content(self, dependency, target, prev_ni):
1435        return dependency.changed_content(target, prev_ni)
1436
1437    def _changed_source(self, dependency, target, prev_ni):
1438        target_env = dependency.get_build_env()
1439        type = target_env.get_tgt_sig_type()
1440        if type == 'source':
1441            return target_env.decide_source(dependency, target, prev_ni)
1442        else:
1443            return target_env.decide_target(dependency, target, prev_ni)
1444
1445    def _changed_timestamp_then_content(self, dependency, target, prev_ni):
1446        return dependency.changed_timestamp_then_content(target, prev_ni)
1447
1448    def _changed_timestamp_newer(self, dependency, target, prev_ni):
1449        return dependency.changed_timestamp_newer(target, prev_ni)
1450
1451    def _changed_timestamp_match(self, dependency, target, prev_ni):
1452        return dependency.changed_timestamp_match(target, prev_ni)
1453
1454    def _copy_from_cache(self, src, dst):
1455        return self.fs.copy(src, dst)
1456
1457    def _copy2_from_cache(self, src, dst):
1458        return self.fs.copy2(src, dst)
1459
1460    def Decider(self, function):
1461        copy_function = self._copy2_from_cache
1462        if function in ('MD5', 'content'):
1463            if not SCons.Util.md5:
1464                raise UserError("MD5 signatures are not available in this version of Python.")
1465            function = self._changed_content
1466        elif function == 'MD5-timestamp':
1467            function = self._changed_timestamp_then_content
1468        elif function in ('timestamp-newer', 'make'):
1469            function = self._changed_timestamp_newer
1470            copy_function = self._copy_from_cache
1471        elif function == 'timestamp-match':
1472            function = self._changed_timestamp_match
1473        elif not callable(function):
1474            raise UserError("Unknown Decider value %s" % repr(function))
1475
1476        # We don't use AddMethod because we don't want to turn the
1477        # function, which only expects three arguments, into a bound
1478        # method, which would add self as an initial, fourth argument.
1479        self.decide_target = function
1480        self.decide_source = function
1481
1482        self.copy_from_cache = copy_function
1483
1484    def Detect(self, progs):
1485        """Return the first available program in progs.
1486        """
1487        if not SCons.Util.is_List(progs):
1488            progs = [ progs ]
1489        for prog in progs:
1490            path = self.WhereIs(prog)
1491            if path: return prog
1492        return None
1493
1494    def Dictionary(self, *args):
1495        if not args:
1496            return self._dict
1497        dlist = [self._dict[x] for x in args]
1498        if len(dlist) == 1:
1499            dlist = dlist[0]
1500        return dlist
1501
1502    def Dump(self, key = None):
1503        """
1504        Using the standard Python pretty printer, dump the contents of the
1505        scons build environment to stdout.
1506
1507        If the key passed in is anything other than None, then that will
1508        be used as an index into the build environment dictionary and
1509        whatever is found there will be fed into the pretty printer. Note
1510        that this key is case sensitive.
1511        """
1512        import pprint
1513        pp = pprint.PrettyPrinter(indent=2)
1514        if key:
1515            dict = self.Dictionary(key)
1516        else:
1517            dict = self.Dictionary()
1518        return pp.pformat(dict)
1519
1520    def FindIxes(self, paths, prefix, suffix):
1521        """
1522        Search a list of paths for something that matches the prefix and suffix.
1523
1524        paths - the list of paths or nodes.
1525        prefix - construction variable for the prefix.
1526        suffix - construction variable for the suffix.
1527        """
1528
1529        suffix = self.subst('$'+suffix)
1530        prefix = self.subst('$'+prefix)
1531
1532        for path in paths:
1533            dir,name = os.path.split(str(path))
1534            if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
1535                return path
1536
1537    def ParseConfig(self, command, function=None, unique=1):
1538        """
1539        Use the specified function to parse the output of the command
1540        in order to modify the current environment.  The 'command' can
1541        be a string or a list of strings representing a command and
1542        its arguments.  'Function' is an optional argument that takes
1543        the environment, the output of the command, and the unique flag.
1544        If no function is specified, MergeFlags, which treats the output
1545        as the result of a typical 'X-config' command (i.e. gtk-config),
1546        will merge the output into the appropriate variables.
1547        """
1548        if function is None:
1549            def parse_conf(env, cmd, unique=unique):
1550                return env.MergeFlags(cmd, unique)
1551            function = parse_conf
1552        if SCons.Util.is_List(command):
1553            command = ' '.join(command)
1554        command = self.subst(command)
1555        return function(self, self.backtick(command))
1556
1557    def ParseDepends(self, filename, must_exist=None, only_one=0):
1558        """
1559        Parse a mkdep-style file for explicit dependencies.  This is
1560        completely abusable, and should be unnecessary in the "normal"
1561        case of proper SCons configuration, but it may help make
1562        the transition from a Make hierarchy easier for some people
1563        to swallow.  It can also be genuinely useful when using a tool
1564        that can write a .d file, but for which writing a scanner would
1565        be too complicated.
1566        """
1567        filename = self.subst(filename)
1568        try:
1569            fp = open(filename, 'r')
1570        except IOError:
1571            if must_exist:
1572                raise
1573            return
1574        lines = SCons.Util.LogicalLines(fp).readlines()
1575        lines = [l for l in lines if l[0] != '#']
1576        tdlist = []
1577        for line in lines:
1578            try:
1579                target, depends = line.split(':', 1)
1580            except (AttributeError, ValueError):
1581                # Throws AttributeError if line isn't a string.  Can throw
1582                # ValueError if line doesn't split into two or more elements.
1583                pass
1584            else:
1585                tdlist.append((target.split(), depends.split()))
1586        if only_one:
1587            targets = []
1588            for td in tdlist:
1589                targets.extend(td[0])
1590            if len(targets) > 1:
1591                raise SCons.Errors.UserError(
1592                            "More than one dependency target found in `%s':  %s"
1593                                            % (filename, targets))
1594        for target, depends in tdlist:
1595            self.Depends(target, depends)
1596
1597    def Platform(self, platform):
1598        platform = self.subst(platform)
1599        return SCons.Platform.Platform(platform)(self)
1600
1601    def Prepend(self, **kw):
1602        """Prepend values to existing construction variables
1603        in an Environment.
1604        """
1605        kw = copy_non_reserved_keywords(kw)
1606        for key, val in kw.items():
1607            # It would be easier on the eyes to write this using
1608            # "continue" statements whenever we finish processing an item,
1609            # but Python 1.5.2 apparently doesn't let you use "continue"
1610            # within try:-except: blocks, so we have to nest our code.
1611            try:
1612                orig = self._dict[key]
1613            except KeyError:
1614                # No existing variable in the environment, so just set
1615                # it to the new value.
1616                self._dict[key] = val
1617            else:
1618                try:
1619                    # Check if the original looks like a dictionary.
1620                    # If it is, we can't just try adding the value because
1621                    # dictionaries don't have __add__() methods, and
1622                    # things like UserList will incorrectly coerce the
1623                    # original dict to a list (which we don't want).
1624                    update_dict = orig.update
1625                except AttributeError:
1626                    try:
1627                        # Most straightforward:  just try to add them
1628                        # together.  This will work in most cases, when the
1629                        # original and new values are of compatible types.
1630                        self._dict[key] = val + orig
1631                    except (KeyError, TypeError):
1632                        try:
1633                            # Check if the added value is a list.
1634                            add_to_val = val.append
1635                        except AttributeError:
1636                            # The added value isn't a list, but the
1637                            # original is (by process of elimination),
1638                            # so insert the the new value in the original
1639                            # (if there's one to insert).
1640                            if val:
1641                                orig.insert(0, val)
1642                        else:
1643                            # The added value is a list, so append
1644                            # the original to it (if there's a value
1645                            # to append).
1646                            if orig:
1647                                add_to_val(orig)
1648                            self._dict[key] = val
1649                else:
1650                    # The original looks like a dictionary, so update it
1651                    # based on what we think the value looks like.
1652                    if SCons.Util.is_List(val):
1653                        for v in val:
1654                            orig[v] = None
1655                    else:
1656                        try:
1657                            update_dict(val)
1658                        except (AttributeError, TypeError, ValueError):
1659                            if SCons.Util.is_Dict(val):
1660                                for k, v in val.items():
1661                                    orig[k] = v
1662                            else:
1663                                orig[val] = None
1664        self.scanner_map_delete(kw)
1665
1666    def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
1667                       delete_existing=1):
1668        """Prepend path elements to the path 'name' in the 'ENV'
1669        dictionary for this environment.  Will only add any particular
1670        path once, and will normpath and normcase all paths to help
1671        assure this.  This can also handle the case where the env
1672        variable is a list instead of a string.
1673
1674        If delete_existing is 0, a newpath which is already in the path
1675        will not be moved to the front (it will be left where it is).
1676        """
1677
1678        orig = ''
1679        if envname in self._dict and name in self._dict[envname]:
1680            orig = self._dict[envname][name]
1681
1682        nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing,
1683                                    canonicalize=self._canonicalize)
1684
1685        if envname not in self._dict:
1686            self._dict[envname] = {}
1687
1688        self._dict[envname][name] = nv
1689
1690    def PrependUnique(self, delete_existing=0, **kw):
1691        """Prepend values to existing construction variables
1692        in an Environment, if they're not already there.
1693        If delete_existing is 1, removes existing values first, so
1694        values move to front.
1695        """
1696        kw = copy_non_reserved_keywords(kw)
1697        for key, val in kw.items():
1698            if SCons.Util.is_List(val):
1699                val = _delete_duplicates(val, not delete_existing)
1700            if key not in self._dict or self._dict[key] in ('', None):
1701                self._dict[key] = val
1702            elif SCons.Util.is_Dict(self._dict[key]) and \
1703                 SCons.Util.is_Dict(val):
1704                self._dict[key].update(val)
1705            elif SCons.Util.is_List(val):
1706                dk = self._dict[key]
1707                if not SCons.Util.is_List(dk):
1708                    dk = [dk]
1709                if delete_existing:
1710                    dk = [x for x in dk if x not in val]
1711                else:
1712                    val = [x for x in val if x not in dk]
1713                self._dict[key] = val + dk
1714            else:
1715                dk = self._dict[key]
1716                if SCons.Util.is_List(dk):
1717                    # By elimination, val is not a list.  Since dk is a
1718                    # list, wrap val in a list first.
1719                    if delete_existing:
1720                        dk = [x for x in dk if x not in val]
1721                        self._dict[key] = [val] + dk
1722                    else:
1723                        if not val in dk:
1724                            self._dict[key] = [val] + dk
1725                else:
1726                    if delete_existing:
1727                        dk = [x for x in dk if x not in val]
1728                    self._dict[key] = val + dk
1729        self.scanner_map_delete(kw)
1730
1731    def Replace(self, **kw):
1732        """Replace existing construction variables in an Environment
1733        with new construction variables and/or values.
1734        """
1735        try:
1736            kwbd = kw['BUILDERS']
1737        except KeyError:
1738            pass
1739        else:
1740            kwbd = BuilderDict(kwbd,self)
1741            del kw['BUILDERS']
1742            self.__setitem__('BUILDERS', kwbd)
1743        kw = copy_non_reserved_keywords(kw)
1744        self._update(semi_deepcopy(kw))
1745        self.scanner_map_delete(kw)
1746
1747    def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1748        """
1749        Replace old_prefix with new_prefix and old_suffix with new_suffix.
1750
1751        env - Environment used to interpolate variables.
1752        path - the path that will be modified.
1753        old_prefix - construction variable for the old prefix.
1754        old_suffix - construction variable for the old suffix.
1755        new_prefix - construction variable for the new prefix.
1756        new_suffix - construction variable for the new suffix.
1757        """
1758        old_prefix = self.subst('$'+old_prefix)
1759        old_suffix = self.subst('$'+old_suffix)
1760
1761        new_prefix = self.subst('$'+new_prefix)
1762        new_suffix = self.subst('$'+new_suffix)
1763
1764        dir,name = os.path.split(str(path))
1765        if name[:len(old_prefix)] == old_prefix:
1766            name = name[len(old_prefix):]
1767        if name[-len(old_suffix):] == old_suffix:
1768            name = name[:-len(old_suffix)]
1769        return os.path.join(dir, new_prefix+name+new_suffix)
1770
1771    def SetDefault(self, **kw):
1772        for k in kw.keys():
1773            if k in self._dict:
1774                del kw[k]
1775        self.Replace(**kw)
1776
1777    def _find_toolpath_dir(self, tp):
1778        return self.fs.Dir(self.subst(tp)).srcnode().abspath
1779
1780    def Tool(self, tool, toolpath=None, **kw):
1781        if SCons.Util.is_String(tool):
1782            tool = self.subst(tool)
1783            if toolpath is None:
1784                toolpath = self.get('toolpath', [])
1785            toolpath = list(map(self._find_toolpath_dir, toolpath))
1786            tool = SCons.Tool.Tool(tool, toolpath, **kw)
1787        tool(self)
1788
1789    def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1790        """Find prog in the path.
1791        """
1792        if path is None:
1793            try:
1794                path = self['ENV']['PATH']
1795            except KeyError:
1796                pass
1797        elif SCons.Util.is_String(path):
1798            path = self.subst(path)
1799        if pathext is None:
1800            try:
1801                pathext = self['ENV']['PATHEXT']
1802            except KeyError:
1803                pass
1804        elif SCons.Util.is_String(pathext):
1805            pathext = self.subst(pathext)
1806        prog = SCons.Util.CLVar(self.subst(prog)) # support "program --with-args"
1807        path = SCons.Util.WhereIs(prog[0], path, pathext, reject)
1808        if path: return path
1809        return None
1810
1811    #######################################################################
1812    # Public methods for doing real "SCons stuff" (manipulating
1813    # dependencies, setting attributes on targets, etc.).  These begin
1814    # with upper-case letters.  The essential characteristic of methods
1815    # in this section is that they all *should* have corresponding
1816    # same-named global functions.
1817    #######################################################################
1818
1819    def Action(self, *args, **kw):
1820        def subst_string(a, self=self):
1821            if SCons.Util.is_String(a):
1822                a = self.subst(a)
1823            return a
1824        nargs = list(map(subst_string, args))
1825        nkw = self.subst_kw(kw)
1826        return SCons.Action.Action(*nargs, **nkw)
1827
1828    def AddPreAction(self, files, action):
1829        nodes = self.arg2nodes(files, self.fs.Entry)
1830        action = SCons.Action.Action(action)
1831        uniq = {}
1832        for executor in [n.get_executor() for n in nodes]:
1833            uniq[executor] = 1
1834        for executor in uniq.keys():
1835            executor.add_pre_action(action)
1836        return nodes
1837
1838    def AddPostAction(self, files, action):
1839        nodes = self.arg2nodes(files, self.fs.Entry)
1840        action = SCons.Action.Action(action)
1841        uniq = {}
1842        for executor in [n.get_executor() for n in nodes]:
1843            uniq[executor] = 1
1844        for executor in uniq.keys():
1845            executor.add_post_action(action)
1846        return nodes
1847
1848    def Alias(self, target, source=[], action=None, **kw):
1849        tlist = self.arg2nodes(target, self.ans.Alias)
1850        if not SCons.Util.is_List(source):
1851            source = [source]
1852        source = [_f for _f in source if _f]
1853
1854        if not action:
1855            if not source:
1856                # There are no source files and no action, so just
1857                # return a target list of classic Alias Nodes, without
1858                # any builder.  The externally visible effect is that
1859                # this will make the wrapping Script.BuildTask class
1860                # say that there's "Nothing to be done" for this Alias,
1861                # instead of that it's "up to date."
1862                return tlist
1863
1864            # No action, but there are sources.  Re-call all the target
1865            # builders to add the sources to each target.
1866            result = []
1867            for t in tlist:
1868                bld = t.get_builder(AliasBuilder)
1869                result.extend(bld(self, t, source))
1870            return result
1871
1872        nkw = self.subst_kw(kw)
1873        nkw.update({
1874            'action'            : SCons.Action.Action(action),
1875            'source_factory'    : self.fs.Entry,
1876            'multi'             : 1,
1877            'is_explicit'       : None,
1878        })
1879        bld = SCons.Builder.Builder(**nkw)
1880
1881        # Apply the Builder separately to each target so that the Aliases
1882        # stay separate.  If we did one "normal" Builder call with the
1883        # whole target list, then all of the target Aliases would be
1884        # associated under a single Executor.
1885        result = []
1886        for t in tlist:
1887            # Calling the convert() method will cause a new Executor to be
1888            # created from scratch, so we have to explicitly initialize
1889            # it with the target's existing sources, plus our new ones,
1890            # so nothing gets lost.
1891            b = t.get_builder()
1892            if b is None or b is AliasBuilder:
1893                b = bld
1894            else:
1895                nkw['action'] = b.action + action
1896                b = SCons.Builder.Builder(**nkw)
1897            t.convert()
1898            result.extend(b(self, t, t.sources + source))
1899        return result
1900
1901    def AlwaysBuild(self, *targets):
1902        tlist = []
1903        for t in targets:
1904            tlist.extend(self.arg2nodes(t, self.fs.Entry))
1905        for t in tlist:
1906            t.set_always_build()
1907        return tlist
1908
1909    def BuildDir(self, *args, **kw):
1910        msg = """BuildDir() and the build_dir keyword have been deprecated;\n\tuse VariantDir() and the variant_dir keyword instead."""
1911        SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg)
1912        if 'build_dir' in kw:
1913            kw['variant_dir'] = kw['build_dir']
1914            del kw['build_dir']
1915        return self.VariantDir(*args, **kw)
1916
1917    def Builder(self, **kw):
1918        nkw = self.subst_kw(kw)
1919        return SCons.Builder.Builder(**nkw)
1920
1921    def CacheDir(self, path):
1922        import SCons.CacheDir
1923        if path is not None:
1924            path = self.subst(path)
1925        self._CacheDir_path = path
1926
1927    def Clean(self, targets, files):
1928        global CleanTargets
1929        tlist = self.arg2nodes(targets, self.fs.Entry)
1930        flist = self.arg2nodes(files, self.fs.Entry)
1931        for t in tlist:
1932            try:
1933                CleanTargets[t].extend(flist)
1934            except KeyError:
1935                CleanTargets[t] = flist
1936
1937    def Configure(self, *args, **kw):
1938        nargs = [self]
1939        if args:
1940            nargs = nargs + self.subst_list(args)[0]
1941        nkw = self.subst_kw(kw)
1942        nkw['_depth'] = kw.get('_depth', 0) + 1
1943        try:
1944            nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1945        except KeyError:
1946            pass
1947        return SCons.SConf.SConf(*nargs, **nkw)
1948
1949    def Command(self, target, source, action, **kw):
1950        """Builds the supplied target files from the supplied
1951        source files using the supplied action.  Action may
1952        be any type that the Builder constructor will accept
1953        for an action."""
1954        bkw = {
1955            'action' : action,
1956            'target_factory' : self.fs.Entry,
1957            'source_factory' : self.fs.Entry,
1958        }
1959        try: bkw['source_scanner'] = kw['source_scanner']
1960        except KeyError: pass
1961        else: del kw['source_scanner']
1962        bld = SCons.Builder.Builder(**bkw)
1963        return bld(self, target, source, **kw)
1964
1965    def Depends(self, target, dependency):
1966        """Explicity specify that 'target's depend on 'dependency'."""
1967        tlist = self.arg2nodes(target, self.fs.Entry)
1968        dlist = self.arg2nodes(dependency, self.fs.Entry)
1969        for t in tlist:
1970            t.add_dependency(dlist)
1971        return tlist
1972
1973    def Dir(self, name, *args, **kw):
1974        """
1975        """
1976        s = self.subst(name)
1977        if SCons.Util.is_Sequence(s):
1978            result=[]
1979            for e in s:
1980                result.append(self.fs.Dir(e, *args, **kw))
1981            return result
1982        return self.fs.Dir(s, *args, **kw)
1983
1984    def NoClean(self, *targets):
1985        """Tags a target so that it will not be cleaned by -c"""
1986        tlist = []
1987        for t in targets:
1988            tlist.extend(self.arg2nodes(t, self.fs.Entry))
1989        for t in tlist:
1990            t.set_noclean()
1991        return tlist
1992
1993    def NoCache(self, *targets):
1994        """Tags a target so that it will not be cached"""
1995        tlist = []
1996        for t in targets:
1997            tlist.extend(self.arg2nodes(t, self.fs.Entry))
1998        for t in tlist:
1999            t.set_nocache()
2000        return tlist
2001
2002    def Entry(self, name, *args, **kw):
2003        """
2004        """
2005        s = self.subst(name)
2006        if SCons.Util.is_Sequence(s):
2007            result=[]
2008            for e in s:
2009                result.append(self.fs.Entry(e, *args, **kw))
2010            return result
2011        return self.fs.Entry(s, *args, **kw)
2012
2013    def Environment(self, **kw):
2014        return SCons.Environment.Environment(**self.subst_kw(kw))
2015
2016    def Execute(self, action, *args, **kw):
2017        """Directly execute an action through an Environment
2018        """
2019        action = self.Action(action, *args, **kw)
2020        result = action([], [], self)
2021        if isinstance(result, SCons.Errors.BuildError):
2022            errstr = result.errstr
2023            if result.filename:
2024                errstr = result.filename + ': ' + errstr
2025            sys.stderr.write("scons: *** %s\n" % errstr)
2026            return result.status
2027        else:
2028            return result
2029
2030    def File(self, name, *args, **kw):
2031        """
2032        """
2033        s = self.subst(name)
2034        if SCons.Util.is_Sequence(s):
2035            result=[]
2036            for e in s:
2037                result.append(self.fs.File(e, *args, **kw))
2038            return result
2039        return self.fs.File(s, *args, **kw)
2040
2041    def FindFile(self, file, dirs):
2042        file = self.subst(file)
2043        nodes = self.arg2nodes(dirs, self.fs.Dir)
2044        return SCons.Node.FS.find_file(file, tuple(nodes))
2045
2046    def Flatten(self, sequence):
2047        return SCons.Util.flatten(sequence)
2048
2049    def GetBuildPath(self, files):
2050        result = list(map(str, self.arg2nodes(files, self.fs.Entry)))
2051        if SCons.Util.is_List(files):
2052            return result
2053        else:
2054            return result[0]
2055
2056    def Glob(self, pattern, ondisk=True, source=False, strings=False):
2057        return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
2058
2059    def Ignore(self, target, dependency):
2060        """Ignore a dependency."""
2061        tlist = self.arg2nodes(target, self.fs.Entry)
2062        dlist = self.arg2nodes(dependency, self.fs.Entry)
2063        for t in tlist:
2064            t.add_ignore(dlist)
2065        return tlist
2066
2067    def Literal(self, string):
2068        return SCons.Subst.Literal(string)
2069
2070    def Local(self, *targets):
2071        ret = []
2072        for targ in targets:
2073            if isinstance(targ, SCons.Node.Node):
2074                targ.set_local()
2075                ret.append(targ)
2076            else:
2077                for t in self.arg2nodes(targ, self.fs.Entry):
2078                   t.set_local()
2079                   ret.append(t)
2080        return ret
2081
2082    def Precious(self, *targets):
2083        tlist = []
2084        for t in targets:
2085            tlist.extend(self.arg2nodes(t, self.fs.Entry))
2086        for t in tlist:
2087            t.set_precious()
2088        return tlist
2089
2090    def Pseudo(self, *targets):
2091        tlist = []
2092        for t in targets:
2093            tlist.extend(self.arg2nodes(t, self.fs.Entry))
2094        for t in tlist:
2095            t.set_pseudo()
2096        return tlist
2097
2098    def Repository(self, *dirs, **kw):
2099        dirs = self.arg2nodes(list(dirs), self.fs.Dir)
2100        self.fs.Repository(*dirs, **kw)
2101
2102    def Requires(self, target, prerequisite):
2103        """Specify that 'prerequisite' must be built before 'target',
2104        (but 'target' does not actually depend on 'prerequisite'
2105        and need not be rebuilt if it changes)."""
2106        tlist = self.arg2nodes(target, self.fs.Entry)
2107        plist = self.arg2nodes(prerequisite, self.fs.Entry)
2108        for t in tlist:
2109            t.add_prerequisite(plist)
2110        return tlist
2111
2112    def Scanner(self, *args, **kw):
2113        nargs = []
2114        for arg in args:
2115            if SCons.Util.is_String(arg):
2116                arg = self.subst(arg)
2117            nargs.append(arg)
2118        nkw = self.subst_kw(kw)
2119        return SCons.Scanner.Base(*nargs, **nkw)
2120
2121    def SConsignFile(self, name=".sconsign", dbm_module=None):
2122        if name is not None:
2123            name = self.subst(name)
2124            if not os.path.isabs(name):
2125                name = os.path.join(str(self.fs.SConstruct_dir), name)
2126        if name:
2127            name = os.path.normpath(name)
2128            sconsign_dir = os.path.dirname(name)
2129            if sconsign_dir and not os.path.exists(sconsign_dir):
2130                self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
2131        SCons.SConsign.File(name, dbm_module)
2132
2133    def SideEffect(self, side_effect, target):
2134        """Tell scons that side_effects are built as side
2135        effects of building targets."""
2136        side_effects = self.arg2nodes(side_effect, self.fs.Entry)
2137        targets = self.arg2nodes(target, self.fs.Entry)
2138
2139        for side_effect in side_effects:
2140            if side_effect.multiple_side_effect_has_builder():
2141                raise SCons.Errors.UserError("Multiple ways to build the same target were specified for: %s" % str(side_effect))
2142            side_effect.add_source(targets)
2143            side_effect.side_effect = 1
2144            self.Precious(side_effect)
2145            for target in targets:
2146                target.side_effects.append(side_effect)
2147        return side_effects
2148
2149    def SourceCode(self, entry, builder):
2150        """Arrange for a source code builder for (part of) a tree."""
2151        msg = """SourceCode() has been deprecated and there is no replacement.
2152\tIf you need this function, please contact scons-dev@scons.org"""
2153        SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceCodeWarning, msg)
2154        entries = self.arg2nodes(entry, self.fs.Entry)
2155        for entry in entries:
2156            entry.set_src_builder(builder)
2157        return entries
2158
2159    def SourceSignatures(self, type):
2160        global _warn_source_signatures_deprecated
2161        if _warn_source_signatures_deprecated:
2162            msg = "The env.SourceSignatures() method is deprecated;\n" + \
2163                  "\tconvert your build to use the env.Decider() method instead."
2164            SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
2165            _warn_source_signatures_deprecated = False
2166        type = self.subst(type)
2167        self.src_sig_type = type
2168        if type == 'MD5':
2169            if not SCons.Util.md5:
2170                raise UserError("MD5 signatures are not available in this version of Python.")
2171            self.decide_source = self._changed_content
2172        elif type == 'timestamp':
2173            self.decide_source = self._changed_timestamp_match
2174        else:
2175            raise UserError("Unknown source signature type '%s'" % type)
2176
2177    def Split(self, arg):
2178        """This function converts a string or list into a list of strings
2179        or Nodes.  This makes things easier for users by allowing files to
2180        be specified as a white-space separated list to be split.
2181        The input rules are:
2182            - A single string containing names separated by spaces. These will be
2183              split apart at the spaces.
2184            - A single Node instance
2185            - A list containing either strings or Node instances. Any strings
2186              in the list are not split at spaces.
2187        In all cases, the function returns a list of Nodes and strings."""
2188        if SCons.Util.is_List(arg):
2189            return list(map(self.subst, arg))
2190        elif SCons.Util.is_String(arg):
2191            return self.subst(arg).split()
2192        else:
2193            return [self.subst(arg)]
2194
2195    def TargetSignatures(self, type):
2196        global _warn_target_signatures_deprecated
2197        if _warn_target_signatures_deprecated:
2198            msg = "The env.TargetSignatures() method is deprecated;\n" + \
2199                  "\tconvert your build to use the env.Decider() method instead."
2200            SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
2201            _warn_target_signatures_deprecated = False
2202        type = self.subst(type)
2203        self.tgt_sig_type = type
2204        if type in ('MD5', 'content'):
2205            if not SCons.Util.md5:
2206                raise UserError("MD5 signatures are not available in this version of Python.")
2207            self.decide_target = self._changed_content
2208        elif type == 'timestamp':
2209            self.decide_target = self._changed_timestamp_match
2210        elif type == 'build':
2211            self.decide_target = self._changed_build
2212        elif type == 'source':
2213            self.decide_target = self._changed_source
2214        else:
2215            raise UserError("Unknown target signature type '%s'"%type)
2216
2217    def Value(self, value, built_value=None):
2218        """
2219        """
2220        return SCons.Node.Python.Value(value, built_value)
2221
2222    def VariantDir(self, variant_dir, src_dir, duplicate=1):
2223        variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
2224        src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
2225        self.fs.VariantDir(variant_dir, src_dir, duplicate)
2226
2227    def FindSourceFiles(self, node='.'):
2228        """ returns a list of all source files.
2229        """
2230        node = self.arg2nodes(node, self.fs.Entry)[0]
2231
2232        sources = []
2233        def build_source(ss):
2234            for s in ss:
2235                if isinstance(s, SCons.Node.FS.Dir):
2236                    build_source(s.all_children())
2237                elif s.has_builder():
2238                    build_source(s.sources)
2239                elif isinstance(s.disambiguate(), SCons.Node.FS.File):
2240                    sources.append(s)
2241        build_source(node.all_children())
2242
2243        def final_source(node):
2244            while (node != node.srcnode()):
2245              node = node.srcnode()
2246            return node
2247        sources = map( final_source, sources );
2248        # remove duplicates
2249        return list(set(sources))
2250
2251    def FindInstalledFiles(self):
2252        """ returns the list of all targets of the Install and InstallAs Builder.
2253        """
2254        from SCons.Tool import install
2255        if install._UNIQUE_INSTALLED_FILES is None:
2256            install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
2257        return install._UNIQUE_INSTALLED_FILES
2258
2259
2260class OverrideEnvironment(Base):
2261    """A proxy that overrides variables in a wrapped construction
2262    environment by returning values from an overrides dictionary in
2263    preference to values from the underlying subject environment.
2264
2265    This is a lightweight (I hope) proxy that passes through most use of
2266    attributes to the underlying Environment.Base class, but has just
2267    enough additional methods defined to act like a real construction
2268    environment with overridden values.  It can wrap either a Base
2269    construction environment, or another OverrideEnvironment, which
2270    can in turn nest arbitrary OverrideEnvironments...
2271
2272    Note that we do *not* call the underlying base class
2273    (SubsitutionEnvironment) initialization, because we get most of those
2274    from proxying the attributes of the subject construction environment.
2275    But because we subclass SubstitutionEnvironment, this class also
2276    has inherited arg2nodes() and subst*() methods; those methods can't
2277    be proxied because they need *this* object's methods to fetch the
2278    values from the overrides dictionary.
2279    """
2280
2281    def __init__(self, subject, overrides={}):
2282        if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.OverrideEnvironment')
2283        self.__dict__['__subject'] = subject
2284        self.__dict__['overrides'] = overrides
2285
2286    # Methods that make this class act like a proxy.
2287    def __getattr__(self, name):
2288        return getattr(self.__dict__['__subject'], name)
2289    def __setattr__(self, name, value):
2290        setattr(self.__dict__['__subject'], name, value)
2291
2292    # Methods that make this class act like a dictionary.
2293    def __getitem__(self, key):
2294        try:
2295            return self.__dict__['overrides'][key]
2296        except KeyError:
2297            return self.__dict__['__subject'].__getitem__(key)
2298    def __setitem__(self, key, value):
2299        if not is_valid_construction_var(key):
2300            raise SCons.Errors.UserError("Illegal construction variable `%s'" % key)
2301        self.__dict__['overrides'][key] = value
2302    def __delitem__(self, key):
2303        try:
2304            del self.__dict__['overrides'][key]
2305        except KeyError:
2306            deleted = 0
2307        else:
2308            deleted = 1
2309        try:
2310            result = self.__dict__['__subject'].__delitem__(key)
2311        except KeyError:
2312            if not deleted:
2313                raise
2314            result = None
2315        return result
2316    def get(self, key, default=None):
2317        """Emulates the get() method of dictionaries."""
2318        try:
2319            return self.__dict__['overrides'][key]
2320        except KeyError:
2321            return self.__dict__['__subject'].get(key, default)
2322    def has_key(self, key):
2323        try:
2324            self.__dict__['overrides'][key]
2325            return 1
2326        except KeyError:
2327            return key in self.__dict__['__subject']
2328    def __contains__(self, key):
2329        if self.__dict__['overrides'].__contains__(key):
2330            return 1
2331        return self.__dict__['__subject'].__contains__(key)
2332    def Dictionary(self):
2333        """Emulates the items() method of dictionaries."""
2334        d = self.__dict__['__subject'].Dictionary().copy()
2335        d.update(self.__dict__['overrides'])
2336        return d
2337    def items(self):
2338        """Emulates the items() method of dictionaries."""
2339        return list(self.Dictionary().items())
2340
2341    # Overridden private construction environment methods.
2342    def _update(self, dict):
2343        """Update an environment's values directly, bypassing the normal
2344        checks that occur when users try to set items.
2345        """
2346        self.__dict__['overrides'].update(dict)
2347
2348    def gvars(self):
2349        return self.__dict__['__subject'].gvars()
2350
2351    def lvars(self):
2352        lvars = self.__dict__['__subject'].lvars()
2353        lvars.update(self.__dict__['overrides'])
2354        return lvars
2355
2356    # Overridden public construction environment methods.
2357    def Replace(self, **kw):
2358        kw = copy_non_reserved_keywords(kw)
2359        self.__dict__['overrides'].update(semi_deepcopy(kw))
2360
2361# The entry point that will be used by the external world
2362# to refer to a construction environment.  This allows the wrapper
2363# interface to extend a construction environment for its own purposes
2364# by subclassing SCons.Environment.Base and then assigning the
2365# class to SCons.Environment.Environment.
2366
2367Environment = Base
2368
2369# An entry point for returning a proxy subclass instance that overrides
2370# the subst*() methods so they don't actually perform construction
2371# variable substitution.  This is specifically intended to be the shim
2372# layer in between global function calls (which don't want construction
2373# variable substitution) and the DefaultEnvironment() (which would
2374# substitute variables if left to its own devices)."""
2375#
2376# We have to wrap this in a function that allows us to delay definition of
2377# the class until it's necessary, so that when it subclasses Environment
2378# it will pick up whatever Environment subclass the wrapper interface
2379# might have assigned to SCons.Environment.Environment.
2380
2381def NoSubstitutionProxy(subject):
2382    class _NoSubstitutionProxy(Environment):
2383        def __init__(self, subject):
2384            self.__dict__['__subject'] = subject
2385        def __getattr__(self, name):
2386            return getattr(self.__dict__['__subject'], name)
2387        def __setattr__(self, name, value):
2388            return setattr(self.__dict__['__subject'], name, value)
2389        def executor_to_lvars(self, kwdict):
2390            if kwdict.has_key('executor'):
2391                kwdict['lvars'] = kwdict['executor'].get_lvars()
2392                del kwdict['executor']
2393            else:
2394                kwdict['lvars'] = {}
2395        def raw_to_mode(self, dict):
2396            try:
2397                raw = dict['raw']
2398            except KeyError:
2399                pass
2400            else:
2401                del dict['raw']
2402                dict['mode'] = raw
2403        def subst(self, string, *args, **kwargs):
2404            return string
2405        def subst_kw(self, kw, *args, **kwargs):
2406            return kw
2407        def subst_list(self, string, *args, **kwargs):
2408            nargs = (string, self,) + args
2409            nkw = kwargs.copy()
2410            nkw['gvars'] = {}
2411            self.executor_to_lvars(nkw)
2412            self.raw_to_mode(nkw)
2413            return SCons.Subst.scons_subst_list(*nargs, **nkw)
2414        def subst_target_source(self, string, *args, **kwargs):
2415            nargs = (string, self,) + args
2416            nkw = kwargs.copy()
2417            nkw['gvars'] = {}
2418            self.executor_to_lvars(nkw)
2419            self.raw_to_mode(nkw)
2420            return SCons.Subst.scons_subst(*nargs, **nkw)
2421    return _NoSubstitutionProxy(subject)
2422
2423# Local Variables:
2424# tab-width:4
2425# indent-tabs-mode:nil
2426# End:
2427# vim: set expandtab tabstop=4 shiftwidth=4:
2428