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.
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(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.
246 print ' R = exists in repository only'
247 print ' b = implicit builder'
248 print ' B = explicit builder'
249 print ' S = side effect'
250 print ' P = precious'
251 print ' A = always build'
253 print ' N = no clean'
254 print ' H = no cache'
258 tags.append(' E'[IDX(root.exists())])
259 tags.append(' R'[IDX(root.rexists() and not root.exists())])
260 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
261 [0,2][IDX(root.has_builder())]])
262 tags.append(' S'[IDX(root.side_effect)])
263 tags.append(' P'[IDX(root.precious)])
264 tags.append(' A'[IDX(root.always_build)])
265 tags.append(' C'[IDX(root.is_up_to_date())])
266 tags.append(' N'[IDX(root.noclean)])
267 tags.append(' H'[IDX(root.nocache)])
275 margins = list(map(MMM, margin[:-1]))
277 children = child_func(root)
279 if prune and rname in visited and children:
280 print ''.join(tags + margins + ['+-[', rname, ']'])
283 print ''.join(tags + margins + ['+-', rname])
290 for C in children[:-1]:
291 print_tree(C, child_func, prune, idx, margin, visited)
293 print_tree(children[-1], child_func, prune, idx, margin, visited)
298 # Functions for deciding if things are like various types, mainly to
299 # handle UserDict, UserList and UserString like their underlying types.
301 # Yes, all of this manual testing breaks polymorphism, and the real
302 # Pythonic way to do all of this would be to just try it and handle the
303 # exception, but handling the exception when it's not the right type is
310 # An older Python version without new-style classes.
312 # The actual implementations here have been selected after timings
313 # coded up in in bench/is_types.py (from the SCons source tree,
314 # see the scons-src distribution), mostly against Python 1.5.2.
315 # Key results from those timings:
317 # -- Storing the type of the object in a variable (t = type(obj))
318 # slows down the case where it's a native type and the first
319 # comparison will match, but nicely speeds up the case where
320 # it's a different native type. Since that's going to be
321 # common, it's a good tradeoff.
323 # -- The data show that calling isinstance() on an object that's
324 # a native type (dict, list or string) is expensive enough
325 # that checking up front for whether the object is of type
326 # InstanceType is a pretty big win, even though it does slow
327 # down the case where it really *is* an object instance a
331 return t is DictType or \
332 (t is InstanceType and isinstance(obj, UserDict))
336 return t is ListType \
337 or (t is InstanceType and isinstance(obj, UserList))
339 def is_Sequence(obj):
341 return t is ListType \
343 or (t is InstanceType and isinstance(obj, UserList))
347 return t is TupleType
349 if UnicodeType is not None:
352 return t is StringType \
353 or t is UnicodeType \
354 or (t is InstanceType and isinstance(obj, UserString))
358 return t is StringType \
359 or (t is InstanceType and isinstance(obj, UserString))
362 return is_String(obj) or not is_Sequence(obj)
364 def flatten(obj, result=None):
365 """Flatten a sequence to a non-nested list.
367 Flatten() converts either a single scalar or a nested sequence
368 to a non-nested list. Note that flatten() considers strings
369 to be scalars instead of sequences like Python would.
379 flatten_sequence(item, result)
382 def flatten_sequence(sequence, result=None):
383 """Flatten a sequence to a non-nested list.
385 Same as flatten(), but it does not handle the single scalar
386 case. This is slightly more efficient when one knows that
387 the sequence to flatten can not be a scalar.
391 for item in sequence:
395 flatten_sequence(item, result)
399 # Generic convert-to-string functions that abstract away whether or
400 # not the Python we're executing has Unicode support. The wrapper
401 # to_String_for_signature() will use a for_signature() method if the
402 # specified object has one.
404 if UnicodeType is not None:
406 if isinstance(s, UserString):
417 def to_String_for_signature(obj):
419 f = obj.for_signature
420 except AttributeError:
421 return to_String_for_subst(obj)
425 def to_String_for_subst(s):
427 return ' '.join( map(to_String_for_subst, s) )
429 return to_String( s )
432 # A modern Python version with new-style classes, so we can just use
435 # We are using the following trick to speed-up these
436 # functions. Default arguments are used to take a snapshot of the
437 # the global functions and constants used by these functions. This
438 # transforms accesses to global variable into local variables
439 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL).
441 DictTypes = (dict, UserDict)
442 ListTypes = (list, UserList)
443 SequenceTypes = (list, tuple, UserList)
445 # Empirically, Python versions with new-style classes all have
448 # Note that profiling data shows a speed-up when comparing
449 # explicitely with str and unicode instead of simply comparing
450 # with basestring. (at least on Python 2.5.1)
451 StringTypes = (str, unicode, UserString)
453 # Empirically, it is faster to check explicitely for str and
454 # unicode than for basestring.
455 BaseStringTypes = (str, unicode)
457 def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
458 return isinstance(obj, DictTypes)
460 def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
461 return isinstance(obj, ListTypes)
463 def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
464 return isinstance(obj, SequenceTypes)
466 def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
467 return isinstance(obj, tuple)
469 def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
470 return isinstance(obj, StringTypes)
472 def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
473 # Profiling shows that there is an impressive speed-up of 2x
474 # when explicitely checking for strings instead of just not
475 # sequence when the argument (i.e. obj) is already a string.
476 # But, if obj is a not string than it is twice as fast to
477 # check only for 'not sequence'. The following code therefore
478 # assumes that the obj argument is a string must of the time.
479 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
481 def do_flatten(sequence, result, isinstance=isinstance,
482 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
483 for item in sequence:
484 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
487 do_flatten(item, result)
489 def flatten(obj, isinstance=isinstance, StringTypes=StringTypes,
490 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
491 """Flatten a sequence to a non-nested list.
493 Flatten() converts either a single scalar or a nested sequence
494 to a non-nested list. Note that flatten() considers strings
495 to be scalars instead of sequences like Python would.
497 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes):
501 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
504 do_flatten(item, result)
507 def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes,
508 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
509 """Flatten a sequence to a non-nested list.
511 Same as flatten(), but it does not handle the single scalar
512 case. This is slightly more efficient when one knows that
513 the sequence to flatten can not be a scalar.
516 for item in sequence:
517 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
520 do_flatten(item, result)
525 # Generic convert-to-string functions that abstract away whether or
526 # not the Python we're executing has Unicode support. The wrapper
527 # to_String_for_signature() will use a for_signature() method if the
528 # specified object has one.
531 isinstance=isinstance, str=str,
532 UserString=UserString, BaseStringTypes=BaseStringTypes):
533 if isinstance(s,BaseStringTypes):
534 # Early out when already a string!
536 elif isinstance(s, UserString):
537 # s.data can only be either a unicode or a regular
538 # string. Please see the UserString initializer.
543 def to_String_for_subst(s,
544 isinstance=isinstance, str=str, to_String=to_String,
545 BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes,
546 UserString=UserString):
548 # Note that the test cases are sorted by order of probability.
549 if isinstance(s, BaseStringTypes):
551 elif isinstance(s, SequenceTypes):
554 l.append(to_String_for_subst(e))
556 elif isinstance(s, UserString):
557 # s.data can only be either a unicode or a regular
558 # string. Please see the UserString initializer.
563 def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst,
564 AttributeError=AttributeError):
566 f = obj.for_signature
567 except AttributeError:
568 return to_String_for_subst(obj)
574 # The SCons "semi-deep" copy.
576 # This makes separate copies of lists (including UserList objects)
577 # dictionaries (including UserDict objects) and tuples, but just copies
578 # references to anything else it finds.
580 # A special case is any object that has a __semi_deepcopy__() method,
581 # which we invoke to create the copy, which is used by the BuilderDict
582 # class because of its extra initialization argument.
584 # The dispatch table approach used here is a direct rip-off from the
585 # normal Python copy module.
587 _semi_deepcopy_dispatch = d = {}
589 def _semi_deepcopy_dict(x):
591 for key, val in x.items():
592 # The regular Python copy.deepcopy() also deepcopies the key,
595 # copy[semi_deepcopy(key)] = semi_deepcopy(val)
597 # Doesn't seem like we need to, but we'll comment it just in case.
598 copy[key] = semi_deepcopy(val)
600 d[dict] = _semi_deepcopy_dict
602 def _semi_deepcopy_list(x):
603 return list(map(semi_deepcopy, x))
604 d[list] = _semi_deepcopy_list
606 def _semi_deepcopy_tuple(x):
607 return tuple(map(semi_deepcopy, x))
608 d[tuple] = _semi_deepcopy_tuple
610 def _semi_deepcopy_inst(x):
611 if hasattr(x, '__semi_deepcopy__'):
612 return x.__semi_deepcopy__()
613 elif isinstance(x, UserDict):
614 return x.__class__(_semi_deepcopy_dict(x))
615 elif isinstance(x, UserList):
616 return x.__class__(_semi_deepcopy_list(x))
619 d[types.InstanceType] = _semi_deepcopy_inst
621 def semi_deepcopy(x):
622 copier = _semi_deepcopy_dispatch.get(type(x))
631 """A simple generic Proxy class, forwarding all calls to
632 subject. So, for the benefit of the python newbie, what does
633 this really mean? Well, it means that you can take an object, let's
634 call it 'objA', and wrap it in this Proxy class, with a statement
637 proxyObj = Proxy(objA),
639 Then, if in the future, you do something like this
643 since Proxy does not have a 'var1' attribute (but presumably objA does),
644 the request actually is equivalent to saying
648 Inherit from this class to create a Proxy."""
650 def __init__(self, subject):
651 """Wrap an object as a Proxy object"""
652 self.__subject = subject
654 def __getattr__(self, name):
655 """Retrieve an attribute from the wrapped object. If the named
656 attribute doesn't exist, AttributeError is raised"""
657 return getattr(self.__subject, name)
660 """Retrieve the entire wrapped object"""
661 return self.__subject
663 def __cmp__(self, other):
664 if issubclass(other.__class__, self.__subject.__class__):
665 return cmp(self.__subject, other)
666 return cmp(self.__dict__, other.__dict__)
668 # attempt to load the windows registry module:
676 RegOpenKeyEx = _winreg.OpenKeyEx
677 RegEnumKey = _winreg.EnumKey
678 RegEnumValue = _winreg.EnumValue
679 RegQueryValueEx = _winreg.QueryValueEx
680 RegError = _winreg.error
689 RegOpenKeyEx = win32api.RegOpenKeyEx
690 RegEnumKey = win32api.RegEnumKey
691 RegEnumValue = win32api.RegEnumValue
692 RegQueryValueEx = win32api.RegQueryValueEx
693 RegError = win32api.error
696 class _NoError(Exception):
701 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
702 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
703 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
704 HKEY_USERS = hkey_mod.HKEY_USERS
706 def RegGetValue(root, key):
707 """This utility function returns a value in the registry
708 without having to open the key first. Only available on
709 Windows platforms with a version of Python that can read the
710 registry. Returns the same thing as
711 SCons.Util.RegQueryValueEx, except you just specify the entire
712 path to the value, and don't have to bother opening the key
716 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
717 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
718 out = SCons.Util.RegQueryValueEx(k,
722 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
723 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
725 # I would use os.path.split here, but it's not a filesystem
727 p = key.rfind('\\') + 1
728 keyp = key[:p-1] # -1 to omit trailing slash
730 k = RegOpenKeyEx(root, keyp)
731 return RegQueryValueEx(k,val)
736 # Make sure we have a definition of WindowsError so we can
737 # run platform-independent tests of Windows functionality on
738 # platforms other than Windows. (WindowsError is, in fact, an
739 # OSError subclass on Windows.)
740 class WindowsError(OSError):
743 __builtin__.WindowsError = WindowsError
747 HKEY_CLASSES_ROOT = None
748 HKEY_LOCAL_MACHINE = None
749 HKEY_CURRENT_USER = None
752 def RegGetValue(root, key):
755 def RegOpenKeyEx(root, key):
758 if sys.platform == 'win32':
760 def WhereIs(file, path=None, pathext=None, reject=[]):
763 path = os.environ['PATH']
767 path = path.split(os.pathsep)
770 pathext = os.environ['PATHEXT']
772 pathext = '.COM;.EXE;.BAT;.CMD'
773 if is_String(pathext):
774 pathext = pathext.split(os.pathsep)
776 if ext.lower() == file[-len(ext):].lower():
779 if not is_List(reject) and not is_Tuple(reject):
782 f = os.path.join(dir, file)
785 if os.path.isfile(fext):
789 return os.path.normpath(fext)
793 elif os.name == 'os2':
795 def WhereIs(file, path=None, pathext=None, reject=[]):
798 path = os.environ['PATH']
802 path = path.split(os.pathsep)
804 pathext = ['.exe', '.cmd']
806 if ext.lower() == file[-len(ext):].lower():
809 if not is_List(reject) and not is_Tuple(reject):
812 f = os.path.join(dir, file)
815 if os.path.isfile(fext):
819 return os.path.normpath(fext)
825 def WhereIs(file, path=None, pathext=None, reject=[]):
829 path = os.environ['PATH']
833 path = path.split(os.pathsep)
834 if not is_List(reject) and not is_Tuple(reject):
837 f = os.path.join(d, file)
838 if os.path.isfile(f):
842 # os.stat() raises OSError, not IOError if the file
843 # doesn't exist, so in this case we let IOError get
844 # raised so as to not mask possibly serious disk or
847 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
851 return os.path.normpath(f)
855 def PrependPath(oldpath, newpath, sep = os.pathsep,
856 delete_existing=1, canonicalize=None):
857 """This prepends newpath elements to the given oldpath. Will only
858 add any particular path once (leaving the first one it encounters
859 and ignoring the rest, to preserve path order), and will
860 os.path.normpath and os.path.normcase all paths to help assure
861 this. This can also handle the case where the given old path
862 variable is a list instead of a string, in which case a list will
863 be returned instead of a string.
866 Old Path: "/foo/bar:/foo"
867 New Path: "/biz/boom:/foo"
868 Result: "/biz/boom:/foo:/foo/bar"
870 If delete_existing is 0, then adding a path that exists will
871 not move it to the beginning; it will stay where it is in the
874 If canonicalize is not None, it is applied to each element of
881 if not is_List(orig) and not is_Tuple(orig):
882 paths = paths.split(sep)
885 if is_String(newpath):
886 newpaths = newpath.split(sep)
887 elif not is_List(newpath) and not is_Tuple(newpath):
888 newpaths = [ newpath ] # might be a Dir
893 newpaths=list(map(canonicalize, newpaths))
895 if not delete_existing:
896 # First uniquify the old paths, making sure to
897 # preserve the first instance (in Unix/Linux,
898 # the first one wins), and remembering them in normpaths.
899 # Then insert the new paths at the head of the list
900 # if they're not already in the normpaths list.
906 normpath = os.path.normpath(os.path.normcase(path))
907 if normpath not in normpaths:
909 normpaths.append(normpath)
910 newpaths.reverse() # since we're inserting at the head
911 for path in newpaths:
914 normpath = os.path.normpath(os.path.normcase(path))
915 if normpath not in normpaths:
916 result.insert(0, path)
917 normpaths.append(normpath)
921 newpaths = newpaths + paths # prepend new paths
925 # now we add them only if they are unique
926 for path in newpaths:
927 normpath = os.path.normpath(os.path.normcase(path))
928 if path and not normpath in normpaths:
930 normpaths.append(normpath)
935 return sep.join(paths)
937 def AppendPath(oldpath, newpath, sep = os.pathsep,
938 delete_existing=1, canonicalize=None):
939 """This appends new path elements to the given old path. Will
940 only add any particular path once (leaving the last one it
941 encounters and ignoring the rest, to preserve path order), and
942 will os.path.normpath and os.path.normcase all paths to help
943 assure this. This can also handle the case where the given old
944 path variable is a list instead of a string, in which case a list
945 will be returned instead of a string.
948 Old Path: "/foo/bar:/foo"
949 New Path: "/biz/boom:/foo"
950 Result: "/foo/bar:/biz/boom:/foo"
952 If delete_existing is 0, then adding a path that exists
953 will not move it to the end; it will stay where it is in the list.
955 If canonicalize is not None, it is applied to each element of
962 if not is_List(orig) and not is_Tuple(orig):
963 paths = paths.split(sep)
966 if is_String(newpath):
967 newpaths = newpath.split(sep)
968 elif not is_List(newpath) and not is_Tuple(newpath):
969 newpaths = [ newpath ] # might be a Dir
974 newpaths=list(map(canonicalize, newpaths))
976 if not delete_existing:
977 # add old paths to result, then
978 # add new paths if not already present
979 # (I thought about using a dict for normpaths for speed,
980 # but it's not clear hashing the strings would be faster
981 # than linear searching these typically short lists.)
988 normpaths.append(os.path.normpath(os.path.normcase(path)))
989 for path in newpaths:
992 normpath = os.path.normpath(os.path.normcase(path))
993 if normpath not in normpaths:
995 normpaths.append(normpath)
998 # start w/ new paths, add old ones if not present,
1000 newpaths = paths + newpaths # append new paths
1005 # now we add them only if they are unique
1006 for path in newpaths:
1007 normpath = os.path.normpath(os.path.normcase(path))
1008 if path and not normpath in normpaths:
1010 normpaths.append(normpath)
1016 return sep.join(paths)
1018 if sys.platform == 'cygwin':
1019 def get_native_path(path):
1020 """Transforms an absolute path into a native path for the system. In
1021 Cygwin, this converts from a Cygwin path to a Windows one."""
1022 return os.popen('cygpath -w ' + path).read().replace('\n', '')
1024 def get_native_path(path):
1025 """Transforms an absolute path into a native path for the system.
1026 Non-Cygwin version, just leave the path alone."""
1029 display = DisplayEngine()
1032 if is_List(arg) or is_Tuple(arg):
1034 elif is_String(arg):
1039 class CLVar(UserList):
1040 """A class for command-line construction variables.
1042 This is a list that uses Split() to split an initial string along
1043 white-space arguments, and similarly to split any strings that get
1044 added. This allows us to Do the Right Thing with Append() and
1045 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1046 arg2') regardless of whether a user adds a list or a string to a
1047 command-line construction variable.
1049 def __init__(self, seq = []):
1050 UserList.__init__(self, Split(seq))
1051 def __add__(self, other):
1052 return UserList.__add__(self, CLVar(other))
1053 def __radd__(self, other):
1054 return UserList.__radd__(self, CLVar(other))
1055 def __coerce__(self, other):
1056 return (self, CLVar(other))
1058 return ' '.join(self.data)
1060 # A dictionary that preserves the order in which items are added.
1061 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
1062 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
1063 # Including fixes/enhancements from the follow-on discussions.
1064 class OrderedDict(UserDict):
1065 def __init__(self, dict = None):
1067 UserDict.__init__(self, dict)
1069 def __delitem__(self, key):
1070 UserDict.__delitem__(self, key)
1071 self._keys.remove(key)
1073 def __setitem__(self, key, item):
1074 UserDict.__setitem__(self, key, item)
1075 if key not in self._keys: self._keys.append(key)
1078 UserDict.clear(self)
1082 dict = OrderedDict()
1087 return list(zip(self._keys, self.values()))
1090 return self._keys[:]
1094 key = self._keys[-1]
1096 raise KeyError('dictionary is empty')
1103 def setdefault(self, key, failobj = None):
1104 UserDict.setdefault(self, key, failobj)
1105 if key not in self._keys: self._keys.append(key)
1107 def update(self, dict):
1108 for (key, val) in dict.items():
1109 self.__setitem__(key, val)
1112 return list(map(self.get, self._keys))
1114 class Selector(OrderedDict):
1115 """A callable ordered dictionary that maps file suffixes to
1116 dictionary values. We preserve the order in which items are added
1117 so that get_suffix() calls always return the first suffix added."""
1118 def __call__(self, env, source, ext=None):
1121 ext = source[0].suffix
1127 # Try to perform Environment substitution on the keys of
1128 # the dictionary before giving up.
1130 for (k,v) in self.items():
1134 # We only raise an error when variables point
1135 # to the same suffix. If one suffix is literal
1136 # and a variable suffix contains this literal,
1137 # the literal wins and we don't raise an error.
1138 raise KeyError, (s_dict[s_k][0], k, s_k)
1141 return s_dict[ext][1]
1149 if sys.platform == 'cygwin':
1150 # On Cygwin, os.path.normcase() lies, so just report back the
1151 # fact that the underlying Windows OS is case-insensitive.
1152 def case_sensitive_suffixes(s1, s2):
1155 def case_sensitive_suffixes(s1, s2):
1156 return (os.path.normcase(s1) != os.path.normcase(s2))
1158 def adjustixes(fname, pre, suf, ensure_suffix=False):
1160 path, fn = os.path.split(os.path.normpath(fname))
1161 if fn[:len(pre)] != pre:
1162 fname = os.path.join(path, pre + fn)
1163 # Only append a suffix if the suffix we're going to add isn't already
1164 # there, and if either we've been asked to ensure the specific suffix
1165 # is present or there's no suffix on it at all.
1166 if suf and fname[-len(suf):] != suf and \
1167 (ensure_suffix or not splitext(fname)[1]):
1174 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
1175 # ASPN: Python Cookbook: Remove duplicates from a sequence
1176 # (Also in the printed Python Cookbook.)
1179 """Return a list of the elements in s, but without duplicates.
1181 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1182 unique("abcabc") some permutation of ["a", "b", "c"], and
1183 unique(([1, 2], [2, 3], [1, 2])) some permutation of
1186 For best speed, all sequence elements should be hashable. Then
1187 unique() will usually work in linear time.
1189 If not possible, the sequence elements should enjoy a total
1190 ordering, and if list(s).sort() doesn't raise TypeError it's
1191 assumed that they do enjoy a total ordering. Then unique() will
1192 usually work in O(N*log2(N)) time.
1194 If that's not possible either, the sequence elements must support
1195 equality-testing. Then unique() will usually work in quadratic
1203 # Try using a dict first, as that's the fastest and will usually
1204 # work. If it doesn't work, it will usually fail quickly, so it
1205 # usually doesn't cost much to *try* it. It requires that all the
1206 # sequence elements be hashable, and support equality comparison.
1212 pass # move on to the next method
1217 # We can't hash all the elements. Second fastest is to sort,
1218 # which brings the equal elements together; then duplicates are
1219 # easy to weed out in a single pass.
1220 # NOTE: Python's list.sort() was designed to be efficient in the
1221 # presence of many duplicate elements. This isn't true of all
1222 # sort functions in all languages or libraries, so this approach
1223 # is more effective in Python than it may be elsewhere.
1227 pass # move on to the next method
1234 t[lasti] = last = t[i]
1240 # Brute force is all that's left.
1249 # From Alex Martelli,
1250 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
1251 # ASPN: Python Cookbook: Remove duplicates from a sequence
1252 # First comment, dated 2001/10/13.
1253 # (Also in the printed Python Cookbook.)
1255 def uniquer(seq, idfun=None):
1257 def idfun(x): return x
1261 marker = idfun(item)
1262 # in old Python versions:
1263 # if seen.has_key(marker)
1265 if marker in seen: continue
1270 # A more efficient implementation of Alex's uniquer(), this avoids the
1271 # idfun() argument and function-call overhead by assuming that all
1272 # items in the sequence are hashable.
1274 def uniquer_hashables(seq):
1278 #if not item in seen:
1279 if item not in seen:
1286 # Much of the logic here was originally based on recipe 4.9 from the
1287 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1290 def __init__(self, fileobj):
1291 self.fileobj = fileobj
1296 line = self.fileobj.readline()
1299 if line[-2:] == '\\\n':
1300 result.append(line[:-2])
1304 return ''.join(result)
1306 def readlines(self):
1309 line = self.readline()
1317 class UniqueList(UserList):
1318 def __init__(self, seq = []):
1319 UserList.__init__(self, seq)
1321 def __make_unique(self):
1323 self.data = uniquer_hashables(self.data)
1325 def __lt__(self, other):
1326 self.__make_unique()
1327 return UserList.__lt__(self, other)
1328 def __le__(self, other):
1329 self.__make_unique()
1330 return UserList.__le__(self, other)
1331 def __eq__(self, other):
1332 self.__make_unique()
1333 return UserList.__eq__(self, other)
1334 def __ne__(self, other):
1335 self.__make_unique()
1336 return UserList.__ne__(self, other)
1337 def __gt__(self, other):
1338 self.__make_unique()
1339 return UserList.__gt__(self, other)
1340 def __ge__(self, other):
1341 self.__make_unique()
1342 return UserList.__ge__(self, other)
1343 def __cmp__(self, other):
1344 self.__make_unique()
1345 return UserList.__cmp__(self, other)
1347 self.__make_unique()
1348 return UserList.__len__(self)
1349 def __getitem__(self, i):
1350 self.__make_unique()
1351 return UserList.__getitem__(self, i)
1352 def __setitem__(self, i, item):
1353 UserList.__setitem__(self, i, item)
1355 def __getslice__(self, i, j):
1356 self.__make_unique()
1357 return UserList.__getslice__(self, i, j)
1358 def __setslice__(self, i, j, other):
1359 UserList.__setslice__(self, i, j, other)
1361 def __add__(self, other):
1362 result = UserList.__add__(self, other)
1363 result.unique = False
1365 def __radd__(self, other):
1366 result = UserList.__radd__(self, other)
1367 result.unique = False
1369 def __iadd__(self, other):
1370 result = UserList.__iadd__(self, other)
1371 result.unique = False
1373 def __mul__(self, other):
1374 result = UserList.__mul__(self, other)
1375 result.unique = False
1377 def __rmul__(self, other):
1378 result = UserList.__rmul__(self, other)
1379 result.unique = False
1381 def __imul__(self, other):
1382 result = UserList.__imul__(self, other)
1383 result.unique = False
1385 def append(self, item):
1386 UserList.append(self, item)
1388 def insert(self, i):
1389 UserList.insert(self, i)
1391 def count(self, item):
1392 self.__make_unique()
1393 return UserList.count(self, item)
1394 def index(self, item):
1395 self.__make_unique()
1396 return UserList.index(self, item)
1398 self.__make_unique()
1399 UserList.reverse(self)
1400 def sort(self, *args, **kwds):
1401 self.__make_unique()
1402 return UserList.sort(self, *args, **kwds)
1403 def extend(self, other):
1404 UserList.extend(self, other)
1411 A proxy class that wraps a file object, flushing after every write,
1412 and delegating everything else to the wrapped object.
1414 def __init__(self, file):
1416 def write(self, arg):
1418 self.file.write(arg)
1421 # Stdout might be connected to a pipe that has been closed
1422 # by now. The most likely reason for the pipe being closed
1423 # is that the user has press ctrl-c. It this is the case,
1424 # then SCons is currently shutdown. We therefore ignore
1425 # IOError's here so that SCons can continue and shutdown
1426 # properly so that the .sconsign is correctly written
1427 # before SCons exits.
1429 def __getattr__(self, attr):
1430 return getattr(self.file, attr)
1432 def make_path_relative(path):
1433 """ makes an absolute path name to a relative pathname.
1435 if os.path.isabs(path):
1436 drive_s,path = os.path.splitdrive(path)
1440 path=re.compile("/*(.*)").findall(path)[0]
1444 assert( not os.path.isabs( path ) ), path
1449 # The original idea for AddMethod() and RenameFunction() come from the
1450 # following post to the ActiveState Python Cookbook:
1452 # ASPN: Python Cookbook : Install bound methods in an instance
1453 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613
1455 # That code was a little fragile, though, so the following changes
1456 # have been wrung on it:
1458 # * Switched the installmethod() "object" and "function" arguments,
1459 # so the order reflects that the left-hand side is the thing being
1460 # "assigned to" and the right-hand side is the value being assigned.
1462 # * Changed explicit type-checking to the "try: klass = object.__class__"
1463 # block in installmethod() below so that it still works with the
1464 # old-style classes that SCons uses.
1466 # * Replaced the by-hand creation of methods and functions with use of
1467 # the "new" module, as alluded to in Alex Martelli's response to the
1468 # following Cookbook post:
1470 # ASPN: Python Cookbook : Dynamically added methods to a class
1471 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
1473 def AddMethod(object, function, name = None):
1475 Adds either a bound method to an instance or an unbound method to
1476 a class. If name is ommited the name of the specified function
1482 AddMethod(f, A, "add")
1485 AddMethod(lambda self, i: self.l[i], a, "listIndex")
1486 print a.listIndex(5)
1491 name = function.func_name
1493 function = RenameFunction(function, name)
1496 klass = object.__class__
1497 except AttributeError:
1498 # "object" is really a class, so it gets an unbound method.
1499 object.__dict__[name] = new.instancemethod(function, None, object)
1501 # "object" is really an instance, so it gets a bound method.
1502 object.__dict__[name] = new.instancemethod(function, object, klass)
1504 def RenameFunction(function, name):
1506 Returns a function identical to the specified function, but with
1511 # Compatibility for Python 1.5 and 2.1. Can be removed in favor of
1512 # passing function.func_defaults directly to new.function() once
1513 # we base on Python 2.2 or later.
1514 func_defaults = function.func_defaults
1515 if func_defaults is None:
1518 return new.function(function.func_code,
1519 function.func_globals,
1525 def MD5signature(s):
1528 def MD5filesignature(fname, chunksize=65536):
1529 f = open(fname, "rb")
1539 if hasattr(hashlib, 'md5'):
1541 def MD5signature(s):
1544 return m.hexdigest()
1546 def MD5filesignature(fname, chunksize=65536):
1548 f = open(fname, "rb")
1550 blck = f.read(chunksize)
1555 return m.hexdigest()
1557 def MD5collect(signatures):
1559 Collects a list of signatures into an aggregate signature.
1561 signatures - a list of signatures
1562 returns - the aggregate signature
1564 if len(signatures) == 1:
1565 return signatures[0]
1567 return MD5signature(', '.join(signatures))
1571 # Wrap the intern() function so it doesn't throw exceptions if ineligible
1572 # arguments are passed. The intern() function was moved into the sys module in
1577 from sys import intern
1579 def silent_intern(x):
1581 Perform intern() on the passed argument and return the result.
1582 If the input is ineligible (e.g. a unicode string) the original argument is
1583 returned and no exception is thrown.
1592 # From Dinu C. Gherman,
1593 # Python Cookbook, second edition, recipe 6.17, p. 277.
1595 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
1596 # ASPN: Python Cookbook: Null Object Design Pattern
1599 #class Null(object):
1601 """ Null objects always and reliably "do nothing." """
1602 def __new__(cls, *args, **kwargs):
1603 if not '_inst' in vars(cls):
1604 cls._inst = type.__new__(cls, *args, **kwargs)
1606 def __init__(self, *args, **kwargs):
1608 def __call__(self, *args, **kwargs):
1611 return "Null(0x%08X)" % id(self)
1612 def __nonzero__(self):
1614 def __getattr__(self, name):
1616 def __setattr__(self, name, value):
1618 def __delattr__(self, name):
1621 class NullSeq(Null):
1626 def __getitem__(self, i):
1628 def __delitem__(self, i):
1630 def __setitem__(self, i, v):
1638 # indent-tabs-mode:nil
1640 # vim: set expandtab tabstop=4 shiftwidth=4: