OLD | NEW |
(Empty) | |
| 1 # |
| 2 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons Foundation |
| 3 # |
| 4 # Permission is hereby granted, free of charge, to any person obtaining |
| 5 # a copy of this software and associated documentation files (the |
| 6 # "Software"), to deal in the Software without restriction, including |
| 7 # without limitation the rights to use, copy, modify, merge, publish, |
| 8 # distribute, sublicense, and/or sell copies of the Software, and to |
| 9 # permit persons to whom the Software is furnished to do so, subject to |
| 10 # the following conditions: |
| 11 # |
| 12 # The above copyright notice and this permission notice shall be included |
| 13 # in all copies or substantial portions of the Software. |
| 14 # |
| 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 22 # |
| 23 |
| 24 __revision__ = "src/engine/SCons/PathList.py 5134 2010/08/16 23:02:40 bdeegan" |
| 25 |
| 26 __doc__ = """SCons.PathList |
| 27 |
| 28 A module for handling lists of directory paths (the sort of things |
| 29 that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and |
| 30 efficiency as we can while still keeping the evaluation delayed so that we |
| 31 Do the Right Thing (almost) regardless of how the variable is specified. |
| 32 |
| 33 """ |
| 34 |
| 35 import os |
| 36 |
| 37 import SCons.Memoize |
| 38 import SCons.Node |
| 39 import SCons.Util |
| 40 |
| 41 # |
| 42 # Variables to specify the different types of entries in a PathList object: |
| 43 # |
| 44 |
| 45 TYPE_STRING_NO_SUBST = 0 # string with no '$' |
| 46 TYPE_STRING_SUBST = 1 # string containing '$' |
| 47 TYPE_OBJECT = 2 # other object |
| 48 |
| 49 def node_conv(obj): |
| 50 """ |
| 51 This is the "string conversion" routine that we have our substitutions |
| 52 use to return Nodes, not strings. This relies on the fact that an |
| 53 EntryProxy object has a get() method that returns the underlying |
| 54 Node that it wraps, which is a bit of architectural dependence |
| 55 that we might need to break or modify in the future in response to |
| 56 additional requirements. |
| 57 """ |
| 58 try: |
| 59 get = obj.get |
| 60 except AttributeError: |
| 61 if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): |
| 62 result = obj |
| 63 else: |
| 64 result = str(obj) |
| 65 else: |
| 66 result = get() |
| 67 return result |
| 68 |
| 69 class _PathList(object): |
| 70 """ |
| 71 An actual PathList object. |
| 72 """ |
| 73 def __init__(self, pathlist): |
| 74 """ |
| 75 Initializes a PathList object, canonicalizing the input and |
| 76 pre-processing it for quicker substitution later. |
| 77 |
| 78 The stored representation of the PathList is a list of tuples |
| 79 containing (type, value), where the "type" is one of the TYPE_* |
| 80 variables defined above. We distinguish between: |
| 81 |
| 82 strings that contain no '$' and therefore need no |
| 83 delayed-evaluation string substitution (we expect that there |
| 84 will be many of these and that we therefore get a pretty |
| 85 big win from avoiding string substitution) |
| 86 |
| 87 strings that contain '$' and therefore need substitution |
| 88 (the hard case is things like '${TARGET.dir}/include', |
| 89 which require re-evaluation for every target + source) |
| 90 |
| 91 other objects (which may be something like an EntryProxy |
| 92 that needs a method called to return a Node) |
| 93 |
| 94 Pre-identifying the type of each element in the PathList up-front |
| 95 and storing the type in the list of tuples is intended to reduce |
| 96 the amount of calculation when we actually do the substitution |
| 97 over and over for each target. |
| 98 """ |
| 99 if SCons.Util.is_String(pathlist): |
| 100 pathlist = pathlist.split(os.pathsep) |
| 101 elif not SCons.Util.is_Sequence(pathlist): |
| 102 pathlist = [pathlist] |
| 103 |
| 104 pl = [] |
| 105 for p in pathlist: |
| 106 try: |
| 107 index = p.find('$') |
| 108 except (AttributeError, TypeError): |
| 109 type = TYPE_OBJECT |
| 110 else: |
| 111 if index == -1: |
| 112 type = TYPE_STRING_NO_SUBST |
| 113 else: |
| 114 type = TYPE_STRING_SUBST |
| 115 pl.append((type, p)) |
| 116 |
| 117 self.pathlist = tuple(pl) |
| 118 |
| 119 def __len__(self): return len(self.pathlist) |
| 120 |
| 121 def __getitem__(self, i): return self.pathlist[i] |
| 122 |
| 123 def subst_path(self, env, target, source): |
| 124 """ |
| 125 Performs construction variable substitution on a pre-digested |
| 126 PathList for a specific target and source. |
| 127 """ |
| 128 result = [] |
| 129 for type, value in self.pathlist: |
| 130 if type == TYPE_STRING_SUBST: |
| 131 value = env.subst(value, target=target, source=source, |
| 132 conv=node_conv) |
| 133 if SCons.Util.is_Sequence(value): |
| 134 result.extend(value) |
| 135 continue |
| 136 |
| 137 elif type == TYPE_OBJECT: |
| 138 value = node_conv(value) |
| 139 if value: |
| 140 result.append(value) |
| 141 return tuple(result) |
| 142 |
| 143 |
| 144 class PathListCache(object): |
| 145 """ |
| 146 A class to handle caching of PathList lookups. |
| 147 |
| 148 This class gets instantiated once and then deleted from the namespace, |
| 149 so it's used as a Singleton (although we don't enforce that in the |
| 150 usual Pythonic ways). We could have just made the cache a dictionary |
| 151 in the module namespace, but putting it in this class allows us to |
| 152 use the same Memoizer pattern that we use elsewhere to count cache |
| 153 hits and misses, which is very valuable. |
| 154 |
| 155 Lookup keys in the cache are computed by the _PathList_key() method. |
| 156 Cache lookup should be quick, so we don't spend cycles canonicalizing |
| 157 all forms of the same lookup key. For example, 'x:y' and ['x', |
| 158 'y'] logically represent the same list, but we don't bother to |
| 159 split string representations and treat those two equivalently. |
| 160 (Note, however, that we do, treat lists and tuples the same.) |
| 161 |
| 162 The main type of duplication we're trying to catch will come from |
| 163 looking up the same path list from two different clones of the |
| 164 same construction environment. That is, given |
| 165 |
| 166 env2 = env1.Clone() |
| 167 |
| 168 both env1 and env2 will have the same CPPPATH value, and we can |
| 169 cheaply avoid re-parsing both values of CPPPATH by using the |
| 170 common value from this cache. |
| 171 """ |
| 172 if SCons.Memoize.use_memoizer: |
| 173 __metaclass__ = SCons.Memoize.Memoized_Metaclass |
| 174 |
| 175 memoizer_counters = [] |
| 176 |
| 177 def __init__(self): |
| 178 self._memo = {} |
| 179 |
| 180 def _PathList_key(self, pathlist): |
| 181 """ |
| 182 Returns the key for memoization of PathLists. |
| 183 |
| 184 Note that we want this to be pretty quick, so we don't completely |
| 185 canonicalize all forms of the same list. For example, |
| 186 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically |
| 187 represent the same list if you're executing from $ROOT, but |
| 188 we're not going to bother splitting strings into path elements, |
| 189 or massaging strings into Nodes, to identify that equivalence. |
| 190 We just want to eliminate obvious redundancy from the normal |
| 191 case of re-using exactly the same cloned value for a path. |
| 192 """ |
| 193 if SCons.Util.is_Sequence(pathlist): |
| 194 pathlist = tuple(SCons.Util.flatten(pathlist)) |
| 195 return pathlist |
| 196 |
| 197 memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) |
| 198 |
| 199 def PathList(self, pathlist): |
| 200 """ |
| 201 Returns the cached _PathList object for the specified pathlist, |
| 202 creating and caching a new object as necessary. |
| 203 """ |
| 204 pathlist = self._PathList_key(pathlist) |
| 205 try: |
| 206 memo_dict = self._memo['PathList'] |
| 207 except KeyError: |
| 208 memo_dict = {} |
| 209 self._memo['PathList'] = memo_dict |
| 210 else: |
| 211 try: |
| 212 return memo_dict[pathlist] |
| 213 except KeyError: |
| 214 pass |
| 215 |
| 216 result = _PathList(pathlist) |
| 217 |
| 218 memo_dict[pathlist] = result |
| 219 |
| 220 return result |
| 221 |
| 222 PathList = PathListCache().PathList |
| 223 |
| 224 |
| 225 del PathListCache |
| 226 |
| 227 # Local Variables: |
| 228 # tab-width:4 |
| 229 # indent-tabs-mode:nil |
| 230 # End: |
| 231 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |