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