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