9fa7b34153d0e4afdadb32d0f8ab4a6f7aecdeeb
[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 os.path
41 import string
42 from UserDict import UserDict
43
44 import SCons.Action
45 import SCons.Builder
46 from SCons.Debug import logInstanceCreation
47 import SCons.Defaults
48 import SCons.Errors
49 import SCons.Node
50 import SCons.Node.Alias
51 import SCons.Node.FS
52 import SCons.Node.Python
53 import SCons.Platform
54 import SCons.SConsign
55 import SCons.Sig
56 import SCons.Sig.MD5
57 import SCons.Sig.TimeStamp
58 import SCons.Tool
59 import SCons.Util
60 import SCons.Warnings
61
62 class _Null:
63     pass
64
65 _null = _Null
66
67 CleanTargets = {}
68 CalculatorArgs = {}
69
70 # Pull UserError into the global name space for the benefit of
71 # Environment().SourceSignatures(), which has some import statements
72 # which seem to mess up its ability to reference SCons directly.
73 UserError = SCons.Errors.UserError
74
75 def installFunc(target, source, env):
76     """Install a source file into a target using the function specified
77     as the INSTALL construction variable."""
78     try:
79         install = env['INSTALL']
80     except KeyError:
81         raise SCons.Errors.UserError('Missing INSTALL construction variable.')
82     return install(target[0].path, source[0].path, env)
83
84 def installString(target, source, env):
85     return 'Install file: "%s" as "%s"' % (source[0], target[0])
86
87 installAction = SCons.Action.Action(installFunc, installString)
88
89 InstallBuilder = SCons.Builder.Builder(action=installAction)
90
91 def alias_builder(env, target, source):
92     pass
93
94 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
95                                      target_factory = SCons.Node.Alias.default_ans.Alias,
96                                      source_factory = SCons.Node.FS.default_fs.Entry,
97                                      multi = 1)
98
99 def our_deepcopy(x):
100    """deepcopy lists and dictionaries, and just copy the reference
101    for everything else."""
102    if SCons.Util.is_Dict(x):
103        copy = {}
104        for key in x.keys():
105            copy[key] = our_deepcopy(x[key])
106    elif SCons.Util.is_List(x):
107        copy = map(our_deepcopy, x)
108        try:
109            copy = x.__class__(copy)
110        except AttributeError:
111            pass
112    else:
113        copy = x
114    return copy
115
116 def apply_tools(env, tools, toolpath):
117     if tools:
118         # Filter out null tools from the list.
119         tools = filter(None, tools)
120         for tool in tools:
121             if SCons.Util.is_String(tool):
122                 env.Tool(tool, toolpath)
123             else:
124                 tool(env)
125
126 # These names are controlled by SCons; users should never set or override
127 # them.  This warning can optionally be turned off, but scons will still
128 # ignore the illegal variable names even if it's off.
129 reserved_construction_var_names = \
130     ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
131
132 def copy_non_reserved_keywords(dict):
133     result = our_deepcopy(dict)
134     for k in result.keys():
135         if k in reserved_construction_var_names:
136             SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
137                                 "Ignoring attempt to set reserved variable `%s'" % k)
138             del result[k]
139     return result
140
141 class BuilderWrapper:
142     """Wrapper class that associates an environment with a Builder at
143     instantiation."""
144     def __init__(self, env, builder):
145         self.env = env
146         self.builder = builder
147
148     def __call__(self, *args, **kw):
149         return apply(self.builder, (self.env,) + args, kw)
150
151     # This allows a Builder to be executed directly
152     # through the Environment to which it's attached.
153     # In practice, we shouldn't need this, because
154     # builders actually get executed through a Node.
155     # But we do have a unit test for this, and can't
156     # yet rule out that it would be useful in the
157     # future, so leave it for now.
158     def execute(self, **kw):
159         kw['env'] = self.env
160         apply(self.builder.execute, (), kw)
161
162 class BuilderDict(UserDict):
163     """This is a dictionary-like class used by an Environment to hold
164     the Builders.  We need to do this because every time someone changes
165     the Builders in the Environment's BUILDERS dictionary, we must
166     update the Environment's attributes."""
167     def __init__(self, dict, env):
168         # Set self.env before calling the superclass initialization,
169         # because it will end up calling our other methods, which will
170         # need to point the values in this dictionary to self.env.
171         self.env = env
172         UserDict.__init__(self, dict)
173
174     def __setitem__(self, item, val):
175         UserDict.__setitem__(self, item, val)
176         try:
177             self.setenvattr(item, val)
178         except AttributeError:
179             # Have to catch this because sometimes __setitem__ gets
180             # called out of __init__, when we don't have an env
181             # attribute yet, nor do we want one!
182             pass
183
184     def setenvattr(self, item, val):
185         """Set the corresponding environment attribute for this Builder.
186
187         If the value is already a BuilderWrapper, we pull the builder
188         out of it and make another one, so that making a copy of an
189         existing BuilderDict is guaranteed separate wrappers for each
190         Builder + Environment pair."""
191         try:
192             builder = val.builder
193         except AttributeError:
194             builder = val
195         setattr(self.env, item, BuilderWrapper(self.env, builder))
196
197     def __delitem__(self, item):
198         UserDict.__delitem__(self, item)
199         delattr(self.env, item)
200
201     def update(self, dict):
202         for i, v in dict.items():
203             self.__setitem__(i, v)
204
205 class Base:
206     """Base class for construction Environments.  These are
207     the primary objects used to communicate dependency and
208     construction information to the build engine.
209
210     Keyword arguments supplied when the construction Environment
211     is created are construction variables used to initialize the
212     Environment.
213     """
214
215     #######################################################################
216     # This is THE class for interacting with the SCons build engine,
217     # and it contains a lot of stuff, so we're going to try to keep this
218     # a little organized by grouping the methods.
219     #######################################################################
220
221     #######################################################################
222     # Methods that make an Environment act like a dictionary.  These have
223     # the expected standard names for Python mapping objects.  Note that
224     # we don't actually make an Environment a subclass of UserDict for
225     # performance reasons.  Note also that we only supply methods for
226     # dictionary functionality that we actually need and use.
227     #######################################################################
228
229     def __init__(self,
230                  platform=None,
231                  tools=None,
232                  toolpath=[],
233                  options=None,
234                  **kw):
235         if __debug__: logInstanceCreation(self)
236         self.fs = SCons.Node.FS.default_fs
237         self.ans = SCons.Node.Alias.default_ans
238         self.lookup_list = SCons.Node.arg2nodes_lookups
239         self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
240
241         self._dict['__env__'] = self
242         self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
243
244         if platform is None:
245             platform = self._dict.get('PLATFORM', None)
246             if platform is None:
247                 platform = SCons.Platform.Platform()
248         if SCons.Util.is_String(platform):
249             platform = SCons.Platform.Platform(platform)
250         self._dict['PLATFORM'] = str(platform)
251         platform(self)
252
253         # Apply the passed-in variables before calling the tools,
254         # because they may use some of them:
255         apply(self.Replace, (), kw)
256         
257         # Update the environment with the customizable options
258         # before calling the tools, since they may use some of the options: 
259         if options:
260             options.Update(self)
261
262         if tools is None:
263             tools = self._dict.get('TOOLS', None)
264             if tools is None:
265                 tools = ['default']
266         apply_tools(self, tools, toolpath)
267
268         # Reapply the passed in variables after calling the tools,
269         # since they should overide anything set by the tools:
270         apply(self.Replace, (), kw)
271
272         # Update the environment with the customizable options
273         # after calling the tools, since they should override anything
274         # set by the tools:
275         if options:
276             options.Update(self)
277
278     def __cmp__(self, other):
279         # Since an Environment now has an '__env__' construction variable
280         # that refers to itself, delete that variable to avoid infinite
281         # loops when comparing the underlying dictionaries in some Python
282         # versions (*cough* 1.5.2 *cough*)...
283         sdict = self._dict.copy()
284         del sdict['__env__']
285         odict = other._dict.copy()
286         del odict['__env__']
287         return cmp(sdict, odict)
288
289     def __getitem__(self, key):
290         return self._dict[key]
291
292     def __setitem__(self, key, value):
293         if key in reserved_construction_var_names:
294             SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
295                                 "Ignoring attempt to set reserved variable `%s'" % key)
296         elif key == 'BUILDERS':
297             try:
298                 bd = self._dict[key]
299                 for k in bd.keys():
300                     del bd[k]
301             except KeyError:
302                 self._dict[key] = BuilderDict(kwbd, self)
303             self._dict[key].update(value)
304         elif key == 'SCANNERS':
305             self._dict[key] = value
306             self.scanner_map_delete()
307         else:
308             if not SCons.Util.is_valid_construction_var(key):
309                 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
310             self._dict[key] = value
311
312     def __delitem__(self, key):
313         del self._dict[key]
314
315     def items(self):
316         "Emulates the items() method of dictionaries."""
317         return self._dict.items()
318
319     def has_key(self, key):
320         return self._dict.has_key(key)
321
322     def get(self, key, default=None):
323         "Emulates the get() method of dictionaries."""
324         return self._dict.get(key, default)
325
326     #######################################################################
327     # Utility methods that are primarily for internal use by SCons.
328     # These begin with lower-case letters.  Note that the subst() method
329     # is actually already out of the closet and used by people.
330     #######################################################################
331
332     def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
333         if node_factory is _null:
334             node_factory = self.fs.File
335         if lookup_list is _null:
336             lookup_list = self.lookup_list
337
338         if not args:
339             return []
340
341         if SCons.Util.is_List(args):
342             args = SCons.Util.flatten(args)
343         else:
344             args = [args]
345
346         nodes = []
347         for v in args:
348             if SCons.Util.is_String(v):
349                 n = None
350                 for l in lookup_list:
351                     n = l(v)
352                     if not n is None:
353                         break
354                 if not n is None:
355                     if SCons.Util.is_String(n):
356                         n = self.subst(n, raw=1)
357                         if node_factory:
358                             n = node_factory(n)
359                     if SCons.Util.is_List(n):
360                         nodes.extend(n)
361                     else:
362                         nodes.append(n)
363                 elif node_factory:
364                     v = node_factory(self.subst(v, raw=1))
365                     if SCons.Util.is_List(v):
366                         nodes.extend(v)
367                     else:
368                         nodes.append(v)
369             else:
370                 nodes.append(v)
371     
372         return nodes
373
374     def get_calculator(self):
375         try:
376             return self._calculator
377         except AttributeError:
378             try:
379                 module = self._calc_module
380                 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
381             except AttributeError:
382                 # Note that we're calling get_calculator() here, so the
383                 # DefaultEnvironment() must have a _calc_module attribute
384                 # to avoid infinite recursion.
385                 c = SCons.Defaults.DefaultEnvironment().get_calculator()
386             self._calculator = c
387             return c
388
389     def get_builder(self, name):
390         """Fetch the builder with the specified name from the environment.
391         """
392         try:
393             return self._dict['BUILDERS'][name]
394         except KeyError:
395             return None
396
397     def get_scanner(self, skey):
398         """Find the appropriate scanner given a key (usually a file suffix).
399         """
400         try:
401             sm = self.scanner_map
402         except AttributeError:
403             try:
404                 scanners = self._dict['SCANNERS']
405             except KeyError:
406                 self.scanner_map = {}
407                 return None
408             else:
409                 self.scanner_map = sm = {}
410                 # Reverse the scanner list so that, if multiple scanners
411                 # claim they can scan the same suffix, earlier scanners
412                 # in the list will overwrite later scanners, so that
413                 # the result looks like a "first match" to the user.
414                 if not SCons.Util.is_List(scanners):
415                     scanners = [scanners]
416                 scanners.reverse()
417                 for scanner in scanners:
418                     for k in scanner.get_skeys(self):
419                         sm[k] = scanner
420         try:
421             return sm[skey]
422         except KeyError:
423             return None
424
425     def scanner_map_delete(self, kw=None):
426         """Delete the cached scanner map (if we need to).
427         """
428         if not kw is None and not kw.has_key('SCANNERS'):
429             return
430         try:
431             del self.scanner_map
432         except AttributeError:
433             pass
434
435     def subst(self, string, raw=0, target=None, source=None, dict=None, conv=None):
436         """Recursively interpolates construction variables from the
437         Environment into the specified string, returning the expanded
438         result.  Construction variables are specified by a $ prefix
439         in the string and begin with an initial underscore or
440         alphabetic character followed by any number of underscores
441         or alphanumeric characters.  The construction variable names
442         may be surrounded by curly braces to separate the name from
443         trailing characters.
444         """
445         return SCons.Util.scons_subst(string, self, raw, target, source, dict, conv)
446
447     def subst_kw(self, kw, raw=0, target=None, source=None, dict=None):
448         nkw = {}
449         for k, v in kw.items():
450             k = self.subst(k, raw, target, source, dict)
451             if SCons.Util.is_String(v):
452                 v = self.subst(v, raw, target, source, dict)
453             nkw[k] = v
454         return nkw
455
456     def subst_list(self, string, raw=0, target=None, source=None, dict=None, conv=None):
457         """Calls through to SCons.Util.scons_subst_list().  See
458         the documentation for that function."""
459         return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv)
460
461
462     def subst_path(self, path):
463         """Substitute a path list, turning EntryProxies into Nodes
464         and leaving Nodes (and other objects) as-is."""
465
466         if not SCons.Util.is_List(path):
467             path = [path]
468
469         def s(obj):
470             """This is the "string conversion" routine that we have our
471             substitutions use to return Nodes, not strings.  This relies
472             on the fact that an EntryProxy object has a get() method that
473             returns the underlying Node that it wraps, which is a bit of
474             architectural dependence that we might need to break or modify
475             in the future in response to additional requirements."""
476             try:
477                 get = obj.get
478             except AttributeError:
479                 pass
480             else:
481                 obj = get()
482             return obj
483
484         r = []
485         for p in path:
486             if SCons.Util.is_String(p):
487                 p = self.subst(p, conv=s)
488                 if SCons.Util.is_List(p):
489                     if len(p) == 1:
490                         p = p[0]
491                     else:
492                         # We have an object plus a string, or multiple
493                         # objects that we need to smush together.  No choice
494                         # but to make them into a string.
495                         p = string.join(map(SCons.Util.to_String, p), '')
496             else:
497                 p = s(p)
498             r.append(p)
499         return r
500
501     subst_target_source = subst
502
503     def _update(self, dict):
504         """Update an environment's values directly, bypassing the normal
505         checks that occur when users try to set items.
506         """
507         self._dict.update(dict)
508
509     def use_build_signature(self):
510         try:
511             return self._build_signature
512         except AttributeError:
513             b = SCons.Defaults.DefaultEnvironment()._build_signature
514             self._build_signature = b
515             return b
516
517     #######################################################################
518     # Public methods for manipulating an Environment.  These begin with
519     # upper-case letters.  The essential characteristic of methods in
520     # this section is that they do *not* have corresponding same-named
521     # global functions.  For example, a stand-alone Append() function
522     # makes no sense, because Append() is all about appending values to
523     # an Environment's construction variables.
524     #######################################################################
525
526     def Append(self, **kw):
527         """Append values to existing construction variables
528         in an Environment.
529         """
530         kw = copy_non_reserved_keywords(kw)
531         for key, val in kw.items():
532             # It would be easier on the eyes to write this using
533             # "continue" statements whenever we finish processing an item,
534             # but Python 1.5.2 apparently doesn't let you use "continue"
535             # within try:-except: blocks, so we have to nest our code.
536             try:
537                 orig = self._dict[key]
538             except KeyError:
539                 # No existing variable in the environment, so just set
540                 # it to the new value.
541                 self._dict[key] = val
542             else:
543                 try:
544                     # Most straightforward:  just try to add them
545                     # together.  This will work in most cases, when the
546                     # original and new values are of compatible types.
547                     self._dict[key] = orig + val
548                 except TypeError:
549                     try:
550                         # Try to update a dictionary value with another.
551                         # If orig isn't a dictionary, it won't have an
552                         # update() method; if val isn't a dictionary,
553                         # it won't have a keys() method.  Either way,
554                         # it's an AttributeError.
555                         orig.update(val)
556                     except AttributeError:
557                         try:
558                             # Check if the original is a list.
559                             add_to_orig = orig.append
560                         except AttributeError:
561                             # The original isn't a list, but the new
562                             # value is (by process of elimination),
563                             # so insert the original in the new value
564                             # (if there's one to insert) and replace
565                             # the variable with it.
566                             if orig:
567                                 val.insert(0, orig)
568                             self._dict[key] = val
569                         else:
570                             # The original is a list, so append the new
571                             # value to it (if there's a value to append).
572                             if val:
573                                 add_to_orig(val)
574         self.scanner_map_delete(kw)
575
576     def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
577         """Append path elements to the path 'name' in the 'ENV'
578         dictionary for this environment.  Will only add any particular
579         path once, and will normpath and normcase all paths to help
580         assure this.  This can also handle the case where the env
581         variable is a list instead of a string.
582         """
583
584         orig = ''
585         if self._dict.has_key(envname) and self._dict[envname].has_key(name):
586             orig = self._dict[envname][name]
587
588         nv = SCons.Util.AppendPath(orig, newpath, sep)
589             
590         if not self._dict.has_key(envname):
591             self._dict[envname] = {}
592
593         self._dict[envname][name] = nv
594
595     def AppendUnique(self, **kw):
596         """Append values to existing construction variables
597         in an Environment, if they're not already there.
598         """
599         kw = copy_non_reserved_keywords(kw)
600         for key, val in kw.items():
601             if not self._dict.has_key(key):
602                 self._dict[key] = val
603             elif SCons.Util.is_Dict(self._dict[key]) and \
604                  SCons.Util.is_Dict(val):
605                 self._dict[key].update(val)
606             elif SCons.Util.is_List(val):
607                 dk = self._dict[key]
608                 if not SCons.Util.is_List(dk):
609                     dk = [dk]
610                 val = filter(lambda x, dk=dk: x not in dk, val)
611                 self._dict[key] = dk + val
612             else:
613                 dk = self._dict[key]
614                 if SCons.Util.is_List(dk):
615                     if not val in dk:
616                         self._dict[key] = dk + val
617                 else:
618                     self._dict[key] = self._dict[key] + val
619         self.scanner_map_delete(kw)
620
621     def Copy(self, tools=None, toolpath=[], **kw):
622         """Return a copy of a construction Environment.  The
623         copy is like a Python "deep copy"--that is, independent
624         copies are made recursively of each objects--except that
625         a reference is copied when an object is not deep-copyable
626         (like a function).  There are no references to any mutable
627         objects in the original Environment.
628         """
629         clone = copy.copy(self)
630         clone._dict = our_deepcopy(self._dict)
631         clone['__env__'] = clone
632         try:
633             cbd = clone._dict['BUILDERS']
634             clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
635         except KeyError:
636             pass
637         
638         apply_tools(clone, tools, toolpath)
639
640         # Apply passed-in variables after the new tools.
641         kw = copy_non_reserved_keywords(kw)
642         new = {}
643         for key, value in kw.items():
644             new[key] = SCons.Util.scons_subst_once(value, self, key)
645         apply(clone.Replace, (), new)
646         return clone
647
648     def Detect(self, progs):
649         """Return the first available program in progs.
650         """
651         if not SCons.Util.is_List(progs):
652             progs = [ progs ]
653         for prog in progs:
654             path = self.WhereIs(prog)
655             if path: return prog
656         return None
657
658     def Dictionary(self, *args):
659         if not args:
660             return self._dict
661         dlist = map(lambda x, s=self: s._dict[x], args)
662         if len(dlist) == 1:
663             dlist = dlist[0]
664         return dlist
665
666     def Dump(self, key = None):
667         """
668         Using the standard Python pretty printer, dump the contents of the
669         scons build environment to stdout.
670
671         If the key passed in is anything other than None, then that will
672         be used as an index into the build environment dictionary and
673         whatever is found there will be fed into the pretty printer. Note
674         that this key is case sensitive.
675         """
676         import pprint
677         pp = pprint.PrettyPrinter(indent=2)
678         if key:
679             dict = self.Dictionary(key)
680         else:
681             dict = self.Dictionary()
682         return pp.pformat(dict)
683
684     def FindIxes(self, paths, prefix, suffix):
685         """
686         Search a list of paths for something that matches the prefix and suffix.
687
688         paths - the list of paths or nodes.
689         prefix - construction variable for the prefix.
690         suffix - construction variable for the suffix.
691         """
692
693         suffix = self.subst('$'+suffix)
694         prefix = self.subst('$'+prefix)
695
696         for path in paths:
697             dir,name = os.path.split(str(path))
698             if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: 
699                 return path
700
701     def Override(self, overrides):
702         """
703         Produce a modified environment whose variables
704         are overriden by the overrides dictionaries.
705
706         overrides - a dictionary that will override
707         the variables of this environment.
708
709         This function is much more efficient than Copy()
710         or creating a new Environment because it doesn't do
711         a deep copy of the dictionary, and doesn't do a copy
712         at all if there are no overrides.
713         """
714
715         if overrides:
716             env = copy.copy(self)
717             env._dict = copy.copy(self._dict)
718             env['__env__'] = env
719             overrides = copy_non_reserved_keywords(overrides)
720             new = {}
721             for key, value in overrides.items():
722                 new[key] = SCons.Util.scons_subst_once(value, self, key)
723             env._dict.update(new)
724             return env
725         else:
726             return self
727
728     def ParseConfig(self, command, function=None):
729         """
730         Use the specified function to parse the output of the command
731         in order to modify the current environment. The 'command' can
732         be a string or a list of strings representing a command and
733         it's arguments. 'Function' is an optional argument that takes
734         the environment and the output of the command. If no function is
735         specified, the output will be treated as the output of a typical
736         'X-config' command (i.e. gtk-config) and used to append to the
737         ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
738         and CCFLAGS variables.
739         """
740
741         # the default parse function
742         def parse_conf(env, output, fs=self.fs):
743             dict = {
744                 'ASFLAGS'       : [],
745                 'CCFLAGS'       : [],
746                 'CPPFLAGS'      : [],
747                 'CPPPATH'       : [],
748                 'LIBPATH'       : [],
749                 'LIBS'          : [],
750                 'LINKFLAGS'     : [],
751             }
752     
753             params = string.split(output)
754             append_next_arg_to=''       # for multi-word args
755             for arg in params:
756                 if append_next_arg_to:
757                     dict[append_next_arg_to].append(arg)
758                     append_next_arg_to = ''
759                 elif arg[0] != '-':
760                     dict['LIBS'].append(fs.File(arg))
761                 elif arg[:2] == '-L':
762                     dict['LIBPATH'].append(arg[2:])
763                 elif arg[:2] == '-l':
764                     dict['LIBS'].append(arg[2:])
765                 elif arg[:2] == '-I':
766                     dict['CPPPATH'].append(arg[2:])
767                 elif arg[:4] == '-Wa,':
768                     dict['ASFLAGS'].append(arg)
769                 elif arg[:4] == '-Wl,':
770                     dict['LINKFLAGS'].append(arg)
771                 elif arg[:4] == '-Wp,':
772                     dict['CPPFLAGS'].append(arg)
773                 elif arg == '-framework':
774                     dict['LINKFLAGS'].append(arg)
775                     append_next_arg_to='LINKFLAGS'
776                 elif arg == '-mno-cygwin':
777                     dict['CCFLAGS'].append(arg)
778                     dict['LINKFLAGS'].append(arg)
779                 elif arg == '-mwindows':
780                     dict['LINKFLAGS'].append(arg)
781                 elif arg == '-pthread':
782                     dict['CCFLAGS'].append(arg)
783                     dict['LINKFLAGS'].append(arg)
784                 else:
785                     dict['CCFLAGS'].append(arg)
786             apply(env.Append, (), dict)
787     
788         if function is None:
789             function = parse_conf
790         if type(command) is type([]):
791             command = string.join(command)
792         command = self.subst(command)
793         return function(self, os.popen(command).read())
794
795     def ParseDepends(self, filename, must_exist=None):
796         """
797         Parse a mkdep-style file for explicit dependencies.  This is
798         completely abusable, and should be unnecessary in the "normal"
799         case of proper SCons configuration, but it may help make
800         the transition from a Make hierarchy easier for some people
801         to swallow.  It can also be genuinely useful when using a tool
802         that can write a .d file, but for which writing a scanner would
803         be too complicated.
804         """
805         try:
806             fp = open(filename, 'r')
807         except IOError:
808             if must_exist:
809                 raise
810             return
811         for line in SCons.Util.LogicalLines(fp).readlines():
812             if line[0] == '#':
813                 continue
814             try:
815                 target, depends = string.split(line, ':', 1)
816             except:
817                 pass
818             else:
819                 self.Depends(string.split(target), string.split(depends))
820
821     def Platform(self, platform):
822         platform = self.subst(platform)
823         return SCons.Platform.Platform(platform)(self)
824
825     def Prepend(self, **kw):
826         """Prepend values to existing construction variables
827         in an Environment.
828         """
829         kw = copy_non_reserved_keywords(kw)
830         for key, val in kw.items():
831             # It would be easier on the eyes to write this using
832             # "continue" statements whenever we finish processing an item,
833             # but Python 1.5.2 apparently doesn't let you use "continue"
834             # within try:-except: blocks, so we have to nest our code.
835             try:
836                 orig = self._dict[key]
837             except KeyError:
838                 # No existing variable in the environment, so just set
839                 # it to the new value.
840                 self._dict[key] = val
841             else:
842                 try:
843                     # Most straightforward:  just try to add them
844                     # together.  This will work in most cases, when the
845                     # original and new values are of compatible types.
846                     self._dict[key] = val + orig
847                 except TypeError:
848                     try:
849                         # Try to update a dictionary value with another.
850                         # If orig isn't a dictionary, it won't have an
851                         # update() method; if val isn't a dictionary,
852                         # it won't have a keys() method.  Either way,
853                         # it's an AttributeError.
854                         orig.update(val)
855                     except AttributeError:
856                         try:
857                             # Check if the added value is a list.
858                             add_to_val = val.append
859                         except AttributeError:
860                             # The added value isn't a list, but the
861                             # original is (by process of elimination),
862                             # so insert the the new value in the original
863                             # (if there's one to insert).
864                             if val:
865                                 orig.insert(0, val)
866                         else:
867                             # The added value is a list, so append
868                             # the original to it (if there's a value
869                             # to append).
870                             if orig:
871                                 add_to_val(orig)
872                             self._dict[key] = val
873         self.scanner_map_delete(kw)
874
875     def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
876         """Prepend path elements to the path 'name' in the 'ENV'
877         dictionary for this environment.  Will only add any particular
878         path once, and will normpath and normcase all paths to help
879         assure this.  This can also handle the case where the env
880         variable is a list instead of a string.
881         """
882
883         orig = ''
884         if self._dict.has_key(envname) and self._dict[envname].has_key(name):
885             orig = self._dict[envname][name]
886
887         nv = SCons.Util.PrependPath(orig, newpath, sep)
888             
889         if not self._dict.has_key(envname):
890             self._dict[envname] = {}
891
892         self._dict[envname][name] = nv
893
894     def PrependUnique(self, **kw):
895         """Append values to existing construction variables
896         in an Environment, if they're not already there.
897         """
898         kw = copy_non_reserved_keywords(kw)
899         for key, val in kw.items():
900             if not self._dict.has_key(key):
901                 self._dict[key] = val
902             elif SCons.Util.is_Dict(self._dict[key]) and \
903                  SCons.Util.is_Dict(val):
904                 self._dict[key].update(val)
905             elif SCons.Util.is_List(val):
906                 dk = self._dict[key]
907                 if not SCons.Util.is_List(dk):
908                     dk = [dk]
909                 val = filter(lambda x, dk=dk: x not in dk, val)
910                 self._dict[key] = val + dk
911             else:
912                 dk = self._dict[key]
913                 if SCons.Util.is_List(dk):
914                     if not val in dk:
915                         self._dict[key] = val + dk
916                 else:
917                     self._dict[key] = val + dk
918         self.scanner_map_delete(kw)
919
920     def Replace(self, **kw):
921         """Replace existing construction variables in an Environment
922         with new construction variables and/or values.
923         """
924         try:
925             kwbd = our_deepcopy(kw['BUILDERS'])
926             del kw['BUILDERS']
927             self.__setitem__('BUILDERS', kwbd)
928         except KeyError:
929             pass
930         kw = copy_non_reserved_keywords(kw)
931         self._dict.update(our_deepcopy(kw))
932         self.scanner_map_delete(kw)
933
934     def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
935         """
936         Replace old_prefix with new_prefix and old_suffix with new_suffix.
937
938         env - Environment used to interpolate variables.
939         path - the path that will be modified.
940         old_prefix - construction variable for the old prefix.
941         old_suffix - construction variable for the old suffix.
942         new_prefix - construction variable for the new prefix.
943         new_suffix - construction variable for the new suffix.
944         """
945         old_prefix = self.subst('$'+old_prefix)
946         old_suffix = self.subst('$'+old_suffix)
947
948         new_prefix = self.subst('$'+new_prefix)
949         new_suffix = self.subst('$'+new_suffix)
950
951         dir,name = os.path.split(str(path))
952         if name[:len(old_prefix)] == old_prefix:
953             name = name[len(old_prefix):]
954         if name[-len(old_suffix):] == old_suffix:
955             name = name[:-len(old_suffix)]
956         return os.path.join(dir, new_prefix+name+new_suffix)
957
958     def SetDefault(self, **kw):
959         for k in kw.keys():
960             if self._dict.has_key(k):
961                 del kw[k]
962         apply(self.Replace, (), kw)
963
964     def Tool(self, tool, toolpath=[]):
965         tool = self.subst(tool)
966         return SCons.Tool.Tool(tool, map(self.subst, toolpath))(self)
967
968     def WhereIs(self, prog, path=None, pathext=None, reject=[]):
969         """Find prog in the path.  
970         """
971         if path is None:
972             try:
973                 path = self['ENV']['PATH']
974             except KeyError:
975                 pass
976         elif SCons.Util.is_String(path):
977             path = self.subst(path)
978         if pathext is None:
979             try:
980                 pathext = self['ENV']['PATHEXT']
981             except KeyError:
982                 pass
983         elif SCons.Util.is_String(pathext):
984             pathext = self.subst(pathext)
985         path = SCons.Util.WhereIs(prog, path, pathext, reject)
986         if path: return path
987         return None
988
989     #######################################################################
990     # Public methods for doing real "SCons stuff" (manipulating
991     # dependencies, setting attributes on targets, etc.).  These begin
992     # with upper-case letters.  The essential characteristic of methods
993     # in this section is that they all *should* have corresponding
994     # same-named global functions.
995     #######################################################################
996
997     def Action(self, *args, **kw):
998         nargs = self.subst(args)
999         nkw = self.subst_kw(kw)
1000         return apply(SCons.Action.Action, nargs, nkw)
1001
1002     def AddPreAction(self, files, action):
1003         nodes = self.arg2nodes(files, self.fs.Entry)
1004         action = SCons.Action.Action(action)
1005         for n in nodes:
1006             n.add_pre_action(action)
1007         return nodes
1008     
1009     def AddPostAction(self, files, action):
1010         nodes = self.arg2nodes(files, self.fs.Entry)
1011         action = SCons.Action.Action(action)
1012         for n in nodes:
1013             n.add_post_action(action)
1014         return nodes
1015
1016     def Alias(self, target, *source, **kw):
1017         if not SCons.Util.is_List(target):
1018             target = [target]
1019         tlist = []
1020         for t in target:
1021             if not isinstance(t, SCons.Node.Alias.Alias):
1022                 t = self.arg2nodes(self.subst(t), self.ans.Alias)[0]
1023             tlist.append(t)
1024         try:
1025             s = kw['source']
1026         except KeyError:
1027             try:
1028                 s = source[0]
1029             except IndexError:
1030                 s = None
1031         if s:
1032             if not SCons.Util.is_List(s):
1033                 s = [s]
1034             s = filter(None, s)
1035             s = self.arg2nodes(s, self.fs.Entry)
1036             for t in tlist:
1037                 AliasBuilder(self, t, s)
1038         return tlist
1039
1040     def AlwaysBuild(self, *targets):
1041         tlist = []
1042         for t in targets:
1043             tlist.extend(self.arg2nodes(t, self.fs.File))
1044         for t in tlist:
1045             t.set_always_build()
1046         return tlist
1047
1048     def BuildDir(self, build_dir, src_dir, duplicate=1):
1049         build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1050         src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1051         self.fs.BuildDir(build_dir, src_dir, duplicate)
1052
1053     def Builder(self, **kw):
1054         nkw = self.subst_kw(kw)
1055         return apply(SCons.Builder.Builder, [], nkw)
1056
1057     def CacheDir(self, path):
1058         self.fs.CacheDir(self.subst(path))
1059
1060     def Clean(self, targets, files):
1061         global CleanTargets
1062         tlist = self.arg2nodes(targets, self.fs.Entry)
1063         flist = self.arg2nodes(files, self.fs.Entry)
1064         for t in tlist:
1065             try:
1066                 CleanTargets[t].extend(flist)
1067             except KeyError:
1068                 CleanTargets[t] = flist
1069
1070     def Configure(self, *args, **kw):
1071         nargs = [self]
1072         if args:
1073             nargs = nargs + self.subst_list(args)[0]
1074         nkw = self.subst_kw(kw)
1075         nkw['called_from_env_method'] = 1
1076         try:
1077             nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1078         except KeyError:
1079             pass
1080         return apply(SCons.SConf.SConf, nargs, nkw)
1081
1082     def Command(self, target, source, action, **kw):
1083         """Builds the supplied target files from the supplied
1084         source files using the supplied action.  Action may
1085         be any type that the Builder constructor will accept
1086         for an action."""
1087         nkw = self.subst_kw(kw)
1088         nkw['action'] = action
1089         nkw['source_factory'] = self.fs.Entry
1090         bld = apply(SCons.Builder.Builder, (), nkw)
1091         return bld(self, target, source)
1092
1093     def Depends(self, target, dependency):
1094         """Explicity specify that 'target's depend on 'dependency'."""
1095         tlist = self.arg2nodes(target, self.fs.Entry)
1096         dlist = self.arg2nodes(dependency, self.fs.Entry)
1097         for t in tlist:
1098             t.add_dependency(dlist)
1099         return tlist
1100
1101     def Dir(self, name, *args, **kw):
1102         """
1103         """
1104         return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1105
1106     def Environment(self, **kw):
1107         return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1108
1109     def Execute(self, action, *args, **kw):
1110         """Directly execute an action through an Environment
1111         """
1112         action = apply(self.Action, (action,) + args, kw)
1113         return action([], [], self)
1114
1115     def File(self, name, *args, **kw):
1116         """
1117         """
1118         return apply(self.fs.File, (self.subst(name),) + args, kw)
1119
1120     def FindFile(self, file, dirs):
1121         file = self.subst(file)
1122         nodes = self.arg2nodes(dirs, self.fs.Dir)
1123         return SCons.Node.FS.find_file(file, nodes, self.fs.File)
1124
1125     def Flatten(self, sequence):
1126         return SCons.Util.flatten(sequence)
1127
1128     def GetBuildPath(self, files):
1129         result = map(str, self.arg2nodes(files, self.fs.Entry))
1130         if SCons.Util.is_List(files):
1131             return result
1132         else:
1133             return result[0]
1134
1135     def Ignore(self, target, dependency):
1136         """Ignore a dependency."""
1137         tlist = self.arg2nodes(target, self.fs.Entry)
1138         dlist = self.arg2nodes(dependency, self.fs.Entry)
1139         for t in tlist:
1140             t.add_ignore(dlist)
1141         return tlist
1142
1143     def Install(self, dir, source):
1144         """Install specified files in the given directory."""
1145         try:
1146             dnodes = self.arg2nodes(dir, self.fs.Dir)
1147         except TypeError:
1148             raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory.  Perhaps you have the Install() arguments backwards?" % str(dir)
1149         try:
1150             sources = self.arg2nodes(source, self.fs.File)
1151         except TypeError:
1152             if SCons.Util.is_List(source):
1153                 raise SCons.Errors.UserError, "Source `%s' of Install() contains one or more non-files.  Install() source must be one or more files." % repr(map(str, source))
1154             else:
1155                 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file.  Install() source must be one or more files." % str(source)
1156         tgt = []
1157         for dnode in dnodes:
1158             for src in sources:
1159                 target = self.fs.File(src.name, dnode)
1160                 tgt.extend(InstallBuilder(self, target, src))
1161         return tgt
1162
1163     def InstallAs(self, target, source):
1164         """Install sources as targets."""
1165         sources = self.arg2nodes(source, self.fs.File)
1166         targets = self.arg2nodes(target, self.fs.File)
1167         result = []
1168         for src, tgt in map(lambda x, y: (x, y), sources, targets):
1169             result.extend(InstallBuilder(self, tgt, src))
1170         return result
1171
1172     def Literal(self, string):
1173         return SCons.Util.Literal(string)
1174
1175     def Local(self, *targets):
1176         ret = []
1177         for targ in targets:
1178             if isinstance(targ, SCons.Node.Node):
1179                 targ.set_local()
1180                 ret.append(targ)
1181             else:
1182                 for t in self.arg2nodes(targ, self.fs.Entry):
1183                    t.set_local()
1184                    ret.append(t)
1185         return ret
1186
1187     def Precious(self, *targets):
1188         tlist = []
1189         for t in targets:
1190             tlist.extend(self.arg2nodes(t, self.fs.Entry))
1191         for t in tlist:
1192             t.set_precious()
1193         return tlist
1194
1195     def Repository(self, *dirs, **kw):
1196         dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1197         apply(self.fs.Repository, dirs, kw)
1198
1199     def Scanner(self, *args, **kw):
1200         nargs = []
1201         for arg in args:
1202             if SCons.Util.is_String(arg):
1203                 arg = self.subst(arg)
1204             nargs.append(arg)
1205         nkw = self.subst_kw(kw)
1206         return apply(SCons.Scanner.Scanner, nargs, nkw)
1207
1208     def SConsignFile(self, name=".sconsign", dbm_module=None):
1209         name = self.subst(name)
1210         if not os.path.isabs(name):
1211             name = os.path.join(str(self.fs.SConstruct_dir), name)
1212         SCons.SConsign.File(name, dbm_module)
1213
1214     def SideEffect(self, side_effect, target):
1215         """Tell scons that side_effects are built as side 
1216         effects of building targets."""
1217         side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1218         targets = self.arg2nodes(target, self.fs.Entry)
1219
1220         for side_effect in side_effects:
1221             if side_effect.multiple_side_effect_has_builder():
1222                 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1223             side_effect.add_source(targets)
1224             side_effect.side_effect = 1
1225             self.Precious(side_effect)
1226             for target in targets:
1227                 target.side_effects.append(side_effect)
1228         return side_effects
1229
1230     def SourceCode(self, entry, builder):
1231         """Arrange for a source code builder for (part of) a tree."""
1232         entries = self.arg2nodes(entry, self.fs.Entry)
1233         for entry in entries:
1234             entry.set_src_builder(builder)
1235         return entries
1236
1237     def SourceSignatures(self, type):
1238         type = self.subst(type)
1239         if type == 'MD5':
1240             import SCons.Sig.MD5
1241             self._calc_module = SCons.Sig.MD5
1242         elif type == 'timestamp':
1243             import SCons.Sig.TimeStamp
1244             self._calc_module = SCons.Sig.TimeStamp
1245         else:
1246             raise UserError, "Unknown source signature type '%s'"%type
1247
1248     def Split(self, arg):
1249         """This function converts a string or list into a list of strings
1250         or Nodes.  This makes things easier for users by allowing files to
1251         be specified as a white-space separated list to be split.
1252         The input rules are:
1253             - A single string containing names separated by spaces. These will be
1254               split apart at the spaces.
1255             - A single Node instance
1256             - A list containing either strings or Node instances. Any strings
1257               in the list are not split at spaces.
1258         In all cases, the function returns a list of Nodes and strings."""
1259         if SCons.Util.is_List(arg):
1260             return map(self.subst, arg)
1261         elif SCons.Util.is_String(arg):
1262             return string.split(self.subst(arg))
1263         else:
1264             return [self.subst(arg)]
1265
1266     def TargetSignatures(self, type):
1267         type = self.subst(type)
1268         if type == 'build':
1269             self._build_signature = 1
1270         elif type == 'content':
1271             self._build_signature = 0
1272         else:
1273             raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1274
1275     def Value(self, value):
1276         """
1277         """
1278         return SCons.Node.Python.Value(value)
1279
1280 # The entry point that will be used by the external world
1281 # to refer to a construction environment.  This allows the wrapper
1282 # interface to extend a construction environment for its own purposes
1283 # by subclassing SCons.Environment.Base and then assigning the
1284 # class to SCons.Environment.Environment.
1285
1286 Environment = Base
1287
1288 # An entry point for returning a proxy subclass instance that overrides
1289 # the subst*() methods so they don't actually perform construction
1290 # variable substitution.  This is specifically intended to be the shim
1291 # layer in between global function calls (which don't want construction
1292 # variable substitution) and the DefaultEnvironment() (which would
1293 # substitute variables if left to its own devices)."""
1294 #
1295 # We have to wrap this in a function that allows us to delay definition of
1296 # the class until it's necessary, so that when it subclasses Environment
1297 # it will pick up whatever Environment subclass the wrapper interface
1298 # might have assigned to SCons.Environment.Environment.
1299
1300 def NoSubstitutionProxy(subject):
1301     class _NoSubstitutionProxy(Environment):
1302         def __init__(self, subject):
1303             self.__dict__['__subject'] = subject
1304         def __getattr__(self, name):
1305             return getattr(self.__dict__['__subject'], name)
1306         def __setattr__(self, name, value):
1307             return setattr(self.__dict__['__subject'], name, value)
1308         def raw_to_mode(self, dict):
1309             try:
1310                 raw = dict['raw']
1311             except KeyError:
1312                 pass
1313             else:
1314                 del dict['raw']
1315                 dict['mode'] = raw
1316         def subst(self, string, *args, **kwargs):
1317             return string
1318         def subst_kw(self, kw, *args, **kwargs):
1319             return kw
1320         def subst_list(self, string, *args, **kwargs):
1321             nargs = (string, self,) + args
1322             nkw = kwargs.copy()
1323             nkw['gvars'] = {}
1324             self.raw_to_mode(nkw)
1325             return apply(SCons.Util.scons_subst_list, nargs, nkw)
1326         def subst_target_source(self, string, *args, **kwargs):
1327             nargs = (string, self,) + args
1328             nkw = kwargs.copy()
1329             nkw['gvars'] = {}
1330             self.raw_to_mode(nkw)
1331             return apply(SCons.Util.scons_subst, nargs, nkw)
1332     return _NoSubstitutionProxy(subject)