2
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
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:
12
# The above copyright notice and this permission notice shall be included
13
# in all copies or substantial portions of the Software.
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.
24
__revision__ = "src/engine/SCons/PathList.py 2523 2007/12/12 09:37:41 knight"
26
__doc__ = """SCons.PathList
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.
43
# Variables to specify the different types of entries in a PathList object:
46
TYPE_STRING_NO_SUBST = 0 # string with no '$'
47
TYPE_STRING_SUBST = 1 # string containing '$'
48
TYPE_OBJECT = 2 # other object
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.
61
except AttributeError:
62
if isinstance(obj, SCons.Node.Node):
72
An actual PathList object.
74
def __init__(self, pathlist):
76
Initializes a PathList object, canonicalizing the input and
77
pre-processing it for quicker substitution later.
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:
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)
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)
92
other objects (which may be something like an EntryProxy
93
that needs a method called to return a Node)
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.
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]
108
index = string.find(p, '$')
109
except (AttributeError, TypeError):
113
type = TYPE_STRING_NO_SUBST
115
type = TYPE_STRING_SUBST
118
self.pathlist = tuple(pl)
120
def __len__(self): return len(self.pathlist)
122
def __getitem__(self, i): return self.pathlist[i]
124
def subst_path(self, env, target, source):
126
Performs construction variable substitution on a pre-digested
127
PathList for a specific target and source.
130
for type, value in self.pathlist:
131
if type == TYPE_STRING_SUBST:
132
value = env.subst(value, target=target, source=source,
134
if SCons.Util.is_Sequence(value):
135
# It came back as a string or tuple, which in this
136
# case usually means some variable expanded to an
137
# actually Dir node. Concatenate the values.
138
value = string.join(map(str, value), '')
139
elif type == TYPE_OBJECT:
140
value = node_conv(value)
148
A class to handle caching of PathList lookups.
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.
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.)
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
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.
174
if SCons.Memoize.use_memoizer:
175
__metaclass__ = SCons.Memoize.Memoized_Metaclass
177
memoizer_counters = []
182
def _PathList_key(self, pathlist):
184
Returns the key for memoization of PathLists.
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.
195
if SCons.Util.is_Sequence(pathlist):
196
pathlist = tuple(SCons.Util.flatten(pathlist))
199
memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key))
201
def PathList(self, pathlist):
203
Returns the cached _PathList object for the specified pathlist,
204
creating and caching a new object as necessary.
206
pathlist = self._PathList_key(pathlist)
208
memo_dict = self._memo['PathList']
211
self._memo['PathList'] = memo_dict
214
return memo_dict[pathlist]
218
result = _PathList(pathlist)
220
memo_dict[pathlist] = result
224
PathList = PathListCache().PathList