6afa69f40869a525a816349dbe73553b5ae3b2c5
[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 import re
43 import shutil
44 from UserDict import UserDict
45
46 import SCons.Action
47 import SCons.Builder
48 import SCons.Defaults
49 import SCons.Errors
50 import SCons.Node
51 import SCons.Node.Alias
52 import SCons.Node.FS
53 import SCons.Node.Python
54 import SCons.Platform
55 import SCons.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    else:
109        copy = x
110    return copy
111
112 def apply_tools(env, tools, toolpath):
113     if tools:
114         for tool in tools:
115             if SCons.Util.is_String(tool):
116                 env.Tool(tool, toolpath)
117             else:
118                 tool(env)
119
120 class BuilderWrapper:
121     """Wrapper class that associates an environment with a Builder at
122     instantiation."""
123     def __init__(self, env, builder):
124         self.env = env
125         self.builder = builder
126
127     def __call__(self, *args, **kw):
128         return apply(self.builder, (self.env,) + args, kw)
129
130     # This allows a Builder to be executed directly
131     # through the Environment to which it's attached.
132     # In practice, we shouldn't need this, because
133     # builders actually get executed through a Node.
134     # But we do have a unit test for this, and can't
135     # yet rule out that it would be useful in the
136     # future, so leave it for now.
137     def execute(self, **kw):
138         kw['env'] = self.env
139         apply(self.builder.execute, (), kw)
140
141 class BuilderDict(UserDict):
142     """This is a dictionary-like class used by an Environment to hold
143     the Builders.  We need to do this because every time someone changes
144     the Builders in the Environment's BUILDERS dictionary, we must
145     update the Environment's attributes."""
146     def __init__(self, dict, env):
147         # Set self.env before calling the superclass initialization,
148         # because it will end up calling our other methods, which will
149         # need to point the values in this dictionary to self.env.
150         self.env = env
151         UserDict.__init__(self, dict)
152
153     def __setitem__(self, item, val):
154         UserDict.__setitem__(self, item, val)
155         try:
156             self.setenvattr(item, val)
157         except AttributeError:
158             # Have to catch this because sometimes __setitem__ gets
159             # called out of __init__, when we don't have an env
160             # attribute yet, nor do we want one!
161             pass
162
163     def setenvattr(self, item, val):
164         """Set the corresponding environment attribute for this Builder.
165
166         If the value is already a BuilderWrapper, we pull the builder
167         out of it and make another one, so that making a copy of an
168         existing BuilderDict is guaranteed separate wrappers for each
169         Builder + Environment pair."""
170         try:
171             builder = val.builder
172         except AttributeError:
173             builder = val
174         setattr(self.env, item, BuilderWrapper(self.env, builder))
175
176     def __delitem__(self, item):
177         UserDict.__delitem__(self, item)
178         delattr(self.env, item)
179
180     def update(self, dict):
181         for i, v in dict.items():
182             self.__setitem__(i, v)
183
184 class Base:
185     """Base class for construction Environments.  These are
186     the primary objects used to communicate dependency and
187     construction information to the build engine.
188
189     Keyword arguments supplied when the construction Environment
190     is created are construction variables used to initialize the
191     Environment.
192     """
193
194     #######################################################################
195     # This is THE class for interacting with the SCons build engine,
196     # and it contains a lot of stuff, so we're going to try to keep this
197     # a little organized by grouping the methods.
198     #######################################################################
199
200     #######################################################################
201     # Methods that make an Environment act like a dictionary.  These have
202     # the expected standard names for Python mapping objects.  Note that
203     # we don't actually make an Environment a subclass of UserDict for
204     # performance reasons.  Note also that we only supply methods for
205     # dictionary functionality that we actually need and use.
206     #######################################################################
207
208     def __init__(self,
209                  platform=None,
210                  tools=None,
211                  toolpath=[],
212                  options=None,
213                  **kw):
214         self.fs = SCons.Node.FS.default_fs
215         self.ans = SCons.Node.Alias.default_ans
216         self.lookup_list = SCons.Node.arg2nodes_lookups
217         self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
218
219         self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
220
221         if platform is None:
222             platform = self._dict.get('PLATFORM', None)
223             if platform is None:
224                 platform = SCons.Platform.Platform()
225         if SCons.Util.is_String(platform):
226             platform = SCons.Platform.Platform(platform)
227         self._dict['PLATFORM'] = str(platform)
228         platform(self)
229
230         # Apply the passed-in variables before calling the tools,
231         # because they may use some of them:
232         apply(self.Replace, (), kw)
233         
234         # Update the environment with the customizable options
235         # before calling the tools, since they may use some of the options: 
236         if options:
237             options.Update(self)
238
239         if tools is None:
240             tools = self._dict.get('TOOLS', None)
241             if tools is None:
242                 tools = ['default']
243         apply_tools(self, tools, toolpath)
244
245         # Reapply the passed in variables after calling the tools,
246         # since they should overide anything set by the tools:
247         apply(self.Replace, (), kw)
248
249         # Update the environment with the customizable options
250         # after calling the tools, since they should override anything
251         # set by the tools:
252         if options:
253             options.Update(self)
254
255     def __cmp__(self, other):
256         return cmp(self._dict, other._dict)
257
258     def __getitem__(self, key):
259         return self._dict[key]
260
261     def __setitem__(self, key, value):
262         if key in ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']:
263             SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
264                                 "Ignoring attempt to set reserved variable `%s'" % key)
265         elif key == 'BUILDERS':
266             try:
267                 bd = self._dict[key]
268                 for k in bd.keys():
269                     del bd[k]
270             except KeyError:
271                 self._dict[key] = BuilderDict(kwbd, self)
272             self._dict[key].update(value)
273         else:
274             if not SCons.Util.is_valid_construction_var(key):
275                 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
276             self._dict[key] = value
277
278     def __delitem__(self, key):
279         del self._dict[key]
280
281     def items(self):
282         "Emulates the items() method of dictionaries."""
283         return self._dict.items()
284
285     def has_key(self, key):
286         return self._dict.has_key(key)
287
288     def get(self, key, default=None):
289         "Emulates the get() method of dictionaries."""
290         return self._dict.get(key, default)
291
292     #######################################################################
293     # Utility methods that are primarily for internal use by SCons.
294     # These begin with lower-case letters.  Note that the subst() method
295     # is actually already out of the closet and used by people.
296     #######################################################################
297
298     def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
299         if node_factory is _null:
300             node_factory = self.fs.File
301         if lookup_list is _null:
302             lookup_list = self.lookup_list
303
304         if not args:
305             return []
306
307         if not SCons.Util.is_List(args):
308             args = [args]
309
310         nodes = []
311         for v in args:
312             if SCons.Util.is_String(v):
313                 n = None
314                 for l in lookup_list:
315                     n = l(v)
316                     if not n is None:
317                         break
318                 if not n is None:
319                     if SCons.Util.is_String(n):
320                         n = self.subst(n, raw=1)
321                         if node_factory:
322                             n = node_factory(n)
323                     nodes.append(n)
324                 elif node_factory:
325                     v = self.subst(v, raw=1)
326                     nodes.append(node_factory(v))
327             else:
328                 nodes.append(v)
329     
330         return nodes
331
332     def get_calculator(self):
333         try:
334             return self._calculator
335         except AttributeError:
336             try:
337                 module = self._calc_module
338                 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
339             except AttributeError:
340                 # Note that we're calling get_calculator() here, so the
341                 # DefaultEnvironment() must have a _calc_module attribute
342                 # to avoid infinite recursion.
343                 c = SCons.Defaults.DefaultEnvironment().get_calculator()
344             self._calculator = c
345             return c
346
347     def get_builder(self, name):
348         """Fetch the builder with the specified name from the environment.
349         """
350         try:
351             return self._dict['BUILDERS'][name]
352         except KeyError:
353             return None
354
355     def get_scanner(self, skey):
356         """Find the appropriate scanner given a key (usually a file suffix).
357         Does a linear search. Could be sped up by creating a dictionary if
358         this proves too slow.
359         """
360         if self._dict['SCANNERS']:
361             for scanner in self._dict['SCANNERS']:
362                 if skey in scanner.skeys:
363                     return scanner
364         return None
365
366     def subst(self, string, raw=0, target=None, source=None):
367         """Recursively interpolates construction variables from the
368         Environment into the specified string, returning the expanded
369         result.  Construction variables are specified by a $ prefix
370         in the string and begin with an initial underscore or
371         alphabetic character followed by any number of underscores
372         or alphanumeric characters.  The construction variable names
373         may be surrounded by curly braces to separate the name from
374         trailing characters.
375         """
376         if raw:
377             mode = SCons.Util.SUBST_RAW
378         else:
379             mode = SCons.Util.SUBST_CMD
380         return SCons.Util.scons_subst(string, self, mode, target, source)
381     
382     def subst_kw(self, kw, raw=0, target=None, source=None):
383         if raw:
384             mode = SCons.Util.SUBST_RAW
385         else:
386             mode = SCons.Util.SUBST_CMD
387         nkw = {}
388         for k, v in kw.items():
389             k = SCons.Util.scons_subst(k, self, mode, target, source)
390             if SCons.Util.is_String(v):
391                 v = SCons.Util.scons_subst(v, self, mode, target, source)
392             nkw[k] = v
393         return nkw
394     
395     def subst_list(self, string, raw=0, target=None, source=None):
396         """Calls through to SCons.Util.scons_subst_list().  See
397         the documentation for that function."""
398         if raw:
399             mode = SCons.Util.SUBST_RAW
400         else:
401             mode = SCons.Util.SUBST_CMD
402         return SCons.Util.scons_subst_list(string, self, mode, target, source)
403
404     def use_build_signature(self):
405         try:
406             return self._build_signature
407         except AttributeError:
408             b = SCons.Defaults.DefaultEnvironment()._build_signature
409             self._build_signature = b
410             return b
411
412     #######################################################################
413     # Public methods for manipulating an Environment.  These begin with
414     # upper-case letters.  The essential characteristic of methods in
415     # this section is that they do *not* have corresponding same-named
416     # global functions.  For example, a stand-alone Append() function
417     # makes no sense, because Append() is all about appending values to
418     # an Environment's construction variables.
419     #######################################################################
420
421     def Append(self, **kw):
422         """Append values to existing construction variables
423         in an Environment.
424         """
425         kw = our_deepcopy(kw)
426         for key in kw.keys():
427             if not self._dict.has_key(key):
428                 self._dict[key] = kw[key]
429             elif SCons.Util.is_List(self._dict[key]) and not \
430                  SCons.Util.is_List(kw[key]):
431                 if kw[key]:
432                     self._dict[key] = self._dict[key] + [ kw[key] ]
433                 #self._dict[key] = map(None, self._dict[key] + [ kw[key] ])
434             elif SCons.Util.is_List(kw[key]) and not \
435                  SCons.Util.is_List(self._dict[key]):
436                 if self._dict[key]:
437                     self._dict[key] = [ self._dict[key] ] + kw[key]
438                 else:
439                     self._dict[key] = kw[key]
440                 #self._dict[key] = map(None, self._dict[key] + [ kw[key] ])
441             elif SCons.Util.is_Dict(self._dict[key]) and \
442                  SCons.Util.is_Dict(kw[key]):
443                 self._dict[key].update(kw[key])
444             else:
445                 self._dict[key] = self._dict[key] + kw[key]
446
447     def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
448         """Append path elements to the path 'name' in the 'ENV'
449         dictionary for this environment.  Will only add any particular
450         path once, and will normpath and normcase all paths to help
451         assure this.  This can also handle the case where the env
452         variable is a list instead of a string.
453         """
454
455         orig = ''
456         if self._dict.has_key(envname) and self._dict[envname].has_key(name):
457             orig = self._dict[envname][name]
458
459         nv = SCons.Util.AppendPath(orig, newpath, sep)
460             
461         if not self._dict.has_key(envname):
462             self._dict[envname] = {}
463
464         self._dict[envname][name] = nv
465
466     def Copy(self, tools=None, toolpath=[], **kw):
467         """Return a copy of a construction Environment.  The
468         copy is like a Python "deep copy"--that is, independent
469         copies are made recursively of each objects--except that
470         a reference is copied when an object is not deep-copyable
471         (like a function).  There are no references to any mutable
472         objects in the original Environment.
473         """
474         clone = copy.copy(self)
475         clone._dict = our_deepcopy(self._dict)
476         try:
477             cbd = clone._dict['BUILDERS']
478             clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
479         except KeyError:
480             pass
481         
482         apply_tools(clone, tools, toolpath)
483
484         # Apply passed-in variables after the new tools.
485         apply(clone.Replace, (), kw)
486         return clone
487
488     def Detect(self, progs):
489         """Return the first available program in progs.
490         """
491         if not SCons.Util.is_List(progs):
492             progs = [ progs ]
493         for prog in progs:
494             path = self.WhereIs(prog)
495             if path: return prog
496         return None
497
498     def Dictionary(self, *args):
499         if not args:
500             return self._dict
501         dlist = map(lambda x, s=self: s._dict[x], args)
502         if len(dlist) == 1:
503             dlist = dlist[0]
504         return dlist
505
506     def FindIxes(self, paths, prefix, suffix):
507         """
508         Search a list of paths for something that matches the prefix and suffix.
509
510         paths - the list of paths or nodes.
511         prefix - construction variable for the prefix.
512         suffix - construction variable for the suffix.
513         """
514
515         suffix = self.subst('$%s'%suffix)
516         prefix = self.subst('$%s'%prefix)
517
518         for path in paths:
519             dir,name = os.path.split(str(path))
520             if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: 
521                 return path
522
523     def Override(self, overrides):
524         """
525         Produce a modified environment whose variables
526         are overriden by the overrides dictionaries.
527
528         overrides - a dictionary that will override
529         the variables of this environment.
530
531         This function is much more efficient than Copy()
532         or creating a new Environment because it doesn't do
533         a deep copy of the dictionary, and doesn't do a copy
534         at all if there are no overrides.
535         """
536
537         if overrides:
538             env = copy.copy(self)
539             env._dict = copy.copy(self._dict)
540             env._dict.update(overrides)
541             return env
542         else:
543             return self
544
545     def ParseConfig(self, command, function=None):
546         """
547         Use the specified function to parse the output of the command
548         in order to modify the current environment. The 'command'
549         can be a string or a list of strings representing a command and
550         it's arguments. 'Function' is an optional argument that takes
551         the environment and the output of the command. If no function is
552         specified, the output will be treated as the output of a typical
553         'X-config' command (i.e. gtk-config) and used to set the CPPPATH,
554         LIBPATH, LIBS, and CCFLAGS variables.
555         """
556
557         # the default parse function
558         def parse_conf(env, output):
559             dict = {
560                 'CPPPATH' : [],
561                 'LIBPATH' : [],
562                 'LIBS'    : [],
563                 'CCFLAGS' : [],
564             }
565             static_libs = []
566     
567             params = string.split(output)
568             for arg in params:
569                 switch = arg[0:1]
570                 opt = arg[1:2]
571                 if switch == '-':
572                     if opt == 'L':
573                         dict['LIBPATH'].append(arg[2:])
574                     elif opt == 'l':
575                         dict['LIBS'].append(arg[2:])
576                     elif opt == 'I':
577                         dict['CPPPATH'].append(arg[2:])
578                     else:
579                         dict['CCFLAGS'].append(arg)
580                 else:
581                     static_libs.append(arg)
582             apply(env.Append, (), dict)
583             return static_libs
584     
585         if function is None:
586             function = parse_conf
587         if type(command) is type([]):
588             command = string.join(command)
589         command = self.subst(command)
590         return function(self, os.popen(command).read())
591
592     def Platform(self, platform):
593         platform = self.subst(platform)
594         return SCons.Platform.Platform(platform)(self)
595
596     def Prepend(self, **kw):
597         """Prepend values to existing construction variables
598         in an Environment.
599         """
600         kw = our_deepcopy(kw)
601         for key in kw.keys():
602             if not self._dict.has_key(key):
603                 self._dict[key] = kw[key]
604             elif SCons.Util.is_List(self._dict[key]) and not \
605                  SCons.Util.is_List(kw[key]):
606                 self._dict[key] = [ kw[key] ] + self._dict[key]
607             elif SCons.Util.is_List(kw[key]) and not \
608                  SCons.Util.is_List(self._dict[key]):
609                 self._dict[key] = kw[key] + [ self._dict[key] ]
610             elif SCons.Util.is_Dict(self._dict[key]) and \
611                  SCons.Util.is_Dict(kw[key]):
612                 self._dict[key].update(kw[key])
613             else:
614                 self._dict[key] = kw[key] + self._dict[key]
615
616     def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
617         """Prepend path elements to the path 'name' in the 'ENV'
618         dictionary for this environment.  Will only add any particular
619         path once, and will normpath and normcase all paths to help
620         assure this.  This can also handle the case where the env
621         variable is a list instead of a string.
622         """
623
624         orig = ''
625         if self._dict.has_key(envname) and self._dict[envname].has_key(name):
626             orig = self._dict[envname][name]
627
628         nv = SCons.Util.PrependPath(orig, newpath, sep)
629             
630         if not self._dict.has_key(envname):
631             self._dict[envname] = {}
632
633         self._dict[envname][name] = nv
634
635     def Replace(self, **kw):
636         """Replace existing construction variables in an Environment
637         with new construction variables and/or values.
638         """
639         try:
640             kwbd = our_deepcopy(kw['BUILDERS'])
641             del kw['BUILDERS']
642             self.__setitem__('BUILDERS', kwbd)
643         except KeyError:
644             pass
645         self._dict.update(our_deepcopy(kw))
646
647     def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
648         """
649         Replace old_prefix with new_prefix and old_suffix with new_suffix.
650
651         env - Environment used to interpolate variables.
652         path - the path that will be modified.
653         old_prefix - construction variable for the old prefix.
654         old_suffix - construction variable for the old suffix.
655         new_prefix - construction variable for the new prefix.
656         new_suffix - construction variable for the new suffix.
657         """
658         old_prefix = self.subst('$%s'%old_prefix)
659         old_suffix = self.subst('$%s'%old_suffix)
660
661         new_prefix = self.subst('$%s'%new_prefix)
662         new_suffix = self.subst('$%s'%new_suffix)
663
664         dir,name = os.path.split(str(path))
665         if name[:len(old_prefix)] == old_prefix:
666             name = name[len(old_prefix):]
667         if name[-len(old_suffix):] == old_suffix:
668             name = name[:-len(old_suffix)]
669         return os.path.join(dir, new_prefix+name+new_suffix)
670
671     def Tool(self, tool, toolpath=[]):
672         tool = self.subst(tool)
673         return SCons.Tool.Tool(tool, map(self.subst, toolpath))(self)
674
675     def WhereIs(self, prog, path=None, pathext=None):
676         """Find prog in the path.  
677         """
678         if path is None:
679             try:
680                 path = self['ENV']['PATH']
681             except KeyError:
682                 pass
683         elif SCons.Util.is_String(path):
684             path = self.subst(path)
685         if pathext is None:
686             try:
687                 pathext = self['ENV']['PATHEXT']
688             except KeyError:
689                 pass
690         elif SCons.Util.is_String(pathext):
691             pathext = self.subst(pathext)
692         path = SCons.Util.WhereIs(prog, path, pathext)
693         if path: return path
694         return None
695
696     #######################################################################
697     # Public methods for doing real "SCons stuff" (manipulating
698     # dependencies, setting attributes on targets, etc.).  These begin
699     # with upper-case letters.  The essential characteristic of methods
700     # in this section is that they all *should* have corresponding
701     # same-named global functions.
702     #######################################################################
703
704     def Action(self, *args, **kw):
705         nargs = self.subst_list(args)
706         nkw = self.subst_kw(kw)
707         return apply(SCons.Action.Action, nargs, nkw)
708
709     def AddPreAction(self, files, action):
710         nodes = self.arg2nodes(files, self.fs.Entry)
711         action = SCons.Action.Action(action)
712         for n in nodes:
713             n.add_pre_action(action)
714         return nodes
715     
716     def AddPostAction(self, files, action):
717         nodes = self.arg2nodes(files, self.fs.Entry)
718         action = SCons.Action.Action(action)
719         for n in nodes:
720             n.add_post_action(action)
721         return nodes
722
723     def Alias(self, target, *source, **kw):
724         if not SCons.Util.is_List(target):
725             target = [target]
726         tlist = []
727         for t in target:
728             if not isinstance(t, SCons.Node.Alias.Alias):
729                 t = self.arg2nodes(self.subst(t), self.ans.Alias)[0]
730             tlist.append(t)
731         try:
732             s = kw['source']
733         except KeyError:
734             try:
735                 s = source[0]
736             except IndexError:
737                 s = None
738         if s:
739             if not SCons.Util.is_List(s):
740                 s = [s]
741             s = filter(None, s)
742             s = self.arg2nodes(s, self.fs.Entry)
743             for t in tlist:
744                 AliasBuilder(self, t, s)
745         if len(tlist) == 1:
746             tlist = tlist[0]
747         return tlist
748
749     def AlwaysBuild(self, *targets):
750         tlist = []
751         for t in targets:
752             tlist.extend(self.arg2nodes(t, self.fs.File))
753
754         for t in tlist:
755             t.set_always_build()
756
757         if len(tlist) == 1:
758             tlist = tlist[0]
759         return tlist
760
761     def BuildDir(self, build_dir, src_dir, duplicate=1):
762         build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
763         src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
764         self.fs.BuildDir(build_dir, src_dir, duplicate)
765
766     def Builder(self, **kw):
767         nkw = self.subst_kw(kw)
768         return apply(SCons.Builder.Builder, [], nkw)
769
770     def CacheDir(self, path):
771         self.fs.CacheDir(self.subst(path))
772
773     def Clean(self, target, files):
774         global CleanTargets
775
776         if not isinstance(target, SCons.Node.Node):
777             target = self.subst(target)
778             target = self.fs.Entry(target, create=1)
779     
780         if not SCons.Util.is_List(files):
781             files = [files]
782     
783         nodes = []
784         for f in files:
785             if isinstance(f, SCons.Node.Node):
786                 nodes.append(f)
787             else:
788                 nodes.extend(self.arg2nodes(f, self.fs.Entry))
789     
790         try:
791             CleanTargets[target].extend(nodes)
792         except KeyError:
793             CleanTargets[target] = nodes
794
795     def Configure(self, *args, **kw):
796         nargs = [self]
797         if args:
798             nargs = nargs + self.subst_list(args)[0]
799         nkw = self.subst_kw(kw)
800         try:
801             nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
802         except KeyError:
803             pass
804         return apply(SCons.SConf.SConf, nargs, nkw)
805
806     def Command(self, target, source, action):
807         """Builds the supplied target files from the supplied
808         source files using the supplied action.  Action may
809         be any type that the Builder constructor will accept
810         for an action."""
811         bld = SCons.Builder.Builder(action=action,
812                                     source_factory=self.fs.Entry)
813         return bld(self, target, source)
814
815     def Depends(self, target, dependency):
816         """Explicity specify that 'target's depend on 'dependency'."""
817         tlist = self.arg2nodes(target, self.fs.Entry)
818         dlist = self.arg2nodes(dependency, self.fs.Entry)
819         for t in tlist:
820             t.add_dependency(dlist)
821
822         if len(tlist) == 1:
823             tlist = tlist[0]
824         return tlist
825
826     def Dir(self, name, *args, **kw):
827         """
828         """
829         return apply(self.fs.Dir, (self.subst(name),) + args, kw)
830
831     def Environment(self, **kw):
832         return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
833
834     def File(self, name, *args, **kw):
835         """
836         """
837         return apply(self.fs.File, (self.subst(name),) + args, kw)
838
839     def FindFile(self, file, dirs):
840         file = self.subst(file)
841         nodes = self.arg2nodes(dirs, self.fs.Dir)
842         return SCons.Node.FS.find_file(file, nodes, self.fs.File)
843
844     def GetBuildPath(self, files):
845         ret = map(str, self.arg2nodes(files, self.fs.Entry))
846         if len(ret) == 1:
847             return ret[0]
848         return ret
849
850     def Ignore(self, target, dependency):
851         """Ignore a dependency."""
852         tlist = self.arg2nodes(target, self.fs.Entry)
853         dlist = self.arg2nodes(dependency, self.fs.Entry)
854         for t in tlist:
855             t.add_ignore(dlist)
856
857         if len(tlist) == 1:
858             tlist = tlist[0]
859         return tlist
860
861     def Install(self, dir, source):
862         """Install specified files in the given directory."""
863         try:
864             dnodes = self.arg2nodes(dir, self.fs.Dir)
865         except TypeError:
866             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)
867         try:
868             sources = self.arg2nodes(source, self.fs.File)
869         except TypeError:
870             if SCons.Util.is_List(source):
871                 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))
872             else:
873                 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file.  Install() source must be one or more files." % str(source)
874         tgt = []
875         for dnode in dnodes:
876             for src in sources:
877                 target = self.fs.File(src.name, dnode)
878                 tgt.append(InstallBuilder(self, target, src))
879         if len(tgt) == 1:
880             tgt = tgt[0]
881         return tgt
882
883     def InstallAs(self, target, source):
884         """Install sources as targets."""
885         sources = self.arg2nodes(source, self.fs.File)
886         targets = self.arg2nodes(target, self.fs.File)
887         ret = []
888         for src, tgt in map(lambda x, y: (x, y), sources, targets):
889             ret.append(InstallBuilder(self, tgt, src))
890         if len(ret) == 1:
891             ret = ret[0]
892         return ret
893
894     def Literal(self, string):
895         return SCons.Util.Literal(string)
896
897     def Local(self, *targets):
898         ret = []
899         for targ in targets:
900             if isinstance(targ, SCons.Node.Node):
901                 targ.set_local()
902                 ret.append(targ)
903             else:
904                 for t in self.arg2nodes(targ, self.fs.Entry):
905                    t.set_local()
906                    ret.append(t)
907         return ret
908
909     def Precious(self, *targets):
910         tlist = []
911         for t in targets:
912             tlist.extend(self.arg2nodes(t, self.fs.Entry))
913
914         for t in tlist:
915             t.set_precious()
916
917         if len(tlist) == 1:
918             tlist = tlist[0]
919         return tlist
920
921     def Repository(self, *dirs, **kw):
922         dirs = self.arg2nodes(list(dirs), self.fs.Dir)
923         apply(self.fs.Repository, dirs, kw)
924
925     def Scanner(self, *args, **kw):
926         nargs = []
927         for arg in args:
928             if SCons.Util.is_String(arg):
929                 arg = self.subst(arg)
930             nargs.append(arg)
931         nkw = self.subst_kw(kw)
932         return apply(SCons.Scanner.Base, nargs, nkw)
933
934     def SConsignFile(self, name=".sconsign.dbm", dbm_module=None):
935         name = self.subst(name)
936         if not os.path.isabs(name):
937             name = os.path.join(str(self.fs.SConstruct_dir), name)
938         SCons.Sig.SConsignFile(name, dbm_module)
939
940     def SideEffect(self, side_effect, target):
941         """Tell scons that side_effects are built as side 
942         effects of building targets."""
943         side_effects = self.arg2nodes(side_effect, self.fs.Entry)
944         targets = self.arg2nodes(target, self.fs.Entry)
945
946         for side_effect in side_effects:
947             # A builder of 1 means the node is supposed to appear
948             # buildable without actually having a builder, so we allow
949             # it to be a side effect as well.
950             if side_effect.has_builder() and side_effect.builder != 1:
951                 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
952             side_effect.add_source(targets)
953             side_effect.side_effect = 1
954             self.Precious(side_effect)
955             for target in targets:
956                 target.side_effects.append(side_effect)
957         if len(side_effects) == 1:
958             return side_effects[0]
959         else:
960             return side_effects
961
962     def SourceCode(self, entry, builder):
963         """Arrange for a source code builder for (part of) a tree."""
964         entries = self.arg2nodes(entry, self.fs.Entry)
965         for entry in entries:
966             entry.set_src_builder(builder)
967         if len(entries) == 1:
968             return entries[0]
969         return entries
970
971     def SourceSignatures(self, type):
972         type = self.subst(type)
973         if type == 'MD5':
974             import SCons.Sig.MD5
975             self._calc_module = SCons.Sig.MD5
976         elif type == 'timestamp':
977             import SCons.Sig.TimeStamp
978             self._calc_module = SCons.Sig.TimeStamp
979         else:
980             raise UserError, "Unknown source signature type '%s'"%type
981
982     def Split(self, arg):
983         """This function converts a string or list into a list of strings
984         or Nodes.  This makes things easier for users by allowing files to
985         be specified as a white-space separated list to be split.
986         The input rules are:
987             - A single string containing names separated by spaces. These will be
988               split apart at the spaces.
989             - A single Node instance
990             - A list containing either strings or Node instances. Any strings
991               in the list are not split at spaces.
992         In all cases, the function returns a list of Nodes and strings."""
993         if SCons.Util.is_List(arg):
994             return map(self.subst, arg)
995         elif SCons.Util.is_String(arg):
996             return string.split(self.subst(arg))
997         else:
998             return [self.subst(arg)]
999
1000     def TargetSignatures(self, type):
1001         type = self.subst(type)
1002         if type == 'build':
1003             self._build_signature = 1
1004         elif type == 'content':
1005             self._build_signature = 0
1006         else:
1007             raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1008
1009     def Value(self, value):
1010         """
1011         """
1012         return SCons.Node.Python.Value(value)
1013
1014 # The entry point that will be used by the external world
1015 # to refer to a construction environment.  This allows the wrapper
1016 # interface to extend a construction environment for its own purposes
1017 # by subclassing SCons.Environment.Base and then assigning the
1018 # class to SCons.Environment.Environment.
1019
1020 Environment = Base