Package SCons :: Module PathList
[hide private]
[frames] | no frames]

Source Code for Module SCons.PathList

  1  # 
  2  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons 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 5110 2010/07/25 16:14:38 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  import string 
 37   
 38  import SCons.Memoize 
 39  import SCons.Node 
 40  import SCons.Util 
 41   
 42  # 
 43  # Variables to specify the different types of entries in a PathList object: 
 44  # 
 45   
 46  TYPE_STRING_NO_SUBST = 0        # string with no '$' 
 47  TYPE_STRING_SUBST = 1           # string containing '$' 
 48  TYPE_OBJECT = 2                 # other object 
 49   
50 -def node_conv(obj):
51 """ 52 This is the "string conversion" routine that we have our substitutions 53 use to return Nodes, not strings. This relies on the fact that an 54 EntryProxy object has a get() method that returns the underlying 55 Node that it wraps, which is a bit of architectural dependence 56 that we might need to break or modify in the future in response to 57 additional requirements. 58 """ 59 try: 60 get = obj.get 61 except AttributeError: 62 if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): 63 result = obj 64 else: 65 result = str(obj) 66 else: 67 result = get() 68 return result
69
70 -class _PathList:
71 """ 72 An actual PathList object. 73 """
74 - def __init__(self, pathlist):
75 """ 76 Initializes a PathList object, canonicalizing the input and 77 pre-processing it for quicker substitution later. 78 79 The stored representation of the PathList is a list of tuples 80 containing (type, value), where the "type" is one of the TYPE_* 81 variables defined above. We distinguish between: 82 83 strings that contain no '$' and therefore need no 84 delayed-evaluation string substitution (we expect that there 85 will be many of these and that we therefore get a pretty 86 big win from avoiding string substitution) 87 88 strings that contain '$' and therefore need substitution 89 (the hard case is things like '${TARGET.dir}/include', 90 which require re-evaluation for every target + source) 91 92 other objects (which may be something like an EntryProxy 93 that needs a method called to return a Node) 94 95 Pre-identifying the type of each element in the PathList up-front 96 and storing the type in the list of tuples is intended to reduce 97 the amount of calculation when we actually do the substitution 98 over and over for each target. 99 """ 100 if SCons.Util.is_String(pathlist): 101 pathlist = string.split(pathlist, os.pathsep) 102 elif not SCons.Util.is_Sequence(pathlist): 103 pathlist = [pathlist] 104 105 pl = [] 106 for p in pathlist: 107 try: 108 index = string.find(p, '$') 109 except (AttributeError, TypeError): 110 type = TYPE_OBJECT 111 else: 112 if index == -1: 113 type = TYPE_STRING_NO_SUBST 114 else: 115 type = TYPE_STRING_SUBST 116 pl.append((type, p)) 117 118 self.pathlist = tuple(pl)
119
120 - def __len__(self): return len(self.pathlist)
121
122 - def __getitem__(self, i): return self.pathlist[i]
123
124 - def subst_path(self, env, target, source):
125 """ 126 Performs construction variable substitution on a pre-digested 127 PathList for a specific target and source. 128 """ 129 result = [] 130 for type, value in self.pathlist: 131 if type == TYPE_STRING_SUBST: 132 value = env.subst(value, target=target, source=source, 133 conv=node_conv) 134 if SCons.Util.is_Sequence(value): 135 result.extend(value) 136 continue 137 138 elif type == TYPE_OBJECT: 139 value = node_conv(value) 140 if value: 141 result.append(value) 142 return tuple(result)
143 144
145 -class PathListCache:
146 """ 147 A class to handle caching of PathList lookups. 148 149 This class gets instantiated once and then deleted from the namespace, 150 so it's used as a Singleton (although we don't enforce that in the 151 usual Pythonic ways). We could have just made the cache a dictionary 152 in the module namespace, but putting it in this class allows us to 153 use the same Memoizer pattern that we use elsewhere to count cache 154 hits and misses, which is very valuable. 155 156 Lookup keys in the cache are computed by the _PathList_key() method. 157 Cache lookup should be quick, so we don't spend cycles canonicalizing 158 all forms of the same lookup key. For example, 'x:y' and ['x', 159 'y'] logically represent the same list, but we don't bother to 160 split string representations and treat those two equivalently. 161 (Note, however, that we do, treat lists and tuples the same.) 162 163 The main type of duplication we're trying to catch will come from 164 looking up the same path list from two different clones of the 165 same construction environment. That is, given 166 167 env2 = env1.Clone() 168 169 both env1 and env2 will have the same CPPPATH value, and we can 170 cheaply avoid re-parsing both values of CPPPATH by using the 171 common value from this cache. 172 """ 173 if SCons.Memoize.use_memoizer: 174 __metaclass__ = SCons.Memoize.Memoized_Metaclass 175 176 memoizer_counters = [] 177
178 - def __init__(self):
179 self._memo = {}
180
181 - def _PathList_key(self, pathlist):
182 """ 183 Returns the key for memoization of PathLists. 184 185 Note that we want this to be pretty quick, so we don't completely 186 canonicalize all forms of the same list. For example, 187 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically 188 represent the same list if you're executing from $ROOT, but 189 we're not going to bother splitting strings into path elements, 190 or massaging strings into Nodes, to identify that equivalence. 191 We just want to eliminate obvious redundancy from the normal 192 case of re-using exactly the same cloned value for a path. 193 """ 194 if SCons.Util.is_Sequence(pathlist): 195 pathlist = tuple(SCons.Util.flatten(pathlist)) 196 return pathlist
197 198 memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) 199
200 - def PathList(self, pathlist):
201 """ 202 Returns the cached _PathList object for the specified pathlist, 203 creating and caching a new object as necessary. 204 """ 205 pathlist = self._PathList_key(pathlist) 206 try: 207 memo_dict = self._memo['PathList'] 208 except KeyError: 209 memo_dict = {} 210 self._memo['PathList'] = memo_dict 211 else: 212 try: 213 return memo_dict[pathlist] 214 except KeyError: 215 pass 216 217 result = _PathList(pathlist) 218 219 memo_dict[pathlist] = result 220 221 return result
222 223 PathList = PathListCache().PathList 224 225 226 del PathListCache 227 228 # Local Variables: 229 # tab-width:4 230 # indent-tabs-mode:nil 231 # End: 232 # vim: set expandtab tabstop=4 shiftwidth=4: 233