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 collections import UserDict, UserList, UserString
41 # Don't "from types import ..." these because we need to get at the
42 # types module later to look for UnicodeType.
44 InstanceType = types.InstanceType
49 except NameError: UnicodeType = None
50 else: UnicodeType = unicode
52 def dictify(keys, values, result={}):
53 for k, v in zip(keys, values):
58 if _altsep is None and sys.platform == 'win32':
59 # My ActivePython 2.0.1 doesn't set os.altsep! What gives?
62 def rightmost_separator(path, sep):
63 return max(path.rfind(sep), path.rfind(_altsep))
65 def rightmost_separator(path, sep):
66 return path.rfind(sep)
68 # First two from the Python Cookbook, just for completeness.
69 # (Yeah, yeah, YAGNI...)
70 def containsAny(str, set):
71 """Check whether sequence str contains ANY of the items in set."""
76 def containsAll(str, set):
77 """Check whether sequence str contains ALL of the items in set."""
79 if c not in str: return 0
82 def containsOnly(str, set):
83 """Check whether sequence str contains ONLY items in set."""
85 if c not in set: return 0
89 "Same as os.path.splitext() but faster."
90 sep = rightmost_separator(path, os.sep)
92 # An ext is only real if it has at least one non-digit char
93 if dot > sep and not containsOnly(path[dot:], "0123456789."):
94 return path[:dot],path[dot:]
100 Make the drive letter (if any) upper case.
101 This is useful because Windows is inconsitent on the case
102 of the drive letter, which can cause inconsistencies when
103 calculating command signatures.
105 drive, rest = os.path.splitdrive(path)
107 path = drive.upper() + rest
110 class NodeList(UserList):
111 """This class is almost exactly like a regular list of Nodes
112 (actually it can hold any object), with one important difference.
113 If you try to get an attribute from this list, it will return that
114 attribute from every item in the list. For example:
116 >>> someList = NodeList([ ' foo ', ' bar ' ])
120 def __nonzero__(self):
121 return len(self.data) != 0
124 return ' '.join(map(str, self.data))
127 return iter(self.data)
129 def __call__(self, *args, **kwargs):
130 result = [x(*args, **kwargs) for x in self.data]
131 return self.__class__(result)
133 def __getattr__(self, name):
134 result = [getattr(x, name) for x in self.data]
135 return self.__class__(result)
138 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
140 def get_environment_var(varstr):
141 """Given a string, first determine if it looks like a reference
142 to a single environment variable, like "$FOO" or "${FOO}".
143 If so, return that variable with no decorations ("FOO").
144 If not, return None."""
145 mo=_get_env_var.match(to_String(varstr))
157 self.__call__ = self.print_it
159 def print_it(self, text, append_newline=1):
160 if append_newline: text = text + '\n'
162 sys.stdout.write(unicode(text))
164 # Stdout might be connected to a pipe that has been closed
165 # by now. The most likely reason for the pipe being closed
166 # is that the user has press ctrl-c. It this is the case,
167 # then SCons is currently shutdown. We therefore ignore
168 # IOError's here so that SCons can continue and shutdown
169 # properly so that the .sconsign is correctly written
170 # before SCons exits.
173 def dont_print(self, text, append_newline=1):
176 def set_mode(self, mode):
178 self.__call__ = self.print_it
180 self.__call__ = self.dont_print
182 def render_tree(root, child_func, prune=0, margin=[0], visited={}):
184 Render a tree of nodes into an ASCII tree view.
185 root - the root node of the tree
186 child_func - the function called to get the children of a node
187 prune - don't visit the same node twice
188 margin - the format of the left margin to use for children of root.
189 1 results in a pipe, and 0 results in no pipe.
190 visited - a dictionary of visited nodes in the current branch if not prune,
191 or in the whole tree if prune.
196 children = child_func(root)
198 for pipe in margin[:-1]:
200 retval = retval + "| "
202 retval = retval + " "
205 return retval + "+-[" + rname + "]\n"
207 retval = retval + "+-" + rname + "\n"
209 visited = copy.copy(visited)
212 for i in range(len(children)):
213 margin.append(i<len(children)-1)
214 retval = retval + render_tree(children[i], child_func, prune, margin, visited
220 IDX = lambda N: N and 1 or 0
222 def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
224 Print a tree of nodes. This is like render_tree, except it prints
225 lines directly instead of creating a string representation in memory,
226 so that huge trees can be printed.
228 root - the root node of the tree
229 child_func - the function called to get the children of a node
230 prune - don't visit the same node twice
231 showtags - print status information to the left of each node line
232 margin - the format of the left margin to use for children of root.
233 1 results in a pipe, and 0 results in no pipe.
234 visited - a dictionary of visited nodes in the current branch if not prune,
235 or in the whole tree if prune.
243 legend = (' E = exists\n' +
244 ' R = exists in repository only\n' +
245 ' b = implicit builder\n' +
246 ' B = explicit builder\n' +
247 ' S = side effect\n' +
249 ' A = always build\n' +
254 sys.stdout.write(unicode(legend))
257 tags.append(' E'[IDX(root.exists())])
258 tags.append(' R'[IDX(root.rexists() and not root.exists())])
259 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
260 [0,2][IDX(root.has_builder())]])
261 tags.append(' S'[IDX(root.side_effect)])
262 tags.append(' P'[IDX(root.precious)])
263 tags.append(' A'[IDX(root.always_build)])
264 tags.append(' C'[IDX(root.is_up_to_date())])
265 tags.append(' N'[IDX(root.noclean)])
266 tags.append(' H'[IDX(root.nocache)])
274 margins = list(map(MMM, margin[:-1]))
276 children = child_func(root)
278 if prune and rname in visited and children:
279 sys.stdout.write(''.join(tags + margins + ['+-[', rname, ']']) + u'\n')
282 sys.stdout.write(''.join(tags + margins + ['+-', rname]) + u'\n')
289 for C in children[:-1]:
290 print_tree(C, child_func, prune, idx, margin, visited)
292 print_tree(children[-1], child_func, prune, idx, margin, visited)
297 # Functions for deciding if things are like various types, mainly to
298 # handle UserDict, UserList and UserString like their underlying types.
300 # Yes, all of this manual testing breaks polymorphism, and the real
301 # Pythonic way to do all of this would be to just try it and handle the
302 # exception, but handling the exception when it's not the right type is
309 # An older Python version without new-style classes.
311 # The actual implementations here have been selected after timings
312 # coded up in in bench/is_types.py (from the SCons source tree,
313 # see the scons-src distribution), mostly against Python 1.5.2.
314 # Key results from those timings:
316 # -- Storing the type of the object in a variable (t = type(obj))
317 # slows down the case where it's a native type and the first
318 # comparison will match, but nicely speeds up the case where
319 # it's a different native type. Since that's going to be
320 # common, it's a good tradeoff.
322 # -- The data show that calling isinstance() on an object that's
323 # a native type (dict, list or string) is expensive enough
324 # that checking up front for whether the object is of type
325 # InstanceType is a pretty big win, even though it does slow
326 # down the case where it really *is* an object instance a
330 return t is DictType or \
331 (t is InstanceType and isinstance(obj, UserDict))
335 return t is ListType \
336 or (t is InstanceType and isinstance(obj, UserList))
338 def is_Sequence(obj):
340 return t is ListType \
342 or (t is InstanceType and isinstance(obj, UserList))
346 return t is TupleType
348 if UnicodeType is not None:
351 return t is StringType \
352 or t is UnicodeType \
353 or (t is InstanceType and isinstance(obj, UserString))
357 return t is StringType \
358 or (t is InstanceType and isinstance(obj, UserString))
361 return is_String(obj) or not is_Sequence(obj)
363 def flatten(obj, result=None):
364 """Flatten a sequence to a non-nested list.
366 Flatten() converts either a single scalar or a nested sequence
367 to a non-nested list. Note that flatten() considers strings
368 to be scalars instead of sequences like Python would.
378 flatten_sequence(item, result)
381 def flatten_sequence(sequence, result=None):
382 """Flatten a sequence to a non-nested list.
384 Same as flatten(), but it does not handle the single scalar
385 case. This is slightly more efficient when one knows that
386 the sequence to flatten can not be a scalar.
390 for item in sequence:
394 flatten_sequence(item, result)
398 # Generic convert-to-string functions that abstract away whether or
399 # not the Python we're executing has Unicode support. The wrapper
400 # to_String_for_signature() will use a for_signature() method if the
401 # specified object has one.
403 if UnicodeType is not None:
405 if isinstance(s, UserString):
416 def to_String_for_signature(obj):
418 f = obj.for_signature
419 except AttributeError:
420 return to_String_for_subst(obj)
424 def to_String_for_subst(s):
426 return ' '.join( map(to_String_for_subst, s) )
428 return to_String( s )
431 # A modern Python version with new-style classes, so we can just use
434 # We are using the following trick to speed-up these
435 # functions. Default arguments are used to take a snapshot of the
436 # the global functions and constants used by these functions. This
437 # transforms accesses to global variable into local variables
438 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL).
440 DictTypes = (dict, UserDict)
441 ListTypes = (list, UserList)
442 SequenceTypes = (list, tuple, UserList)
444 # Empirically, Python versions with new-style classes all have
447 # Note that profiling data shows a speed-up when comparing
448 # explicitely with str and unicode instead of simply comparing
449 # with basestring. (at least on Python 2.5.1)
450 StringTypes = (str, unicode, UserString)
452 # Empirically, it is faster to check explicitely for str and
453 # unicode than for basestring.
454 BaseStringTypes = (str, unicode)
456 def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
457 return isinstance(obj, DictTypes)
459 def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
460 return isinstance(obj, ListTypes)
462 def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
463 return isinstance(obj, SequenceTypes)
465 def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
466 return isinstance(obj, tuple)
468 def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
469 return isinstance(obj, StringTypes)
471 def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
472 # Profiling shows that there is an impressive speed-up of 2x
473 # when explicitely checking for strings instead of just not
474 # sequence when the argument (i.e. obj) is already a string.
475 # But, if obj is a not string than it is twice as fast to
476 # check only for 'not sequence'. The following code therefore
477 # assumes that the obj argument is a string must of the time.
478 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
480 def do_flatten(sequence, result, isinstance=isinstance,
481 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
482 for item in sequence:
483 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
486 do_flatten(item, result)
488 def flatten(obj, isinstance=isinstance, StringTypes=StringTypes,
489 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
490 """Flatten a sequence to a non-nested list.
492 Flatten() converts either a single scalar or a nested sequence
493 to a non-nested list. Note that flatten() considers strings
494 to be scalars instead of sequences like Python would.
496 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes):
500 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
503 do_flatten(item, result)
506 def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes,
507 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
508 """Flatten a sequence to a non-nested list.
510 Same as flatten(), but it does not handle the single scalar
511 case. This is slightly more efficient when one knows that
512 the sequence to flatten can not be a scalar.
515 for item in sequence:
516 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
519 do_flatten(item, result)
524 # Generic convert-to-string functions that abstract away whether or
525 # not the Python we're executing has Unicode support. The wrapper
526 # to_String_for_signature() will use a for_signature() method if the
527 # specified object has one.
530 isinstance=isinstance, str=str,
531 UserString=UserString, BaseStringTypes=BaseStringTypes):
532 if isinstance(s,BaseStringTypes):
533 # Early out when already a string!
535 elif isinstance(s, UserString):
536 # s.data can only be either a unicode or a regular
537 # string. Please see the UserString initializer.
542 def to_String_for_subst(s,
543 isinstance=isinstance, str=str, to_String=to_String,
544 BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes,
545 UserString=UserString):
547 # Note that the test cases are sorted by order of probability.
548 if isinstance(s, BaseStringTypes):
550 elif isinstance(s, SequenceTypes):
553 l.append(to_String_for_subst(e))
555 elif isinstance(s, UserString):
556 # s.data can only be either a unicode or a regular
557 # string. Please see the UserString initializer.
562 def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst,
563 AttributeError=AttributeError):
565 f = obj.for_signature
566 except AttributeError:
567 return to_String_for_subst(obj)
573 # The SCons "semi-deep" copy.
575 # This makes separate copies of lists (including UserList objects)
576 # dictionaries (including UserDict objects) and tuples, but just copies
577 # references to anything else it finds.
579 # A special case is any object that has a __semi_deepcopy__() method,
580 # which we invoke to create the copy, which is used by the BuilderDict
581 # class because of its extra initialization argument.
583 # The dispatch table approach used here is a direct rip-off from the
584 # normal Python copy module.
586 _semi_deepcopy_dispatch = d = {}
588 def _semi_deepcopy_dict(x):
590 for key, val in x.items():
591 # The regular Python copy.deepcopy() also deepcopies the key,
594 # copy[semi_deepcopy(key)] = semi_deepcopy(val)
596 # Doesn't seem like we need to, but we'll comment it just in case.
597 copy[key] = semi_deepcopy(val)
599 d[dict] = _semi_deepcopy_dict
601 def _semi_deepcopy_list(x):
602 return list(map(semi_deepcopy, x))
603 d[list] = _semi_deepcopy_list
605 def _semi_deepcopy_tuple(x):
606 return tuple(map(semi_deepcopy, x))
607 d[tuple] = _semi_deepcopy_tuple
609 def _semi_deepcopy_inst(x):
610 if hasattr(x, '__semi_deepcopy__'):
611 return x.__semi_deepcopy__()
612 elif isinstance(x, UserDict):
613 return x.__class__(_semi_deepcopy_dict(x))
614 elif isinstance(x, UserList):
615 return x.__class__(_semi_deepcopy_list(x))
618 d[types.InstanceType] = _semi_deepcopy_inst
620 def semi_deepcopy(x):
621 copier = _semi_deepcopy_dispatch.get(type(x))
630 """A simple generic Proxy class, forwarding all calls to
631 subject. So, for the benefit of the python newbie, what does
632 this really mean? Well, it means that you can take an object, let's
633 call it 'objA', and wrap it in this Proxy class, with a statement
636 proxyObj = Proxy(objA),
638 Then, if in the future, you do something like this
642 since Proxy does not have a 'var1' attribute (but presumably objA does),
643 the request actually is equivalent to saying
647 Inherit from this class to create a Proxy."""
649 def __init__(self, subject):
650 """Wrap an object as a Proxy object"""
651 self.__subject = subject
653 def __getattr__(self, name):
654 """Retrieve an attribute from the wrapped object. If the named
655 attribute doesn't exist, AttributeError is raised"""
656 return getattr(self.__subject, name)
659 """Retrieve the entire wrapped object"""
660 return self.__subject
662 def __cmp__(self, other):
663 if issubclass(other.__class__, self.__subject.__class__):
664 return cmp(self.__subject, other)
665 return cmp(self.__dict__, other.__dict__)
667 # attempt to load the windows registry module:
675 RegOpenKeyEx = _winreg.OpenKeyEx
676 RegEnumKey = _winreg.EnumKey
677 RegEnumValue = _winreg.EnumValue
678 RegQueryValueEx = _winreg.QueryValueEx
679 RegError = _winreg.error
688 RegOpenKeyEx = win32api.RegOpenKeyEx
689 RegEnumKey = win32api.RegEnumKey
690 RegEnumValue = win32api.RegEnumValue
691 RegQueryValueEx = win32api.RegQueryValueEx
692 RegError = win32api.error
695 class _NoError(Exception):
700 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
701 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
702 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
703 HKEY_USERS = hkey_mod.HKEY_USERS
705 def RegGetValue(root, key):
706 """This utility function returns a value in the registry
707 without having to open the key first. Only available on
708 Windows platforms with a version of Python that can read the
709 registry. Returns the same thing as
710 SCons.Util.RegQueryValueEx, except you just specify the entire
711 path to the value, and don't have to bother opening the key
715 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
716 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
717 out = SCons.Util.RegQueryValueEx(k,
721 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
722 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
724 # I would use os.path.split here, but it's not a filesystem
726 p = key.rfind('\\') + 1
727 keyp = key[:p-1] # -1 to omit trailing slash
729 k = RegOpenKeyEx(root, keyp)
730 return RegQueryValueEx(k,val)
735 # Make sure we have a definition of WindowsError so we can
736 # run platform-independent tests of Windows functionality on
737 # platforms other than Windows. (WindowsError is, in fact, an
738 # OSError subclass on Windows.)
739 class WindowsError(OSError):
742 __builtin__.WindowsError = WindowsError
746 HKEY_CLASSES_ROOT = None
747 HKEY_LOCAL_MACHINE = None
748 HKEY_CURRENT_USER = None
751 def RegGetValue(root, key):
754 def RegOpenKeyEx(root, key):
757 if sys.platform == 'win32':
759 def WhereIs(file, path=None, pathext=None, reject=[]):
762 path = os.environ['PATH']
766 path = path.split(os.pathsep)
769 pathext = os.environ['PATHEXT']
771 pathext = '.COM;.EXE;.BAT;.CMD'
772 if is_String(pathext):
773 pathext = pathext.split(os.pathsep)
775 if ext.lower() == file[-len(ext):].lower():
778 if not is_List(reject) and not is_Tuple(reject):
781 f = os.path.join(dir, file)
784 if os.path.isfile(fext):
788 return os.path.normpath(fext)
792 elif os.name == 'os2':
794 def WhereIs(file, path=None, pathext=None, reject=[]):
797 path = os.environ['PATH']
801 path = path.split(os.pathsep)
803 pathext = ['.exe', '.cmd']
805 if ext.lower() == file[-len(ext):].lower():
808 if not is_List(reject) and not is_Tuple(reject):
811 f = os.path.join(dir, file)
814 if os.path.isfile(fext):
818 return os.path.normpath(fext)
824 def WhereIs(file, path=None, pathext=None, reject=[]):
828 path = os.environ['PATH']
832 path = path.split(os.pathsep)
833 if not is_List(reject) and not is_Tuple(reject):
836 f = os.path.join(d, file)
837 if os.path.isfile(f):
841 # os.stat() raises OSError, not IOError if the file
842 # doesn't exist, so in this case we let IOError get
843 # raised so as to not mask possibly serious disk or
846 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
850 return os.path.normpath(f)
854 def PrependPath(oldpath, newpath, sep = os.pathsep,
855 delete_existing=1, canonicalize=None):
856 """This prepends newpath elements to the given oldpath. Will only
857 add any particular path once (leaving the first one it encounters
858 and ignoring the rest, to preserve path order), and will
859 os.path.normpath and os.path.normcase all paths to help assure
860 this. This can also handle the case where the given old path
861 variable is a list instead of a string, in which case a list will
862 be returned instead of a string.
865 Old Path: "/foo/bar:/foo"
866 New Path: "/biz/boom:/foo"
867 Result: "/biz/boom:/foo:/foo/bar"
869 If delete_existing is 0, then adding a path that exists will
870 not move it to the beginning; it will stay where it is in the
873 If canonicalize is not None, it is applied to each element of
880 if not is_List(orig) and not is_Tuple(orig):
881 paths = paths.split(sep)
884 if is_String(newpath):
885 newpaths = newpath.split(sep)
886 elif not is_List(newpath) and not is_Tuple(newpath):
887 newpaths = [ newpath ] # might be a Dir
892 newpaths=list(map(canonicalize, newpaths))
894 if not delete_existing:
895 # First uniquify the old paths, making sure to
896 # preserve the first instance (in Unix/Linux,
897 # the first one wins), and remembering them in normpaths.
898 # Then insert the new paths at the head of the list
899 # if they're not already in the normpaths list.
905 normpath = os.path.normpath(os.path.normcase(path))
906 if normpath not in normpaths:
908 normpaths.append(normpath)
909 newpaths.reverse() # since we're inserting at the head
910 for path in newpaths:
913 normpath = os.path.normpath(os.path.normcase(path))
914 if normpath not in normpaths:
915 result.insert(0, path)
916 normpaths.append(normpath)
920 newpaths = newpaths + paths # prepend new paths
924 # now we add them only if they are unique
925 for path in newpaths:
926 normpath = os.path.normpath(os.path.normcase(path))
927 if path and not normpath in normpaths:
929 normpaths.append(normpath)
934 return sep.join(paths)
936 def AppendPath(oldpath, newpath, sep = os.pathsep,
937 delete_existing=1, canonicalize=None):
938 """This appends new path elements to the given old path. Will
939 only add any particular path once (leaving the last one it
940 encounters and ignoring the rest, to preserve path order), and
941 will os.path.normpath and os.path.normcase all paths to help
942 assure this. This can also handle the case where the given old
943 path variable is a list instead of a string, in which case a list
944 will be returned instead of a string.
947 Old Path: "/foo/bar:/foo"
948 New Path: "/biz/boom:/foo"
949 Result: "/foo/bar:/biz/boom:/foo"
951 If delete_existing is 0, then adding a path that exists
952 will not move it to the end; it will stay where it is in the list.
954 If canonicalize is not None, it is applied to each element of
961 if not is_List(orig) and not is_Tuple(orig):
962 paths = paths.split(sep)
965 if is_String(newpath):
966 newpaths = newpath.split(sep)
967 elif not is_List(newpath) and not is_Tuple(newpath):
968 newpaths = [ newpath ] # might be a Dir
973 newpaths=list(map(canonicalize, newpaths))
975 if not delete_existing:
976 # add old paths to result, then
977 # add new paths if not already present
978 # (I thought about using a dict for normpaths for speed,
979 # but it's not clear hashing the strings would be faster
980 # than linear searching these typically short lists.)
987 normpaths.append(os.path.normpath(os.path.normcase(path)))
988 for path in newpaths:
991 normpath = os.path.normpath(os.path.normcase(path))
992 if normpath not in normpaths:
994 normpaths.append(normpath)
997 # start w/ new paths, add old ones if not present,
999 newpaths = paths + newpaths # append new paths
1004 # now we add them only if they are unique
1005 for path in newpaths:
1006 normpath = os.path.normpath(os.path.normcase(path))
1007 if path and not normpath in normpaths:
1009 normpaths.append(normpath)
1015 return sep.join(paths)
1017 if sys.platform == 'cygwin':
1018 def get_native_path(path):
1019 """Transforms an absolute path into a native path for the system. In
1020 Cygwin, this converts from a Cygwin path to a Windows one."""
1021 return os.popen('cygpath -w ' + path).read().replace('\n', '')
1023 def get_native_path(path):
1024 """Transforms an absolute path into a native path for the system.
1025 Non-Cygwin version, just leave the path alone."""
1028 display = DisplayEngine()
1031 if is_List(arg) or is_Tuple(arg):
1033 elif is_String(arg):
1038 class CLVar(UserList):
1039 """A class for command-line construction variables.
1041 This is a list that uses Split() to split an initial string along
1042 white-space arguments, and similarly to split any strings that get
1043 added. This allows us to Do the Right Thing with Append() and
1044 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1045 arg2') regardless of whether a user adds a list or a string to a
1046 command-line construction variable.
1048 def __init__(self, seq = []):
1049 UserList.__init__(self, Split(seq))
1050 def __add__(self, other):
1051 return UserList.__add__(self, CLVar(other))
1052 def __radd__(self, other):
1053 return UserList.__radd__(self, CLVar(other))
1054 def __coerce__(self, other):
1055 return (self, CLVar(other))
1057 return ' '.join(self.data)
1059 # A dictionary that preserves the order in which items are added.
1060 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
1061 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
1062 # Including fixes/enhancements from the follow-on discussions.
1063 class OrderedDict(UserDict):
1064 def __init__(self, dict = None):
1066 UserDict.__init__(self, dict)
1068 def __delitem__(self, key):
1069 UserDict.__delitem__(self, key)
1070 self._keys.remove(key)
1072 def __setitem__(self, key, item):
1073 UserDict.__setitem__(self, key, item)
1074 if key not in self._keys: self._keys.append(key)
1077 UserDict.clear(self)
1081 dict = OrderedDict()
1086 return list(zip(self._keys, self.values()))
1089 return self._keys[:]
1093 key = self._keys[-1]
1095 raise KeyError('dictionary is empty')
1102 def setdefault(self, key, failobj = None):
1103 UserDict.setdefault(self, key, failobj)
1104 if key not in self._keys: self._keys.append(key)
1106 def update(self, dict):
1107 for (key, val) in dict.items():
1108 self.__setitem__(key, val)
1111 return list(map(self.get, self._keys))
1113 class Selector(OrderedDict):
1114 """A callable ordered dictionary that maps file suffixes to
1115 dictionary values. We preserve the order in which items are added
1116 so that get_suffix() calls always return the first suffix added."""
1117 def __call__(self, env, source, ext=None):
1120 ext = source[0].suffix
1126 # Try to perform Environment substitution on the keys of
1127 # the dictionary before giving up.
1129 for (k,v) in self.items():
1133 # We only raise an error when variables point
1134 # to the same suffix. If one suffix is literal
1135 # and a variable suffix contains this literal,
1136 # the literal wins and we don't raise an error.
1137 raise KeyError, (s_dict[s_k][0], k, s_k)
1140 return s_dict[ext][1]
1148 if sys.platform == 'cygwin':
1149 # On Cygwin, os.path.normcase() lies, so just report back the
1150 # fact that the underlying Windows OS is case-insensitive.
1151 def case_sensitive_suffixes(s1, s2):
1154 def case_sensitive_suffixes(s1, s2):
1155 return (os.path.normcase(s1) != os.path.normcase(s2))
1157 def adjustixes(fname, pre, suf, ensure_suffix=False):
1159 path, fn = os.path.split(os.path.normpath(fname))
1160 if fn[:len(pre)] != pre:
1161 fname = os.path.join(path, pre + fn)
1162 # Only append a suffix if the suffix we're going to add isn't already
1163 # there, and if either we've been asked to ensure the specific suffix
1164 # is present or there's no suffix on it at all.
1165 if suf and fname[-len(suf):] != suf and \
1166 (ensure_suffix or not splitext(fname)[1]):
1173 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
1174 # ASPN: Python Cookbook: Remove duplicates from a sequence
1175 # (Also in the printed Python Cookbook.)
1178 """Return a list of the elements in s, but without duplicates.
1180 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1181 unique("abcabc") some permutation of ["a", "b", "c"], and
1182 unique(([1, 2], [2, 3], [1, 2])) some permutation of
1185 For best speed, all sequence elements should be hashable. Then
1186 unique() will usually work in linear time.
1188 If not possible, the sequence elements should enjoy a total
1189 ordering, and if list(s).sort() doesn't raise TypeError it's
1190 assumed that they do enjoy a total ordering. Then unique() will
1191 usually work in O(N*log2(N)) time.
1193 If that's not possible either, the sequence elements must support
1194 equality-testing. Then unique() will usually work in quadratic
1202 # Try using a dict first, as that's the fastest and will usually
1203 # work. If it doesn't work, it will usually fail quickly, so it
1204 # usually doesn't cost much to *try* it. It requires that all the
1205 # sequence elements be hashable, and support equality comparison.
1211 pass # move on to the next method
1216 # We can't hash all the elements. Second fastest is to sort,
1217 # which brings the equal elements together; then duplicates are
1218 # easy to weed out in a single pass.
1219 # NOTE: Python's list.sort() was designed to be efficient in the
1220 # presence of many duplicate elements. This isn't true of all
1221 # sort functions in all languages or libraries, so this approach
1222 # 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 def silent_intern(x):
1572 Perform sys.intern() on the passed argument and return the result.
1573 If the input is ineligible (e.g. a unicode string) the original argument is
1574 returned and no exception is thrown.
1577 return sys.intern(x)
1583 # From Dinu C. Gherman,
1584 # Python Cookbook, second edition, recipe 6.17, p. 277.
1586 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
1587 # ASPN: Python Cookbook: Null Object Design Pattern
1590 #class Null(object):
1592 """ Null objects always and reliably "do nothing." """
1593 def __new__(cls, *args, **kwargs):
1594 if not '_inst' in vars(cls):
1595 cls._inst = type.__new__(cls, *args, **kwargs)
1597 def __init__(self, *args, **kwargs):
1599 def __call__(self, *args, **kwargs):
1602 return "Null(0x%08X)" % id(self)
1603 def __nonzero__(self):
1605 def __getattr__(self, name):
1607 def __setattr__(self, name, value):
1609 def __delattr__(self, name):
1612 class NullSeq(Null):
1617 def __getitem__(self, i):
1619 def __delitem__(self, i):
1621 def __setitem__(self, i, v):
1629 # indent-tabs-mode:nil
1631 # vim: set expandtab tabstop=4 shiftwidth=4: