3 Various utility functions go here.
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:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
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.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
39 from UserDict import UserDict
40 from UserList import UserList
41 from UserString import UserString
43 # Don't "from types import ..." these because we need to get at the
44 # types module later to look for UnicodeType.
45 DictType = types.DictType
46 InstanceType = types.InstanceType
47 ListType = types.ListType
48 StringType = types.StringType
49 TupleType = types.TupleType
51 def dictify(keys, values, result={}):
52 for k, v in zip(keys, values):
57 if _altsep is None and sys.platform == 'win32':
58 # My ActivePython 2.0.1 doesn't set os.altsep! What gives?
61 def rightmost_separator(path, sep):
62 return max(path.rfind(sep), path.rfind(_altsep))
64 def rightmost_separator(path, sep):
65 return path.rfind(sep)
67 # First two from the Python Cookbook, just for completeness.
68 # (Yeah, yeah, YAGNI...)
69 def containsAny(str, set):
70 """Check whether sequence str contains ANY of the items in set."""
75 def containsAll(str, set):
76 """Check whether sequence str contains ALL of the items in set."""
78 if c not in str: return 0
81 def containsOnly(str, set):
82 """Check whether sequence str contains ONLY items in set."""
84 if c not in set: return 0
88 "Same as os.path.splitext() but faster."
89 sep = rightmost_separator(path, os.sep)
91 # An ext is only real if it has at least one non-digit char
92 if dot > sep and not containsOnly(path[dot:], "0123456789."):
93 return path[:dot],path[dot:]
99 Make the drive letter (if any) upper case.
100 This is useful because Windows is inconsitent on the case
101 of the drive letter, which can cause inconsistencies when
102 calculating command signatures.
104 drive, rest = os.path.splitdrive(path)
106 path = drive.upper() + rest
109 class NodeList(UserList):
110 """This class is almost exactly like a regular list of Nodes
111 (actually it can hold any object), with one important difference.
112 If you try to get an attribute from this list, it will return that
113 attribute from every item in the list. For example:
115 >>> someList = NodeList([ ' foo ', ' bar ' ])
119 def __nonzero__(self):
120 return len(self.data) != 0
123 return ' '.join(map(str, self.data))
126 return iter(self.data)
128 def __call__(self, *args, **kwargs):
129 result = [x(*args, **kwargs) for x in self.data]
130 return self.__class__(result)
132 def __getattr__(self, name):
133 result = [getattr(x, name) for x in self.data]
134 return self.__class__(result)
137 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
139 def get_environment_var(varstr):
140 """Given a string, first determine if it looks like a reference
141 to a single environment variable, like "$FOO" or "${FOO}".
142 If so, return that variable with no decorations ("FOO").
143 If not, return None."""
144 mo=_get_env_var.match(to_String(varstr))
156 self.__call__ = self.print_it
158 def print_it(self, text, append_newline=1):
159 if append_newline: text = text + '\n'
161 sys.stdout.write(text)
163 # Stdout might be connected to a pipe that has been closed
164 # by now. The most likely reason for the pipe being closed
165 # is that the user has press ctrl-c. It this is the case,
166 # then SCons is currently shutdown. We therefore ignore
167 # IOError's here so that SCons can continue and shutdown
168 # properly so that the .sconsign is correctly written
169 # before SCons exits.
172 def dont_print(self, text, append_newline=1):
175 def set_mode(self, mode):
177 self.__call__ = self.print_it
179 self.__call__ = self.dont_print
181 def render_tree(root, child_func, prune=0, margin=[0], visited={}):
183 Render a tree of nodes into an ASCII tree view.
184 root - the root node of the tree
185 child_func - the function called to get the children of a node
186 prune - don't visit the same node twice
187 margin - the format of the left margin to use for children of root.
188 1 results in a pipe, and 0 results in no pipe.
189 visited - a dictionary of visited nodes in the current branch if not prune,
190 or in the whole tree if prune.
195 children = child_func(root)
197 for pipe in margin[:-1]:
199 retval = retval + "| "
201 retval = retval + " "
204 return retval + "+-[" + rname + "]\n"
206 retval = retval + "+-" + rname + "\n"
208 visited = copy.copy(visited)
211 for i in range(len(children)):
212 margin.append(i<len(children)-1)
213 retval = retval + render_tree(children[i], child_func, prune, margin, visited
219 IDX = lambda N: N and 1 or 0
221 def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
223 Print a tree of nodes. This is like render_tree, except it prints
224 lines directly instead of creating a string representation in memory,
225 so that huge trees can be printed.
227 root - the root node of the tree
228 child_func - the function called to get the children of a node
229 prune - don't visit the same node twice
230 showtags - print status information to the left of each node line
231 margin - the format of the left margin to use for children of root.
232 1 results in a pipe, and 0 results in no pipe.
233 visited - a dictionary of visited nodes in the current branch if not prune,
234 or in the whole tree if prune.
243 print ' R = exists in repository only'
244 print ' b = implicit builder'
245 print ' B = explicit builder'
246 print ' S = side effect'
247 print ' P = precious'
248 print ' A = always build'
250 print ' N = no clean'
251 print ' H = no cache'
255 tags.append(' E'[IDX(root.exists())])
256 tags.append(' R'[IDX(root.rexists() and not root.exists())])
257 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
258 [0,2][IDX(root.has_builder())]])
259 tags.append(' S'[IDX(root.side_effect)])
260 tags.append(' P'[IDX(root.precious)])
261 tags.append(' A'[IDX(root.always_build)])
262 tags.append(' C'[IDX(root.is_up_to_date())])
263 tags.append(' N'[IDX(root.noclean)])
264 tags.append(' H'[IDX(root.nocache)])
272 margins = list(map(MMM, margin[:-1]))
274 children = child_func(root)
276 if prune and rname in visited and children:
277 print ''.join(tags + margins + ['+-[', rname, ']'])
280 print ''.join(tags + margins + ['+-', rname])
287 for C in children[:-1]:
288 print_tree(C, child_func, prune, idx, margin, visited)
290 print_tree(children[-1], child_func, prune, idx, margin, visited)
295 # Functions for deciding if things are like various types, mainly to
296 # handle UserDict, UserList and UserString like their underlying types.
298 # Yes, all of this manual testing breaks polymorphism, and the real
299 # Pythonic way to do all of this would be to just try it and handle the
300 # exception, but handling the exception when it's not the right type is
307 # An older Python version without new-style classes.
309 # The actual implementations here have been selected after timings
310 # coded up in in bench/is_types.py (from the SCons source tree,
311 # see the scons-src distribution), mostly against Python 1.5.2.
312 # Key results from those timings:
314 # -- Storing the type of the object in a variable (t = type(obj))
315 # slows down the case where it's a native type and the first
316 # comparison will match, but nicely speeds up the case where
317 # it's a different native type. Since that's going to be
318 # common, it's a good tradeoff.
320 # -- The data show that calling isinstance() on an object that's
321 # a native type (dict, list or string) is expensive enough
322 # that checking up front for whether the object is of type
323 # InstanceType is a pretty big win, even though it does slow
324 # down the case where it really *is* an object instance a
328 return t is DictType or \
329 (t is InstanceType and isinstance(obj, UserDict))
333 return t is ListType \
334 or (t is InstanceType and isinstance(obj, UserList))
336 def is_Sequence(obj):
338 return t is ListType \
340 or (t is InstanceType and isinstance(obj, UserList))
344 return t is TupleType
346 if hasattr(types, 'UnicodeType'):
349 return t is StringType \
350 or t is UnicodeType \
351 or (t is InstanceType and isinstance(obj, UserString))
355 return t is StringType \
356 or (t is InstanceType and isinstance(obj, UserString))
359 return is_String(obj) or not is_Sequence(obj)
361 def flatten(obj, result=None):
362 """Flatten a sequence to a non-nested list.
364 Flatten() converts either a single scalar or a nested sequence
365 to a non-nested list. Note that flatten() considers strings
366 to be scalars instead of sequences like Python would.
376 flatten_sequence(item, result)
379 def flatten_sequence(sequence, result=None):
380 """Flatten a sequence to a non-nested list.
382 Same as flatten(), but it does not handle the single scalar
383 case. This is slightly more efficient when one knows that
384 the sequence to flatten can not be a scalar.
388 for item in sequence:
392 flatten_sequence(item, result)
396 # Generic convert-to-string functions that abstract away whether or
397 # not the Python we're executing has Unicode support. The wrapper
398 # to_String_for_signature() will use a for_signature() method if the
399 # specified object has one.
401 if hasattr(types, 'UnicodeType'):
402 UnicodeType = types.UnicodeType
404 if isinstance(s, UserString):
415 def to_String_for_signature(obj):
417 f = obj.for_signature
418 except AttributeError:
419 return to_String_for_subst(obj)
423 def to_String_for_subst(s):
425 return ' '.join( map(to_String_for_subst, s) )
427 return to_String( s )
430 # A modern Python version with new-style classes, so we can just use
433 # We are using the following trick to speed-up these
434 # functions. Default arguments are used to take a snapshot of the
435 # the global functions and constants used by these functions. This
436 # transforms accesses to global variable into local variables
437 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL).
439 DictTypes = (dict, UserDict)
440 ListTypes = (list, UserList)
441 SequenceTypes = (list, tuple, UserList)
443 # Empirically, Python versions with new-style classes all have
446 # Note that profiling data shows a speed-up when comparing
447 # explicitely with str and unicode instead of simply comparing
448 # with basestring. (at least on Python 2.5.1)
449 StringTypes = (str, unicode, UserString)
451 # Empirically, it is faster to check explicitely for str and
452 # unicode than for basestring.
453 BaseStringTypes = (str, unicode)
455 def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
456 return isinstance(obj, DictTypes)
458 def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
459 return isinstance(obj, ListTypes)
461 def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
462 return isinstance(obj, SequenceTypes)
464 def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
465 return isinstance(obj, tuple)
467 def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
468 return isinstance(obj, StringTypes)
470 def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
471 # Profiling shows that there is an impressive speed-up of 2x
472 # when explicitely checking for strings instead of just not
473 # sequence when the argument (i.e. obj) is already a string.
474 # But, if obj is a not string than it is twice as fast to
475 # check only for 'not sequence'. The following code therefore
476 # assumes that the obj argument is a string must of the time.
477 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
479 def do_flatten(sequence, result, isinstance=isinstance,
480 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
481 for item in sequence:
482 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
485 do_flatten(item, result)
487 def flatten(obj, isinstance=isinstance, StringTypes=StringTypes,
488 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
489 """Flatten a sequence to a non-nested list.
491 Flatten() converts either a single scalar or a nested sequence
492 to a non-nested list. Note that flatten() considers strings
493 to be scalars instead of sequences like Python would.
495 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes):
499 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
502 do_flatten(item, result)
505 def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes,
506 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
507 """Flatten a sequence to a non-nested list.
509 Same as flatten(), but it does not handle the single scalar
510 case. This is slightly more efficient when one knows that
511 the sequence to flatten can not be a scalar.
514 for item in sequence:
515 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
518 do_flatten(item, result)
523 # Generic convert-to-string functions that abstract away whether or
524 # not the Python we're executing has Unicode support. The wrapper
525 # to_String_for_signature() will use a for_signature() method if the
526 # specified object has one.
529 isinstance=isinstance, str=str,
530 UserString=UserString, BaseStringTypes=BaseStringTypes):
531 if isinstance(s,BaseStringTypes):
532 # Early out when already a string!
534 elif isinstance(s, UserString):
535 # s.data can only be either a unicode or a regular
536 # string. Please see the UserString initializer.
541 def to_String_for_subst(s,
542 isinstance=isinstance, str=str, to_String=to_String,
543 BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes,
544 UserString=UserString):
546 # Note that the test cases are sorted by order of probability.
547 if isinstance(s, BaseStringTypes):
549 elif isinstance(s, SequenceTypes):
552 l.append(to_String_for_subst(e))
554 elif isinstance(s, UserString):
555 # s.data can only be either a unicode or a regular
556 # string. Please see the UserString initializer.
561 def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst,
562 AttributeError=AttributeError):
564 f = obj.for_signature
565 except AttributeError:
566 return to_String_for_subst(obj)
572 # The SCons "semi-deep" copy.
574 # This makes separate copies of lists (including UserList objects)
575 # dictionaries (including UserDict objects) and tuples, but just copies
576 # references to anything else it finds.
578 # A special case is any object that has a __semi_deepcopy__() method,
579 # which we invoke to create the copy, which is used by the BuilderDict
580 # class because of its extra initialization argument.
582 # The dispatch table approach used here is a direct rip-off from the
583 # normal Python copy module.
585 _semi_deepcopy_dispatch = d = {}
587 def _semi_deepcopy_dict(x):
589 for key, val in x.items():
590 # The regular Python copy.deepcopy() also deepcopies the key,
593 # copy[semi_deepcopy(key)] = semi_deepcopy(val)
595 # Doesn't seem like we need to, but we'll comment it just in case.
596 copy[key] = semi_deepcopy(val)
598 d[types.DictionaryType] = _semi_deepcopy_dict
600 def _semi_deepcopy_list(x):
601 return list(map(semi_deepcopy, x))
602 d[types.ListType] = _semi_deepcopy_list
604 def _semi_deepcopy_tuple(x):
605 return tuple(map(semi_deepcopy, x))
606 d[types.TupleType] = _semi_deepcopy_tuple
608 def _semi_deepcopy_inst(x):
609 if hasattr(x, '__semi_deepcopy__'):
610 return x.__semi_deepcopy__()
611 elif isinstance(x, UserDict):
612 return x.__class__(_semi_deepcopy_dict(x))
613 elif isinstance(x, UserList):
614 return x.__class__(_semi_deepcopy_list(x))
617 d[types.InstanceType] = _semi_deepcopy_inst
619 def semi_deepcopy(x):
620 copier = _semi_deepcopy_dispatch.get(type(x))
629 """A simple generic Proxy class, forwarding all calls to
630 subject. So, for the benefit of the python newbie, what does
631 this really mean? Well, it means that you can take an object, let's
632 call it 'objA', and wrap it in this Proxy class, with a statement
635 proxyObj = Proxy(objA),
637 Then, if in the future, you do something like this
641 since Proxy does not have a 'var1' attribute (but presumably objA does),
642 the request actually is equivalent to saying
646 Inherit from this class to create a Proxy."""
648 def __init__(self, subject):
649 """Wrap an object as a Proxy object"""
650 self.__subject = subject
652 def __getattr__(self, name):
653 """Retrieve an attribute from the wrapped object. If the named
654 attribute doesn't exist, AttributeError is raised"""
655 return getattr(self.__subject, name)
658 """Retrieve the entire wrapped object"""
659 return self.__subject
661 def __cmp__(self, other):
662 if issubclass(other.__class__, self.__subject.__class__):
663 return cmp(self.__subject, other)
664 return cmp(self.__dict__, other.__dict__)
666 # attempt to load the windows registry module:
674 RegOpenKeyEx = _winreg.OpenKeyEx
675 RegEnumKey = _winreg.EnumKey
676 RegEnumValue = _winreg.EnumValue
677 RegQueryValueEx = _winreg.QueryValueEx
678 RegError = _winreg.error
687 RegOpenKeyEx = win32api.RegOpenKeyEx
688 RegEnumKey = win32api.RegEnumKey
689 RegEnumValue = win32api.RegEnumValue
690 RegQueryValueEx = win32api.RegQueryValueEx
691 RegError = win32api.error
694 class _NoError(Exception):
699 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
700 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
701 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
702 HKEY_USERS = hkey_mod.HKEY_USERS
704 def RegGetValue(root, key):
705 """This utility function returns a value in the registry
706 without having to open the key first. Only available on
707 Windows platforms with a version of Python that can read the
708 registry. Returns the same thing as
709 SCons.Util.RegQueryValueEx, except you just specify the entire
710 path to the value, and don't have to bother opening the key
714 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
715 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
716 out = SCons.Util.RegQueryValueEx(k,
720 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
721 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
723 # I would use os.path.split here, but it's not a filesystem
725 p = key.rfind('\\') + 1
726 keyp = key[:p-1] # -1 to omit trailing slash
728 k = RegOpenKeyEx(root, keyp)
729 return RegQueryValueEx(k,val)
734 # Make sure we have a definition of WindowsError so we can
735 # run platform-independent tests of Windows functionality on
736 # platforms other than Windows. (WindowsError is, in fact, an
737 # OSError subclass on Windows.)
738 class WindowsError(OSError):
741 __builtin__.WindowsError = WindowsError
745 HKEY_CLASSES_ROOT = None
746 HKEY_LOCAL_MACHINE = None
747 HKEY_CURRENT_USER = None
750 def RegGetValue(root, key):
753 def RegOpenKeyEx(root, key):
756 if sys.platform == 'win32':
758 def WhereIs(file, path=None, pathext=None, reject=[]):
761 path = os.environ['PATH']
765 path = path.split(os.pathsep)
768 pathext = os.environ['PATHEXT']
770 pathext = '.COM;.EXE;.BAT;.CMD'
771 if is_String(pathext):
772 pathext = pathext.split(os.pathsep)
774 if ext.lower() == file[-len(ext):].lower():
777 if not is_List(reject) and not is_Tuple(reject):
780 f = os.path.join(dir, file)
783 if os.path.isfile(fext):
787 return os.path.normpath(fext)
791 elif os.name == 'os2':
793 def WhereIs(file, path=None, pathext=None, reject=[]):
796 path = os.environ['PATH']
800 path = path.split(os.pathsep)
802 pathext = ['.exe', '.cmd']
804 if ext.lower() == file[-len(ext):].lower():
807 if not is_List(reject) and not is_Tuple(reject):
810 f = os.path.join(dir, file)
813 if os.path.isfile(fext):
817 return os.path.normpath(fext)
823 def WhereIs(file, path=None, pathext=None, reject=[]):
827 path = os.environ['PATH']
831 path = path.split(os.pathsep)
832 if not is_List(reject) and not is_Tuple(reject):
835 f = os.path.join(d, file)
836 if os.path.isfile(f):
840 # os.stat() raises OSError, not IOError if the file
841 # doesn't exist, so in this case we let IOError get
842 # raised so as to not mask possibly serious disk or
845 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
849 return os.path.normpath(f)
853 def PrependPath(oldpath, newpath, sep = os.pathsep,
854 delete_existing=1, canonicalize=None):
855 """This prepends newpath elements to the given oldpath. Will only
856 add any particular path once (leaving the first one it encounters
857 and ignoring the rest, to preserve path order), and will
858 os.path.normpath and os.path.normcase all paths to help assure
859 this. This can also handle the case where the given old path
860 variable is a list instead of a string, in which case a list will
861 be returned instead of a string.
864 Old Path: "/foo/bar:/foo"
865 New Path: "/biz/boom:/foo"
866 Result: "/biz/boom:/foo:/foo/bar"
868 If delete_existing is 0, then adding a path that exists will
869 not move it to the beginning; it will stay where it is in the
872 If canonicalize is not None, it is applied to each element of
879 if not is_List(orig) and not is_Tuple(orig):
880 paths = paths.split(sep)
883 if is_String(newpath):
884 newpaths = newpath.split(sep)
885 elif not is_List(newpath) and not is_Tuple(newpath):
886 newpaths = [ newpath ] # might be a Dir
891 newpaths=list(map(canonicalize, newpaths))
893 if not delete_existing:
894 # First uniquify the old paths, making sure to
895 # preserve the first instance (in Unix/Linux,
896 # the first one wins), and remembering them in normpaths.
897 # Then insert the new paths at the head of the list
898 # if they're not already in the normpaths list.
904 normpath = os.path.normpath(os.path.normcase(path))
905 if normpath not in normpaths:
907 normpaths.append(normpath)
908 newpaths.reverse() # since we're inserting at the head
909 for path in newpaths:
912 normpath = os.path.normpath(os.path.normcase(path))
913 if normpath not in normpaths:
914 result.insert(0, path)
915 normpaths.append(normpath)
919 newpaths = newpaths + paths # prepend new paths
923 # now we add them only if they are unique
924 for path in newpaths:
925 normpath = os.path.normpath(os.path.normcase(path))
926 if path and not normpath in normpaths:
928 normpaths.append(normpath)
933 return sep.join(paths)
935 def AppendPath(oldpath, newpath, sep = os.pathsep,
936 delete_existing=1, canonicalize=None):
937 """This appends new path elements to the given old path. Will
938 only add any particular path once (leaving the last one it
939 encounters and ignoring the rest, to preserve path order), and
940 will os.path.normpath and os.path.normcase all paths to help
941 assure this. This can also handle the case where the given old
942 path variable is a list instead of a string, in which case a list
943 will be returned instead of a string.
946 Old Path: "/foo/bar:/foo"
947 New Path: "/biz/boom:/foo"
948 Result: "/foo/bar:/biz/boom:/foo"
950 If delete_existing is 0, then adding a path that exists
951 will not move it to the end; it will stay where it is in the list.
953 If canonicalize is not None, it is applied to each element of
960 if not is_List(orig) and not is_Tuple(orig):
961 paths = paths.split(sep)
964 if is_String(newpath):
965 newpaths = newpath.split(sep)
966 elif not is_List(newpath) and not is_Tuple(newpath):
967 newpaths = [ newpath ] # might be a Dir
972 newpaths=list(map(canonicalize, newpaths))
974 if not delete_existing:
975 # add old paths to result, then
976 # add new paths if not already present
977 # (I thought about using a dict for normpaths for speed,
978 # but it's not clear hashing the strings would be faster
979 # than linear searching these typically short lists.)
986 normpaths.append(os.path.normpath(os.path.normcase(path)))
987 for path in newpaths:
990 normpath = os.path.normpath(os.path.normcase(path))
991 if normpath not in normpaths:
993 normpaths.append(normpath)
996 # start w/ new paths, add old ones if not present,
998 newpaths = paths + newpaths # append new paths
1003 # now we add them only if they are unique
1004 for path in newpaths:
1005 normpath = os.path.normpath(os.path.normcase(path))
1006 if path and not normpath in normpaths:
1008 normpaths.append(normpath)
1014 return sep.join(paths)
1016 if sys.platform == 'cygwin':
1017 def get_native_path(path):
1018 """Transforms an absolute path into a native path for the system. In
1019 Cygwin, this converts from a Cygwin path to a Windows one."""
1020 return os.popen('cygpath -w ' + path).read().replace('\n', '')
1022 def get_native_path(path):
1023 """Transforms an absolute path into a native path for the system.
1024 Non-Cygwin version, just leave the path alone."""
1027 display = DisplayEngine()
1030 if is_List(arg) or is_Tuple(arg):
1032 elif is_String(arg):
1037 class CLVar(UserList):
1038 """A class for command-line construction variables.
1040 This is a list that uses Split() to split an initial string along
1041 white-space arguments, and similarly to split any strings that get
1042 added. This allows us to Do the Right Thing with Append() and
1043 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1044 arg2') regardless of whether a user adds a list or a string to a
1045 command-line construction variable.
1047 def __init__(self, seq = []):
1048 UserList.__init__(self, Split(seq))
1049 def __add__(self, other):
1050 return UserList.__add__(self, CLVar(other))
1051 def __radd__(self, other):
1052 return UserList.__radd__(self, CLVar(other))
1053 def __coerce__(self, other):
1054 return (self, CLVar(other))
1056 return ' '.join(self.data)
1058 # A dictionary that preserves the order in which items are added.
1059 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
1060 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
1061 # Including fixes/enhancements from the follow-on discussions.
1062 class OrderedDict(UserDict):
1063 def __init__(self, dict = None):
1065 UserDict.__init__(self, dict)
1067 def __delitem__(self, key):
1068 UserDict.__delitem__(self, key)
1069 self._keys.remove(key)
1071 def __setitem__(self, key, item):
1072 UserDict.__setitem__(self, key, item)
1073 if key not in self._keys: self._keys.append(key)
1076 UserDict.clear(self)
1080 dict = OrderedDict()
1085 return list(zip(self._keys, self.values()))
1088 return self._keys[:]
1092 key = self._keys[-1]
1094 raise KeyError('dictionary is empty')
1101 def setdefault(self, key, failobj = None):
1102 UserDict.setdefault(self, key, failobj)
1103 if key not in self._keys: self._keys.append(key)
1105 def update(self, dict):
1106 for (key, val) in dict.items():
1107 self.__setitem__(key, val)
1110 return list(map(self.get, self._keys))
1112 class Selector(OrderedDict):
1113 """A callable ordered dictionary that maps file suffixes to
1114 dictionary values. We preserve the order in which items are added
1115 so that get_suffix() calls always return the first suffix added."""
1116 def __call__(self, env, source, ext=None):
1119 ext = source[0].suffix
1125 # Try to perform Environment substitution on the keys of
1126 # the dictionary before giving up.
1128 for (k,v) in self.items():
1132 # We only raise an error when variables point
1133 # to the same suffix. If one suffix is literal
1134 # and a variable suffix contains this literal,
1135 # the literal wins and we don't raise an error.
1136 raise KeyError, (s_dict[s_k][0], k, s_k)
1139 return s_dict[ext][1]
1147 if sys.platform == 'cygwin':
1148 # On Cygwin, os.path.normcase() lies, so just report back the
1149 # fact that the underlying Windows OS is case-insensitive.
1150 def case_sensitive_suffixes(s1, s2):
1153 def case_sensitive_suffixes(s1, s2):
1154 return (os.path.normcase(s1) != os.path.normcase(s2))
1156 def adjustixes(fname, pre, suf, ensure_suffix=False):
1158 path, fn = os.path.split(os.path.normpath(fname))
1159 if fn[:len(pre)] != pre:
1160 fname = os.path.join(path, pre + fn)
1161 # Only append a suffix if the suffix we're going to add isn't already
1162 # there, and if either we've been asked to ensure the specific suffix
1163 # is present or there's no suffix on it at all.
1164 if suf and fname[-len(suf):] != suf and \
1165 (ensure_suffix or not splitext(fname)[1]):
1172 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
1173 # ASPN: Python Cookbook: Remove duplicates from a sequence
1174 # (Also in the printed Python Cookbook.)
1177 """Return a list of the elements in s, but without duplicates.
1179 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1180 unique("abcabc") some permutation of ["a", "b", "c"], and
1181 unique(([1, 2], [2, 3], [1, 2])) some permutation of
1184 For best speed, all sequence elements should be hashable. Then
1185 unique() will usually work in linear time.
1187 If not possible, the sequence elements should enjoy a total
1188 ordering, and if list(s).sort() doesn't raise TypeError it's
1189 assumed that they do enjoy a total ordering. Then unique() will
1190 usually work in O(N*log2(N)) time.
1192 If that's not possible either, the sequence elements must support
1193 equality-testing. Then unique() will usually work in quadratic
1201 # Try using a dict first, as that's the fastest and will usually
1202 # work. If it doesn't work, it will usually fail quickly, so it
1203 # usually doesn't cost much to *try* it. It requires that all the
1204 # sequence elements be hashable, and support equality comparison.
1210 pass # move on to the next method
1215 # We can't hash all the elements. Second fastest is to sort,
1216 # which brings the equal elements together; then duplicates are
1217 # easy to weed out in a single pass.
1218 # NOTE: Python's list.sort() was designed to be efficient in the
1219 # presence of many duplicate elements. This isn't true of all
1220 # sort functions in all languages or libraries, so this approach
1221 # is more effective in Python than it may be elsewhere.
1226 pass # move on to the next method
1233 t[lasti] = last = t[i]
1239 # Brute force is all that's left.
1248 # From Alex Martelli,
1249 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
1250 # ASPN: Python Cookbook: Remove duplicates from a sequence
1251 # First comment, dated 2001/10/13.
1252 # (Also in the printed Python Cookbook.)
1254 def uniquer(seq, idfun=None):
1256 def idfun(x): return x
1260 marker = idfun(item)
1261 # in old Python versions:
1262 # if seen.has_key(marker)
1264 if marker in seen: continue
1269 # A more efficient implementation of Alex's uniquer(), this avoids the
1270 # idfun() argument and function-call overhead by assuming that all
1271 # items in the sequence are hashable.
1273 def uniquer_hashables(seq):
1277 #if not item in seen:
1278 if item not in seen:
1285 # Much of the logic here was originally based on recipe 4.9 from the
1286 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1289 def __init__(self, fileobj):
1290 self.fileobj = fileobj
1295 line = self.fileobj.readline()
1298 if line[-2:] == '\\\n':
1299 result.append(line[:-2])
1303 return ''.join(result)
1305 def readlines(self):
1308 line = self.readline()
1316 class UniqueList(UserList):
1317 def __init__(self, seq = []):
1318 UserList.__init__(self, seq)
1320 def __make_unique(self):
1322 self.data = uniquer_hashables(self.data)
1324 def __lt__(self, other):
1325 self.__make_unique()
1326 return UserList.__lt__(self, other)
1327 def __le__(self, other):
1328 self.__make_unique()
1329 return UserList.__le__(self, other)
1330 def __eq__(self, other):
1331 self.__make_unique()
1332 return UserList.__eq__(self, other)
1333 def __ne__(self, other):
1334 self.__make_unique()
1335 return UserList.__ne__(self, other)
1336 def __gt__(self, other):
1337 self.__make_unique()
1338 return UserList.__gt__(self, other)
1339 def __ge__(self, other):
1340 self.__make_unique()
1341 return UserList.__ge__(self, other)
1342 def __cmp__(self, other):
1343 self.__make_unique()
1344 return UserList.__cmp__(self, other)
1346 self.__make_unique()
1347 return UserList.__len__(self)
1348 def __getitem__(self, i):
1349 self.__make_unique()
1350 return UserList.__getitem__(self, i)
1351 def __setitem__(self, i, item):
1352 UserList.__setitem__(self, i, item)
1354 def __getslice__(self, i, j):
1355 self.__make_unique()
1356 return UserList.__getslice__(self, i, j)
1357 def __setslice__(self, i, j, other):
1358 UserList.__setslice__(self, i, j, other)
1360 def __add__(self, other):
1361 result = UserList.__add__(self, other)
1362 result.unique = False
1364 def __radd__(self, other):
1365 result = UserList.__radd__(self, other)
1366 result.unique = False
1368 def __iadd__(self, other):
1369 result = UserList.__iadd__(self, other)
1370 result.unique = False
1372 def __mul__(self, other):
1373 result = UserList.__mul__(self, other)
1374 result.unique = False
1376 def __rmul__(self, other):
1377 result = UserList.__rmul__(self, other)
1378 result.unique = False
1380 def __imul__(self, other):
1381 result = UserList.__imul__(self, other)
1382 result.unique = False
1384 def append(self, item):
1385 UserList.append(self, item)
1387 def insert(self, i):
1388 UserList.insert(self, i)
1390 def count(self, item):
1391 self.__make_unique()
1392 return UserList.count(self, item)
1393 def index(self, item):
1394 self.__make_unique()
1395 return UserList.index(self, item)
1397 self.__make_unique()
1398 UserList.reverse(self)
1399 def sort(self, *args, **kwds):
1400 self.__make_unique()
1401 return UserList.sort(self, *args, **kwds)
1402 def extend(self, other):
1403 UserList.extend(self, other)
1410 A proxy class that wraps a file object, flushing after every write,
1411 and delegating everything else to the wrapped object.
1413 def __init__(self, file):
1415 def write(self, arg):
1417 self.file.write(arg)
1420 # Stdout might be connected to a pipe that has been closed
1421 # by now. The most likely reason for the pipe being closed
1422 # is that the user has press ctrl-c. It this is the case,
1423 # then SCons is currently shutdown. We therefore ignore
1424 # IOError's here so that SCons can continue and shutdown
1425 # properly so that the .sconsign is correctly written
1426 # before SCons exits.
1428 def __getattr__(self, attr):
1429 return getattr(self.file, attr)
1431 def make_path_relative(path):
1432 """ makes an absolute path name to a relative pathname.
1434 if os.path.isabs(path):
1435 drive_s,path = os.path.splitdrive(path)
1439 path=re.compile("/*(.*)").findall(path)[0]
1443 assert( not os.path.isabs( path ) ), path
1448 # The original idea for AddMethod() and RenameFunction() come from the
1449 # following post to the ActiveState Python Cookbook:
1451 # ASPN: Python Cookbook : Install bound methods in an instance
1452 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613
1454 # That code was a little fragile, though, so the following changes
1455 # have been wrung on it:
1457 # * Switched the installmethod() "object" and "function" arguments,
1458 # so the order reflects that the left-hand side is the thing being
1459 # "assigned to" and the right-hand side is the value being assigned.
1461 # * Changed explicit type-checking to the "try: klass = object.__class__"
1462 # block in installmethod() below so that it still works with the
1463 # old-style classes that SCons uses.
1465 # * Replaced the by-hand creation of methods and functions with use of
1466 # the "new" module, as alluded to in Alex Martelli's response to the
1467 # following Cookbook post:
1469 # ASPN: Python Cookbook : Dynamically added methods to a class
1470 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
1472 def AddMethod(object, function, name = None):
1474 Adds either a bound method to an instance or an unbound method to
1475 a class. If name is ommited the name of the specified function
1481 AddMethod(f, A, "add")
1484 AddMethod(lambda self, i: self.l[i], a, "listIndex")
1485 print a.listIndex(5)
1490 name = function.func_name
1492 function = RenameFunction(function, name)
1495 klass = object.__class__
1496 except AttributeError:
1497 # "object" is really a class, so it gets an unbound method.
1498 object.__dict__[name] = new.instancemethod(function, None, object)
1500 # "object" is really an instance, so it gets a bound method.
1501 object.__dict__[name] = new.instancemethod(function, object, klass)
1503 def RenameFunction(function, name):
1505 Returns a function identical to the specified function, but with
1510 # Compatibility for Python 1.5 and 2.1. Can be removed in favor of
1511 # passing function.func_defaults directly to new.function() once
1512 # we base on Python 2.2 or later.
1513 func_defaults = function.func_defaults
1514 if func_defaults is None:
1517 return new.function(function.func_code,
1518 function.func_globals,
1524 def MD5signature(s):
1527 def MD5filesignature(fname, chunksize=65536):
1528 f = open(fname, "rb")
1538 if hasattr(hashlib, 'md5'):
1540 def MD5signature(s):
1543 return m.hexdigest()
1545 def MD5filesignature(fname, chunksize=65536):
1547 f = open(fname, "rb")
1549 blck = f.read(chunksize)
1554 return m.hexdigest()
1556 def MD5collect(signatures):
1558 Collects a list of signatures into an aggregate signature.
1560 signatures - a list of signatures
1561 returns - the aggregate signature
1563 if len(signatures) == 1:
1564 return signatures[0]
1566 return MD5signature(', '.join(signatures))
1570 # Wrap the intern() function so it doesn't throw exceptions if ineligible
1571 # arguments are passed. The intern() function was moved into the sys module in
1576 from sys import intern
1578 def silent_intern(x):
1580 Perform intern() on the passed argument and return the result.
1581 If the input is ineligible (e.g. a unicode string) the original argument is
1582 returned and no exception is thrown.
1591 # From Dinu C. Gherman,
1592 # Python Cookbook, second edition, recipe 6.17, p. 277.
1594 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
1595 # ASPN: Python Cookbook: Null Object Design Pattern
1598 #class Null(object):
1600 """ Null objects always and reliably "do nothing." """
1601 def __new__(cls, *args, **kwargs):
1602 if not '_inst' in vars(cls):
1603 cls._inst = type.__new__(cls, *args, **kwargs)
1605 def __init__(self, *args, **kwargs):
1607 def __call__(self, *args, **kwargs):
1610 return "Null(0x%08X)" % id(self)
1611 def __nonzero__(self):
1613 def __getattr__(self, name):
1615 def __setattr__(self, name, value):
1617 def __delattr__(self, name):
1620 class NullSeq(Null):
1625 def __getitem__(self, i):
1627 def __delitem__(self, i):
1629 def __setitem__(self, i, v):
1637 # indent-tabs-mode:nil
1639 # vim: set expandtab tabstop=4 shiftwidth=4: