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 |