Apply Memoizer to cache more return values from various methods. (Kevin Quick)
[scons.git] / src / engine / SCons / Util.py
1 """SCons.Util
2
3 Various utility functions go here.
4
5 """
6
7 #
8 # __COPYRIGHT__
9 #
10 # Permission is hereby granted, free of charge, to any person obtaining
11 # a copy of this software and associated documentation files (the
12 # "Software"), to deal in the Software without restriction, including
13 # without limitation the rights to use, copy, modify, merge, publish,
14 # distribute, sublicense, and/or sell copies of the Software, and to
15 # permit persons to whom the Software is furnished to do so, subject to
16 # the following conditions:
17 #
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
20 #
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #
29
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
31
32
33 import copy
34 import os
35 import os.path
36 import re
37 import stat
38 import string
39 import sys
40 import types
41 from UserDict import UserDict
42 import UserList
43
44 import SCons.Errors
45
46 try:
47     from UserString import UserString
48 except ImportError:
49     # "Borrowed" from the Python 2.2 UserString module
50     # and modified slightly for use with SCons.
51     class UserString:
52         def __init__(self, seq):
53             if is_String(seq):
54                 self.data = seq
55             elif isinstance(seq, UserString):
56                 self.data = seq.data[:]
57             else:
58                 self.data = str(seq)
59         def __str__(self): return str(self.data)
60         def __repr__(self): return repr(self.data)
61         def __int__(self): return int(self.data)
62         def __long__(self): return long(self.data)
63         def __float__(self): return float(self.data)
64         def __complex__(self): return complex(self.data)
65         def __hash__(self): return hash(self.data)
66
67         def __cmp__(self, string):
68             if isinstance(string, UserString):
69                 return cmp(self.data, string.data)
70             else:
71                 return cmp(self.data, string)
72         def __contains__(self, char):
73             return char in self.data
74
75         def __len__(self): return len(self.data)
76         def __getitem__(self, index): return self.__class__(self.data[index])
77         def __getslice__(self, start, end):
78             start = max(start, 0); end = max(end, 0)
79             return self.__class__(self.data[start:end])
80
81         def __add__(self, other):
82             if isinstance(other, UserString):
83                 return self.__class__(self.data + other.data)
84             elif is_String(other):
85                 return self.__class__(self.data + other)
86             else:
87                 return self.__class__(self.data + str(other))
88         def __radd__(self, other):
89             if is_String(other):
90                 return self.__class__(other + self.data)
91             else:
92                 return self.__class__(str(other) + self.data)
93         def __mul__(self, n):
94             return self.__class__(self.data*n)
95         __rmul__ = __mul__
96
97 #
98 import __builtin__
99 try:
100     __builtin__.zip
101 except AttributeError:
102     def zip(*lists):
103         result = []
104         for i in xrange(len(lists[0])):
105             result.append(tuple(map(lambda l, i=i: l[i], lists)))
106         return result
107     __builtin__.zip = zip
108
109 _altsep = os.altsep
110 if _altsep is None and sys.platform == 'win32':
111     # My ActivePython 2.0.1 doesn't set os.altsep!  What gives?
112     _altsep = '/'
113 if _altsep:
114     def rightmost_separator(path, sep, _altsep=_altsep):
115         rfind = string.rfind
116         return max(rfind(path, sep), rfind(path, _altsep))
117 else:
118     rightmost_separator = string.rfind
119
120 # First two from the Python Cookbook, just for completeness.
121 # (Yeah, yeah, YAGNI...)
122 def containsAny(str, set):
123     """Check whether sequence str contains ANY of the items in set."""
124     for c in set:
125         if c in str: return 1
126     return 0
127
128 def containsAll(str, set):
129     """Check whether sequence str contains ALL of the items in set."""
130     for c in set:
131         if c not in str: return 0
132     return 1
133
134 def containsOnly(str, set):
135     """Check whether sequence str contains ONLY items in set."""
136     for c in str:
137         if c not in set: return 0
138     return 1
139
140 def splitext(path):
141     "Same as os.path.splitext() but faster."
142     sep = rightmost_separator(path, os.sep)
143     dot = string.rfind(path, '.')
144     # An ext is only real if it has at least one non-digit char
145     if dot > sep and not containsOnly(path[dot:], "0123456789."):
146         return path[:dot],path[dot:]
147     else:
148         return path,""
149
150 def updrive(path):
151     """
152     Make the drive letter (if any) upper case.
153     This is useful because Windows is inconsitent on the case
154     of the drive letter, which can cause inconsistencies when
155     calculating command signatures.
156     """
157     drive, rest = os.path.splitdrive(path)
158     if drive:
159         path = string.upper(drive) + rest
160     return path
161
162 #
163 # Generic convert-to-string functions that abstract away whether or
164 # not the Python we're executing has Unicode support.  The wrapper
165 # to_String_for_signature() will use a for_signature() method if the
166 # specified object has one.
167 #
168 if hasattr(types, 'UnicodeType'):
169     def to_String(s):
170         if isinstance(s, UserString):
171             t = type(s.data)
172         else:
173             t = type(s)
174         if t is types.UnicodeType:
175             return unicode(s)
176         else:
177             return str(s)
178 else:
179     to_String = str
180
181 def to_String_for_signature(obj):
182     try:
183         f = obj.for_signature
184     except AttributeError:
185         return to_String(obj)
186     else:
187         return f()
188
189 # Indexed by the SUBST_* constants below.
190 _strconv = [to_String, to_String, to_String_for_signature]
191
192 class Literal:
193     """A wrapper for a string.  If you use this object wrapped
194     around a string, then it will be interpreted as literal.
195     When passed to the command interpreter, all special
196     characters will be escaped."""
197     def __init__(self, lstr):
198         self.lstr = lstr
199
200     def __str__(self):
201         return self.lstr
202
203     def escape(self, escape_func):
204         return escape_func(self.lstr)
205
206     def for_signature(self):
207         return self.lstr
208
209     def is_literal(self):
210         return 1
211
212 class SpecialAttrWrapper:
213     """This is a wrapper for what we call a 'Node special attribute.'
214     This is any of the attributes of a Node that we can reference from
215     Environment variable substitution, such as $TARGET.abspath or
216     $SOURCES[1].filebase.  We implement the same methods as Literal
217     so we can handle special characters, plus a for_signature method,
218     such that we can return some canonical string during signature
219     calculation to avoid unnecessary rebuilds."""
220
221     def __init__(self, lstr, for_signature=None):
222         """The for_signature parameter, if supplied, will be the
223         canonical string we return from for_signature().  Else
224         we will simply return lstr."""
225         self.lstr = lstr
226         if for_signature:
227             self.forsig = for_signature
228         else:
229             self.forsig = lstr
230
231     def __str__(self):
232         return self.lstr
233
234     def escape(self, escape_func):
235         return escape_func(self.lstr)
236
237     def for_signature(self):
238         return self.forsig
239
240     def is_literal(self):
241         return 1
242
243 class CallableComposite(UserList.UserList):
244     """A simple composite callable class that, when called, will invoke all
245     of its contained callables with the same arguments."""
246     def __call__(self, *args, **kwargs):
247         retvals = map(lambda x, args=args, kwargs=kwargs: apply(x,
248                                                                 args,
249                                                                 kwargs),
250                       self.data)
251         if self.data and (len(self.data) == len(filter(callable, retvals))):
252             return self.__class__(retvals)
253         return NodeList(retvals)
254
255 class NodeList(UserList.UserList):
256     """This class is almost exactly like a regular list of Nodes
257     (actually it can hold any object), with one important difference.
258     If you try to get an attribute from this list, it will return that
259     attribute from every item in the list.  For example:
260
261     >>> someList = NodeList([ '  foo  ', '  bar  ' ])
262     >>> someList.strip()
263     [ 'foo', 'bar' ]
264     """
265     def __nonzero__(self):
266         return len(self.data) != 0
267
268     def __str__(self):
269         return string.join(map(str, self.data))
270
271     def __getattr__(self, name):
272         if not self.data:
273             # If there is nothing in the list, then we have no attributes to
274             # pass through, so raise AttributeError for everything.
275             raise AttributeError, "NodeList has no attribute: %s" % name
276
277         # Return a list of the attribute, gotten from every element
278         # in the list
279         attrList = map(lambda x, n=name: getattr(x, n), self.data)
280
281         # Special case.  If the attribute is callable, we do not want
282         # to return a list of callables.  Rather, we want to return a
283         # single callable that, when called, will invoke the function on
284         # all elements of this list.
285         if self.data and (len(self.data) == len(filter(callable, attrList))):
286             return CallableComposite(attrList)
287         return self.__class__(attrList)
288
289 _valid_var = re.compile(r'[_a-zA-Z]\w*$')
290 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
291
292 def is_valid_construction_var(varstr):
293     """Return if the specified string is a legitimate construction
294     variable.
295     """
296     return _valid_var.match(varstr)
297
298 def get_environment_var(varstr):
299     """Given a string, first determine if it looks like a reference
300     to a single environment variable, like "$FOO" or "${FOO}".
301     If so, return that variable with no decorations ("FOO").
302     If not, return None."""
303     mo=_get_env_var.match(to_String(varstr))
304     if mo:
305         var = mo.group(1)
306         if var[0] == '{':
307             return var[1:-1]
308         else:
309             return var
310     else:
311         return None
312
313 def quote_spaces(arg):
314     """Generic function for putting double quotes around any string that
315     has white space in it."""
316     if ' ' in arg or '\t' in arg:
317         return '"%s"' % arg
318     else:
319         return str(arg)
320
321 class CmdStringHolder(UserString):
322     """This is a special class used to hold strings generated by
323     scons_subst() and scons_subst_list().  It defines a special method
324     escape().  When passed a function with an escape algorithm for a
325     particular platform, it will return the contained string with the
326     proper escape sequences inserted.
327
328     This should really be a subclass of UserString, but that module
329     doesn't exist in Python 1.5.2."""
330     def __init__(self, cmd, literal=None):
331         UserString.__init__(self, cmd)
332         self.literal = literal
333
334     def is_literal(self):
335         return self.literal
336
337     def escape(self, escape_func, quote_func=quote_spaces):
338         """Escape the string with the supplied function.  The
339         function is expected to take an arbitrary string, then
340         return it with all special characters escaped and ready
341         for passing to the command interpreter.
342
343         After calling this function, the next call to str() will
344         return the escaped string.
345         """
346
347         if self.is_literal():
348             return escape_func(self.data)
349         elif ' ' in self.data or '\t' in self.data:
350             return quote_func(self.data)
351         else:
352             return self.data
353
354 class DisplayEngine:
355     def __init__(self):
356         self.__call__ = self.print_it
357
358     def print_it(self, text, append_newline=1):
359         if append_newline: text = text + '\n'
360         sys.stdout.write(text)
361
362     def dont_print(self, text, append_newline=1):
363         pass
364
365     def set_mode(self, mode):
366         if mode:
367             self.__call__ = self.print_it
368         else:
369             self.__call__ = self.dont_print
370
371 def escape_list(list, escape_func):
372     """Escape a list of arguments by running the specified escape_func
373     on every object in the list that has an escape() method."""
374     def escape(obj, escape_func=escape_func):
375         try:
376             e = obj.escape
377         except AttributeError:
378             return obj
379         else:
380             return e(escape_func)
381     return map(escape, list)
382
383 class NLWrapper:
384     """A wrapper class that delays turning a list of sources or targets
385     into a NodeList until it's needed.  The specified function supplied
386     when the object is initialized is responsible for turning raw nodes
387     into proxies that implement the special attributes like .abspath,
388     .source, etc.  This way, we avoid creating those proxies just
389     "in case" someone is going to use $TARGET or the like, and only
390     go through the trouble if we really have to.
391
392     In practice, this might be a wash performance-wise, but it's a little
393     cleaner conceptually...
394     """
395
396     def __init__(self, list, func):
397         self.list = list
398         self.func = func
399     def _create_nodelist(self):
400         try:
401             return self.nodelist
402         except AttributeError:
403             list = self.list
404             if list is None:
405                 list = []
406             elif not is_List(list):
407                 list = [list]
408             # The map(self.func) call is what actually turns
409             # a list into appropriate proxies.
410             self.nodelist = NodeList(map(self.func, list))
411         return self.nodelist
412
413 class Targets_or_Sources(UserList.UserList):
414     """A class that implements $TARGETS or $SOURCES expansions by in turn
415     wrapping a NLWrapper.  This class handles the different methods used
416     to access the list, calling the NLWrapper to create proxies on demand.
417
418     Note that we subclass UserList.UserList purely so that the is_List()
419     function will identify an object of this class as a list during
420     variable expansion.  We're not really using any UserList.UserList
421     methods in practice.
422     """
423     def __init__(self, nl):
424         self.nl = nl
425     def __getattr__(self, attr):
426         nl = self.nl._create_nodelist()
427         return getattr(nl, attr)
428     def __getitem__(self, i):
429         nl = self.nl._create_nodelist()
430         return nl[i]
431     def __getslice__(self, i, j):
432         nl = self.nl._create_nodelist()
433         i = max(i, 0); j = max(j, 0)
434         return nl[i:j]
435     def __str__(self):
436         nl = self.nl._create_nodelist()
437         return str(nl)
438     def __repr__(self):
439         nl = self.nl._create_nodelist()
440         return repr(nl)
441
442 class Target_or_Source:
443     """A class that implements $TARGET or $SOURCE expansions by in turn
444     wrapping a NLWrapper.  This class handles the different methods used
445     to access an individual proxy Node, calling the NLWrapper to create
446     a proxy on demand.
447     """
448     def __init__(self, nl):
449         self.nl = nl
450     def __getattr__(self, attr):
451         nl = self.nl._create_nodelist()
452         try:
453             nl0 = nl[0]
454         except IndexError:
455             # If there is nothing in the list, then we have no attributes to
456             # pass through, so raise AttributeError for everything.
457             raise AttributeError, "NodeList has no attribute: %s" % attr
458         return getattr(nl0, attr)
459     def __str__(self):
460         nl = self.nl._create_nodelist()
461         try:
462             nl0 = nl[0]
463         except IndexError:
464             return ''
465         return str(nl0)
466     def __repr__(self):
467         nl = self.nl._create_nodelist()
468         try:
469             nl0 = nl[0]
470         except IndexError:
471             return ''
472         return repr(nl0)
473
474 def subst_dict(target, source):
475     """Create a dictionary for substitution of special
476     construction variables.
477
478     This translates the following special arguments:
479
480     target - the target (object or array of objects),
481              used to generate the TARGET and TARGETS
482              construction variables
483
484     source - the source (object or array of objects),
485              used to generate the SOURCES and SOURCE
486              construction variables
487     """
488     dict = {}
489
490     if target:
491         tnl = NLWrapper(target, lambda x: x.get_subst_proxy())
492         dict['TARGETS'] = Targets_or_Sources(tnl)
493         dict['TARGET'] = Target_or_Source(tnl)
494
495     if source:
496         def get_src_subst_proxy(node):
497             try:
498                 rfile = node.rfile
499             except AttributeError:
500                 pass
501             else:
502                 node = rfile()
503             return node.get_subst_proxy()
504         snl = NLWrapper(source, get_src_subst_proxy)
505         dict['SOURCES'] = Targets_or_Sources(snl)
506         dict['SOURCE'] = Target_or_Source(snl)
507
508     return dict
509
510 # Constants for the "mode" parameter to scons_subst_list() and
511 # scons_subst().  SUBST_RAW gives the raw command line.  SUBST_CMD
512 # gives a command line suitable for passing to a shell.  SUBST_SIG
513 # gives a command line appropriate for calculating the signature
514 # of a command line...if this changes, we should rebuild.
515 SUBST_CMD = 0
516 SUBST_RAW = 1
517 SUBST_SIG = 2
518
519 _rm = re.compile(r'\$[()]')
520 _remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)')
521
522 # Indexed by the SUBST_* constants above.
523 _regex_remove = [ _rm, None, _remove ]
524
525 # Regular expressions for splitting strings and handling substitutions,
526 # for use by the scons_subst() and scons_subst_list() functions:
527 #
528 # The first expression compiled matches all of the $-introduced tokens
529 # that we need to process in some way, and is used for substitutions.
530 # The expressions it matches are:
531 #
532 #       "$$"
533 #       "$("
534 #       "$)"
535 #       "$variable"             [must begin with alphabetic or underscore]
536 #       "${any stuff}"
537 #
538 # The second expression compiled is used for splitting strings into tokens
539 # to be processed, and it matches all of the tokens listed above, plus
540 # the following that affect how arguments do or don't get joined together:
541 #
542 #       "   "                   [white space]
543 #       "non-white-space"       [without any dollar signs]
544 #       "$"                     [single dollar sign]
545 #
546 _dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}'
547 _dollar_exps = re.compile(r'(%s)' % _dollar_exps_str)
548 _separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str)
549
550 # This regular expression is used to replace strings of multiple white
551 # space characters in the string result from the scons_subst() function.
552 _space_sep = re.compile(r'[\t ]+(?![^{]*})')
553
554 def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
555     """Expand a string containing construction variable substitutions.
556
557     This is the work-horse function for substitutions in file names
558     and the like.  The companion scons_subst_list() function (below)
559     handles separating command lines into lists of arguments, so see
560     that function if that's what you're looking for.
561     """
562     if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0:
563         return strSubst
564
565     class StringSubber:
566         """A class to construct the results of a scons_subst() call.
567
568         This binds a specific construction environment, mode, target and
569         source with two methods (substitute() and expand()) that handle
570         the expansion.
571         """
572         def __init__(self, env, mode, target, source, conv, gvars):
573             self.env = env
574             self.mode = mode
575             self.target = target
576             self.source = source
577             self.conv = conv
578             self.gvars = gvars
579
580         def expand_var_once(self, var, lvars):
581             try:
582                 return lvars[var]
583             except KeyError:
584                 try:
585                     return self.gvars[var]
586                 except KeyError:
587                     return ''
588
589         def expand(self, s, lvars):
590             """Expand a single "token" as necessary, returning an
591             appropriate string containing the expansion.
592
593             This handles expanding different types of things (strings,
594             lists, callables) appropriately.  It calls the wrapper
595             substitute() method to re-expand things as necessary, so that
596             the results of expansions of side-by-side strings still get
597             re-evaluated separately, not smushed together.
598             """
599             if is_String(s):
600                 try:
601                     s0, s1 = s[:2]
602                 except (IndexError, ValueError):
603                     return s
604                 if s0 == '$':
605                     if s1 == '$':
606                         return '$'
607                     elif s1 in '()':
608                         return s
609                     else:
610                         key = s[1:]
611                         if key[0] == '{' or string.find(key, '.') >= 0:
612                             if key[0] == '{':
613                                 key = key[1:-1]
614                             try:
615                                 s = eval(key, self.gvars, lvars)
616                             except (IndexError, NameError, TypeError):
617                                 return ''
618                             except SyntaxError,e:
619                                 if self.target:
620                                     raise SCons.Errors.BuildError, (self.target[0], "Syntax error `%s' trying to evaluate `%s'" % (e,s))
621                                 else:
622                                     raise SCons.Errors.UserError, "Syntax error `%s' trying to evaluate `%s'" % (e,s)
623                         else:
624                             s = self.expand_var_once(key, lvars)
625
626                         # Before re-expanding the result, handle
627                         # recursive expansion by copying the local
628                         # variable dictionary and overwriting a null
629                         # string for the value of the variable name
630                         # we just expanded.
631                         #
632                         # This could potentially be optimized by only
633                         # copying lvars when s contains more expansions,
634                         # but lvars is usually supposed to be pretty
635                         # small, and deeply nested variable expansions
636                         # are probably more the exception than the norm,
637                         # so it should be tolerable for now.
638                         lv = lvars.copy()
639                         var = string.split(key, '.')[0]
640                         lv[var] = ''
641                         return self.substitute(s, lv)
642                 else:
643                     return s
644             elif is_List(s):
645                 def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars):
646                     return conv(substitute(l, lvars))
647                 r = map(func, s)
648                 return string.join(r)
649             elif callable(s):
650                 try:
651                     s = s(target=self.target,
652                          source=self.source,
653                          env=self.env,
654                          for_signature=(self.mode != SUBST_CMD))
655                 except TypeError:
656                     # This probably indicates that it's a callable
657                     # object that doesn't match our calling arguments
658                     # (like an Action).
659                     s = str(s)
660                 return self.substitute(s, lvars)
661             elif s is None:
662                 return ''
663             else:
664                 return s
665
666         def substitute(self, args, lvars):
667             """Substitute expansions in an argument or list of arguments.
668
669             This serves as a wrapper for splitting up a string into
670             separate tokens.  __cacheable__
671             """
672             if is_String(args) and not isinstance(args, CmdStringHolder):
673                 try:
674                     def sub_match(match, conv=self.conv, expand=self.expand, lvars=lvars):
675                         return conv(expand(match.group(1), lvars))
676                     result = _dollar_exps.sub(sub_match, args)
677                 except TypeError:
678                     # If the internal conversion routine doesn't return
679                     # strings (it could be overridden to return Nodes, for
680                     # example), then the 1.5.2 re module will throw this
681                     # exception.  Back off to a slower, general-purpose
682                     # algorithm that works for all data types.
683                     args = _separate_args.findall(args)
684                     result = []
685                     for a in args:
686                         result.append(self.conv(self.expand(a, lvars)))
687                     try:
688                         result = string.join(result, '')
689                     except TypeError:
690                         if len(result) == 1:
691                             result = result[0]
692                 return result
693             else:
694                 return self.expand(args, lvars)
695
696     if conv is None:
697         conv = _strconv[mode]
698
699     # Doing this every time is a bit of a waste, since the Executor
700     # has typically already populated the OverrideEnvironment with
701     # $TARGET/$SOURCE variables.  We're keeping this (for now), though,
702     # because it supports existing behavior that allows us to call
703     # an Action directly with an arbitrary target+source pair, which
704     # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
705     # If we dropped that behavior (or found another way to cover it),
706     # we could get rid of this call completely and just rely on the
707     # Executor setting the variables.
708     d = subst_dict(target, source)
709     if d:
710         lvars = lvars.copy()
711         lvars.update(d)
712
713     # We're (most likely) going to eval() things.  If Python doesn't
714     # find a __builtin__ value in the global dictionary used for eval(),
715     # it copies the current __builtin__ values for you.  Avoid this by
716     # setting it explicitly and then deleting, so we don't pollute the
717     # construction environment Dictionary(ies) that are typically used
718     # for expansion.
719     gvars['__builtin__'] = __builtin__
720
721     ss = StringSubber(env, mode, target, source, conv, gvars)
722     result = ss.substitute(strSubst, lvars)
723
724     try:
725         del gvars['__builtin__']
726     except KeyError:
727         pass
728
729     if is_String(result):
730         # Remove $(-$) pairs and any stuff in between,
731         # if that's appropriate.
732         remove = _regex_remove[mode]
733         if remove:
734             result = remove.sub('', result)
735         if mode != SUBST_RAW:
736             # Compress strings of white space characters into
737             # a single space.
738             result = string.strip(_space_sep.sub(' ', result))
739
740     return result
741
742 def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
743     """Substitute construction variables in a string (or list or other
744     object) and separate the arguments into a command list.
745
746     The companion scons_subst() function (above) handles basic
747     substitutions within strings, so see that function instead
748     if that's what you're looking for.
749     """
750     class ListSubber(UserList.UserList):
751         """A class to construct the results of a scons_subst_list() call.
752
753         Like StringSubber, this class binds a specific construction
754         environment, mode, target and source with two methods
755         (substitute() and expand()) that handle the expansion.
756
757         In addition, however, this class is used to track the state of
758         the result(s) we're gathering so we can do the appropriate thing
759         whenever we have to append another word to the result--start a new
760         line, start a new word, append to the current word, etc.  We do
761         this by setting the "append" attribute to the right method so
762         that our wrapper methods only need ever call ListSubber.append(),
763         and the rest of the object takes care of doing the right thing
764         internally.
765         """
766         def __init__(self, env, mode, target, source, conv, gvars):
767             UserList.UserList.__init__(self, [])
768             self.env = env
769             self.mode = mode
770             self.target = target
771             self.source = source
772             self.conv = conv
773             self.gvars = gvars
774
775             if self.mode == SUBST_RAW:
776                 self.add_strip = lambda x, s=self: s.append(x)
777             else:
778                 self.add_strip = lambda x, s=self: None
779             self.in_strip = None
780             self.next_line()
781
782         def expand(self, s, lvars, within_list):
783             """Expand a single "token" as necessary, appending the
784             expansion to the current result.
785
786             This handles expanding different types of things (strings,
787             lists, callables) appropriately.  It calls the wrapper
788             substitute() method to re-expand things as necessary, so that
789             the results of expansions of side-by-side strings still get
790             re-evaluated separately, not smushed together.
791             """
792
793             if is_String(s):
794                 try:
795                     s0, s1 = s[:2]
796                 except (IndexError, ValueError):
797                     self.append(s)
798                     return
799                 if s0 == '$':
800                     if s1 == '$':
801                         self.append('$')
802                     elif s1 == '(':
803                         self.open_strip('$(')
804                     elif s1 == ')':
805                         self.close_strip('$)')
806                     else:
807                         key = s[1:]
808                         if key[0] == '{':
809                             key = key[1:-1]
810                         try:
811                             s = eval(key, self.gvars, lvars)
812                         except (IndexError, NameError, TypeError):
813                             return
814                         except SyntaxError,e:
815                             if self.target:
816                                 raise SCons.Errors.BuildError, (self.target[0], "Syntax error `%s' trying to evaluate `%s'" % (e,s))
817                             else:
818                                 raise SCons.Errors.UserError, "Syntax error `%s' trying to evaluate `%s'" % (e,s)
819                         else:
820                             # Before re-expanding the result, handle
821                             # recursive expansion by copying the local
822                             # variable dictionary and overwriting a null
823                             # string for the value of the variable name
824                             # we just expanded.
825                             lv = lvars.copy()
826                             var = string.split(key, '.')[0]
827                             lv[var] = ''
828                             self.substitute(s, lv, 0)
829                             self.this_word()
830                 else:
831                     self.append(s)
832             elif is_List(s):
833                 for a in s:
834                     self.substitute(a, lvars, 1)
835                     self.next_word()
836             elif callable(s):
837                 try:
838                     s = s(target=self.target,
839                          source=self.source,
840                          env=self.env,
841                          for_signature=(self.mode != SUBST_CMD))
842                 except TypeError:
843                     # This probably indicates that it's a callable
844                     # object that doesn't match our calling arguments
845                     # (like an Action).
846                     s = str(s)
847                 self.substitute(s, lvars, within_list)
848             elif s is None:
849                 self.this_word()
850             else:
851                 self.append(s)
852
853         def substitute(self, args, lvars, within_list):
854             """Substitute expansions in an argument or list of arguments.
855
856             This serves as a wrapper for splitting up a string into
857             separate tokens.
858             """
859
860             if is_String(args) and not isinstance(args, CmdStringHolder):
861                 args = _separate_args.findall(args)
862                 for a in args:
863                     if a[0] in ' \t\n\r\f\v':
864                         if '\n' in a:
865                             self.next_line()
866                         elif within_list:
867                             self.append(a)
868                         else:
869                             self.next_word()
870                     else:
871                         self.expand(a, lvars, within_list)
872             else:
873                 self.expand(args, lvars, within_list)
874
875         def next_line(self):
876             """Arrange for the next word to start a new line.  This
877             is like starting a new word, except that we have to append
878             another line to the result."""
879             UserList.UserList.append(self, [])
880             self.next_word()
881
882         def this_word(self):
883             """Arrange for the next word to append to the end of the
884             current last word in the result."""
885             self.append = self.add_to_current_word
886
887         def next_word(self):
888             """Arrange for the next word to start a new word."""
889             self.append = self.add_new_word
890
891         def add_to_current_word(self, x):
892             """Append the string x to the end of the current last word
893             in the result.  If that is not possible, then just add
894             it as a new word.  Make sure the entire concatenated string
895             inherits the object attributes of x (in particular, the
896             escape function) by wrapping it as CmdStringHolder."""
897
898             if not self.in_strip or self.mode != SUBST_SIG:
899                 try:
900                     current_word = self[-1][-1]
901                 except IndexError:
902                     self.add_new_word(x)
903                 else:
904                     # All right, this is a hack and it should probably
905                     # be refactored out of existence in the future.
906                     # The issue is that we want to smoosh words together
907                     # and make one file name that gets escaped if
908                     # we're expanding something like foo$EXTENSION,
909                     # but we don't want to smoosh them together if
910                     # it's something like >$TARGET, because then we'll
911                     # treat the '>' like it's part of the file name.
912                     # So for now, just hard-code looking for the special
913                     # command-line redirection characters...
914                     try:
915                         last_char = str(current_word)[-1]
916                     except IndexError:
917                         last_char = '\0'
918                     if last_char in '<>|':
919                         self.add_new_word(x)
920                     else:
921                         y = current_word + x
922                         literal1 = self.literal(self[-1][-1])
923                         literal2 = self.literal(x)
924                         y = self.conv(y)
925                         if is_String(y):
926                             y = CmdStringHolder(y, literal1 or literal2)
927                         self[-1][-1] = y
928
929         def add_new_word(self, x):
930             if not self.in_strip or self.mode != SUBST_SIG:
931                 literal = self.literal(x)
932                 x = self.conv(x)
933                 if is_String(x):
934                     x = CmdStringHolder(x, literal)
935                 self[-1].append(x)
936             self.append = self.add_to_current_word
937
938         def literal(self, x):
939             try:
940                 l = x.is_literal
941             except AttributeError:
942                 return None
943             else:
944                 return l()
945
946         def open_strip(self, x):
947             """Handle the "open strip" $( token."""
948             self.add_strip(x)
949             self.in_strip = 1
950
951         def close_strip(self, x):
952             """Handle the "close strip" $) token."""
953             self.add_strip(x)
954             self.in_strip = None
955
956     if conv is None:
957         conv = _strconv[mode]
958
959     # Doing this every time is a bit of a waste, since the Executor
960     # has typically already populated the OverrideEnvironment with
961     # $TARGET/$SOURCE variables.  We're keeping this (for now), though,
962     # because it supports existing behavior that allows us to call
963     # an Action directly with an arbitrary target+source pair, which
964     # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
965     # If we dropped that behavior (or found another way to cover it),
966     # we could get rid of this call completely and just rely on the
967     # Executor setting the variables.
968     d = subst_dict(target, source)
969     if d:
970         lvars = lvars.copy()
971         lvars.update(d)
972
973     # We're (most likely) going to eval() things.  If Python doesn't
974     # find a __builtin__ value in the global dictionary used for eval(),
975     # it copies the current __builtin__ values for you.  Avoid this by
976     # setting it explicitly and then deleting, so we don't pollute the
977     # construction environment Dictionary(ies) that are typically used
978     # for expansion.
979     gvars['__builtins__'] = __builtins__
980
981     ls = ListSubber(env, mode, target, source, conv, gvars)
982     ls.substitute(strSubst, lvars, 0)
983
984     try:
985         del gvars['__builtins__']
986     except KeyError:
987         pass
988
989     return ls.data
990
991 def scons_subst_once(strSubst, env, key):
992     """Perform single (non-recursive) substitution of a single
993     construction variable keyword.
994
995     This is used when setting a variable when copying or overriding values
996     in an Environment.  We want to capture (expand) the old value before
997     we override it, so people can do things like:
998
999         env2 = env.Copy(CCFLAGS = '$CCFLAGS -g')
1000
1001     We do this with some straightforward, brute-force code here...
1002     """
1003     if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0:
1004         return strSubst
1005
1006     matchlist = ['$' + key, '${' + key + '}']
1007     val = env.get(key, '')
1008     def sub_match(match, val=val, matchlist=matchlist):
1009         a = match.group(1)
1010         if a in matchlist:
1011             a = val
1012         if is_List(a):
1013             return string.join(map(str, a))
1014         else:
1015             return str(a)
1016
1017     if is_List(strSubst):
1018         result = []
1019         for arg in strSubst:
1020             if is_String(arg):
1021                 if arg in matchlist:
1022                     arg = val
1023                     if is_List(arg):
1024                         result.extend(arg)
1025                     else:
1026                         result.append(arg)
1027                 else:
1028                     result.append(_dollar_exps.sub(sub_match, arg))
1029             else:
1030                 result.append(arg)
1031         return result
1032     elif is_String(strSubst):
1033         return _dollar_exps.sub(sub_match, strSubst)
1034     else:
1035         return strSubst
1036
1037 def render_tree(root, child_func, prune=0, margin=[0], visited={}):
1038     """
1039     Render a tree of nodes into an ASCII tree view.
1040     root - the root node of the tree
1041     child_func - the function called to get the children of a node
1042     prune - don't visit the same node twice
1043     margin - the format of the left margin to use for children of root.
1044        1 results in a pipe, and 0 results in no pipe.
1045     visited - a dictionary of visited nodes in the current branch if not prune,
1046        or in the whole tree if prune.
1047     """
1048
1049     rname = str(root)
1050
1051     if visited.has_key(rname):
1052         return ""
1053
1054     children = child_func(root)
1055     retval = ""
1056     for pipe in margin[:-1]:
1057         if pipe:
1058             retval = retval + "| "
1059         else:
1060             retval = retval + "  "
1061
1062     retval = retval + "+-" + rname + "\n"
1063     if not prune:
1064         visited = copy.copy(visited)
1065     visited[rname] = 1
1066
1067     for i in range(len(children)):
1068         margin.append(i<len(children)-1)
1069         retval = retval + render_tree(children[i], child_func, prune, margin, visited
1070 )
1071         margin.pop()
1072
1073     return retval
1074
1075 IDX = lambda N: N and 1 or 0
1076
1077 def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
1078     """
1079     Print a tree of nodes.  This is like render_tree, except it prints
1080     lines directly instead of creating a string representation in memory,
1081     so that huge trees can be printed.
1082
1083     root - the root node of the tree
1084     child_func - the function called to get the children of a node
1085     prune - don't visit the same node twice
1086     showtags - print status information to the left of each node line
1087     margin - the format of the left margin to use for children of root.
1088        1 results in a pipe, and 0 results in no pipe.
1089     visited - a dictionary of visited nodes in the current branch if not prune,
1090        or in the whole tree if prune.
1091     """
1092
1093     rname = str(root)
1094
1095     if visited.has_key(rname):
1096         return
1097
1098     if showtags:
1099
1100         if showtags == 2:
1101             print ' E       = exists'
1102             print '  R      = exists in repository only'
1103             print '   b     = implicit builder'
1104             print '   B     = explicit builder'
1105             print '    S    = side effect'
1106             print '     P   = precious'
1107             print '      A  = always build'
1108             print '       C = current'
1109             print ''
1110
1111         tags = ['[']
1112         tags.append(' E'[IDX(root.exists())])
1113         tags.append(' R'[IDX(root.rexists() and not root.exists())])
1114         tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
1115                            [0,2][IDX(root.has_builder())]])
1116         tags.append(' S'[IDX(root.side_effect)])
1117         tags.append(' P'[IDX(root.precious)])
1118         tags.append(' A'[IDX(root.always_build)])
1119         tags.append(' C'[IDX(root.current())])
1120         tags.append(']')
1121
1122     else:
1123         tags = []
1124
1125     def MMM(m):
1126         return ["  ","| "][m]
1127     margins = map(MMM, margin[:-1])
1128
1129     print string.join(tags + margins + ['+-', rname], '')
1130
1131     if prune:
1132         visited[rname] = 1
1133
1134     children = child_func(root)
1135     if children:
1136         margin.append(1)
1137         map(lambda C, cf=child_func, p=prune, i=IDX(showtags), m=margin, v=visited:
1138                    print_tree(C, cf, p, i, m, v),
1139             children[:-1])
1140         margin[-1] = 0
1141         print_tree(children[-1], child_func, prune, IDX(showtags), margin, visited)
1142         margin.pop()
1143
1144 def is_Dict(e):
1145     return type(e) is types.DictType or isinstance(e, UserDict)
1146
1147 def is_List(e):
1148     return type(e) is types.ListType or isinstance(e, UserList.UserList)
1149
1150 if hasattr(types, 'UnicodeType'):
1151     def is_String(e):
1152         return type(e) is types.StringType \
1153             or type(e) is types.UnicodeType \
1154             or isinstance(e, UserString)
1155 else:
1156     def is_String(e):
1157         return type(e) is types.StringType or isinstance(e, UserString)
1158
1159 def is_Scalar(e):
1160     return is_String(e) or not is_List(e)
1161
1162 def flatten(sequence, scalarp=is_Scalar, result=None):
1163     if result is None:
1164         result = []
1165     for item in sequence:
1166         if scalarp(item):
1167             result.append(item)
1168         else:
1169             flatten(item, scalarp, result)
1170     return result
1171
1172 class Proxy:
1173     """A simple generic Proxy class, forwarding all calls to
1174     subject.  So, for the benefit of the python newbie, what does
1175     this really mean?  Well, it means that you can take an object, let's
1176     call it 'objA', and wrap it in this Proxy class, with a statement
1177     like this
1178
1179                  proxyObj = Proxy(objA),
1180
1181     Then, if in the future, you do something like this
1182
1183                  x = proxyObj.var1,
1184
1185     since Proxy does not have a 'var1' attribute (but presumably objA does),
1186     the request actually is equivalent to saying
1187
1188                  x = objA.var1
1189
1190     Inherit from this class to create a Proxy."""
1191
1192     def __init__(self, subject):
1193         """Wrap an object as a Proxy object"""
1194         self.__subject = subject
1195
1196     def __getattr__(self, name):
1197         """Retrieve an attribute from the wrapped object.  If the named
1198            attribute doesn't exist, AttributeError is raised"""
1199         return getattr(self.__subject, name)
1200
1201     def get(self):
1202         """Retrieve the entire wrapped object"""
1203         return self.__subject
1204
1205     def __cmp__(self, other):
1206         if issubclass(other.__class__, self.__subject.__class__):
1207             return cmp(self.__subject, other)
1208         return cmp(self.__dict__, other.__dict__)
1209
1210 # attempt to load the windows registry module:
1211 can_read_reg = 0
1212 try:
1213     import _winreg
1214
1215     can_read_reg = 1
1216     hkey_mod = _winreg
1217
1218     RegOpenKeyEx = _winreg.OpenKeyEx
1219     RegEnumKey = _winreg.EnumKey
1220     RegEnumValue = _winreg.EnumValue
1221     RegQueryValueEx = _winreg.QueryValueEx
1222     RegError = _winreg.error
1223
1224 except ImportError:
1225     try:
1226         import win32api
1227         import win32con
1228         can_read_reg = 1
1229         hkey_mod = win32con
1230
1231         RegOpenKeyEx = win32api.RegOpenKeyEx
1232         RegEnumKey = win32api.RegEnumKey
1233         RegEnumValue = win32api.RegEnumValue
1234         RegQueryValueEx = win32api.RegQueryValueEx
1235         RegError = win32api.error
1236
1237     except ImportError:
1238         class _NoError(Exception):
1239             pass
1240         RegError = _NoError
1241
1242 if can_read_reg:
1243     HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
1244     HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
1245     HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
1246     HKEY_USERS = hkey_mod.HKEY_USERS
1247
1248     def RegGetValue(root, key):
1249         """This utility function returns a value in the registry
1250         without having to open the key first.  Only available on
1251         Windows platforms with a version of Python that can read the
1252         registry.  Returns the same thing as
1253         SCons.Util.RegQueryValueEx, except you just specify the entire
1254         path to the value, and don't have to bother opening the key
1255         first.  So:
1256
1257         Instead of:
1258           k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1259                 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
1260           out = SCons.Util.RegQueryValueEx(k,
1261                 'ProgramFilesDir')
1262
1263         You can write:
1264           out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1265                 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
1266         """
1267         # I would use os.path.split here, but it's not a filesystem
1268         # path...
1269         p = key.rfind('\\') + 1
1270         keyp = key[:p]
1271         val = key[p:]
1272         k = SCons.Util.RegOpenKeyEx(root, keyp)
1273         return SCons.Util.RegQueryValueEx(k,val)
1274
1275 if sys.platform == 'win32':
1276
1277     class _WhereIs:
1278         def __call__(self, file, path=None, pathext=None, reject=[]):
1279             "__cacheable__"
1280             if path is None:
1281                 try:
1282                     path = os.environ['PATH']
1283                 except KeyError:
1284                     return None
1285             if is_String(path):
1286                 path = string.split(path, os.pathsep)
1287             if pathext is None:
1288                 try:
1289                     pathext = os.environ['PATHEXT']
1290                 except KeyError:
1291                     pathext = '.COM;.EXE;.BAT;.CMD'
1292             if is_String(pathext):
1293                 pathext = string.split(pathext, os.pathsep)
1294             for ext in pathext:
1295                 if string.lower(ext) == string.lower(file[-len(ext):]):
1296                     pathext = ['']
1297                     break
1298             if not is_List(reject):
1299                 reject = [reject]
1300             for dir in path:
1301                 f = os.path.join(dir, file)
1302                 for ext in pathext:
1303                     fext = f + ext
1304                     if os.path.isfile(fext):
1305                         try:
1306                             reject.index(fext)
1307                         except ValueError:
1308                             return os.path.normpath(fext)
1309                         continue
1310             return None
1311
1312 elif os.name == 'os2':
1313
1314     class _WhereIs:
1315         def __call__(self, file, path=None, pathext=None, reject=[]):
1316             "__cacheable__"
1317             if path is None:
1318                 try:
1319                     path = os.environ['PATH']
1320                 except KeyError:
1321                     return None
1322             if is_String(path):
1323                 path = string.split(path, os.pathsep)
1324             if pathext is None:
1325                 pathext = ['.exe', '.cmd']
1326             for ext in pathext:
1327                 if string.lower(ext) == string.lower(file[-len(ext):]):
1328                     pathext = ['']
1329                     break
1330             if not is_List(reject):
1331                 reject = [reject]
1332             for dir in path:
1333                 f = os.path.join(dir, file)
1334                 for ext in pathext:
1335                     fext = f + ext
1336                     if os.path.isfile(fext):
1337                         try:
1338                             reject.index(fext)
1339                         except ValueError:
1340                             return os.path.normpath(fext)
1341                         continue
1342             return None
1343
1344 else:
1345
1346     class _WhereIs:
1347         def __call__(self, file, path=None, pathext=None, reject=[]):
1348             "__cacheable__"
1349             if path is None:
1350                 try:
1351                     path = os.environ['PATH']
1352                 except KeyError:
1353                     return None
1354             if is_String(path):
1355                 path = string.split(path, os.pathsep)
1356             if not is_List(reject):
1357                 reject = [reject]
1358             for d in path:
1359                 f = os.path.join(d, file)
1360                 if os.path.isfile(f):
1361                     try:
1362                         st = os.stat(f)
1363                     except OSError:
1364                         continue
1365                     if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
1366                         try:
1367                             reject.index(f)
1368                         except ValueError:
1369                             return os.path.normpath(f)
1370                         continue
1371             return None
1372
1373 WhereIs = _WhereIs()
1374
1375 def PrependPath(oldpath, newpath, sep = os.pathsep):
1376     """This prepends newpath elements to the given oldpath.  Will only
1377     add any particular path once (leaving the first one it encounters
1378     and ignoring the rest, to preserve path order), and will
1379     os.path.normpath and os.path.normcase all paths to help assure
1380     this.  This can also handle the case where the given old path
1381     variable is a list instead of a string, in which case a list will
1382     be returned instead of a string.
1383
1384     Example:
1385       Old Path: "/foo/bar:/foo"
1386       New Path: "/biz/boom:/foo"
1387       Result:   "/biz/boom:/foo:/foo/bar"
1388     """
1389
1390     orig = oldpath
1391     is_list = 1
1392     paths = orig
1393     if not is_List(orig):
1394         paths = string.split(paths, sep)
1395         is_list = 0
1396
1397     if is_List(newpath):
1398         newpaths = newpath
1399     else:
1400         newpaths = string.split(newpath, sep)
1401
1402     newpaths = newpaths + paths # prepend new paths
1403
1404     normpaths = []
1405     paths = []
1406     # now we add them only if they are unique
1407     for path in newpaths:
1408         normpath = os.path.normpath(os.path.normcase(path))
1409         if path and not normpath in normpaths:
1410             paths.append(path)
1411             normpaths.append(normpath)
1412
1413     if is_list:
1414         return paths
1415     else:
1416         return string.join(paths, sep)
1417
1418 def AppendPath(oldpath, newpath, sep = os.pathsep):
1419     """This appends new path elements to the given old path.  Will
1420     only add any particular path once (leaving the last one it
1421     encounters and ignoring the rest, to preserve path order), and
1422     will os.path.normpath and os.path.normcase all paths to help
1423     assure this.  This can also handle the case where the given old
1424     path variable is a list instead of a string, in which case a list
1425     will be returned instead of a string.
1426
1427     Example:
1428       Old Path: "/foo/bar:/foo"
1429       New Path: "/biz/boom:/foo"
1430       Result:   "/foo/bar:/biz/boom:/foo"
1431     """
1432
1433     orig = oldpath
1434     is_list = 1
1435     paths = orig
1436     if not is_List(orig):
1437         paths = string.split(paths, sep)
1438         is_list = 0
1439
1440     if is_List(newpath):
1441         newpaths = newpath
1442     else:
1443         newpaths = string.split(newpath, sep)
1444
1445     newpaths = paths + newpaths # append new paths
1446     newpaths.reverse()
1447
1448     normpaths = []
1449     paths = []
1450     # now we add them only of they are unique
1451     for path in newpaths:
1452         normpath = os.path.normpath(os.path.normcase(path))
1453         if path and not normpath in normpaths:
1454             paths.append(path)
1455             normpaths.append(normpath)
1456
1457     paths.reverse()
1458
1459     if is_list:
1460         return paths
1461     else:
1462         return string.join(paths, sep)
1463
1464
1465 def dir_index(directory):
1466     files = []
1467     for f in os.listdir(directory):
1468         fullname = os.path.join(directory, f)
1469         files.append(fullname)
1470
1471     # os.listdir() isn't guaranteed to return files in any specific order,
1472     # but some of the test code expects sorted output.
1473     files.sort()
1474     return files
1475
1476 def fs_delete(path, remove=1):
1477     try:
1478         if os.path.exists(path):
1479             if os.path.isfile(path):
1480                 if remove: os.unlink(path)
1481                 display("Removed " + path)
1482             elif os.path.isdir(path) and not os.path.islink(path):
1483                 # delete everything in the dir
1484                 for p in dir_index(path):
1485                     if os.path.isfile(p):
1486                         if remove: os.unlink(p)
1487                         display("Removed " + p)
1488                     else:
1489                         fs_delete(p, remove)
1490                 # then delete dir itself
1491                 if remove: os.rmdir(path)
1492                 display("Removed directory " + path)
1493     except OSError, e:
1494         print "scons: Could not remove '%s':" % str(path), e.strerror
1495
1496 if sys.platform == 'cygwin':
1497     def get_native_path(path):
1498         """Transforms an absolute path into a native path for the system.  In
1499         Cygwin, this converts from a Cygwin path to a Win32 one."""
1500         return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '')
1501 else:
1502     def get_native_path(path):
1503         """Transforms an absolute path into a native path for the system.
1504         Non-Cygwin version, just leave the path alone."""
1505         return path
1506
1507 display = DisplayEngine()
1508
1509 def Split(arg):
1510     if is_List(arg):
1511         return arg
1512     elif is_String(arg):
1513         return string.split(arg)
1514     else:
1515         return [arg]
1516
1517 class CLVar(UserList.UserList):
1518     """A class for command-line construction variables.
1519
1520     This is a list that uses Split() to split an initial string along
1521     white-space arguments, and similarly to split any strings that get
1522     added.  This allows us to Do the Right Thing with Append() and
1523     Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1524     arg2') regardless of whether a user adds a list or a string to a
1525     command-line construction variable.
1526     """
1527     def __init__(self, seq = []):
1528         UserList.UserList.__init__(self, Split(seq))
1529     def __coerce__(self, other):
1530         return (self, CLVar(other))
1531     def __str__(self):
1532         return string.join(self.data)
1533
1534 # A dictionary that preserves the order in which items are added.
1535 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
1536 #     http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
1537 # Including fixes/enhancements from the follow-on discussions.
1538 class OrderedDict(UserDict):
1539     def __init__(self, dict = None):
1540         self._keys = []
1541         UserDict.__init__(self, dict)
1542
1543     def __delitem__(self, key):
1544         UserDict.__delitem__(self, key)
1545         self._keys.remove(key)
1546
1547     def __setitem__(self, key, item):
1548         UserDict.__setitem__(self, key, item)
1549         if key not in self._keys: self._keys.append(key)
1550
1551     def clear(self):
1552         UserDict.clear(self)
1553         self._keys = []
1554
1555     def copy(self):
1556         dict = OrderedDict()
1557         dict.update(self)
1558         return dict
1559
1560     def items(self):
1561         return zip(self._keys, self.values())
1562
1563     def keys(self):
1564         return self._keys[:]
1565
1566     def popitem(self):
1567         try:
1568             key = self._keys[-1]
1569         except IndexError:
1570             raise KeyError('dictionary is empty')
1571
1572         val = self[key]
1573         del self[key]
1574
1575         return (key, val)
1576
1577     def setdefault(self, key, failobj = None):
1578         UserDict.setdefault(self, key, failobj)
1579         if key not in self._keys: self._keys.append(key)
1580
1581     def update(self, dict):
1582         for (key, val) in dict.items():
1583             self.__setitem__(key, val)
1584
1585     def values(self):
1586         return map(self.get, self._keys)
1587
1588 class Selector(OrderedDict):
1589     """A callable ordered dictionary that maps file suffixes to
1590     dictionary values.  We preserve the order in which items are added
1591     so that get_suffix() calls always return the first suffix added."""
1592     def __call__(self, env, source):
1593         try:
1594             ext = splitext(str(source[0]))[1]
1595         except IndexError:
1596             ext = ""
1597         try:
1598             return self[ext]
1599         except KeyError:
1600             # Try to perform Environment substitution on the keys of
1601             # the dictionary before giving up.
1602             s_dict = {}
1603             for (k,v) in self.items():
1604                 if not k is None:
1605                     s_k = env.subst(k)
1606                     if s_dict.has_key(s_k):
1607                         # We only raise an error when variables point
1608                         # to the same suffix.  If one suffix is literal
1609                         # and a variable suffix contains this literal,
1610                         # the literal wins and we don't raise an error.
1611                         raise KeyError, (s_dict[s_k][0], k, s_k)
1612                     s_dict[s_k] = (k,v)
1613             try:
1614                 return s_dict[ext][1]
1615             except KeyError:
1616                 try:
1617                     return self[None]
1618                 except KeyError:
1619                     return None
1620
1621
1622 if sys.platform == 'cygwin':
1623     # On Cygwin, os.path.normcase() lies, so just report back the
1624     # fact that the underlying Win32 OS is case-insensitive.
1625     def case_sensitive_suffixes(s1, s2):
1626         return 0
1627 else:
1628     def case_sensitive_suffixes(s1, s2):
1629         return (os.path.normcase(s1) != os.path.normcase(s2))
1630
1631 def adjustixes(fname, pre, suf):
1632     if pre:
1633         path, fn = os.path.split(os.path.normpath(fname))
1634         if fn[:len(pre)] != pre:
1635             fname = os.path.join(path, pre + fn)
1636     # Only append a suffix if the file does not have one.
1637     if suf and not splitext(fname)[1] and fname[-len(suf):] != suf:
1638             fname = fname + suf
1639     return fname
1640
1641
1642 def unique(s):
1643     """Return a list of the elements in s, but without duplicates.
1644
1645     For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1646     unique("abcabc") some permutation of ["a", "b", "c"], and
1647     unique(([1, 2], [2, 3], [1, 2])) some permutation of
1648     [[2, 3], [1, 2]].
1649
1650     For best speed, all sequence elements should be hashable.  Then
1651     unique() will usually work in linear time.
1652
1653     If not possible, the sequence elements should enjoy a total
1654     ordering, and if list(s).sort() doesn't raise TypeError it's
1655     assumed that they do enjoy a total ordering.  Then unique() will
1656     usually work in O(N*log2(N)) time.
1657
1658     If that's not possible either, the sequence elements must support
1659     equality-testing.  Then unique() will usually work in quadratic
1660     time.
1661     """
1662
1663     n = len(s)
1664     if n == 0:
1665         return []
1666
1667     # Try using a dict first, as that's the fastest and will usually
1668     # work.  If it doesn't work, it will usually fail quickly, so it
1669     # usually doesn't cost much to *try* it.  It requires that all the
1670     # sequence elements be hashable, and support equality comparison.
1671     u = {}
1672     try:
1673         for x in s:
1674             u[x] = 1
1675     except TypeError:
1676         del u  # move on to the next method
1677     else:
1678         return u.keys()
1679
1680     # We can't hash all the elements.  Second fastest is to sort,
1681     # which brings the equal elements together; then duplicates are
1682     # easy to weed out in a single pass.
1683     # NOTE:  Python's list.sort() was designed to be efficient in the
1684     # presence of many duplicate elements.  This isn't true of all
1685     # sort functions in all languages or libraries, so this approach
1686     # is more effective in Python than it may be elsewhere.
1687     try:
1688         t = list(s)
1689         t.sort()
1690     except TypeError:
1691         del t  # move on to the next method
1692     else:
1693         assert n > 0
1694         last = t[0]
1695         lasti = i = 1
1696         while i < n:
1697             if t[i] != last:
1698                 t[lasti] = last = t[i]
1699                 lasti = lasti + 1
1700             i = i + 1
1701         return t[:lasti]
1702
1703     # Brute force is all that's left.
1704     u = []
1705     for x in s:
1706         if x not in u:
1707             u.append(x)
1708     return u
1709
1710 # Much of the logic here was originally based on recipe 4.9 from the
1711 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1712 class LogicalLines:
1713
1714     def __init__(self, fileobj):
1715         self.fileobj = fileobj
1716
1717     def readline(self):
1718         result = []
1719         while 1:
1720             line = self.fileobj.readline()
1721             if not line:
1722                 break
1723             if line[-2:] == '\\\n':
1724                 result.append(line[:-2])
1725             else:
1726                 result.append(line)
1727                 break
1728         return string.join(result, '')
1729
1730     def readlines(self):
1731         result = []
1732         while 1:
1733             line = self.readline()
1734             if not line:
1735                 break
1736             result.append(line)
1737         return result