d673d22a16099469d79f9b1d12af02b740e9823b
[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 import __builtin__
33 import copy
34 import os
35 import os.path
36 import re
37 import string
38 import sys
39 import stat
40 import types
41
42 from UserDict import UserDict
43 from UserList import UserList
44
45 # Don't "from types import ..." these because we need to get at the
46 # types module later to look for UnicodeType.
47 DictType        = types.DictType
48 InstanceType    = types.InstanceType
49 ListType        = types.ListType
50 StringType      = types.StringType
51 TupleType       = types.TupleType
52
53 try:
54     from UserString import UserString
55 except ImportError:
56     # "Borrowed" from the Python 2.2 UserString module
57     # and modified slightly for use with SCons.
58     class UserString:
59         def __init__(self, seq):
60             if is_String(seq):
61                 self.data = seq
62             elif isinstance(seq, UserString):
63                 self.data = seq.data[:]
64             else:
65                 self.data = str(seq)
66         def __str__(self): return str(self.data)
67         def __repr__(self): return repr(self.data)
68         def __int__(self): return int(self.data)
69         def __long__(self): return long(self.data)
70         def __float__(self): return float(self.data)
71         def __complex__(self): return complex(self.data)
72         def __hash__(self): return hash(self.data)
73
74         def __cmp__(self, string):
75             if isinstance(string, UserString):
76                 return cmp(self.data, string.data)
77             else:
78                 return cmp(self.data, string)
79         def __contains__(self, char):
80             return char in self.data
81
82         def __len__(self): return len(self.data)
83         def __getitem__(self, index): return self.__class__(self.data[index])
84         def __getslice__(self, start, end):
85             start = max(start, 0); end = max(end, 0)
86             return self.__class__(self.data[start:end])
87
88         def __add__(self, other):
89             if isinstance(other, UserString):
90                 return self.__class__(self.data + other.data)
91             elif is_String(other):
92                 return self.__class__(self.data + other)
93             else:
94                 return self.__class__(self.data + str(other))
95         def __radd__(self, other):
96             if is_String(other):
97                 return self.__class__(other + self.data)
98             else:
99                 return self.__class__(str(other) + self.data)
100         def __mul__(self, n):
101             return self.__class__(self.data*n)
102         __rmul__ = __mul__
103
104 #
105 try:
106     __builtin__.zip
107 except AttributeError:
108     def zip(*lists):
109         result = []
110         for i in xrange(len(lists[0])):
111             result.append(tuple(map(lambda l, i=i: l[i], lists)))
112         return result
113     __builtin__.zip = zip
114
115 _altsep = os.altsep
116 if _altsep is None and sys.platform == 'win32':
117     # My ActivePython 2.0.1 doesn't set os.altsep!  What gives?
118     _altsep = '/'
119 if _altsep:
120     def rightmost_separator(path, sep, _altsep=_altsep):
121         rfind = string.rfind
122         return max(rfind(path, sep), rfind(path, _altsep))
123 else:
124     rightmost_separator = string.rfind
125
126 # First two from the Python Cookbook, just for completeness.
127 # (Yeah, yeah, YAGNI...)
128 def containsAny(str, set):
129     """Check whether sequence str contains ANY of the items in set."""
130     for c in set:
131         if c in str: return 1
132     return 0
133
134 def containsAll(str, set):
135     """Check whether sequence str contains ALL of the items in set."""
136     for c in set:
137         if c not in str: return 0
138     return 1
139
140 def containsOnly(str, set):
141     """Check whether sequence str contains ONLY items in set."""
142     for c in str:
143         if c not in set: return 0
144     return 1
145
146 def splitext(path):
147     "Same as os.path.splitext() but faster."
148     sep = rightmost_separator(path, os.sep)
149     dot = string.rfind(path, '.')
150     # An ext is only real if it has at least one non-digit char
151     if dot > sep and not containsOnly(path[dot:], "0123456789."):
152         return path[:dot],path[dot:]
153     else:
154         return path,""
155
156 def updrive(path):
157     """
158     Make the drive letter (if any) upper case.
159     This is useful because Windows is inconsitent on the case
160     of the drive letter, which can cause inconsistencies when
161     calculating command signatures.
162     """
163     drive, rest = os.path.splitdrive(path)
164     if drive:
165         path = string.upper(drive) + rest
166     return path
167
168 #
169 # Generic convert-to-string functions that abstract away whether or
170 # not the Python we're executing has Unicode support.  The wrapper
171 # to_String_for_signature() will use a for_signature() method if the
172 # specified object has one.
173 #
174 if hasattr(types, 'UnicodeType'):
175     UnicodeType = types.UnicodeType
176     def to_String(s):
177         if isinstance(s, UserString):
178             t = type(s.data)
179         else:
180             t = type(s)
181         if t is UnicodeType:
182             return unicode(s)
183         else:
184             return str(s)
185 else:
186     to_String = str
187
188 def to_String_for_signature(obj):
189     try:
190         f = obj.for_signature
191     except AttributeError:
192         return to_String(obj)
193     else:
194         return f()
195
196 class CallableComposite(UserList):
197     """A simple composite callable class that, when called, will invoke all
198     of its contained callables with the same arguments."""
199     def __call__(self, *args, **kwargs):
200         retvals = map(lambda x, args=args, kwargs=kwargs: apply(x,
201                                                                 args,
202                                                                 kwargs),
203                       self.data)
204         if self.data and (len(self.data) == len(filter(callable, retvals))):
205             return self.__class__(retvals)
206         return NodeList(retvals)
207
208 class NodeList(UserList):
209     """This class is almost exactly like a regular list of Nodes
210     (actually it can hold any object), with one important difference.
211     If you try to get an attribute from this list, it will return that
212     attribute from every item in the list.  For example:
213
214     >>> someList = NodeList([ '  foo  ', '  bar  ' ])
215     >>> someList.strip()
216     [ 'foo', 'bar' ]
217     """
218     def __nonzero__(self):
219         return len(self.data) != 0
220
221     def __str__(self):
222         return string.join(map(str, self.data))
223
224     def __getattr__(self, name):
225         if not self.data:
226             # If there is nothing in the list, then we have no attributes to
227             # pass through, so raise AttributeError for everything.
228             raise AttributeError, "NodeList has no attribute: %s" % name
229
230         # Return a list of the attribute, gotten from every element
231         # in the list
232         attrList = map(lambda x, n=name: getattr(x, n), self.data)
233
234         # Special case.  If the attribute is callable, we do not want
235         # to return a list of callables.  Rather, we want to return a
236         # single callable that, when called, will invoke the function on
237         # all elements of this list.
238         if self.data and (len(self.data) == len(filter(callable, attrList))):
239             return CallableComposite(attrList)
240         return self.__class__(attrList)
241
242 _valid_var = re.compile(r'[_a-zA-Z]\w*$')
243 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
244
245 def is_valid_construction_var(varstr):
246     """Return if the specified string is a legitimate construction
247     variable.
248     """
249     return _valid_var.match(varstr)
250
251 def get_environment_var(varstr):
252     """Given a string, first determine if it looks like a reference
253     to a single environment variable, like "$FOO" or "${FOO}".
254     If so, return that variable with no decorations ("FOO").
255     If not, return None."""
256     mo=_get_env_var.match(to_String(varstr))
257     if mo:
258         var = mo.group(1)
259         if var[0] == '{':
260             return var[1:-1]
261         else:
262             return var
263     else:
264         return None
265
266 class DisplayEngine:
267     def __init__(self):
268         self.__call__ = self.print_it
269
270     def print_it(self, text, append_newline=1):
271         if append_newline: text = text + '\n'
272         sys.stdout.write(text)
273
274     def dont_print(self, text, append_newline=1):
275         pass
276
277     def set_mode(self, mode):
278         if mode:
279             self.__call__ = self.print_it
280         else:
281             self.__call__ = self.dont_print
282
283 def render_tree(root, child_func, prune=0, margin=[0], visited={}):
284     """
285     Render a tree of nodes into an ASCII tree view.
286     root - the root node of the tree
287     child_func - the function called to get the children of a node
288     prune - don't visit the same node twice
289     margin - the format of the left margin to use for children of root.
290        1 results in a pipe, and 0 results in no pipe.
291     visited - a dictionary of visited nodes in the current branch if not prune,
292        or in the whole tree if prune.
293     """
294
295     rname = str(root)
296
297     if visited.has_key(rname):
298         return ""
299
300     children = child_func(root)
301     retval = ""
302     for pipe in margin[:-1]:
303         if pipe:
304             retval = retval + "| "
305         else:
306             retval = retval + "  "
307
308     retval = retval + "+-" + rname + "\n"
309     if not prune:
310         visited = copy.copy(visited)
311     visited[rname] = 1
312
313     for i in range(len(children)):
314         margin.append(i<len(children)-1)
315         retval = retval + render_tree(children[i], child_func, prune, margin, visited
316 )
317         margin.pop()
318
319     return retval
320
321 IDX = lambda N: N and 1 or 0
322
323 def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
324     """
325     Print a tree of nodes.  This is like render_tree, except it prints
326     lines directly instead of creating a string representation in memory,
327     so that huge trees can be printed.
328
329     root - the root node of the tree
330     child_func - the function called to get the children of a node
331     prune - don't visit the same node twice
332     showtags - print status information to the left of each node line
333     margin - the format of the left margin to use for children of root.
334        1 results in a pipe, and 0 results in no pipe.
335     visited - a dictionary of visited nodes in the current branch if not prune,
336        or in the whole tree if prune.
337     """
338
339     rname = str(root)
340
341     if visited.has_key(rname):
342         return
343
344     if showtags:
345
346         if showtags == 2:
347             print ' E        = exists'
348             print '  R       = exists in repository only'
349             print '   b      = implicit builder'
350             print '   B      = explicit builder'
351             print '    S     = side effect'
352             print '     P    = precious'
353             print '      A   = always build'
354             print '       C  = current'
355             print '        N = no clean'
356             print ''
357
358         tags = ['[']
359         tags.append(' E'[IDX(root.exists())])
360         tags.append(' R'[IDX(root.rexists() and not root.exists())])
361         tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
362                            [0,2][IDX(root.has_builder())]])
363         tags.append(' S'[IDX(root.side_effect)])
364         tags.append(' P'[IDX(root.precious)])
365         tags.append(' A'[IDX(root.always_build)])
366         tags.append(' C'[IDX(root.current())])
367         tags.append(' N'[IDX(root.noclean)])
368         tags.append(']')
369
370     else:
371         tags = []
372
373     def MMM(m):
374         return ["  ","| "][m]
375     margins = map(MMM, margin[:-1])
376
377     print string.join(tags + margins + ['+-', rname], '')
378
379     if prune:
380         visited[rname] = 1
381
382     children = child_func(root)
383     if children:
384         margin.append(1)
385         map(lambda C, cf=child_func, p=prune, i=IDX(showtags), m=margin, v=visited:
386                    print_tree(C, cf, p, i, m, v),
387             children[:-1])
388         margin[-1] = 0
389         print_tree(children[-1], child_func, prune, IDX(showtags), margin, visited)
390         margin.pop()
391
392
393
394 # Functions for deciding if things are like various types, mainly to
395 # handle UserDict, UserList and UserString like their underlying types.
396 #
397 # Yes, all of this manual testing breaks polymorphism, and the real
398 # Pythonic way to do all of this would be to just try it and handle the
399 # exception, but handling the exception when it's not the right type is
400 # too slow.
401 #
402 # The actual implementations here have been selected after timings
403 # coded up in in bench/is_types.py (from the SCons source tree, see the
404 # scons-src distribution).  Key results from those timings:
405 #
406 #   --  Storing the type of the object in a variable (t = type(obj))
407 #       slows down the case where it's a native type and the first
408 #       comparison will match, but nicely speeds up the case where
409 #       it's a different native type.  Since that's going to be common,
410 #       it's a good tradeoff.
411 #
412 #   --  The data show that calling isinstance() on an object that's
413 #       a native type (dict, list or string) is expensive enough that
414 #       checking up front for whether the object is of type InstanceType
415 #       is a pretty big win, even though it does slow down the case
416 #       where it really *is* an object instance a little bit.
417
418 def is_Dict(obj):
419     t = type(obj)
420     return t is DictType or \
421            (t is InstanceType and isinstance(obj, UserDict))
422
423 def is_List(obj):
424     t = type(obj)
425     return t is ListType \
426         or (t is InstanceType and isinstance(obj, UserList))
427
428 def is_Tuple(obj):
429     t = type(obj)
430     return t is TupleType
431
432 if hasattr(types, 'UnicodeType'):
433     def is_String(obj):
434         t = type(obj)
435         return t is StringType \
436             or t is UnicodeType \
437             or (t is InstanceType and isinstance(obj, UserString))
438 else:
439     def is_String(obj):
440         t = type(obj)
441         return t is StringType \
442             or (t is InstanceType and isinstance(obj, UserString))
443
444
445
446 def is_Scalar(e):
447     return is_String(e) or (not is_List(e) and not is_Tuple(e))
448
449 def flatten(sequence, scalarp=is_Scalar, result=None):
450     if result is None:
451         result = []
452     for item in sequence:
453         if scalarp(item):
454             result.append(item)
455         else:
456             flatten(item, scalarp, result)
457     return result
458
459 class Proxy:
460     """A simple generic Proxy class, forwarding all calls to
461     subject.  So, for the benefit of the python newbie, what does
462     this really mean?  Well, it means that you can take an object, let's
463     call it 'objA', and wrap it in this Proxy class, with a statement
464     like this
465
466                  proxyObj = Proxy(objA),
467
468     Then, if in the future, you do something like this
469
470                  x = proxyObj.var1,
471
472     since Proxy does not have a 'var1' attribute (but presumably objA does),
473     the request actually is equivalent to saying
474
475                  x = objA.var1
476
477     Inherit from this class to create a Proxy."""
478
479     def __init__(self, subject):
480         """Wrap an object as a Proxy object"""
481         self.__subject = subject
482
483     def __getattr__(self, name):
484         """Retrieve an attribute from the wrapped object.  If the named
485            attribute doesn't exist, AttributeError is raised"""
486         return getattr(self.__subject, name)
487
488     def get(self):
489         """Retrieve the entire wrapped object"""
490         return self.__subject
491
492     def __cmp__(self, other):
493         if issubclass(other.__class__, self.__subject.__class__):
494             return cmp(self.__subject, other)
495         return cmp(self.__dict__, other.__dict__)
496
497 # attempt to load the windows registry module:
498 can_read_reg = 0
499 try:
500     import _winreg
501
502     can_read_reg = 1
503     hkey_mod = _winreg
504
505     RegOpenKeyEx = _winreg.OpenKeyEx
506     RegEnumKey = _winreg.EnumKey
507     RegEnumValue = _winreg.EnumValue
508     RegQueryValueEx = _winreg.QueryValueEx
509     RegError = _winreg.error
510
511 except ImportError:
512     try:
513         import win32api
514         import win32con
515         can_read_reg = 1
516         hkey_mod = win32con
517
518         RegOpenKeyEx = win32api.RegOpenKeyEx
519         RegEnumKey = win32api.RegEnumKey
520         RegEnumValue = win32api.RegEnumValue
521         RegQueryValueEx = win32api.RegQueryValueEx
522         RegError = win32api.error
523
524     except ImportError:
525         class _NoError(Exception):
526             pass
527         RegError = _NoError
528
529 if can_read_reg:
530     HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
531     HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
532     HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
533     HKEY_USERS = hkey_mod.HKEY_USERS
534
535     def RegGetValue(root, key):
536         """This utility function returns a value in the registry
537         without having to open the key first.  Only available on
538         Windows platforms with a version of Python that can read the
539         registry.  Returns the same thing as
540         SCons.Util.RegQueryValueEx, except you just specify the entire
541         path to the value, and don't have to bother opening the key
542         first.  So:
543
544         Instead of:
545           k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
546                 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
547           out = SCons.Util.RegQueryValueEx(k,
548                 'ProgramFilesDir')
549
550         You can write:
551           out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
552                 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
553         """
554         # I would use os.path.split here, but it's not a filesystem
555         # path...
556         p = key.rfind('\\') + 1
557         keyp = key[:p]
558         val = key[p:]
559         k = RegOpenKeyEx(root, keyp)
560         return RegQueryValueEx(k,val)
561
562 if sys.platform == 'win32':
563
564     def WhereIs(file, path=None, pathext=None, reject=[]):
565         if path is None:
566             try:
567                 path = os.environ['PATH']
568             except KeyError:
569                 return None
570         if is_String(path):
571             path = string.split(path, os.pathsep)
572         if pathext is None:
573             try:
574                 pathext = os.environ['PATHEXT']
575             except KeyError:
576                 pathext = '.COM;.EXE;.BAT;.CMD'
577         if is_String(pathext):
578             pathext = string.split(pathext, os.pathsep)
579         for ext in pathext:
580             if string.lower(ext) == string.lower(file[-len(ext):]):
581                 pathext = ['']
582                 break
583         if not is_List(reject) and not is_Tuple(reject):
584             reject = [reject]
585         for dir in path:
586             f = os.path.join(dir, file)
587             for ext in pathext:
588                 fext = f + ext
589                 if os.path.isfile(fext):
590                     try:
591                         reject.index(fext)
592                     except ValueError:
593                         return os.path.normpath(fext)
594                     continue
595         return None
596
597 elif os.name == 'os2':
598
599     def WhereIs(file, path=None, pathext=None, reject=[]):
600         if path is None:
601             try:
602                 path = os.environ['PATH']
603             except KeyError:
604                 return None
605         if is_String(path):
606             path = string.split(path, os.pathsep)
607         if pathext is None:
608             pathext = ['.exe', '.cmd']
609         for ext in pathext:
610             if string.lower(ext) == string.lower(file[-len(ext):]):
611                 pathext = ['']
612                 break
613         if not is_List(reject) and not is_Tuple(reject):
614             reject = [reject]
615         for dir in path:
616             f = os.path.join(dir, file)
617             for ext in pathext:
618                 fext = f + ext
619                 if os.path.isfile(fext):
620                     try:
621                         reject.index(fext)
622                     except ValueError:
623                         return os.path.normpath(fext)
624                     continue
625         return None
626
627 else:
628
629     def WhereIs(file, path=None, pathext=None, reject=[]):
630         if path is None:
631             try:
632                 path = os.environ['PATH']
633             except KeyError:
634                 return None
635         if is_String(path):
636             path = string.split(path, os.pathsep)
637         if not is_List(reject) and not is_Tuple(reject):
638             reject = [reject]
639         for d in path:
640             f = os.path.join(d, file)
641             if os.path.isfile(f):
642                 try:
643                     st = os.stat(f)
644                 except OSError:
645                     # os.stat() raises OSError, not IOError if the file
646                     # doesn't exist, so in this case we let IOError get
647                     # raised so as to not mask possibly serious disk or
648                     # network issues.
649                     continue
650                 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
651                     try:
652                         reject.index(f)
653                     except ValueError:
654                         return os.path.normpath(f)
655                     continue
656         return None
657
658 def PrependPath(oldpath, newpath, sep = os.pathsep):
659     """This prepends newpath elements to the given oldpath.  Will only
660     add any particular path once (leaving the first one it encounters
661     and ignoring the rest, to preserve path order), and will
662     os.path.normpath and os.path.normcase all paths to help assure
663     this.  This can also handle the case where the given old path
664     variable is a list instead of a string, in which case a list will
665     be returned instead of a string.
666
667     Example:
668       Old Path: "/foo/bar:/foo"
669       New Path: "/biz/boom:/foo"
670       Result:   "/biz/boom:/foo:/foo/bar"
671     """
672
673     orig = oldpath
674     is_list = 1
675     paths = orig
676     if not is_List(orig) and not is_Tuple(orig):
677         paths = string.split(paths, sep)
678         is_list = 0
679
680     if is_List(newpath) or is_Tuple(newpath):
681         newpaths = newpath
682     else:
683         newpaths = string.split(newpath, sep)
684
685     newpaths = newpaths + paths # prepend new paths
686
687     normpaths = []
688     paths = []
689     # now we add them only if they are unique
690     for path in newpaths:
691         normpath = os.path.normpath(os.path.normcase(path))
692         if path and not normpath in normpaths:
693             paths.append(path)
694             normpaths.append(normpath)
695
696     if is_list:
697         return paths
698     else:
699         return string.join(paths, sep)
700
701 def AppendPath(oldpath, newpath, sep = os.pathsep):
702     """This appends new path elements to the given old path.  Will
703     only add any particular path once (leaving the last one it
704     encounters and ignoring the rest, to preserve path order), and
705     will os.path.normpath and os.path.normcase all paths to help
706     assure this.  This can also handle the case where the given old
707     path variable is a list instead of a string, in which case a list
708     will be returned instead of a string.
709
710     Example:
711       Old Path: "/foo/bar:/foo"
712       New Path: "/biz/boom:/foo"
713       Result:   "/foo/bar:/biz/boom:/foo"
714     """
715
716     orig = oldpath
717     is_list = 1
718     paths = orig
719     if not is_List(orig) and not is_Tuple(orig):
720         paths = string.split(paths, sep)
721         is_list = 0
722
723     if is_List(newpath) or is_Tuple(newpath):
724         newpaths = newpath
725     else:
726         newpaths = string.split(newpath, sep)
727
728     newpaths = paths + newpaths # append new paths
729     newpaths.reverse()
730
731     normpaths = []
732     paths = []
733     # now we add them only of they are unique
734     for path in newpaths:
735         normpath = os.path.normpath(os.path.normcase(path))
736         if path and not normpath in normpaths:
737             paths.append(path)
738             normpaths.append(normpath)
739
740     paths.reverse()
741
742     if is_list:
743         return paths
744     else:
745         return string.join(paths, sep)
746
747 if sys.platform == 'cygwin':
748     def get_native_path(path):
749         """Transforms an absolute path into a native path for the system.  In
750         Cygwin, this converts from a Cygwin path to a Windows one."""
751         return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '')
752 else:
753     def get_native_path(path):
754         """Transforms an absolute path into a native path for the system.
755         Non-Cygwin version, just leave the path alone."""
756         return path
757
758 display = DisplayEngine()
759
760 def Split(arg):
761     if is_List(arg) or is_Tuple(arg):
762         return arg
763     elif is_String(arg):
764         return string.split(arg)
765     else:
766         return [arg]
767
768 class CLVar(UserList):
769     """A class for command-line construction variables.
770
771     This is a list that uses Split() to split an initial string along
772     white-space arguments, and similarly to split any strings that get
773     added.  This allows us to Do the Right Thing with Append() and
774     Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
775     arg2') regardless of whether a user adds a list or a string to a
776     command-line construction variable.
777     """
778     def __init__(self, seq = []):
779         UserList.__init__(self, Split(seq))
780     def __coerce__(self, other):
781         return (self, CLVar(other))
782     def __str__(self):
783         return string.join(self.data)
784
785 # A dictionary that preserves the order in which items are added.
786 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
787 #     http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
788 # Including fixes/enhancements from the follow-on discussions.
789 class OrderedDict(UserDict):
790     def __init__(self, dict = None):
791         self._keys = []
792         UserDict.__init__(self, dict)
793
794     def __delitem__(self, key):
795         UserDict.__delitem__(self, key)
796         self._keys.remove(key)
797
798     def __setitem__(self, key, item):
799         UserDict.__setitem__(self, key, item)
800         if key not in self._keys: self._keys.append(key)
801
802     def clear(self):
803         UserDict.clear(self)
804         self._keys = []
805
806     def copy(self):
807         dict = OrderedDict()
808         dict.update(self)
809         return dict
810
811     def items(self):
812         return zip(self._keys, self.values())
813
814     def keys(self):
815         return self._keys[:]
816
817     def popitem(self):
818         try:
819             key = self._keys[-1]
820         except IndexError:
821             raise KeyError('dictionary is empty')
822
823         val = self[key]
824         del self[key]
825
826         return (key, val)
827
828     def setdefault(self, key, failobj = None):
829         UserDict.setdefault(self, key, failobj)
830         if key not in self._keys: self._keys.append(key)
831
832     def update(self, dict):
833         for (key, val) in dict.items():
834             self.__setitem__(key, val)
835
836     def values(self):
837         return map(self.get, self._keys)
838
839 class Selector(OrderedDict):
840     """A callable ordered dictionary that maps file suffixes to
841     dictionary values.  We preserve the order in which items are added
842     so that get_suffix() calls always return the first suffix added."""
843     def __call__(self, env, source):
844         try:
845             ext = splitext(str(source[0]))[1]
846         except IndexError:
847             ext = ""
848         try:
849             return self[ext]
850         except KeyError:
851             # Try to perform Environment substitution on the keys of
852             # the dictionary before giving up.
853             s_dict = {}
854             for (k,v) in self.items():
855                 if not k is None:
856                     s_k = env.subst(k)
857                     if s_dict.has_key(s_k):
858                         # We only raise an error when variables point
859                         # to the same suffix.  If one suffix is literal
860                         # and a variable suffix contains this literal,
861                         # the literal wins and we don't raise an error.
862                         raise KeyError, (s_dict[s_k][0], k, s_k)
863                     s_dict[s_k] = (k,v)
864             try:
865                 return s_dict[ext][1]
866             except KeyError:
867                 try:
868                     return self[None]
869                 except KeyError:
870                     return None
871
872
873 if sys.platform == 'cygwin':
874     # On Cygwin, os.path.normcase() lies, so just report back the
875     # fact that the underlying Windows OS is case-insensitive.
876     def case_sensitive_suffixes(s1, s2):
877         return 0
878 else:
879     def case_sensitive_suffixes(s1, s2):
880         return (os.path.normcase(s1) != os.path.normcase(s2))
881
882 def adjustixes(fname, pre, suf):
883     if pre:
884         path, fn = os.path.split(os.path.normpath(fname))
885         if fn[:len(pre)] != pre:
886             fname = os.path.join(path, pre + fn)
887     # Only append a suffix if the file does not have one.
888     if suf and not splitext(fname)[1] and fname[-len(suf):] != suf:
889             fname = fname + suf
890     return fname
891
892
893 def unique(s):
894     """Return a list of the elements in s, but without duplicates.
895
896     For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
897     unique("abcabc") some permutation of ["a", "b", "c"], and
898     unique(([1, 2], [2, 3], [1, 2])) some permutation of
899     [[2, 3], [1, 2]].
900
901     For best speed, all sequence elements should be hashable.  Then
902     unique() will usually work in linear time.
903
904     If not possible, the sequence elements should enjoy a total
905     ordering, and if list(s).sort() doesn't raise TypeError it's
906     assumed that they do enjoy a total ordering.  Then unique() will
907     usually work in O(N*log2(N)) time.
908
909     If that's not possible either, the sequence elements must support
910     equality-testing.  Then unique() will usually work in quadratic
911     time.
912     """
913
914     n = len(s)
915     if n == 0:
916         return []
917
918     # Try using a dict first, as that's the fastest and will usually
919     # work.  If it doesn't work, it will usually fail quickly, so it
920     # usually doesn't cost much to *try* it.  It requires that all the
921     # sequence elements be hashable, and support equality comparison.
922     u = {}
923     try:
924         for x in s:
925             u[x] = 1
926     except TypeError:
927         del u  # move on to the next method
928     else:
929         return u.keys()
930
931     # We can't hash all the elements.  Second fastest is to sort,
932     # which brings the equal elements together; then duplicates are
933     # easy to weed out in a single pass.
934     # NOTE:  Python's list.sort() was designed to be efficient in the
935     # presence of many duplicate elements.  This isn't true of all
936     # sort functions in all languages or libraries, so this approach
937     # is more effective in Python than it may be elsewhere.
938     try:
939         t = list(s)
940         t.sort()
941     except TypeError:
942         del t  # move on to the next method
943     else:
944         assert n > 0
945         last = t[0]
946         lasti = i = 1
947         while i < n:
948             if t[i] != last:
949                 t[lasti] = last = t[i]
950                 lasti = lasti + 1
951             i = i + 1
952         return t[:lasti]
953
954     # Brute force is all that's left.
955     u = []
956     for x in s:
957         if x not in u:
958             u.append(x)
959     return u
960
961 # Much of the logic here was originally based on recipe 4.9 from the
962 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
963 class LogicalLines:
964
965     def __init__(self, fileobj):
966         self.fileobj = fileobj
967
968     def readline(self):
969         result = []
970         while 1:
971             line = self.fileobj.readline()
972             if not line:
973                 break
974             if line[-2:] == '\\\n':
975                 result.append(line[:-2])
976             else:
977                 result.append(line)
978                 break
979         return string.join(result, '')
980
981     def readlines(self):
982         result = []
983         while 1:
984             line = self.readline()
985             if not line:
986                 break
987             result.append(line)
988         return result