Fix Value node expansion in command-line strings. (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:
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, dict=None, conv=None, gvars=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.
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 dict is None:
697         dict = subst_dict(target, source)
698     if conv is None:
699         conv = _strconv[mode]
700     if gvars is None:
701         gvars = env.Dictionary()
702
703     # We're (most likely) going to eval() things.  If Python doesn't
704     # find a __builtin__ value in the global dictionary used for eval(),
705     # it copies the current __builtin__ values for you.  Avoid this by
706     # setting it explicitly and then deleting, so we don't pollute the
707     # construction environment Dictionary(ies) that are typically used
708     # for expansion.
709     gvars['__builtin__'] = __builtin__
710
711     ss = StringSubber(env, mode, target, source, conv, gvars)
712     result = ss.substitute(strSubst, dict)
713
714     try:
715         del gvars['__builtin__']
716     except KeyError:
717         pass
718
719     if is_String(result):
720         # Remove $(-$) pairs and any stuff in between,
721         # if that's appropriate.
722         remove = _regex_remove[mode]
723         if remove:
724             result = remove.sub('', result)
725         if mode != SUBST_RAW:
726             # Compress strings of white space characters into
727             # a single space.
728             result = string.strip(_space_sep.sub(' ', result))
729
730     return result
731
732 def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None, conv=None, gvars=None):
733     """Substitute construction variables in a string (or list or other
734     object) and separate the arguments into a command list.
735
736     The companion scons_subst() function (above) handles basic
737     substitutions within strings, so see that function instead
738     if that's what you're looking for.
739     """
740     class ListSubber(UserList.UserList):
741         """A class to construct the results of a scons_subst_list() call.
742
743         Like StringSubber, this class binds a specific construction
744         environment, mode, target and source with two methods
745         (substitute() and expand()) that handle the expansion.
746
747         In addition, however, this class is used to track the state of
748         the result(s) we're gathering so we can do the appropriate thing
749         whenever we have to append another word to the result--start a new
750         line, start a new word, append to the current word, etc.  We do
751         this by setting the "append" attribute to the right method so
752         that our wrapper methods only need ever call ListSubber.append(),
753         and the rest of the object takes care of doing the right thing
754         internally.
755         """
756         def __init__(self, env, mode, target, source, conv, gvars):
757             UserList.UserList.__init__(self, [])
758             self.env = env
759             self.mode = mode
760             self.target = target
761             self.source = source
762             self.conv = conv
763             self.gvars = gvars
764
765             if self.mode == SUBST_RAW:
766                 self.add_strip = lambda x, s=self: s.append(x)
767             else:
768                 self.add_strip = lambda x, s=self: None
769             self.in_strip = None
770             self.next_line()
771
772         def expand(self, s, lvars, within_list):
773             """Expand a single "token" as necessary, appending the
774             expansion to the current result.
775
776             This handles expanding different types of things (strings,
777             lists, callables) appropriately.  It calls the wrapper
778             substitute() method to re-expand things as necessary, so that
779             the results of expansions of side-by-side strings still get
780             re-evaluated separately, not smushed together.
781             """
782
783             if is_String(s):
784                 try:
785                     s0, s1 = s[:2]
786                 except (IndexError, ValueError):
787                     self.append(s)
788                     return
789                 if s0 == '$':
790                     if s1 == '$':
791                         self.append('$')
792                     elif s1 == '(':
793                         self.open_strip('$(')
794                     elif s1 == ')':
795                         self.close_strip('$)')
796                     else:
797                         key = s[1:]
798                         if key[0] == '{':
799                             key = key[1:-1]
800                         try:
801                             s = eval(key, self.gvars, lvars)
802                         except (IndexError, NameError, TypeError):
803                             return
804                         except SyntaxError,e:
805                             if self.target:
806                                 raise SCons.Errors.BuildError, (self.target[0], "Syntax error `%s' trying to evaluate `%s'" % (e,s))
807                             else:
808                                 raise SCons.Errors.UserError, "Syntax error `%s' trying to evaluate `%s'" % (e,s)
809                         else:
810                             # Before re-expanding the result, handle
811                             # recursive expansion by copying the local
812                             # variable dictionary and overwriting a null
813                             # string for the value of the variable name
814                             # we just expanded.
815                             lv = lvars.copy()
816                             var = string.split(key, '.')[0]
817                             lv[var] = ''
818                             self.substitute(s, lv, 0)
819                             self.this_word()
820                 else:
821                     self.append(s)
822             elif is_List(s):
823                 for a in s:
824                     self.substitute(a, lvars, 1)
825                     self.next_word()
826             elif callable(s):
827                 try:
828                     s = s(target=self.target,
829                          source=self.source,
830                          env=self.env,
831                          for_signature=(self.mode != SUBST_CMD))
832                 except TypeError:
833                     # This probably indicates that it's a callable
834                     # object that doesn't match our calling arguments
835                     # (like an Action).
836                     s = str(s)
837                 self.substitute(s, lvars, within_list)
838             elif s is None:
839                 self.this_word()
840             else:
841                 self.append(s)
842
843         def substitute(self, args, lvars, within_list):
844             """Substitute expansions in an argument or list of arguments.
845
846             This serves as a wrapper for splitting up a string into
847             separate tokens.
848             """
849
850             if is_String(args) and not isinstance(args, CmdStringHolder):
851                 args = _separate_args.findall(args)
852                 for a in args:
853                     if a[0] in ' \t\n\r\f\v':
854                         if '\n' in a:
855                             self.next_line()
856                         elif within_list:
857                             self.append(a)
858                         else:
859                             self.next_word()
860                     else:
861                         self.expand(a, lvars, within_list)
862             else:
863                 self.expand(args, lvars, within_list)
864
865         def next_line(self):
866             """Arrange for the next word to start a new line.  This
867             is like starting a new word, except that we have to append
868             another line to the result."""
869             UserList.UserList.append(self, [])
870             self.next_word()
871
872         def this_word(self):
873             """Arrange for the next word to append to the end of the
874             current last word in the result."""
875             self.append = self.add_to_current_word
876
877         def next_word(self):
878             """Arrange for the next word to start a new word."""
879             self.append = self.add_new_word
880
881         def add_to_current_word(self, x):
882             """Append the string x to the end of the current last word
883             in the result.  If that is not possible, then just add
884             it as a new word.  Make sure the entire concatenated string
885             inherits the object attributes of x (in particular, the
886             escape function) by wrapping it as CmdStringHolder."""
887
888             if not self.in_strip or self.mode != SUBST_SIG:
889                 try:
890                     current_word = self[-1][-1]
891                 except IndexError:
892                     self.add_new_word(x)
893                 else:
894                     # All right, this is a hack and it should probably
895                     # be refactored out of existence in the future.
896                     # The issue is that we want to smoosh words together
897                     # and make one file name that gets escaped if
898                     # we're expanding something like foo$EXTENSION,
899                     # but we don't want to smoosh them together if
900                     # it's something like >$TARGET, because then we'll
901                     # treat the '>' like it's part of the file name.
902                     # So for now, just hard-code looking for the special
903                     # command-line redirection characters...
904                     try:
905                         last_char = str(current_word)[-1]
906                     except IndexError:
907                         last_char = '\0'
908                     if last_char in '<>|':
909                         self.add_new_word(x)
910                     else:
911                         y = current_word + x
912                         literal1 = self.literal(self[-1][-1])
913                         literal2 = self.literal(x)
914                         y = self.conv(y)
915                         if is_String(y):
916                             y = CmdStringHolder(y, literal1 or literal2)
917                         self[-1][-1] = y
918
919         def add_new_word(self, x):
920             if not self.in_strip or self.mode != SUBST_SIG:
921                 literal = self.literal(x)
922                 x = self.conv(x)
923                 if is_String(x):
924                     x = CmdStringHolder(x, literal)
925                 self[-1].append(x)
926             self.append = self.add_to_current_word
927
928         def literal(self, x):
929             try:
930                 l = x.is_literal
931             except AttributeError:
932                 return None
933             else:
934                 return l()
935
936         def open_strip(self, x):
937             """Handle the "open strip" $( token."""
938             self.add_strip(x)
939             self.in_strip = 1
940
941         def close_strip(self, x):
942             """Handle the "close strip" $) token."""
943             self.add_strip(x)
944             self.in_strip = None
945
946     if dict is None:
947         dict = subst_dict(target, source)
948     if conv is None:
949         conv = _strconv[mode]
950     if gvars is None:
951         gvars = env.Dictionary()
952
953     # We're (most likely) going to eval() things.  If Python doesn't
954     # find a __builtin__ value in the global dictionary used for eval(),
955     # it copies the current __builtin__ values for you.  Avoid this by
956     # setting it explicitly and then deleting, so we don't pollute the
957     # construction environment Dictionary(ies) that are typically used
958     # for expansion.
959     gvars['__builtins__'] = __builtins__
960
961     ls = ListSubber(env, mode, target, source, conv, gvars)
962     ls.substitute(strSubst, dict, 0)
963
964     try:
965         del gvars['__builtins__']
966     except KeyError:
967         pass
968
969     return ls.data
970
971 def scons_subst_once(strSubst, env, key):
972     """Perform single (non-recursive) substitution of a single
973     construction variable keyword.
974
975     This is used when setting a variable when copying or overriding values
976     in an Environment.  We want to capture (expand) the old value before
977     we override it, so people can do things like:
978
979         env2 = env.Copy(CCFLAGS = '$CCFLAGS -g')
980
981     We do this with some straightforward, brute-force code here...
982     """
983     if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0:
984         return strSubst
985
986     matchlist = ['$' + key, '${' + key + '}']
987     val = env.get(key, '')
988     def sub_match(match, val=val, matchlist=matchlist):
989         a = match.group(1)
990         if a in matchlist:
991             a = val
992         if is_List(a):
993             return string.join(map(str, a))
994         else:
995             return str(a)
996
997     if is_List(strSubst):
998         result = []
999         for arg in strSubst:
1000             if is_String(arg):
1001                 if arg in matchlist:
1002                     arg = val
1003                     if is_List(arg):
1004                         result.extend(arg)
1005                     else:
1006                         result.append(arg)
1007                 else:
1008                     result.append(_dollar_exps.sub(sub_match, arg))
1009             else:
1010                 result.append(arg)
1011         return result
1012     elif is_String(strSubst):
1013         return _dollar_exps.sub(sub_match, strSubst)
1014     else:
1015         return strSubst
1016
1017 def render_tree(root, child_func, prune=0, margin=[0], visited={}):
1018     """
1019     Render a tree of nodes into an ASCII tree view.
1020     root - the root node of the tree
1021     child_func - the function called to get the children of a node
1022     prune - don't visit the same node twice
1023     margin - the format of the left margin to use for children of root.
1024        1 results in a pipe, and 0 results in no pipe.
1025     visited - a dictionary of visited nodes in the current branch if not prune,
1026        or in the whole tree if prune.
1027     """
1028
1029     rname = str(root)
1030
1031     if visited.has_key(rname):
1032         return ""
1033
1034     children = child_func(root)
1035     retval = ""
1036     for pipe in margin[:-1]:
1037         if pipe:
1038             retval = retval + "| "
1039         else:
1040             retval = retval + "  "
1041
1042     retval = retval + "+-" + rname + "\n"
1043     if not prune:
1044         visited = copy.copy(visited)
1045     visited[rname] = 1
1046
1047     for i in range(len(children)):
1048         margin.append(i<len(children)-1)
1049         retval = retval + render_tree(children[i], child_func, prune, margin, visited
1050 )
1051         margin.pop()
1052
1053     return retval
1054
1055 def print_tree(root, child_func, prune=0, margin=[0], visited={}):
1056     """
1057     Print a tree of nodes.  This is like render_tree, except it prints
1058     lines directly instead of creating a string representation in memory,
1059     so that huge trees can be printed.
1060
1061     root - the root node of the tree
1062     child_func - the function called to get the children of a node
1063     prune - don't visit the same node twice
1064     margin - the format of the left margin to use for children of root.
1065        1 results in a pipe, and 0 results in no pipe.
1066     visited - a dictionary of visited nodes in the current branch if not prune,
1067        or in the whole tree if prune.
1068     """
1069
1070     rname = str(root)
1071
1072     if visited.has_key(rname):
1073         return
1074
1075     def MMM(m):
1076         return ["  ","| "][m]
1077     print string.join(map(MMM, margin[:-1]), '') + "+-" + rname
1078
1079     if prune:
1080         visited[rname] = 1
1081         
1082     children = child_func(root)
1083     if children:
1084         margin.append(1)
1085         map(lambda C, cf=child_func, p=prune, m=margin, v=visited:
1086                    print_tree(C, cf, p, m, v),
1087             children[:-1])
1088         margin[-1] = 0
1089         print_tree(children[-1], child_func, prune, margin, visited)
1090         margin.pop()
1091                   
1092
1093 def is_Dict(e):
1094     return type(e) is types.DictType or isinstance(e, UserDict)
1095
1096 def is_List(e):
1097     return type(e) is types.ListType or isinstance(e, UserList.UserList)
1098
1099 if hasattr(types, 'UnicodeType'):
1100     def is_String(e):
1101         return type(e) is types.StringType \
1102             or type(e) is types.UnicodeType \
1103             or isinstance(e, UserString)
1104 else:
1105     def is_String(e):
1106         return type(e) is types.StringType or isinstance(e, UserString)
1107
1108 def is_Scalar(e):
1109     return is_String(e) or not is_List(e)
1110
1111 def flatten(sequence, scalarp=is_Scalar, result=None):
1112     if result is None:
1113         result = []
1114     for item in sequence:
1115         if scalarp(item):
1116             result.append(item)
1117         else:
1118             flatten(item, scalarp, result)
1119     return result
1120
1121 class Proxy:
1122     """A simple generic Proxy class, forwarding all calls to
1123     subject.  So, for the benefit of the python newbie, what does
1124     this really mean?  Well, it means that you can take an object, let's
1125     call it 'objA', and wrap it in this Proxy class, with a statement
1126     like this
1127
1128                  proxyObj = Proxy(objA),
1129
1130     Then, if in the future, you do something like this
1131
1132                  x = proxyObj.var1,
1133
1134     since Proxy does not have a 'var1' attribute (but presumably objA does),
1135     the request actually is equivalent to saying
1136
1137                  x = objA.var1
1138
1139     Inherit from this class to create a Proxy."""
1140
1141     def __init__(self, subject):
1142         """Wrap an object as a Proxy object"""
1143         self.__subject = subject
1144
1145     def __getattr__(self, name):
1146         """Retrieve an attribute from the wrapped object.  If the named
1147            attribute doesn't exist, AttributeError is raised"""
1148         return getattr(self.__subject, name)
1149
1150     def get(self):
1151         """Retrieve the entire wrapped object"""
1152         return self.__subject
1153
1154     def __cmp__(self, other):
1155         if issubclass(other.__class__, self.__subject.__class__):
1156             return cmp(self.__subject, other)
1157         return cmp(self.__dict__, other.__dict__)
1158
1159 # attempt to load the windows registry module:
1160 can_read_reg = 0
1161 try:
1162     import _winreg
1163
1164     can_read_reg = 1
1165     hkey_mod = _winreg
1166
1167     RegOpenKeyEx = _winreg.OpenKeyEx
1168     RegEnumKey = _winreg.EnumKey
1169     RegEnumValue = _winreg.EnumValue
1170     RegQueryValueEx = _winreg.QueryValueEx
1171     RegError = _winreg.error
1172
1173 except ImportError:
1174     try:
1175         import win32api
1176         import win32con
1177         can_read_reg = 1
1178         hkey_mod = win32con
1179
1180         RegOpenKeyEx = win32api.RegOpenKeyEx
1181         RegEnumKey = win32api.RegEnumKey
1182         RegEnumValue = win32api.RegEnumValue
1183         RegQueryValueEx = win32api.RegQueryValueEx
1184         RegError = win32api.error
1185
1186     except ImportError:
1187         class _NoError(Exception):
1188             pass
1189         RegError = _NoError
1190
1191 if can_read_reg:
1192     HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
1193     HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
1194     HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
1195     HKEY_USERS = hkey_mod.HKEY_USERS
1196
1197     def RegGetValue(root, key):
1198         """This utility function returns a value in the registry
1199         without having to open the key first.  Only available on
1200         Windows platforms with a version of Python that can read the
1201         registry.  Returns the same thing as
1202         SCons.Util.RegQueryValueEx, except you just specify the entire
1203         path to the value, and don't have to bother opening the key
1204         first.  So:
1205
1206         Instead of:
1207           k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1208                 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
1209           out = SCons.Util.RegQueryValueEx(k,
1210                 'ProgramFilesDir')
1211
1212         You can write:
1213           out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1214                 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
1215         """
1216         # I would use os.path.split here, but it's not a filesystem
1217         # path...
1218         p = key.rfind('\\') + 1
1219         keyp = key[:p]
1220         val = key[p:]
1221         k = SCons.Util.RegOpenKeyEx(root, keyp)
1222         return SCons.Util.RegQueryValueEx(k,val)
1223
1224 if sys.platform == 'win32':
1225
1226     def WhereIs(file, path=None, pathext=None, reject=[]):
1227         if path is None:
1228             try:
1229                 path = os.environ['PATH']
1230             except KeyError:
1231                 return None
1232         if is_String(path):
1233             path = string.split(path, os.pathsep)
1234         if pathext is None:
1235             try:
1236                 pathext = os.environ['PATHEXT']
1237             except KeyError:
1238                 pathext = '.COM;.EXE;.BAT;.CMD'
1239         if is_String(pathext):
1240             pathext = string.split(pathext, os.pathsep)
1241         for ext in pathext:
1242             if string.lower(ext) == string.lower(file[-len(ext):]):
1243                 pathext = ['']
1244                 break
1245         if not is_List(reject):
1246             reject = [reject]
1247         for dir in path:
1248             f = os.path.join(dir, file)
1249             for ext in pathext:
1250                 fext = f + ext
1251                 if os.path.isfile(fext):
1252                     try:
1253                         reject.index(fext)
1254                     except ValueError:
1255                         return os.path.normpath(fext)
1256                     continue
1257         return None
1258
1259 elif os.name == 'os2':
1260
1261     def WhereIs(file, path=None, pathext=None, reject=[]):
1262         if path is None:
1263             try:
1264                 path = os.environ['PATH']
1265             except KeyError:
1266                 return None
1267         if is_String(path):
1268             path = string.split(path, os.pathsep)
1269         if pathext is None:
1270             pathext = ['.exe', '.cmd']
1271         for ext in pathext:
1272             if string.lower(ext) == string.lower(file[-len(ext):]):
1273                 pathext = ['']
1274                 break
1275         if not is_List(reject):
1276             reject = [reject]
1277         for dir in path:
1278             f = os.path.join(dir, file)
1279             for ext in pathext:
1280                 fext = f + ext
1281                 if os.path.isfile(fext):
1282                     try:
1283                         reject.index(fext)
1284                     except ValueError:
1285                         return os.path.normpath(fext)
1286                     continue
1287         return None
1288
1289 else:
1290
1291     def WhereIs(file, path=None, pathext=None, reject=[]):
1292         if path is None:
1293             try:
1294                 path = os.environ['PATH']
1295             except KeyError:
1296                 return None
1297         if is_String(path):
1298             path = string.split(path, os.pathsep)
1299         if not is_List(reject):
1300             reject = [reject]
1301         for d in path:
1302             f = os.path.join(d, file)
1303             if os.path.isfile(f):
1304                 try:
1305                     st = os.stat(f)
1306                 except OSError:
1307                     continue
1308                 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
1309                     try:
1310                         reject.index(f)
1311                     except ValueError:
1312                         return os.path.normpath(f)
1313                     continue
1314         return None
1315
1316 def PrependPath(oldpath, newpath, sep = os.pathsep):
1317     """This prepends newpath elements to the given oldpath.  Will only
1318     add any particular path once (leaving the first one it encounters
1319     and ignoring the rest, to preserve path order), and will
1320     os.path.normpath and os.path.normcase all paths to help assure
1321     this.  This can also handle the case where the given old path
1322     variable is a list instead of a string, in which case a list will
1323     be returned instead of a string.
1324
1325     Example:
1326       Old Path: "/foo/bar:/foo"
1327       New Path: "/biz/boom:/foo"
1328       Result:   "/biz/boom:/foo:/foo/bar"
1329     """
1330
1331     orig = oldpath
1332     is_list = 1
1333     paths = orig
1334     if not is_List(orig):
1335         paths = string.split(paths, sep)
1336         is_list = 0
1337
1338     if is_List(newpath):
1339         newpaths = newpath
1340     else:
1341         newpaths = string.split(newpath, sep)
1342
1343     newpaths = newpaths + paths # prepend new paths
1344
1345     normpaths = []
1346     paths = []
1347     # now we add them only if they are unique
1348     for path in newpaths:
1349         normpath = os.path.normpath(os.path.normcase(path))
1350         if path and not normpath in normpaths:
1351             paths.append(path)
1352             normpaths.append(normpath)
1353
1354     if is_list:
1355         return paths
1356     else:
1357         return string.join(paths, sep)
1358
1359 def AppendPath(oldpath, newpath, sep = os.pathsep):
1360     """This appends new path elements to the given old path.  Will
1361     only add any particular path once (leaving the last one it
1362     encounters and ignoring the rest, to preserve path order), and
1363     will os.path.normpath and os.path.normcase all paths to help
1364     assure this.  This can also handle the case where the given old
1365     path variable is a list instead of a string, in which case a list
1366     will be returned instead of a string.
1367
1368     Example:
1369       Old Path: "/foo/bar:/foo"
1370       New Path: "/biz/boom:/foo"
1371       Result:   "/foo/bar:/biz/boom:/foo"
1372     """
1373
1374     orig = oldpath
1375     is_list = 1
1376     paths = orig
1377     if not is_List(orig):
1378         paths = string.split(paths, sep)
1379         is_list = 0
1380
1381     if is_List(newpath):
1382         newpaths = newpath
1383     else:
1384         newpaths = string.split(newpath, sep)
1385
1386     newpaths = paths + newpaths # append new paths
1387     newpaths.reverse()
1388
1389     normpaths = []
1390     paths = []
1391     # now we add them only of they are unique
1392     for path in newpaths:
1393         normpath = os.path.normpath(os.path.normcase(path))
1394         if path and not normpath in normpaths:
1395             paths.append(path)
1396             normpaths.append(normpath)
1397
1398     paths.reverse()
1399
1400     if is_list:
1401         return paths
1402     else:
1403         return string.join(paths, sep)
1404
1405
1406 def dir_index(directory):
1407     files = []
1408     for f in os.listdir(directory):
1409         fullname = os.path.join(directory, f)
1410         files.append(fullname)
1411
1412     # os.listdir() isn't guaranteed to return files in any specific order,
1413     # but some of the test code expects sorted output.
1414     files.sort()
1415     return files
1416
1417 def fs_delete(path, remove=1):
1418     try:
1419         if os.path.exists(path):
1420             if os.path.isfile(path):
1421                 if remove: os.unlink(path)
1422                 display("Removed " + path)
1423             elif os.path.isdir(path) and not os.path.islink(path):
1424                 # delete everything in the dir
1425                 for p in dir_index(path):
1426                     if os.path.isfile(p):
1427                         if remove: os.unlink(p)
1428                         display("Removed " + p)
1429                     else:
1430                         fs_delete(p, remove)
1431                 # then delete dir itself
1432                 if remove: os.rmdir(path)
1433                 display("Removed directory " + path)
1434     except OSError, e:
1435         print "scons: Could not remove '%s':" % str(path), e.strerror
1436
1437 if sys.platform == 'cygwin':
1438     def get_native_path(path):
1439         """Transforms an absolute path into a native path for the system.  In
1440         Cygwin, this converts from a Cygwin path to a Win32 one."""
1441         return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '')
1442 else:
1443     def get_native_path(path):
1444         """Transforms an absolute path into a native path for the system.
1445         Non-Cygwin version, just leave the path alone."""
1446         return path
1447
1448 display = DisplayEngine()
1449
1450 def Split(arg):
1451     if is_List(arg):
1452         return arg
1453     elif is_String(arg):
1454         return string.split(arg)
1455     else:
1456         return [arg]
1457
1458 class CLVar(UserList.UserList):
1459     """A class for command-line construction variables.
1460
1461     This is a list that uses Split() to split an initial string along
1462     white-space arguments, and similarly to split any strings that get
1463     added.  This allows us to Do the Right Thing with Append() and
1464     Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1465     arg2') regardless of whether a user adds a list or a string to a
1466     command-line construction variable.
1467     """
1468     def __init__(self, seq = []):
1469         UserList.UserList.__init__(self, Split(seq))
1470     def __coerce__(self, other):
1471         return (self, CLVar(other))
1472     def __str__(self):
1473         return string.join(self.data)
1474
1475 # A dictionary that preserves the order in which items are added.
1476 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
1477 #     http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
1478 # Including fixes/enhancements from the follow-on discussions.
1479 class OrderedDict(UserDict):
1480     def __init__(self, dict = None):
1481         self._keys = []
1482         UserDict.__init__(self, dict)
1483
1484     def __delitem__(self, key):
1485         UserDict.__delitem__(self, key)
1486         self._keys.remove(key)
1487
1488     def __setitem__(self, key, item):
1489         UserDict.__setitem__(self, key, item)
1490         if key not in self._keys: self._keys.append(key)
1491
1492     def clear(self):
1493         UserDict.clear(self)
1494         self._keys = []
1495
1496     def copy(self):
1497         dict = OrderedDict()
1498         dict.update(self)
1499         return dict
1500
1501     def items(self):
1502         return zip(self._keys, self.values())
1503
1504     def keys(self):
1505         return self._keys[:]
1506
1507     def popitem(self):
1508         try:
1509             key = self._keys[-1]
1510         except IndexError:
1511             raise KeyError('dictionary is empty')
1512
1513         val = self[key]
1514         del self[key]
1515
1516         return (key, val)
1517
1518     def setdefault(self, key, failobj = None):
1519         UserDict.setdefault(self, key, failobj)
1520         if key not in self._keys: self._keys.append(key)
1521
1522     def update(self, dict):
1523         for (key, val) in dict.items():
1524             self.__setitem__(key, val)
1525
1526     def values(self):
1527         return map(self.get, self._keys)
1528
1529 class Selector(OrderedDict):
1530     """A callable ordered dictionary that maps file suffixes to
1531     dictionary values.  We preserve the order in which items are added
1532     so that get_suffix() calls always return the first suffix added."""
1533     def __call__(self, env, source):
1534         try:
1535             ext = splitext(str(source[0]))[1]
1536         except IndexError:
1537             ext = ""
1538         try:
1539             return self[ext]
1540         except KeyError:
1541             # Try to perform Environment substitution on the keys of
1542             # the dictionary before giving up.
1543             s_dict = {}
1544             for (k,v) in self.items():
1545                 if not k is None:
1546                     s_k = env.subst(k)
1547                     if s_dict.has_key(s_k):
1548                         # We only raise an error when variables point
1549                         # to the same suffix.  If one suffix is literal
1550                         # and a variable suffix contains this literal,
1551                         # the literal wins and we don't raise an error.
1552                         raise KeyError, (s_dict[s_k][0], k, s_k)
1553                     s_dict[s_k] = (k,v)
1554             try:
1555                 return s_dict[ext][1]
1556             except KeyError:
1557                 try:
1558                     return self[None]
1559                 except KeyError:
1560                     return None
1561
1562
1563 if sys.platform == 'cygwin':
1564     # On Cygwin, os.path.normcase() lies, so just report back the
1565     # fact that the underlying Win32 OS is case-insensitive.
1566     def case_sensitive_suffixes(s1, s2):
1567         return 0
1568 else:
1569     def case_sensitive_suffixes(s1, s2):
1570         return (os.path.normcase(s1) != os.path.normcase(s2))
1571
1572 def adjustixes(fname, pre, suf):
1573     if pre:
1574         path, fn = os.path.split(os.path.normpath(fname))
1575         if fn[:len(pre)] != pre:
1576             fname = os.path.join(path, pre + fn)
1577     # Only append a suffix if the file does not have one.
1578     if suf and not splitext(fname)[1] and fname[-len(suf):] != suf:
1579             fname = fname + suf
1580     return fname
1581
1582
1583 def unique(s):
1584     """Return a list of the elements in s, but without duplicates.
1585
1586     For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1587     unique("abcabc") some permutation of ["a", "b", "c"], and
1588     unique(([1, 2], [2, 3], [1, 2])) some permutation of
1589     [[2, 3], [1, 2]].
1590
1591     For best speed, all sequence elements should be hashable.  Then
1592     unique() will usually work in linear time.
1593
1594     If not possible, the sequence elements should enjoy a total
1595     ordering, and if list(s).sort() doesn't raise TypeError it's
1596     assumed that they do enjoy a total ordering.  Then unique() will
1597     usually work in O(N*log2(N)) time.
1598
1599     If that's not possible either, the sequence elements must support
1600     equality-testing.  Then unique() will usually work in quadratic
1601     time.
1602     """
1603
1604     n = len(s)
1605     if n == 0:
1606         return []
1607
1608     # Try using a dict first, as that's the fastest and will usually
1609     # work.  If it doesn't work, it will usually fail quickly, so it
1610     # usually doesn't cost much to *try* it.  It requires that all the
1611     # sequence elements be hashable, and support equality comparison.
1612     u = {}
1613     try:
1614         for x in s:
1615             u[x] = 1
1616     except TypeError:
1617         del u  # move on to the next method
1618     else:
1619         return u.keys()
1620
1621     # We can't hash all the elements.  Second fastest is to sort,
1622     # which brings the equal elements together; then duplicates are
1623     # easy to weed out in a single pass.
1624     # NOTE:  Python's list.sort() was designed to be efficient in the
1625     # presence of many duplicate elements.  This isn't true of all
1626     # sort functions in all languages or libraries, so this approach
1627     # is more effective in Python than it may be elsewhere.
1628     try:
1629         t = list(s)
1630         t.sort()
1631     except TypeError:
1632         del t  # move on to the next method
1633     else:
1634         assert n > 0
1635         last = t[0]
1636         lasti = i = 1
1637         while i < n:
1638             if t[i] != last:
1639                 t[lasti] = last = t[i]
1640                 lasti = lasti + 1
1641             i = i + 1
1642         return t[:lasti]
1643
1644     # Brute force is all that's left.
1645     u = []
1646     for x in s:
1647         if x not in u:
1648             u.append(x)
1649     return u
1650
1651 # Much of the logic here was originally based on recipe 4.9 from the
1652 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1653 class LogicalLines:
1654
1655     def __init__(self, fileobj):
1656         self.fileobj = fileobj
1657
1658     def readline(self):
1659         result = []
1660         while 1:
1661             line = self.fileobj.readline()
1662             if not line:
1663                 break
1664             if line[-2:] == '\\\n':
1665                 result.append(line[:-2])
1666             else:
1667                 result.append(line)
1668                 break
1669         return string.join(result, '')
1670
1671     def readlines(self):
1672         result = []
1673         while 1:
1674             line = self.readline()
1675             if not line:
1676                 break
1677             result.append(line)
1678         return result