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