1 """SCons.Util
2
3 Various utility functions go here.
4
5 """
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 __revision__ = "src/engine/SCons/Util.py 2928 2008/04/29 22:44:09 knight"
31
32 import SCons.compat
33
34 import copy
35 import os
36 import os.path
37 import re
38 import string
39 import sys
40 import types
41
42 from UserDict import UserDict
43 from UserList import UserList
44 from UserString import UserString
45
46
47
48 DictType = types.DictType
49 InstanceType = types.InstanceType
50 ListType = types.ListType
51 StringType = types.StringType
52 TupleType = types.TupleType
53
54 -def dictify(keys, values, result={}):
58
59 _altsep = os.altsep
60 if _altsep is None and sys.platform == 'win32':
61
62 _altsep = '/'
63 if _altsep:
67 else:
68 rightmost_separator = string.rfind
69
70
71
73 """Check whether sequence str contains ANY of the items in set."""
74 for c in set:
75 if c in str: return 1
76 return 0
77
79 """Check whether sequence str contains ALL of the items in set."""
80 for c in set:
81 if c not in str: return 0
82 return 1
83
85 """Check whether sequence str contains ONLY items in set."""
86 for c in str:
87 if c not in set: return 0
88 return 1
89
91 "Same as os.path.splitext() but faster."
92 sep = rightmost_separator(path, os.sep)
93 dot = string.rfind(path, '.')
94
95 if dot > sep and not containsOnly(path[dot:], "0123456789."):
96 return path[:dot],path[dot:]
97 else:
98 return path,""
99
101 """
102 Make the drive letter (if any) upper case.
103 This is useful because Windows is inconsitent on the case
104 of the drive letter, which can cause inconsistencies when
105 calculating command signatures.
106 """
107 drive, rest = os.path.splitdrive(path)
108 if drive:
109 path = string.upper(drive) + rest
110 return path
111
113 """A simple composite callable class that, when called, will invoke all
114 of its contained callables with the same arguments."""
116 retvals = map(lambda x, args=args, kwargs=kwargs: apply(x,
117 args,
118 kwargs),
119 self.data)
120 if self.data and (len(self.data) == len(filter(callable, retvals))):
121 return self.__class__(retvals)
122 return NodeList(retvals)
123
125 """This class is almost exactly like a regular list of Nodes
126 (actually it can hold any object), with one important difference.
127 If you try to get an attribute from this list, it will return that
128 attribute from every item in the list. For example:
129
130 >>> someList = NodeList([ ' foo ', ' bar ' ])
131 >>> someList.strip()
132 [ 'foo', 'bar' ]
133 """
135 return len(self.data) != 0
136
138 return string.join(map(str, self.data))
139
141 if not self.data:
142
143
144 raise AttributeError, "NodeList has no attribute: %s" % name
145
146
147
148 attrList = map(lambda x, n=name: getattr(x, n), self.data)
149
150
151
152
153
154 if self.data and (len(self.data) == len(filter(callable, attrList))):
155 return CallableComposite(attrList)
156 return self.__class__(attrList)
157
158 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
159
161 """Given a string, first determine if it looks like a reference
162 to a single environment variable, like "$FOO" or "${FOO}".
163 If so, return that variable with no decorations ("FOO").
164 If not, return None."""
165 mo=_get_env_var.match(to_String(varstr))
166 if mo:
167 var = mo.group(1)
168 if var[0] == '{':
169 return var[1:-1]
170 else:
171 return var
172 else:
173 return None
174
178
179 - def print_it(self, text, append_newline=1):
180 if append_newline: text = text + '\n'
181 try:
182 sys.stdout.write(text)
183 except IOError:
184
185
186
187
188
189
190
191 pass
192
195
201
202 -def render_tree(root, child_func, prune=0, margin=[0], visited={}):
203 """
204 Render a tree of nodes into an ASCII tree view.
205 root - the root node of the tree
206 child_func - the function called to get the children of a node
207 prune - don't visit the same node twice
208 margin - the format of the left margin to use for children of root.
209 1 results in a pipe, and 0 results in no pipe.
210 visited - a dictionary of visited nodes in the current branch if not prune,
211 or in the whole tree if prune.
212 """
213
214 rname = str(root)
215
216 children = child_func(root)
217 retval = ""
218 for pipe in margin[:-1]:
219 if pipe:
220 retval = retval + "| "
221 else:
222 retval = retval + " "
223
224 if visited.has_key(rname):
225 return retval + "+-[" + rname + "]\n"
226
227 retval = retval + "+-" + rname + "\n"
228 if not prune:
229 visited = copy.copy(visited)
230 visited[rname] = 1
231
232 for i in range(len(children)):
233 margin.append(i<len(children)-1)
234 retval = retval + render_tree(children[i], child_func, prune, margin, visited
235 )
236 margin.pop()
237
238 return retval
239
240 IDX = lambda N: N and 1 or 0
241
242 -def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
243 """
244 Print a tree of nodes. This is like render_tree, except it prints
245 lines directly instead of creating a string representation in memory,
246 so that huge trees can be printed.
247
248 root - the root node of the tree
249 child_func - the function called to get the children of a node
250 prune - don't visit the same node twice
251 showtags - print status information to the left of each node line
252 margin - the format of the left margin to use for children of root.
253 1 results in a pipe, and 0 results in no pipe.
254 visited - a dictionary of visited nodes in the current branch if not prune,
255 or in the whole tree if prune.
256 """
257
258 rname = str(root)
259
260 if showtags:
261
262 if showtags == 2:
263 print ' E = exists'
264 print ' R = exists in repository only'
265 print ' b = implicit builder'
266 print ' B = explicit builder'
267 print ' S = side effect'
268 print ' P = precious'
269 print ' A = always build'
270 print ' C = current'
271 print ' N = no clean'
272 print ' H = no cache'
273 print ''
274
275 tags = ['[']
276 tags.append(' E'[IDX(root.exists())])
277 tags.append(' R'[IDX(root.rexists() and not root.exists())])
278 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
279 [0,2][IDX(root.has_builder())]])
280 tags.append(' S'[IDX(root.side_effect)])
281 tags.append(' P'[IDX(root.precious)])
282 tags.append(' A'[IDX(root.always_build)])
283 tags.append(' C'[IDX(root.is_up_to_date())])
284 tags.append(' N'[IDX(root.noclean)])
285 tags.append(' H'[IDX(root.nocache)])
286 tags.append(']')
287
288 else:
289 tags = []
290
291 def MMM(m):
292 return [" ","| "][m]
293 margins = map(MMM, margin[:-1])
294
295 children = child_func(root)
296
297 if prune and visited.has_key(rname) and children:
298 print string.join(tags + margins + ['+-[', rname, ']'], '')
299 return
300
301 print string.join(tags + margins + ['+-', rname], '')
302
303 visited[rname] = 1
304
305 if children:
306 margin.append(1)
307 map(lambda C, cf=child_func, p=prune, i=IDX(showtags), m=margin, v=visited:
308 print_tree(C, cf, p, i, m, v),
309 children[:-1])
310 margin[-1] = 0
311 print_tree(children[-1], child_func, prune, IDX(showtags), margin, visited)
312 margin.pop()
313
314
315
316
317
318
319
320
321
322
323
324 try:
327 except TypeError:
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
348 t = type(obj)
349 return t is DictType or \
350 (t is InstanceType and isinstance(obj, UserDict))
351
353 t = type(obj)
354 return t is ListType \
355 or (t is InstanceType and isinstance(obj, UserList))
356
358 t = type(obj)
359 return t is ListType \
360 or t is TupleType \
361 or (t is InstanceType and isinstance(obj, UserList))
362
364 t = type(obj)
365 return t is TupleType
366
367 if hasattr(types, 'UnicodeType'):
369 t = type(obj)
370 return t is StringType \
371 or t is UnicodeType \
372 or (t is InstanceType and isinstance(obj, UserString))
373 else: