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