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__"
41 from collections import UserDict, UserList, UserString
43 # Don't "from types import ..." these because we need to get at the
44 # types module later to look for UnicodeType.
46 InstanceType = types.InstanceType
51 except NameError: UnicodeType = None
52 else: UnicodeType = unicode
54 def dictify(keys, values, result={}):
55 for k, v in zip(keys, values):
60 if _altsep is None and sys.platform == 'win32':
61 # My ActivePython 2.0.1 doesn't set os.altsep! What gives?
64 def rightmost_separator(path, sep):
65 return max(path.rfind(sep), path.rfind(_altsep))
67 def rightmost_separator(path, sep):
68 return path.rfind(sep)
70 # First two from the Python Cookbook, just for completeness.
71 # (Yeah, yeah, YAGNI...)
72 def containsAny(str, set):
73 """Check whether sequence str contains ANY of the items in set."""
78 def containsAll(str, set):
79 """Check whether sequence str contains ALL of the items in set."""
81 if c not in str: return 0
84 def containsOnly(str, set):
85 """Check whether sequence str contains ONLY items in set."""
87 if c not in set: return 0
91 "Same as os.path.splitext() but faster."
92 sep = rightmost_separator(path, os.sep)
94 # An ext is only real if it has at least one non-digit char
95 if dot > sep and not containsOnly(path[dot:], "0123456789."):
96 return path[:dot],path[dot:]
102 Make the drive letter (if any) upper case.
103 This is useful because Windows is inconsitent on the case
104 of the drive letter, which can cause inconsistencies when
105 calculating command signatures.
107 drive, rest = os.path.splitdrive(path)
109 path = drive.upper() + rest
112 class NodeList(UserList):
113 """This class is almost exactly like a regular list of Nodes
114 (actually it can hold any object), with one important difference.
115 If you try to get an attribute from this list, it will return that
116 attribute from every item in the list. For example:
118 >>> someList = NodeList([ ' foo ', ' bar ' ])
122 def __nonzero__(self):
123 return len(self.data) != 0
126 return ' '.join(map(str, self.data))
129 return iter(self.data)
131 def __call__(self, *args, **kwargs):
132 result = [x(*args, **kwargs) for x in self.data]
133 return self.__class__(result)
135 def __getattr__(self, name):
136 result = [getattr(x, name) for x in self.data]
137 return self.__class__(result)
140 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
142 def get_environment_var(varstr):
143 """Given a string, first determine if it looks like a reference
144 to a single environment variable, like "$FOO" or "${FOO}".
145 If so, return that variable with no decorations ("FOO").
146 If not, return None."""
147 mo=_get_env_var.match(to_String(varstr))
159 self.__call__ = self.print_it
161 def print_it(self, text, append_newline=1):
162 if append_newline: text = text + '\n'
164 sys.stdout.write(unicode(text))
166 # Stdout might be connected to a pipe that has been closed
167 # by now. The most likely reason for the pipe being closed
168 # is that the user has press ctrl-c. It this is the case,
169 # then SCons is currently shutdown. We therefore ignore
170 # IOError's here so that SCons can continue and shutdown
171 # properly so that the .sconsign is correctly written
172 # before SCons exits.
175 def dont_print(self, text, append_newline=1):
178 def set_mode(self, mode):
180 self.__call__ = self.print_it
182 self.__call__ = self.dont_print
184 def render_tree(root, child_func, prune=0, margin=[0], visited={}):
186 Render a tree of nodes into an ASCII tree view.
187 root - the root node of the tree
188 child_func - the function called to get the children of a node
189 prune - don't visit the same node twice
190 margin - the format of the left margin to use for children of root.
191 1 results in a pipe, and 0 results in no pipe.
192 visited - a dictionary of visited nodes in the current branch if not prune,
193 or in the whole tree if prune.
198 children = child_func(root)
200 for pipe in margin[:-1]:
202 retval = retval + "| "
204 retval = retval + " "
207 return retval + "+-[" + rname + "]\n"
209 retval = retval + "+-" + rname + "\n"
211 visited = copy.copy(visited)
214 for i in range(len(children)):
215 margin.append(i<len(children)-1)
216 retval = retval + render_tree(children[i], child_func, prune, margin, visited
222 IDX = lambda N: N and 1 or 0
224 def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
226 Print a tree of nodes. This is like render_tree, except it prints
227 lines directly instead of creating a string representation in memory,
228 so that huge trees can be printed.
230 root - the root node of the tree
231 child_func - the function called to get the children of a node
232 prune - don't visit the same node twice
233 showtags - print status information to the left of each node line
234 margin - the format of the left margin to use for children of root.
235 1 results in a pipe, and 0 results in no pipe.
236 visited - a dictionary of visited nodes in the current branch if not prune,
237 or in the whole tree if prune.
245 legend = (' E = exists\n' +
246 ' R = exists in repository only\n' +
247 ' b = implicit builder\n' +
248 ' B = explicit builder\n' +
249 ' S = side effect\n' +
251 ' A = always build\n' +
256 sys.stdout.write(unicode(legend))
259 tags.append(' E'[IDX(root.exists())])
260 tags.append(' R'[IDX(root.rexists() and not root.exists())])
261 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
262 [0,2][IDX(root.has_builder())]])
263 tags.append(' S'[IDX(root.side_effect)])
264 tags.append(' P'[IDX(root.precious)])
265 tags.append(' A'[IDX(root.always_build)])
266 tags.append(' C'[IDX(root.is_up_to_date())])
267 tags.append(' N'[IDX(root.noclean)])
268 tags.append(' H'[IDX(root.nocache)])
276 margins = list(map(MMM, margin[:-1]))
278 children = child_func(root)
280 if prune and rname in visited and children:
281 sys.stdout.write(''.join(tags + margins + ['+-[', rname, ']']) + u'\n')
284 sys.stdout.write(''.join(tags + margins + ['+-', rname]) + u'\n')
291 for C in children[:-1]:
292 print_tree(C, child_func, prune, idx, margin, visited)
294 print_tree(children[-1], child_func, prune, idx, margin, visited)
299 # Functions for deciding if things are like various types, mainly to
300 # handle UserDict, UserList and UserString like their underlying types.
302 # Yes, all of this manual testing breaks polymorphism, and the real
303 # Pythonic way to do all of this would be to just try it and handle the
304 # exception, but handling the exception when it's not the right type is
311 # An older Python version without new-style classes.
313 # The actual implementations here have been selected after timings
314 # coded up in in bench/is_types.py (from the SCons source tree,
315 # see the scons-src distribution), mostly against Python 1.5.2.
316 # Key results from those timings:
318 # -- Storing the type of the object in a variable (t = type(obj))
319 # slows down the case where it's a native type and the first
320 # comparison will match, but nicely speeds up the case where
321 # it's a different native type. Since that's going to be
322 # common, it's a good tradeoff.
324 # -- The data show that calling isinstance() on an object that's
325 # a native type (dict, list or string) is expensive enough
326 # that checking up front for whether the object is of type
327 # InstanceType is a pretty big win, even though it does slow
328 # down the case where it really *is* an object instance a
332 return t is DictType or \
333 (t is InstanceType and isinstance(obj, UserDict))
337 return t is ListType \
338 or (t is InstanceType and isinstance(obj, UserList))
340 def is_Sequence(obj):
342 return t is ListType \
344 or (t is InstanceType and isinstance(obj, UserList))
348 return t is TupleType
350 if UnicodeType is not None:
353 return t is StringType \
354 or t is UnicodeType \
355 or (t is InstanceType and isinstance(obj, UserString))
359 return t is StringType \
360 or (t is InstanceType and isinstance(obj, UserString))
363 return is_String(obj) or not is_Sequence(obj)
365 def flatten(obj, result=None):
366 """Flatten a sequence to a non-nested list.
368 Flatten() converts either a single scalar or a nested sequence
369 to a non-nested list. Note that flatten() considers strings
370 to be scalars instead of sequences like Python would.
380 flatten_sequence(item, result)
383 def flatten_sequence(sequence, result=None):
384 """Flatten a sequence to a non-nested list.
386 Same as flatten(), but it does not handle the single scalar
387 case. This is slightly more efficient when one knows that
388 the sequence to flatten can not be a scalar.
392 for item in sequence:
396 flatten_sequence(item, result)
400 # Generic convert-to-string functions that abstract away whether or
401 # not the Python we're executing has Unicode support. The wrapper
402 # to_String_for_signature() will use a for_signature() method if the
403 # specified object has one.
405 if UnicodeType is not None:
407 if isinstance(s, UserString):
418 def to_String_for_signature(obj):
420 f = obj.for_signature
421 except AttributeError:
422 return to_String_for_subst(obj)
426 def to_String_for_subst(s):
428 return ' '.join( map(to_String_for_subst, s) )
430 return to_String( s )
433 # A modern Python version with new-style classes, so we can just use
436 # We are using the following trick to speed-up these
437 # functions. Default arguments are used to take a snapshot of the
438 # the global functions and constants used by these functions. This
439 # transforms accesses to global variable into local variables
440 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL).
442 DictTypes = (dict, UserDict)
443 ListTypes = (list, UserList)
444 SequenceTypes = (list, tuple, UserList)
446 # Empirically, Python versions with new-style classes all have
449 # Note that profiling data shows a speed-up when comparing
450 # explicitely with str and unicode instead of simply comparing
451 # with basestring. (at least on Python 2.5.1)
452 StringTypes = (str, unicode, UserString)
454 # Empirically, it is faster to check explicitely for str and
455 # unicode than for basestring.
456 BaseStringTypes = (str, unicode)
458 def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
459 return isinstance(obj, DictTypes)
461 def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
462 return isinstance(obj, ListTypes)
464 def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
465 return isinstance(obj, SequenceTypes)
467 def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
468 return isinstance(obj, tuple)
470 def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
471 return isinstance(obj, StringTypes)
473 def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
474 # Profiling shows that there is an impressive speed-up of 2x
475 # when explicitely checking for strings instead of just not
476 # sequence when the argument (i.e. obj) is already a string.
477 # But, if obj is a not string than it is twice as fast to
478 # check only for 'not sequence'. The following code therefore
479 # assumes that the obj argument is a string must of the time.
480 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
482 def do_flatten(sequence, result, isinstance=isinstance,
483 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
484 for item in sequence:
485 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
488 do_flatten(item, result)
490 def flatten(obj, isinstance=isinstance, StringTypes=StringTypes,
491 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
492 """Flatten a sequence to a non-nested list.
494 Flatten() converts either a single scalar or a nested sequence
495 to a non-nested list. Note that flatten() considers strings
496 to be scalars instead of sequences like Python would.
498 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes):
502 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
505 do_flatten(item, result)
508 def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes,
509 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
510 """Flatten a sequence to a non-nested list.
512 Same as flatten(), but it does not handle the single scalar
513 case. This is slightly more efficient when one knows that
514 the sequence to flatten can not be a scalar.
517 for item in sequence:
518 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
521 do_flatten(item, result)
526 # Generic convert-to-string functions that abstract away whether or
527 # not the Python we're executing has Unicode support. The wrapper
528 # to_String_for_signature() will use a for_signature() method if the
529 # specified object has one.
532 isinstance=isinstance, str=str,
533 UserString=UserString, BaseStringTypes=BaseStringTypes):
534 if isinstance(s,BaseStringTypes):
535 # Early out when already a string!
537 elif isinstance(s, UserString):
538 # s.data can only be either a unicode or a regular
539 # string. Please see the UserString initializer.
544 def to_String_for_subst(s,
545 isinstance=isinstance, str=str, to_String=to_String,
546 BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes,
547 UserString=UserString):
549 # Note that the test cases are sorted by order of probability.
550 if isinstance(s, BaseStringTypes):
552 elif isinstance(s, SequenceTypes):
555 l.append(to_String_for_subst(e))
557 elif isinstance(s, UserString):
558 # s.data can only be either a unicode or a regular
559 # string. Please see the UserString initializer.
564 def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst,
565 AttributeError=AttributeError):
567 f = obj.for_signature
568 except AttributeError:
569 return to_String_for_subst(obj)
575 # The SCons "semi-deep" copy.
577 # This makes separate copies of lists (including UserList objects)
578 # dictionaries (including UserDict objects) and tuples, but just copies
579 # references to anything else it finds.
581 # A special case is any object that has a __semi_deepcopy__() method,
582 # which we invoke to create the copy, which is used by the BuilderDict
583 # class because of its extra initialization argument.
585 # The dispatch table approach used here is a direct rip-off from the
586 # normal Python copy module.
588 _semi_deepcopy_dispatch = d = {}
590 def _semi_deepcopy_dict(x):
592 for key, val in x.items():
593 # The regular Python copy.deepcopy() also deepcopies the key,
596 # copy[semi_deepcopy(key)] = semi_deepcopy(val)
598 # Doesn't seem like we need to, but we'll comment it just in case.
599 copy[key] = semi_deepcopy(val)
601 d[dict] = _semi_deepcopy_dict
603 def _semi_deepcopy_list(x):
604 return list(map(semi_deepcopy, x))
605 d[list] = _semi_deepcopy_list
607 def _semi_deepcopy_tuple(x):
608 return tuple(map(semi_deepcopy, x))
609 d[tuple] = _semi_deepcopy_tuple
611 def _semi_deepcopy_inst(x):
612 if hasattr(x, '__semi_deepcopy__'):
613 return x.__semi_deepcopy__()
614 elif isinstance(x, UserDict):
615 return x.__class__(_semi_deepcopy_dict(x))
616 elif isinstance(x, UserList):
617 return x.__class__(_semi_deepcopy_list(x))
620 d[types.InstanceType] = _semi_deepcopy_inst
622 def semi_deepcopy(x):
623 copier = _semi_deepcopy_dispatch.get(type(x))
632 """A simple generic Proxy class, forwarding all calls to
633 subject. So, for the benefit of the python newbie, what does
634 this really mean? Well, it means that you can take an object, let's
635 call it 'objA', and wrap it in this Proxy class, with a statement
638 proxyObj = Proxy(objA),
640 Then, if in the future, you do something like this
644 since Proxy does not have a 'var1' attribute (but presumably objA does),
645 the request actually is equivalent to saying
649 Inherit from this class to create a Proxy."""
651 def __init__(self, subject):
652 """Wrap an object as a Proxy object"""
653 self.__subject = subject
655 def __getattr__(self, name):
656 """Retrieve an attribute from the wrapped object. If the named
657 attribute doesn't exist, AttributeError is raised"""
658 return getattr(self.__subject, name)
661 """Retrieve the entire wrapped object"""
662 return self.__subject
664 def __cmp__(self, other):
665 if issubclass(other.__class__, self.__subject.__class__):
666 return cmp(self.__subject, other)
667 return cmp(self.__dict__, other.__dict__)
669 # attempt to load the windows registry module:
677 RegOpenKeyEx = _winreg.OpenKeyEx
678 RegEnumKey = _winreg.EnumKey
679 RegEnumValue = _winreg.EnumValue
680 RegQueryValueEx = _winreg.QueryValueEx
681 RegError = _winreg.error
690 RegOpenKeyEx = win32api.RegOpenKeyEx
691 RegEnumKey = win32api.RegEnumKey
692 RegEnumValue = win32api.RegEnumValue
693 RegQueryValueEx = win32api.RegQueryValueEx
694 RegError = win32api.error
697 class _NoError(Exception):
702 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
703 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
704 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
705 HKEY_USERS = hkey_mod.HKEY_USERS
707 def RegGetValue(root, key):
708 """This utility function returns a value in the registry
709 without having to open the key first. Only available on
710 Windows platforms with a version of Python that can read the
711 registry. Returns the same thing as
712 SCons.Util.RegQueryValueEx, except you just specify the entire
713 path to the value, and don't have to bother opening the key
717 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
718 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
719 out = SCons.Util.RegQueryValueEx(k,
723 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
724 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
726 # I would use os.path.split here, but it's not a filesystem
728 p = key.rfind('\\') + 1
729 keyp = key[:p-1] # -1 to omit trailing slash
731 k = RegOpenKeyEx(root, keyp)
732 return RegQueryValueEx(k,val)
737 # Make sure we have a definition of WindowsError so we can
738 # run platform-independent tests of Windows functionality on
739 # platforms other than Windows. (WindowsError is, in fact, an
740 # OSError subclass on Windows.)
741 class WindowsError(OSError):
744 builtins.WindowsError = WindowsError
748 HKEY_CLASSES_ROOT = None
749 HKEY_LOCAL_MACHINE = None
750 HKEY_CURRENT_USER = None
753 def RegGetValue(root, key):
756 def RegOpenKeyEx(root, key):
759 if sys.platform == 'win32':
761 def WhereIs(file, path=None, pathext=None, reject=[]):
764 path = os.environ['PATH']
768 path = path.split(os.pathsep)
771 pathext = os.environ['PATHEXT']
773 pathext = '.COM;.EXE;.BAT;.CMD'
774 if is_String(pathext):
775 pathext = pathext.split(os.pathsep)
777 if ext.lower() == file[-len(ext):].lower():
780 if not is_List(reject) and not is_Tuple(reject):
783 f = os.path.join(dir, file)
786 if os.path.isfile(fext):
790 return os.path.normpath(fext)
794 elif os.name == 'os2':
796 def WhereIs(file, path=None, pathext=None, reject=[]):
799 path = os.environ['PATH']
803 path = path.split(os.pathsep)
805 pathext = ['.exe', '.cmd']
807 if ext.lower() == file[-len(ext):].lower():
810 if not is_List(reject) and not is_Tuple(reject):
813 f = os.path.join(dir, file)
816 if os.path.isfile(fext):
820 return os.path.normpath(fext)
826 def WhereIs(file, path=None, pathext=None, reject=[]):
830 path = os.environ['PATH']
834 path = path.split(os.pathsep)
835 if not is_List(reject) and not is_Tuple(reject):
838 f = os.path.join(d, file)
839 if os.path.isfile(f):
843 # os.stat() raises OSError, not IOError if the file
844 # doesn't exist, so in this case we let IOError get
845 # raised so as to not mask possibly serious disk or
848 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
852 return os.path.normpath(f)
856 def PrependPath(oldpath, newpath, sep = os.pathsep,
857 delete_existing=1, canonicalize=None):
858 """This prepends newpath elements to the given oldpath. Will only
859 add any particular path once (leaving the first one it encounters
860 and ignoring the rest, to preserve path order), and will
861 os.path.normpath and os.path.normcase all paths to help assure
862 this. This can also handle the case where the given old path
863 variable is a list instead of a string, in which case a list will
864 be returned instead of a string.
867 Old Path: "/foo/bar:/foo"
868 New Path: "/biz/boom:/foo"
869 Result: "/biz/boom:/foo:/foo/bar"
871 If delete_existing is 0, then adding a path that exists will
872 not move it to the beginning; it will stay where it is in the
875 If canonicalize is not None, it is applied to each element of
882 if not is_List(orig) and not is_Tuple(orig):
883 paths = paths.split(sep)
886 if is_String(newpath):
887 newpaths = newpath.split(sep)
888 elif not is_List(newpath) and not is_Tuple(newpath):
889 newpaths = [ newpath ] # might be a Dir
894 newpaths=list(map(canonicalize, newpaths))
896 if not delete_existing:
897 # First uniquify the old paths, making sure to
898 # preserve the first instance (in Unix/Linux,
899 # the first one wins), and remembering them in normpaths.
900 # Then insert the new paths at the head of the list
901 # if they're not already in the normpaths list.
907 normpath = os.path.normpath(os.path.normcase(path))
908 if normpath not in normpaths:
910 normpaths.append(normpath)
911 newpaths.reverse() # since we're inserting at the head
912 for path in newpaths:
915 normpath = os.path.normpath(os.path.normcase(path))
916 if normpath not in normpaths:
917 result.insert(0, path)
918 normpaths.append(normpath)
922 newpaths = newpaths + paths # prepend new paths
926 # now we add them only if they are unique
927 for path in newpaths:
928 normpath = os.path.normpath(os.path.normcase(path))
929 if path and not normpath in normpaths:
931 normpaths.append(normpath)
936 return sep.join(paths)
938 def AppendPath(oldpath, newpath, sep = os.pathsep,
939 delete_existing=1, canonicalize=None):
940 """This appends new path elements to the given old path. Will
941 only add any particular path once (leaving the last one it
942 encounters and ignoring the rest, to preserve path order), and
943 will os.path.normpath and os.path.normcase all paths to help
944 assure this. This can also handle the case where the given old
945 path variable is a list instead of a string, in which case a list
946 will be returned instead of a string.
949 Old Path: "/foo/bar:/foo"
950 New Path: "/biz/boom:/foo"
951 Result: "/foo/bar:/biz/boom:/foo"
953 If delete_existing is 0, then adding a path that exists
954 will not move it to the end; it will stay where it is in the list.
956 If canonicalize is not None, it is applied to each element of
963 if not is_List(orig) and not is_Tuple(orig):
964 paths = paths.split(sep)
967 if is_String(newpath):
968 newpaths = newpath.split(sep)
969 elif not is_List(newpath) and not is_Tuple(newpath):
970 newpaths = [ newpath ] # might be a Dir
975 newpaths=list(map(canonicalize, newpaths))
977 if not delete_existing:
978 # add old paths to result, then
979 # add new paths if not already present
980 # (I thought about using a dict for normpaths for speed,
981 # but it's not clear hashing the strings would be faster
982 # than linear searching these typically short lists.)
989 normpaths.append(os.path.normpath(os.path.normcase(path)))
990 for path in newpaths:
993 normpath = os.path.normpath(os.path.normcase(path))
994 if normpath not in normpaths:
996 normpaths.append(normpath)
999 # start w/ new paths, add old ones if not present,
1001 newpaths = paths + newpaths # append new paths
1006 # now we add them only if they are unique
1007 for path in newpaths:
1008 normpath = os.path.normpath(os.path.normcase(path))
1009 if path and not normpath in normpaths:
1011 normpaths.append(normpath)
1017 return sep.join(paths)
1019 if sys.platform == 'cygwin':
1020 def get_native_path(path):
1021 """Transforms an absolute path into a native path for the system. In
1022 Cygwin, this converts from a Cygwin path to a Windows one."""
1023 return os.popen('cygpath -w ' + path).read().replace('\n', '')
1025 def get_native_path(path):
1026 """Transforms an absolute path into a native path for the system.
1027 Non-Cygwin version, just leave the path alone."""
1030 display = DisplayEngine()
1033 if is_List(arg) or is_Tuple(arg):
1035 elif is_String(arg):
1040 class CLVar(UserList):
1041 """A class for command-line construction variables.
1043 This is a list that uses Split() to split an initial string along
1044 white-space arguments, and similarly to split any strings that get
1045 added. This allows us to Do the Right Thing with Append() and
1046 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1047 arg2') regardless of whether a user adds a list or a string to a
1048 command-line construction variable.
1050 def __init__(self, seq = []):
1051 UserList.__init__(self, Split(seq))
1052 def __add__(self, other):
1053 return UserList.__add__(self, CLVar(other))
1054 def __radd__(self, other):
1055 return UserList.__radd__(self, CLVar(other))
1056 def __coerce__(self, other):
1057 return (self, CLVar(other))
1059 return ' '.join(self.data)
1061 # A dictionary that preserves the order in which items are added.
1062 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
1063 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
1064 # Including fixes/enhancements from the follow-on discussions.
1065 class OrderedDict(UserDict):
1066 def __init__(self, dict = None):
1068 UserDict.__init__(self, dict)
1070 def __delitem__(self, key):
1071 UserDict.__delitem__(self, key)
1072 self._keys.remove(key)
1074 def __setitem__(self, key, item):
1075 UserDict.__setitem__(self, key, item)
1076 if key not in self._keys: self._keys.append(key)
1079 UserDict.clear(self)
1083 dict = OrderedDict()
1088 return list(zip(self._keys, self.values()))
1091 return self._keys[:]
1095 key = self._keys[-1]
1097 raise KeyError('dictionary is empty')
1104 def setdefault(self, key, failobj = None):
1105 UserDict.setdefault(self, key, failobj)
1106 if key not in self._keys: self._keys.append(key)
1108 def update(self, dict):
1109 for (key, val) in dict.items():
1110 self.__setitem__(key, val)
1113 return list(map(self.get, self._keys))
1115 class Selector(OrderedDict):
1116 """A callable ordered dictionary that maps file suffixes to
1117 dictionary values. We preserve the order in which items are added
1118 so that get_suffix() calls always return the first suffix added."""
1119 def __call__(self, env, source, ext=None):
1122 ext = source[0].suffix
1128 # Try to perform Environment substitution on the keys of
1129 # the dictionary before giving up.
1131 for (k,v) in self.items():
1135 # We only raise an error when variables point
1136 # to the same suffix. If one suffix is literal
1137 # and a variable suffix contains this literal,
1138 # the literal wins and we don't raise an error.
1139 raise KeyError, (s_dict[s_k][0], k, s_k)
1142 return s_dict[ext][1]
1150 if sys.platform == 'cygwin':
1151 # On Cygwin, os.path.normcase() lies, so just report back the
1152 # fact that the underlying Windows OS is case-insensitive.
1153 def case_sensitive_suffixes(s1, s2):
1156 def case_sensitive_suffixes(s1, s2):
1157 return (os.path.normcase(s1) != os.path.normcase(s2))
1159 def adjustixes(fname, pre, suf, ensure_suffix=False):
1161 path, fn = os.path.split(os.path.normpath(fname))
1162 if fn[:len(pre)] != pre:
1163 fname = os.path.join(path, pre + fn)
1164 # Only append a suffix if the suffix we're going to add isn't already
1165 # there, and if either we've been asked to ensure the specific suffix
1166 # is present or there's no suffix on it at all.
1167 if suf and fname[-len(suf):] != suf and \
1168 (ensure_suffix or not splitext(fname)[1]):
1175 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
1176 # ASPN: Python Cookbook: Remove duplicates from a sequence
1177 # (Also in the printed Python Cookbook.)
1180 """Return a list of the elements in s, but without duplicates.
1182 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1183 unique("abcabc") some permutation of ["a", "b", "c"], and
1184 unique(([1, 2], [2, 3], [1, 2])) some permutation of
1187 For best speed, all sequence elements should be hashable. Then
1188 unique() will usually work in linear time.
1190 If not possible, the sequence elements should enjoy a total
1191 ordering, and if list(s).sort() doesn't raise TypeError it's
1192 assumed that they do enjoy a total ordering. Then unique() will
1193 usually work in O(N*log2(N)) time.
1195 If that's not possible either, the sequence elements must support
1196 equality-testing. Then unique() will usually work in quadratic
1204 # Try using a dict first, as that's the fastest and will usually
1205 # work. If it doesn't work, it will usually fail quickly, so it
1206 # usually doesn't cost much to *try* it. It requires that all the
1207 # sequence elements be hashable, and support equality comparison.
1213 pass # move on to the next method
1218 # We can't hash all the elements. Second fastest is to sort,
1219 # which brings the equal elements together; then duplicates are
1220 # easy to weed out in a single pass.
1221 # NOTE: Python's list.sort() was designed to be efficient in the
1222 # presence of many duplicate elements. This isn't true of all
1223 # sort functions in all languages or libraries, so this approach
1224 # is more effective in Python than it may be elsewhere.
1228 pass # move on to the next method
1235 t[lasti] = last = t[i]
1241 # Brute force is all that's left.
1250 # From Alex Martelli,
1251 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
1252 # ASPN: Python Cookbook: Remove duplicates from a sequence
1253 # First comment, dated 2001/10/13.
1254 # (Also in the printed Python Cookbook.)
1256 def uniquer(seq, idfun=None):
1258 def idfun(x): return x
1262 marker = idfun(item)
1263 # in old Python versions:
1264 # if seen.has_key(marker)
1266 if marker in seen: continue
1271 # A more efficient implementation of Alex's uniquer(), this avoids the
1272 # idfun() argument and function-call overhead by assuming that all
1273 # items in the sequence are hashable.
1275 def uniquer_hashables(seq):
1279 #if not item in seen:
1280 if item not in seen:
1287 # Much of the logic here was originally based on recipe 4.9 from the
1288 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1291 def __init__(self, fileobj):
1292 self.fileobj = fileobj
1297 line = self.fileobj.readline()
1300 if line[-2:] == '\\\n':
1301 result.append(line[:-2])
1305 return ''.join(result)
1307 def readlines(self):
1310 line = self.readline()
1318 class UniqueList(UserList):
1319 def __init__(self, seq = []):
1320 UserList.__init__(self, seq)
1322 def __make_unique(self):
1324 self.data = uniquer_hashables(self.data)
1326 def __lt__(self, other):
1327 self.__make_unique()
1328 return UserList.__lt__(self, other)
1329 def __le__(self, other):
1330 self.__make_unique()
1331 return UserList.__le__(self, other)
1332 def __eq__(self, other):
1333 self.__make_unique()
1334 return UserList.__eq__(self, other)
1335 def __ne__(self, other):
1336 self.__make_unique()
1337 return UserList.__ne__(self, other)
1338 def __gt__(self, other):
1339 self.__make_unique()
1340 return UserList.__gt__(self, other)
1341 def __ge__(self, other):
1342 self.__make_unique()
1343 return UserList.__ge__(self, other)
1344 def __cmp__(self, other):
1345 self.__make_unique()
1346 return UserList.__cmp__(self, other)
1348 self.__make_unique()
1349 return UserList.__len__(self)
1350 def __getitem__(self, i):
1351 self.__make_unique()
1352 return UserList.__getitem__(self, i)
1353 def __setitem__(self, i, item):
1354 UserList.__setitem__(self, i, item)
1356 def __getslice__(self, i, j):
1357 self.__make_unique()
1358 return UserList.__getslice__(self, i, j)
1359 def __setslice__(self, i, j, other):
1360 UserList.__setslice__(self, i, j, other)
1362 def __add__(self, other):
1363 result = UserList.__add__(self, other)
1364 result.unique = False
1366 def __radd__(self, other):
1367 result = UserList.__radd__(self, other)
1368 result.unique = False
1370 def __iadd__(self, other):
1371 result = UserList.__iadd__(self, other)
1372 result.unique = False
1374 def __mul__(self, other):
1375 result = UserList.__mul__(self, other)
1376 result.unique = False
1378 def __rmul__(self, other):
1379 result = UserList.__rmul__(self, other)
1380 result.unique = False
1382 def __imul__(self, other):
1383 result = UserList.__imul__(self, other)
1384 result.unique = False
1386 def append(self, item):
1387 UserList.append(self, item)
1389 def insert(self, i):
1390 UserList.insert(self, i)
1392 def count(self, item):
1393 self.__make_unique()
1394 return UserList.count(self, item)
1395 def index(self, item):
1396 self.__make_unique()
1397 return UserList.index(self, item)
1399 self.__make_unique()
1400 UserList.reverse(self)
1401 def sort(self, *args, **kwds):
1402 self.__make_unique()
1403 return UserList.sort(self, *args, **kwds)
1404 def extend(self, other):
1405 UserList.extend(self, other)
1412 A proxy class that wraps a file object, flushing after every write,
1413 and delegating everything else to the wrapped object.
1415 def __init__(self, file):
1417 def write(self, arg):
1419 self.file.write(arg)
1422 # Stdout might be connected to a pipe that has been closed
1423 # by now. The most likely reason for the pipe being closed
1424 # is that the user has press ctrl-c. It this is the case,
1425 # then SCons is currently shutdown. We therefore ignore
1426 # IOError's here so that SCons can continue and shutdown
1427 # properly so that the .sconsign is correctly written
1428 # before SCons exits.
1430 def __getattr__(self, attr):
1431 return getattr(self.file, attr)
1433 def make_path_relative(path):
1434 """ makes an absolute path name to a relative pathname.
1436 if os.path.isabs(path):
1437 drive_s,path = os.path.splitdrive(path)
1441 path=re.compile("/*(.*)").findall(path)[0]
1445 assert( not os.path.isabs( path ) ), path
1450 # The original idea for AddMethod() and RenameFunction() come from the
1451 # following post to the ActiveState Python Cookbook:
1453 # ASPN: Python Cookbook : Install bound methods in an instance
1454 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613
1456 # That code was a little fragile, though, so the following changes
1457 # have been wrung on it:
1459 # * Switched the installmethod() "object" and "function" arguments,
1460 # so the order reflects that the left-hand side is the thing being
1461 # "assigned to" and the right-hand side is the value being assigned.
1463 # * Changed explicit type-checking to the "try: klass = object.__class__"
1464 # block in installmethod() below so that it still works with the
1465 # old-style classes that SCons uses.
1467 # * Replaced the by-hand creation of methods and functions with use of
1468 # the "new" module, as alluded to in Alex Martelli's response to the
1469 # following Cookbook post:
1471 # ASPN: Python Cookbook : Dynamically added methods to a class
1472 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
1474 def AddMethod(object, function, name = None):
1476 Adds either a bound method to an instance or an unbound method to
1477 a class. If name is ommited the name of the specified function
1483 AddMethod(f, A, "add")
1486 AddMethod(lambda self, i: self.l[i], a, "listIndex")
1487 print a.listIndex(5)
1492 name = function.func_name
1494 function = RenameFunction(function, name)
1497 klass = object.__class__
1498 except AttributeError:
1499 # "object" is really a class, so it gets an unbound method.
1500 object.__dict__[name] = new.instancemethod(function, None, object)
1502 # "object" is really an instance, so it gets a bound method.
1503 object.__dict__[name] = new.instancemethod(function, object, klass)
1505 def RenameFunction(function, name):
1507 Returns a function identical to the specified function, but with
1512 # Compatibility for Python 1.5 and 2.1. Can be removed in favor of
1513 # passing function.func_defaults directly to new.function() once
1514 # we base on Python 2.2 or later.
1515 func_defaults = function.func_defaults
1516 if func_defaults is None:
1519 return new.function(function.func_code,
1520 function.func_globals,
1526 def MD5signature(s):
1529 def MD5filesignature(fname, chunksize=65536):
1530 f = open(fname, "rb")
1540 if hasattr(hashlib, 'md5'):
1542 def MD5signature(s):
1545 return m.hexdigest()
1547 def MD5filesignature(fname, chunksize=65536):
1549 f = open(fname, "rb")
1551 blck = f.read(chunksize)
1556 return m.hexdigest()
1558 def MD5collect(signatures):
1560 Collects a list of signatures into an aggregate signature.
1562 signatures - a list of signatures
1563 returns - the aggregate signature
1565 if len(signatures) == 1:
1566 return signatures[0]
1568 return MD5signature(', '.join(signatures))
1572 def silent_intern(x):
1574 Perform sys.intern() on the passed argument and return the result.
1575 If the input is ineligible (e.g. a unicode string) the original argument is
1576 returned and no exception is thrown.
1579 return sys.intern(x)
1585 # From Dinu C. Gherman,
1586 # Python Cookbook, second edition, recipe 6.17, p. 277.
1588 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
1589 # ASPN: Python Cookbook: Null Object Design Pattern
1592 #class Null(object):
1594 """ Null objects always and reliably "do nothing." """
1595 def __new__(cls, *args, **kwargs):
1596 if not '_inst' in vars(cls):
1597 cls._inst = type.__new__(cls, *args, **kwargs)
1599 def __init__(self, *args, **kwargs):
1601 def __call__(self, *args, **kwargs):
1604 return "Null(0x%08X)" % id(self)
1605 def __nonzero__(self):
1607 def __getattr__(self, name):
1609 def __setattr__(self, name, value):
1611 def __delattr__(self, name):
1614 class NullSeq(Null):
1619 def __getitem__(self, i):
1621 def __delitem__(self, i):
1623 def __setitem__(self, i, v):
1631 # indent-tabs-mode:nil
1633 # vim: set expandtab tabstop=4 shiftwidth=4: