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