Toplevel directory cleanup
[senf.git] / tools / scons-1.2.0 / engine / SCons / PathList.py
1 #
2 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 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 3842 2008/12/20 22:59:52 scons"
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