1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 __revision__ = "src/engine/SCons/PathList.py 2014/09/27 12:51:43 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
43
44
45 TYPE_STRING_NO_SUBST = 0
46 TYPE_STRING_SUBST = 1
47 TYPE_OBJECT = 2
48
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
70 """
71 An actual PathList object.
72 """
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
122
144
145
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
181
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
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
230
231
232
233
234