Package SCons :: Module Util
[hide private]
[frames] | no frames]

Source Code for Module SCons.Util

   1  """SCons.Util 
   2   
   3  Various utility functions go here. 
   4   
   5  """ 
   6   
   7  # 
   8  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation 
   9  # 
  10  # Permission is hereby granted, free of charge, to any person obtaining 
  11  # a copy of this software and associated documentation files (the 
  12  # "Software"), to deal in the Software without restriction, including 
  13  # without limitation the rights to use, copy, modify, merge, publish, 
  14  # distribute, sublicense, and/or sell copies of the Software, and to 
  15  # permit persons to whom the Software is furnished to do so, subject to 
  16  # the following conditions: 
  17  # 
  18  # The above copyright notice and this permission notice shall be included 
  19  # in all copies or substantial portions of the Software. 
  20  # 
  21  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  22  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  23  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  24  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  25  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  26  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  27  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  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  # Don't "from types import ..." these because we need to get at the 
  47  # types module later to look for UnicodeType. 
  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={}):
55 for k, v in zip(keys, values): 56 result[k] = v 57 return result
58 59 _altsep = os.altsep 60 if _altsep is None and sys.platform == 'win32': 61 # My ActivePython 2.0.1 doesn't set os.altsep! What gives? 62 _altsep = '/' 63 if _altsep:
64 - def rightmost_separator(path, sep, _altsep=_altsep):
65 rfind = string.rfind 66 return max(rfind(path, sep), rfind(path, _altsep))
67 else: 68 rightmost_separator = string.rfind 69 70 # First two from the Python Cookbook, just for completeness. 71 # (Yeah, yeah, YAGNI...)
72 -def containsAny(str, set):
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
78 -def containsAll(str, set):
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
84 -def containsOnly(str, set):
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
90 -def splitext(path):
91 "Same as os.path.splitext() but faster." 92 sep = rightmost_separator(path, os.sep) 93 dot = string.rfind(path, '.') 94 # An ext is only real if it has at least one non-digit char 95 if dot > sep and not containsOnly(path[dot:], "0123456789."): 96 return path[:dot],path[dot:] 97 else: 98 return path,""
99
100 -def updrive(path):
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
112 -class CallableComposite(UserList):
113 """A simple composite callable class that, when called, will invoke all 114 of its contained callables with the same arguments."""
115 - def __call__(self, *args, **kwargs):
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
124 -class NodeList(UserList):
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 """
134 - def __nonzero__(self):
135 return len(self.data) != 0
136
137 - def __str__(self):
138 return string.join(map(str, self.data))
139
140 - def __getattr__(self, name):
141 if not self.data: 142 # If there is nothing in the list, then we have no attributes to 143 # pass through, so raise AttributeError for everything. 144 raise AttributeError, "NodeList has no attribute: %s" % name 145 146 # Return a list of the attribute, gotten from every element 147 # in the list 148 attrList = map(lambda x, n=name: getattr(x, n), self.data) 149 150 # Special case. If the attribute is callable, we do not want 151 # to return a list of callables. Rather, we want to return a 152 # single callable that, when called, will invoke the function on 153 # all elements of this list. 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
160 -def get_environment_var(varstr):
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
175 -class DisplayEngine:
176 - def __init__(self):
177 self.__call__ = self.print_it
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 # Stdout might be connected to a pipe that has been closed 185 # by now. The most likely reason for the pipe being closed 186 # is that the user has press ctrl-c. It this is the case, 187 # then SCons is currently shutdown. We therefore ignore 188 # IOError's here so that SCons can continue and shutdown 189 # properly so that the .sconsign is correctly written 190 # before SCons exits. 191 pass
192
193 - def dont_print(self, text, append_newline=1):
194 pass
195
196 - def set_mode(self, mode):
197 if mode: 198 self.__call__ = self.print_it 199 else: 200 self.__call__ = self.dont_print
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 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 # Functions for deciding if things are like various types, mainly to 317 # handle UserDict, UserList and UserString like their underlying types. 318 # 319 # Yes, all of this manual testing breaks polymorphism, and the real 320 # Pythonic way to do all of this would be to just try it and handle the 321 # exception, but handling the exception when it's not the right type is 322 # often too slow. 323 324 try:
325 - class mystr(str):
326 pass
327 except TypeError: 328 # An older Python version without new-style classes. 329 # 330 # The actual implementations here have been selected after timings 331 # coded up in in bench/is_types.py (from the SCons source tree, 332 # see the scons-src distribution), mostly against Python 1.5.2. 333 # Key results from those timings: 334 # 335 # -- Storing the type of the object in a variable (t = type(obj)) 336 # slows down the case where it's a native type and the first 337 # comparison will match, but nicely speeds up the case where 338 # it's a different native type. Since that's going to be 339 # common, it's a good tradeoff. 340 # 341 # -- The data show that calling isinstance() on an object that's 342 # a native type (dict, list or string) is expensive enough 343 # that checking up front for whether the object is of type 344 # InstanceType is a pretty big win, even though it does slow 345 # down the case where it really *is* an object instance a 346 # little bit.
347 - def is_Dict(obj):
348 t = type(obj) 349 return t is DictType or \ 350 (t is InstanceType and isinstance(obj, UserDict))
351
352 - def is_List(obj):
353 t = type(obj) 354 return t is ListType \ 355 or (t is InstanceType and isinstance(obj, UserList))
356
357 - def is_Sequence(obj):
358 t = type(obj) 359 return t is ListType \ 360 or t is TupleType \ 361 or (t is InstanceType and isinstance(obj, UserList))
362
363 - def is_Tuple(obj):
364 t = type(obj) 365 return t is TupleType
366 367 if hasattr(types, 'UnicodeType'):
368 - def is_String(obj):
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:
374 - def is_String(obj):
375 t = type