| OLD | NEW |
| 1 """SCons.Util | 1 """SCons.Util |
| 2 | 2 |
| 3 Various utility functions go here. | 3 Various utility functions go here. |
| 4 | 4 |
| 5 """ | 5 """ |
| 6 | 6 |
| 7 # | 7 # |
| 8 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundat
ion | 8 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons F
oundation |
| 9 # | 9 # |
| 10 # Permission is hereby granted, free of charge, to any person obtaining | 10 # Permission is hereby granted, free of charge, to any person obtaining |
| 11 # a copy of this software and associated documentation files (the | 11 # a copy of this software and associated documentation files (the |
| 12 # "Software"), to deal in the Software without restriction, including | 12 # "Software"), to deal in the Software without restriction, including |
| 13 # without limitation the rights to use, copy, modify, merge, publish, | 13 # without limitation the rights to use, copy, modify, merge, publish, |
| 14 # distribute, sublicense, and/or sell copies of the Software, and to | 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 | 15 # permit persons to whom the Software is furnished to do so, subject to |
| 16 # the following conditions: | 16 # the following conditions: |
| 17 # | 17 # |
| 18 # The above copyright notice and this permission notice shall be included | 18 # The above copyright notice and this permission notice shall be included |
| 19 # in all copies or substantial portions of the Software. | 19 # in all copies or substantial portions of the Software. |
| 20 # | 20 # |
| 21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | 21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | 22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 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 | 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 | 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. | 27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 28 # | 28 # |
| 29 | 29 |
| 30 __revision__ = "src/engine/SCons/Util.py 3842 2008/12/20 22:59:52 scons" | 30 __revision__ = "src/engine/SCons/Util.py 3897 2009/01/13 06:45:54 scons" |
| 31 | 31 |
| 32 import copy | 32 import copy |
| 33 import os | 33 import os |
| 34 import os.path | 34 import os.path |
| 35 import re | 35 import re |
| 36 import string | 36 import string |
| 37 import sys | 37 import sys |
| 38 import types | 38 import types |
| 39 | 39 |
| 40 from UserDict import UserDict | 40 from UserDict import UserDict |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 100 Make the drive letter (if any) upper case. | 100 Make the drive letter (if any) upper case. |
| 101 This is useful because Windows is inconsitent on the case | 101 This is useful because Windows is inconsitent on the case |
| 102 of the drive letter, which can cause inconsistencies when | 102 of the drive letter, which can cause inconsistencies when |
| 103 calculating command signatures. | 103 calculating command signatures. |
| 104 """ | 104 """ |
| 105 drive, rest = os.path.splitdrive(path) | 105 drive, rest = os.path.splitdrive(path) |
| 106 if drive: | 106 if drive: |
| 107 path = string.upper(drive) + rest | 107 path = string.upper(drive) + rest |
| 108 return path | 108 return path |
| 109 | 109 |
| 110 class CallableComposite(UserList): | |
| 111 """A simple composite callable class that, when called, will invoke all | |
| 112 of its contained callables with the same arguments.""" | |
| 113 def __call__(self, *args, **kwargs): | |
| 114 retvals = map(lambda x, args=args, kwargs=kwargs: apply(x, | |
| 115 args, | |
| 116 kwargs), | |
| 117 self.data) | |
| 118 if self.data and (len(self.data) == len(filter(callable, retvals))): | |
| 119 return self.__class__(retvals) | |
| 120 return NodeList(retvals) | |
| 121 | |
| 122 class NodeList(UserList): | 110 class NodeList(UserList): |
| 123 """This class is almost exactly like a regular list of Nodes | 111 """This class is almost exactly like a regular list of Nodes |
| 124 (actually it can hold any object), with one important difference. | 112 (actually it can hold any object), with one important difference. |
| 125 If you try to get an attribute from this list, it will return that | 113 If you try to get an attribute from this list, it will return that |
| 126 attribute from every item in the list. For example: | 114 attribute from every item in the list. For example: |
| 127 | 115 |
| 128 >>> someList = NodeList([ ' foo ', ' bar ' ]) | 116 >>> someList = NodeList([ ' foo ', ' bar ' ]) |
| 129 >>> someList.strip() | 117 >>> someList.strip() |
| 130 [ 'foo', 'bar' ] | 118 [ 'foo', 'bar' ] |
| 131 """ | 119 """ |
| 132 def __nonzero__(self): | 120 def __nonzero__(self): |
| 133 return len(self.data) != 0 | 121 return len(self.data) != 0 |
| 134 | 122 |
| 135 def __str__(self): | 123 def __str__(self): |
| 136 return string.join(map(str, self.data)) | 124 return string.join(map(str, self.data)) |
| 137 | 125 |
| 126 def __iter__(self): |
| 127 return iter(self.data) |
| 128 |
| 129 def __call__(self, *args, **kwargs): |
| 130 result = map(lambda x, args=args, kwargs=kwargs: apply(x, |
| 131 args, |
| 132 kwargs), |
| 133 self.data) |
| 134 return self.__class__(result) |
| 135 |
| 138 def __getattr__(self, name): | 136 def __getattr__(self, name): |
| 139 if not self.data: | 137 result = map(lambda x, n=name: getattr(x, n), self.data) |
| 140 # If there is nothing in the list, then we have no attributes to | 138 return self.__class__(result) |
| 141 # pass through, so raise AttributeError for everything. | |
| 142 raise AttributeError, "NodeList has no attribute: %s" % name | |
| 143 | 139 |
| 144 # Return a list of the attribute, gotten from every element | |
| 145 # in the list | |
| 146 attrList = map(lambda x, n=name: getattr(x, n), self.data) | |
| 147 | |
| 148 # Special case. If the attribute is callable, we do not want | |
| 149 # to return a list of callables. Rather, we want to return a | |
| 150 # single callable that, when called, will invoke the function on | |
| 151 # all elements of this list. | |
| 152 if self.data and (len(self.data) == len(filter(callable, attrList))): | |
| 153 return CallableComposite(attrList) | |
| 154 return self.__class__(attrList) | |
| 155 | 140 |
| 156 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') | 141 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') |
| 157 | 142 |
| 158 def get_environment_var(varstr): | 143 def get_environment_var(varstr): |
| 159 """Given a string, first determine if it looks like a reference | 144 """Given a string, first determine if it looks like a reference |
| 160 to a single environment variable, like "$FOO" or "${FOO}". | 145 to a single environment variable, like "$FOO" or "${FOO}". |
| 161 If so, return that variable with no decorations ("FOO"). | 146 If so, return that variable with no decorations ("FOO"). |
| 162 If not, return None.""" | 147 If not, return None.""" |
| 163 mo=_get_env_var.match(to_String(varstr)) | 148 mo=_get_env_var.match(to_String(varstr)) |
| 164 if mo: | 149 if mo: |
| (...skipping 672 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 837 # network issues. | 822 # network issues. |
| 838 continue | 823 continue |
| 839 if stat.S_IMODE(st[stat.ST_MODE]) & 0111: | 824 if stat.S_IMODE(st[stat.ST_MODE]) & 0111: |
| 840 try: | 825 try: |
| 841 reject.index(f) | 826 reject.index(f) |
| 842 except ValueError: | 827 except ValueError: |
| 843 return os.path.normpath(f) | 828 return os.path.normpath(f) |
| 844 continue | 829 continue |
| 845 return None | 830 return None |
| 846 | 831 |
| 847 def PrependPath(oldpath, newpath, sep = os.pathsep, delete_existing=1): | 832 def PrependPath(oldpath, newpath, sep = os.pathsep, |
| 833 delete_existing=1, canonicalize=None): |
| 848 """This prepends newpath elements to the given oldpath. Will only | 834 """This prepends newpath elements to the given oldpath. Will only |
| 849 add any particular path once (leaving the first one it encounters | 835 add any particular path once (leaving the first one it encounters |
| 850 and ignoring the rest, to preserve path order), and will | 836 and ignoring the rest, to preserve path order), and will |
| 851 os.path.normpath and os.path.normcase all paths to help assure | 837 os.path.normpath and os.path.normcase all paths to help assure |
| 852 this. This can also handle the case where the given old path | 838 this. This can also handle the case where the given old path |
| 853 variable is a list instead of a string, in which case a list will | 839 variable is a list instead of a string, in which case a list will |
| 854 be returned instead of a string. | 840 be returned instead of a string. |
| 855 | 841 |
| 856 Example: | 842 Example: |
| 857 Old Path: "/foo/bar:/foo" | 843 Old Path: "/foo/bar:/foo" |
| 858 New Path: "/biz/boom:/foo" | 844 New Path: "/biz/boom:/foo" |
| 859 Result: "/biz/boom:/foo:/foo/bar" | 845 Result: "/biz/boom:/foo:/foo/bar" |
| 860 | 846 |
| 861 If delete_existing is 0, then adding a path that exists will | 847 If delete_existing is 0, then adding a path that exists will |
| 862 not move it to the beginning; it will stay where it is in the | 848 not move it to the beginning; it will stay where it is in the |
| 863 list. | 849 list. |
| 850 |
| 851 If canonicalize is not None, it is applied to each element of |
| 852 newpath before use. |
| 864 """ | 853 """ |
| 865 | 854 |
| 866 orig = oldpath | 855 orig = oldpath |
| 867 is_list = 1 | 856 is_list = 1 |
| 868 paths = orig | 857 paths = orig |
| 869 if not is_List(orig) and not is_Tuple(orig): | 858 if not is_List(orig) and not is_Tuple(orig): |
| 870 paths = string.split(paths, sep) | 859 paths = string.split(paths, sep) |
| 871 is_list = 0 | 860 is_list = 0 |
| 872 | 861 |
| 873 if is_List(newpath) or is_Tuple(newpath): | 862 if is_String(newpath): |
| 863 newpaths = string.split(newpath, sep) |
| 864 elif not is_List(newpath) and not is_Tuple(newpath): |
| 865 newpaths = [ newpath ] # might be a Dir |
| 866 else: |
| 874 newpaths = newpath | 867 newpaths = newpath |
| 875 else: | 868 |
| 876 newpaths = string.split(newpath, sep) | 869 if canonicalize: |
| 870 newpaths=map(canonicalize, newpaths) |
| 877 | 871 |
| 878 if not delete_existing: | 872 if not delete_existing: |
| 879 # First uniquify the old paths, making sure to | 873 # First uniquify the old paths, making sure to |
| 880 # preserve the first instance (in Unix/Linux, | 874 # preserve the first instance (in Unix/Linux, |
| 881 # the first one wins), and remembering them in normpaths. | 875 # the first one wins), and remembering them in normpaths. |
| 882 # Then insert the new paths at the head of the list | 876 # Then insert the new paths at the head of the list |
| 883 # if they're not already in the normpaths list. | 877 # if they're not already in the normpaths list. |
| 884 result = [] | 878 result = [] |
| 885 normpaths = [] | 879 normpaths = [] |
| 886 for path in paths: | 880 for path in paths: |
| (...skipping 23 matching lines...) Expand all Loading... |
| 910 normpath = os.path.normpath(os.path.normcase(path)) | 904 normpath = os.path.normpath(os.path.normcase(path)) |
| 911 if path and not normpath in normpaths: | 905 if path and not normpath in normpaths: |
| 912 paths.append(path) | 906 paths.append(path) |
| 913 normpaths.append(normpath) | 907 normpaths.append(normpath) |
| 914 | 908 |
| 915 if is_list: | 909 if is_list: |
| 916 return paths | 910 return paths |
| 917 else: | 911 else: |
| 918 return string.join(paths, sep) | 912 return string.join(paths, sep) |
| 919 | 913 |
| 920 def AppendPath(oldpath, newpath, sep = os.pathsep, delete_existing=1): | 914 def AppendPath(oldpath, newpath, sep = os.pathsep, |
| 915 delete_existing=1, canonicalize=None): |
| 921 """This appends new path elements to the given old path. Will | 916 """This appends new path elements to the given old path. Will |
| 922 only add any particular path once (leaving the last one it | 917 only add any particular path once (leaving the last one it |
| 923 encounters and ignoring the rest, to preserve path order), and | 918 encounters and ignoring the rest, to preserve path order), and |
| 924 will os.path.normpath and os.path.normcase all paths to help | 919 will os.path.normpath and os.path.normcase all paths to help |
| 925 assure this. This can also handle the case where the given old | 920 assure this. This can also handle the case where the given old |
| 926 path variable is a list instead of a string, in which case a list | 921 path variable is a list instead of a string, in which case a list |
| 927 will be returned instead of a string. | 922 will be returned instead of a string. |
| 928 | 923 |
| 929 Example: | 924 Example: |
| 930 Old Path: "/foo/bar:/foo" | 925 Old Path: "/foo/bar:/foo" |
| 931 New Path: "/biz/boom:/foo" | 926 New Path: "/biz/boom:/foo" |
| 932 Result: "/foo/bar:/biz/boom:/foo" | 927 Result: "/foo/bar:/biz/boom:/foo" |
| 933 | 928 |
| 934 If delete_existing is 0, then adding a path that exists | 929 If delete_existing is 0, then adding a path that exists |
| 935 will not move it to the end; it will stay where it is in the list. | 930 will not move it to the end; it will stay where it is in the list. |
| 931 |
| 932 If canonicalize is not None, it is applied to each element of |
| 933 newpath before use. |
| 936 """ | 934 """ |
| 937 | 935 |
| 938 orig = oldpath | 936 orig = oldpath |
| 939 is_list = 1 | 937 is_list = 1 |
| 940 paths = orig | 938 paths = orig |
| 941 if not is_List(orig) and not is_Tuple(orig): | 939 if not is_List(orig) and not is_Tuple(orig): |
| 942 paths = string.split(paths, sep) | 940 paths = string.split(paths, sep) |
| 943 is_list = 0 | 941 is_list = 0 |
| 944 | 942 |
| 945 if is_List(newpath) or is_Tuple(newpath): | 943 if is_String(newpath): |
| 944 newpaths = string.split(newpath, sep) |
| 945 elif not is_List(newpath) and not is_Tuple(newpath): |
| 946 newpaths = [ newpath ] # might be a Dir |
| 947 else: |
| 946 newpaths = newpath | 948 newpaths = newpath |
| 947 else: | 949 |
| 948 newpaths = string.split(newpath, sep) | 950 if canonicalize: |
| 951 newpaths=map(canonicalize, newpaths) |
| 949 | 952 |
| 950 if not delete_existing: | 953 if not delete_existing: |
| 951 # add old paths to result, then | 954 # add old paths to result, then |
| 952 # add new paths if not already present | 955 # add new paths if not already present |
| 953 # (I thought about using a dict for normpaths for speed, | 956 # (I thought about using a dict for normpaths for speed, |
| 954 # but it's not clear hashing the strings would be faster | 957 # but it's not clear hashing the strings would be faster |
| 955 # than linear searching these typically short lists.) | 958 # than linear searching these typically short lists.) |
| 956 result = [] | 959 result = [] |
| 957 normpaths = [] | 960 normpaths = [] |
| 958 for path in paths: | 961 for path in paths: |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1082 for (key, val) in dict.items(): | 1085 for (key, val) in dict.items(): |
| 1083 self.__setitem__(key, val) | 1086 self.__setitem__(key, val) |
| 1084 | 1087 |
| 1085 def values(self): | 1088 def values(self): |
| 1086 return map(self.get, self._keys) | 1089 return map(self.get, self._keys) |
| 1087 | 1090 |
| 1088 class Selector(OrderedDict): | 1091 class Selector(OrderedDict): |
| 1089 """A callable ordered dictionary that maps file suffixes to | 1092 """A callable ordered dictionary that maps file suffixes to |
| 1090 dictionary values. We preserve the order in which items are added | 1093 dictionary values. We preserve the order in which items are added |
| 1091 so that get_suffix() calls always return the first suffix added.""" | 1094 so that get_suffix() calls always return the first suffix added.""" |
| 1092 def __call__(self, env, source): | 1095 def __call__(self, env, source, ext=None): |
| 1093 try: | 1096 if ext is None: |
| 1094 ext = source[0].suffix | 1097 try: |
| 1095 except IndexError: | 1098 ext = source[0].suffix |
| 1096 ext = "" | 1099 except IndexError: |
| 1100 ext = "" |
| 1097 try: | 1101 try: |
| 1098 return self[ext] | 1102 return self[ext] |
| 1099 except KeyError: | 1103 except KeyError: |
| 1100 # Try to perform Environment substitution on the keys of | 1104 # Try to perform Environment substitution on the keys of |
| 1101 # the dictionary before giving up. | 1105 # the dictionary before giving up. |
| 1102 s_dict = {} | 1106 s_dict = {} |
| 1103 for (k,v) in self.items(): | 1107 for (k,v) in self.items(): |
| 1104 if not k is None: | 1108 if not k is None: |
| 1105 s_k = env.subst(k) | 1109 s_k = env.subst(k) |
| 1106 if s_dict.has_key(s_k): | 1110 if s_dict.has_key(s_k): |
| (...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1542 return MD5signature(string.join(signatures, ', ')) | 1546 return MD5signature(string.join(signatures, ', ')) |
| 1543 | 1547 |
| 1544 | 1548 |
| 1545 | 1549 |
| 1546 # From Dinu C. Gherman, | 1550 # From Dinu C. Gherman, |
| 1547 # Python Cookbook, second edition, recipe 6.17, p. 277. | 1551 # Python Cookbook, second edition, recipe 6.17, p. 277. |
| 1548 # Also: | 1552 # Also: |
| 1549 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 | 1553 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 |
| 1550 # ASPN: Python Cookbook: Null Object Design Pattern | 1554 # ASPN: Python Cookbook: Null Object Design Pattern |
| 1551 | 1555 |
| 1556 # TODO(1.5): |
| 1557 #class Null(object): |
| 1552 class Null: | 1558 class Null: |
| 1553 """ Null objects always and reliably "do nothging." """ | 1559 """ Null objects always and reliably "do nothing." """ |
| 1554 | |
| 1555 def __new__(cls, *args, **kwargs): | 1560 def __new__(cls, *args, **kwargs): |
| 1556 if not '_inst' in vars(cls): | 1561 if not '_inst' in vars(cls): |
| 1557 #cls._inst = type.__new__(cls, *args, **kwargs) | 1562 #cls._inst = type.__new__(cls, *args, **kwargs) |
| 1558 cls._inst = apply(type.__new__, (cls,) + args, kwargs) | 1563 cls._inst = apply(type.__new__, (cls,) + args, kwargs) |
| 1559 return cls._inst | 1564 return cls._inst |
| 1560 def __init__(self, *args, **kwargs): | 1565 def __init__(self, *args, **kwargs): |
| 1561 pass | 1566 pass |
| 1562 def __call__(self, *args, **kwargs): | 1567 def __call__(self, *args, **kwargs): |
| 1563 return self | 1568 return self |
| 1564 def __repr__(self): | 1569 def __repr__(self): |
| 1565 return "Null()" | 1570 return "Null(0x%08X)" % id(self) |
| 1566 def __nonzero__(self): | 1571 def __nonzero__(self): |
| 1567 return False | 1572 return False |
| 1568 def __getattr__(self, mname): | 1573 def __getattr__(self, name): |
| 1569 return self | 1574 return self |
| 1570 def __setattr__(self, name, value): | 1575 def __setattr__(self, name, value): |
| 1571 return self | 1576 return self |
| 1572 def __delattr__(self, name): | 1577 def __delattr__(self, name): |
| 1573 return self | 1578 return self |
| 1574 | 1579 |
| 1580 class NullSeq(Null): |
| 1581 def __len__(self): |
| 1582 return 0 |
| 1583 def __iter__(self): |
| 1584 return iter(()) |
| 1585 def __getitem__(self, i): |
| 1586 return self |
| 1587 def __delitem__(self, i): |
| 1588 return self |
| 1589 def __setitem__(self, i, v): |
| 1590 return self |
| 1575 | 1591 |
| 1576 | 1592 |
| 1577 del __revision__ | 1593 del __revision__ |
| OLD | NEW |