From e311c23480bb3a8246fd12510d63c3fb66164470 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Fri, 20 Aug 2010 11:01:34 -0400 Subject: ship scons-local with project so it's not a dependency --- scons/scons-local-2.0.1/SCons/Util.py | 1492 +++++++++++++++++++++++++++++++++ 1 file changed, 1492 insertions(+) create mode 100644 scons/scons-local-2.0.1/SCons/Util.py (limited to 'scons/scons-local-2.0.1/SCons/Util.py') diff --git a/scons/scons-local-2.0.1/SCons/Util.py b/scons/scons-local-2.0.1/SCons/Util.py new file mode 100644 index 00000000..1ffbd032 --- /dev/null +++ b/scons/scons-local-2.0.1/SCons/Util.py @@ -0,0 +1,1492 @@ +"""SCons.Util + +Various utility functions go here. +""" +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Util.py 5134 2010/08/16 23:02:40 bdeegan" + +import os +import sys +import copy +import re +import types + +from collections import UserDict, UserList, UserString + +# Don't "from types import ..." these because we need to get at the +# types module later to look for UnicodeType. +InstanceType = types.InstanceType +MethodType = types.MethodType +FunctionType = types.FunctionType +try: unicode +except NameError: UnicodeType = None +else: UnicodeType = unicode + +def dictify(keys, values, result={}): + for k, v in zip(keys, values): + result[k] = v + return result + +_altsep = os.altsep +if _altsep is None and sys.platform == 'win32': + # My ActivePython 2.0.1 doesn't set os.altsep! What gives? + _altsep = '/' +if _altsep: + def rightmost_separator(path, sep): + return max(path.rfind(sep), path.rfind(_altsep)) +else: + def rightmost_separator(path, sep): + return path.rfind(sep) + +# First two from the Python Cookbook, just for completeness. +# (Yeah, yeah, YAGNI...) +def containsAny(str, set): + """Check whether sequence str contains ANY of the items in set.""" + for c in set: + if c in str: return 1 + return 0 + +def containsAll(str, set): + """Check whether sequence str contains ALL of the items in set.""" + for c in set: + if c not in str: return 0 + return 1 + +def containsOnly(str, set): + """Check whether sequence str contains ONLY items in set.""" + for c in str: + if c not in set: return 0 + return 1 + +def splitext(path): + "Same as os.path.splitext() but faster." + sep = rightmost_separator(path, os.sep) + dot = path.rfind('.') + # An ext is only real if it has at least one non-digit char + if dot > sep and not containsOnly(path[dot:], "0123456789."): + return path[:dot],path[dot:] + else: + return path,"" + +def updrive(path): + """ + Make the drive letter (if any) upper case. + This is useful because Windows is inconsitent on the case + of the drive letter, which can cause inconsistencies when + calculating command signatures. + """ + drive, rest = os.path.splitdrive(path) + if drive: + path = drive.upper() + rest + return path + +class NodeList(UserList): + """This class is almost exactly like a regular list of Nodes + (actually it can hold any object), with one important difference. + If you try to get an attribute from this list, it will return that + attribute from every item in the list. For example: + + >>> someList = NodeList([ ' foo ', ' bar ' ]) + >>> someList.strip() + [ 'foo', 'bar' ] + """ + def __nonzero__(self): + return len(self.data) != 0 + + def __str__(self): + return ' '.join(map(str, self.data)) + + def __iter__(self): + return iter(self.data) + + def __call__(self, *args, **kwargs): + result = [x(*args, **kwargs) for x in self.data] + return self.__class__(result) + + def __getattr__(self, name): + result = [getattr(x, name) for x in self.data] + return self.__class__(result) + + +_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') + +def get_environment_var(varstr): + """Given a string, first determine if it looks like a reference + to a single environment variable, like "$FOO" or "${FOO}". + If so, return that variable with no decorations ("FOO"). + If not, return None.""" + mo=_get_env_var.match(to_String(varstr)) + if mo: + var = mo.group(1) + if var[0] == '{': + return var[1:-1] + else: + return var + else: + return None + +class DisplayEngine(object): + print_it = True + def __call__(self, text, append_newline=1): + if not self.print_it: + return + if append_newline: text = text + '\n' + try: + sys.stdout.write(unicode(text)) + except IOError: + # Stdout might be connected to a pipe that has been closed + # by now. The most likely reason for the pipe being closed + # is that the user has press ctrl-c. It this is the case, + # then SCons is currently shutdown. We therefore ignore + # IOError's here so that SCons can continue and shutdown + # properly so that the .sconsign is correctly written + # before SCons exits. + pass + + def set_mode(self, mode): + self.print_it = mode + +def render_tree(root, child_func, prune=0, margin=[0], visited={}): + """ + Render a tree of nodes into an ASCII tree view. + root - the root node of the tree + child_func - the function called to get the children of a node + prune - don't visit the same node twice + margin - the format of the left margin to use for children of root. + 1 results in a pipe, and 0 results in no pipe. + visited - a dictionary of visited nodes in the current branch if not prune, + or in the whole tree if prune. + """ + + rname = str(root) + + children = child_func(root) + retval = "" + for pipe in margin[:-1]: + if pipe: + retval = retval + "| " + else: + retval = retval + " " + + if rname in visited: + return retval + "+-[" + rname + "]\n" + + retval = retval + "+-" + rname + "\n" + if not prune: + visited = copy.copy(visited) + visited[rname] = 1 + + for i in range(len(children)): + margin.append(i 0 + last = t[0] + lasti = i = 1 + while i < n: + if t[i] != last: + t[lasti] = last = t[i] + lasti = lasti + 1 + i = i + 1 + return t[:lasti] + del t + + # Brute force is all that's left. + u = [] + for x in s: + if x not in u: + u.append(x) + return u + + + +# From Alex Martelli, +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 +# ASPN: Python Cookbook: Remove duplicates from a sequence +# First comment, dated 2001/10/13. +# (Also in the printed Python Cookbook.) + +def uniquer(seq, idfun=None): + if idfun is None: + def idfun(x): return x + seen = {} + result = [] + for item in seq: + marker = idfun(item) + # in old Python versions: + # if seen.has_key(marker) + # but in new ones: + if marker in seen: continue + seen[marker] = 1 + result.append(item) + return result + +# A more efficient implementation of Alex's uniquer(), this avoids the +# idfun() argument and function-call overhead by assuming that all +# items in the sequence are hashable. + +def uniquer_hashables(seq): + seen = {} + result = [] + for item in seq: + #if not item in seen: + if item not in seen: + seen[item] = 1 + result.append(item) + return result + + + +# Much of the logic here was originally based on recipe 4.9 from the +# Python CookBook, but we had to dumb it way down for Python 1.5.2. +class LogicalLines(object): + + def __init__(self, fileobj): + self.fileobj = fileobj + + def readline(self): + result = [] + while True: + line = self.fileobj.readline() + if not line: + break + if line[-2:] == '\\\n': + result.append(line[:-2]) + else: + result.append(line) + break + return ''.join(result) + + def readlines(self): + result = [] + while True: + line = self.readline() + if not line: + break + result.append(line) + return result + + + +class UniqueList(UserList): + def __init__(self, seq = []): + UserList.__init__(self, seq) + self.unique = True + def __make_unique(self): + if not self.unique: + self.data = uniquer_hashables(self.data) + self.unique = True + def __lt__(self, other): + self.__make_unique() + return UserList.__lt__(self, other) + def __le__(self, other): + self.__make_unique() + return UserList.__le__(self, other) + def __eq__(self, other): + self.__make_unique() + return UserList.__eq__(self, other) + def __ne__(self, other): + self.__make_unique() + return UserList.__ne__(self, other) + def __gt__(self, other): + self.__make_unique() + return UserList.__gt__(self, other) + def __ge__(self, other): + self.__make_unique() + return UserList.__ge__(self, other) + def __cmp__(self, other): + self.__make_unique() + return UserList.__cmp__(self, other) + def __len__(self): + self.__make_unique() + return UserList.__len__(self) + def __getitem__(self, i): + self.__make_unique() + return UserList.__getitem__(self, i) + def __setitem__(self, i, item): + UserList.__setitem__(self, i, item) + self.unique = False + def __getslice__(self, i, j): + self.__make_unique() + return UserList.__getslice__(self, i, j) + def __setslice__(self, i, j, other): + UserList.__setslice__(self, i, j, other) + self.unique = False + def __add__(self, other): + result = UserList.__add__(self, other) + result.unique = False + return result + def __radd__(self, other): + result = UserList.__radd__(self, other) + result.unique = False + return result + def __iadd__(self, other): + result = UserList.__iadd__(self, other) + result.unique = False + return result + def __mul__(self, other): + result = UserList.__mul__(self, other) + result.unique = False + return result + def __rmul__(self, other): + result = UserList.__rmul__(self, other) + result.unique = False + return result + def __imul__(self, other): + result = UserList.__imul__(self, other) + result.unique = False + return result + def append(self, item): + UserList.append(self, item) + self.unique = False + def insert(self, i): + UserList.insert(self, i) + self.unique = False + def count(self, item): + self.__make_unique() + return UserList.count(self, item) + def index(self, item): + self.__make_unique() + return UserList.index(self, item) + def reverse(self): + self.__make_unique() + UserList.reverse(self) + def sort(self, *args, **kwds): + self.__make_unique() + return UserList.sort(self, *args, **kwds) + def extend(self, other): + UserList.extend(self, other) + self.unique = False + + +class Unbuffered(object): + """ + A proxy class that wraps a file object, flushing after every write, + and delegating everything else to the wrapped object. + """ + def __init__(self, file): + self.file = file + self.softspace = 0 ## backward compatibility; not supported in Py3k + def write(self, arg): + try: + self.file.write(arg) + self.file.flush() + except IOError: + # Stdout might be connected to a pipe that has been closed + # by now. The most likely reason for the pipe being closed + # is that the user has press ctrl-c. It this is the case, + # then SCons is currently shutdown. We therefore ignore + # IOError's here so that SCons can continue and shutdown + # properly so that the .sconsign is correctly written + # before SCons exits. + pass + def __getattr__(self, attr): + return getattr(self.file, attr) + +def make_path_relative(path): + """ makes an absolute path name to a relative pathname. + """ + if os.path.isabs(path): + drive_s,path = os.path.splitdrive(path) + + import re + if not drive_s: + path=re.compile("/*(.*)").findall(path)[0] + else: + path=path[1:] + + assert( not os.path.isabs( path ) ), path + return path + + + +# The original idea for AddMethod() and RenameFunction() come from the +# following post to the ActiveState Python Cookbook: +# +# ASPN: Python Cookbook : Install bound methods in an instance +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 +# +# That code was a little fragile, though, so the following changes +# have been wrung on it: +# +# * Switched the installmethod() "object" and "function" arguments, +# so the order reflects that the left-hand side is the thing being +# "assigned to" and the right-hand side is the value being assigned. +# +# * Changed explicit type-checking to the "try: klass = object.__class__" +# block in installmethod() below so that it still works with the +# old-style classes that SCons uses. +# +# * Replaced the by-hand creation of methods and functions with use of +# the "new" module, as alluded to in Alex Martelli's response to the +# following Cookbook post: +# +# ASPN: Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 + +def AddMethod(obj, function, name=None): + """ + Adds either a bound method to an instance or an unbound method to + a class. If name is ommited the name of the specified function + is used by default. + Example: + a = A() + def f(self, x, y): + self.z = x + y + AddMethod(f, A, "add") + a.add(2, 4) + print a.z + AddMethod(lambda self, i: self.l[i], a, "listIndex") + print a.listIndex(5) + """ + if name is None: + name = function.func_name + else: + function = RenameFunction(function, name) + + if hasattr(obj, '__class__') and obj.__class__ is not type: + # "obj" is an instance, so it gets a bound method. + setattr(obj, name, MethodType(function, obj, obj.__class__)) + else: + # "obj" is a class, so it gets an unbound method. + setattr(obj, name, MethodType(function, None, obj)) + +def RenameFunction(function, name): + """ + Returns a function identical to the specified function, but with + the specified name. + """ + return FunctionType(function.func_code, + function.func_globals, + name, + function.func_defaults) + + +md5 = False +def MD5signature(s): + return str(s) + +def MD5filesignature(fname, chunksize=65536): + f = open(fname, "rb") + result = f.read() + f.close() + return result + +try: + import hashlib +except ImportError: + pass +else: + if hasattr(hashlib, 'md5'): + md5 = True + def MD5signature(s): + m = hashlib.md5() + m.update(str(s)) + return m.hexdigest() + + def MD5filesignature(fname, chunksize=65536): + m = hashlib.md5() + f = open(fname, "rb") + while True: + blck = f.read(chunksize) + if not blck: + break + m.update(str(blck)) + f.close() + return m.hexdigest() + +def MD5collect(signatures): + """ + Collects a list of signatures into an aggregate signature. + + signatures - a list of signatures + returns - the aggregate signature + """ + if len(signatures) == 1: + return signatures[0] + else: + return MD5signature(', '.join(signatures)) + + + +def silent_intern(x): + """ + Perform sys.intern() on the passed argument and return the result. + If the input is ineligible (e.g. a unicode string) the original argument is + returned and no exception is thrown. + """ + try: + return sys.intern(x) + except TypeError: + return x + + + +# From Dinu C. Gherman, +# Python Cookbook, second edition, recipe 6.17, p. 277. +# Also: +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 +# ASPN: Python Cookbook: Null Object Design Pattern + +#TODO??? class Null(object): +class Null(object): + """ Null objects always and reliably "do nothing." """ + def __new__(cls, *args, **kwargs): + if not '_instance' in vars(cls): + cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) + return cls._instance + def __init__(self, *args, **kwargs): + pass + def __call__(self, *args, **kwargs): + return self + def __repr__(self): + return "Null(0x%08X)" % id(self) + def __nonzero__(self): + return False + def __getattr__(self, name): + return self + def __setattr__(self, name, value): + return self + def __delattr__(self, name): + return self + +class NullSeq(Null): + def __len__(self): + return 0 + def __iter__(self): + return iter(()) + def __getitem__(self, i): + return self + def __delitem__(self, i): + return self + def __setitem__(self, i, v): + return self + + +del __revision__ + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v1.2.3