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

Source Code for Module SCons.PathList

  1  # 
  2  # Copyright (c) 2001 - 2014 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  2014/08/24 12:12:31 garyo" 
 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(SCons.Util.flatten(value)) 135 elif value: 136 result.append(value) 137 elif type == TYPE_OBJECT: 138 value = node_conv(value) 139 if value: 140 result.append(value) 141 elif value: 142 result.append(value) 143 return tuple(result)
144 145
146 -class PathListCache(object):
147 """ 148 A class to handle caching of PathList lookups. 149 150 This class gets instantiated once and then deleted from the namespace, 151 so it's used as a Singleton (although we don't enforce that in the 152 usual Pythonic ways). We could have just made the cache a dictionary 153 in the module namespace, but putting it in this class allows us to 154 use the same Memoizer pattern that we use elsewhere to count cache 155 hits and misses, which is very valuable. 156 157 Lookup keys in the cache are computed by the _PathList_key() method. 158 Cache lookup should be quick, so we don't spend cycles canonicalizing 159 all forms of the same lookup key. For example, 'x:y' and ['x', 160 'y'] logically represent the same list, but we don't bother to 161 split string representations and treat those two equivalently. 162 (Note, however, that we do, treat lists and tuples the same.) 163 164 The main type of duplication we're trying to catch will come from 165 looking up the same path list from two different clones of the 166 same construction environment. That is, given 167 168 env2 = env1.Clone() 169 170 both env1 and env2 will have the same CPPPATH value, and we can 171 cheaply avoid re-parsing both values of CPPPATH by using the 172 common value from this cache. 173 """ 174 if SCons.Memoize.use_memoizer: 175 __metaclass__ = SCons.Memoize.Memoized_Metaclass 176 177 memoizer_counters = [] 178
179 - def __init__(self):
180 self._memo = {}
181
182 - def _PathList_key(self, pathlist):
183 """ 184 Returns the key for memoization of PathLists. 185 186 Note that we want this to be pretty quick, so we don't completely 187 canonicalize all forms of the same list. For example, 188 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically 189 represent the same list if you're executing from $ROOT, but 190 we're not going to bother splitting strings into path elements, 191 or massaging strings into Nodes, to identify that equivalence. 192 We just want to eliminate obvious redundancy from the normal 193 case of re-using exactly the same cloned value for a path. 194 """ 195 if SCons.Util.is_Sequence(pathlist): 196 pathlist = tuple(SCons.Util.flatten(pathlist)) 197 return pathlist
198 199 memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) 200
201 - def PathList(self, pathlist):
202 """ 203 Returns the cached _PathList object for the specified pathlist, 204 creating and caching a new object as necessary. 205 """ 206 pathlist = self._PathList_key(pathlist) 207 try: 208 memo_dict = self._memo['PathList'] 209 except KeyError: 210 memo_dict = {} 211 self._memo['PathList'] = memo_dict 212 else: 213 try: 214 return memo_dict[pathlist] 215 except KeyError: 216 pass 217 218 result = _PathList(pathlist) 219 220 memo_dict[pathlist] = result 221 222 return result
223 224 PathList = PathListCache().PathList 225 226 227 del PathListCache 228 229 # Local Variables: 230 # tab-width:4 231 # indent-tabs-mode:nil 232 # End: 233 # vim: set expandtab tabstop=4 shiftwidth=4: 234