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 3266 2008/08/12 07:31:01 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(obj) 376 return t is StringType \ 377 or (t is InstanceType and isinstance(obj, UserString))
378
379 - def is_Scalar(obj):
380 return is_String(obj) or not is_Sequence(obj)
381
382 - def flatten(obj, result=None):
383 """Flatten a sequence to a non-nested list. 384 385 Flatten() converts either a single scalar or a nested sequence 386 to a non-nested list. Note that flatten() considers strings 387 to be scalars instead of sequences like Python would. 388 """ 389 if is_Scalar(obj): 390 return [obj] 391 if result is None: 392 result = [] 393 for item in obj: 394 if is_Scalar(item): 395 result.append(item) 396 else: 397 flatten_sequence(item, result) 398 return result
399
400 - def flatten_sequence(sequence, result=None):
401 """Flatten a sequence to a non-nested list. 402 403 Same as flatten(), but it does not handle the single scalar 404 case. This is slightly more efficient when one knows that 405 the sequence to flatten can not be a scalar. 406 """ 407 if result is None: 408 result = [] 409 for item in sequence: 410 if is_Scalar(item): 411 result.append(item) 412 else: 413 flatten_sequence(item, result) 414 return result
415 416 # 417 # Generic convert-to-string functions that abstract away whether or 418 # not the Python we're executing has Unicode support. The wrapper 419 # to_String_for_signature() will use a for_signature() method if the 420 # specified object has one. 421 # 422 if hasattr(types, 'UnicodeType'): 423 UnicodeType = types.UnicodeType
424 - def to_String(s):
425 if isinstance(s, UserString): 426 t = type(s.data) 427 else: 428 t = type(s) 429 if t is UnicodeType: 430 return unicode(s) 431 else: 432 return str(s)
433 else: 434 to_String = str 435
436 - def to_String_for_signature(obj):
437 try: 438 f = obj.for_signature 439 except AttributeError: 440 return to_String_for_subst(obj) 441 else: 442 return f()
443
444 - def to_String_for_subst(s):
445 if is_Sequence( s ): 446 return string.join( map(to_String_for_subst, s) ) 447 448 return to_String( s )
449 450 else: 451 # A modern Python version with new-style classes, so we can just use 452 # isinstance(). 453 # 454 # We are using the following trick to speed-up these 455 # functions. Default arguments are used to take a snapshot of the 456 # the global functions and constants used by these functions. This 457 # transforms accesses to global variable into local variables 458 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL). 459 460 DictTypes = (dict, UserDict) 461 ListTypes = (list, UserList) 462 SequenceTypes = (list, tuple, UserList) 463 464 # Empirically, Python versions with new-style classes all have 465 # unicode. 466 # 467 # Note that profiling data shows a speed-up when comparing 468 # explicitely with str and unicode instead of simply comparing 469 # with basestring. (at least on Python 2.5.1) 470 StringTypes = (str, unicode, UserString) 471 472 # Empirically, it is faster to check explicitely for str and 473 # unicode than for basestring. 474 BaseStringTypes = (str, unicode) 475
476 - def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
477 return isinstance(obj, DictTypes)
478
479 - def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
480 return isinstance(obj, ListTypes)
481
482 - def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
483 return isinstance(obj, SequenceTypes)
484
485 - def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
486 return isinstance(obj, tuple)
487
488 - def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
489 return isinstance(obj, StringTypes)
490
491 - def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
492 # Profiling shows that there is an impressive speed-up of 2x 493 # when explicitely checking for strings instead of just not 494 # sequence when the argument (i.e. obj) is already a string. 495 # But, if obj is a not string than it is twice as fast to 496 # check only for 'not sequence'. The following code therefore 497 # assumes that the obj argument is a string must of the time. 498 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
499
500 - def do_flatten(sequence, result, isinstance=isinstance, 501 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
502 for item in sequence: 503 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 504 result.append(item) 505 else: 506 do_flatten(item, result)
507
508 - def flatten(obj, isinstance=isinstance, StringTypes=StringTypes, 509 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
510 """Flatten a sequence to a non-nested list. 511 512 Flatten() converts either a single scalar or a nested sequence 513 to a non-nested list. Note that flatten() considers strings 514 to be scalars instead of sequences like Python would. 515 """ 516 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes): 517 return [obj] 518 result = [] 519 for item in obj: 520 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 521 result.append(item) 522 else: 523 do_flatten(item, result) 524 return result
525
526 - def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes, 527 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
528 """Flatten a sequence to a non-nested list. 529 530 Same as flatten(), but it does not handle the single scalar 531 case. This is slightly more efficient when one knows that 532 the sequence to flatten can not be a scalar. 533 """ 534 result = [] 535 for item in sequence: 536 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 537 result.append(item) 538 else: 539 do_flatten(item, result) 540 return result
541 542 543 # 544 # Generic convert-to-string functions that abstract away whether or 545 # not the Python we're executing has Unicode support. The wrapper 546 # to_String_for_signature() will use a for_signature() method if the 547 # specified object has one. 548 #
549 - def to_String(s, 550 isinstance=isinstance, str=str, 551 UserString=UserString, BaseStringTypes=BaseStringTypes):
552 if isinstance(s,BaseStringTypes): 553 # Early out when already a string! 554 return s 555 elif isinstance(s, UserString): 556 # s.data can only be either a unicode or a regular 557 # string. Please see the UserString initializer. 558 return s.data 559 else: 560 return str(s)
561
562 - def to_String_for_subst(s, 563 isinstance=isinstance, join=string.join, str=str, to_String=to_String, 564 BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes, 565 UserString=UserString):
566 567 # Note that the test cases are sorted by order of probability. 568 if isinstance(s, BaseStringTypes): 569 return s 570 elif isinstance(s, SequenceTypes): 571 l = [] 572 for e in s: 573 l.append(to_String_for_subst(e)) 574 return join( s ) 575 elif isinstance(s, UserString): 576 # s.data can only be either a unicode or a regular 577 # string. Please see the UserString initializer. 578 return s.data 579 else: 580 return str(s)
581
582 - def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst, 583 AttributeError=AttributeError):
584 try: 585 f = obj.for_signature 586 except AttributeError: 587 return to_String_for_subst(obj) 588 else: 589 return f()
590 591 592 593 # The SCons "semi-deep" copy. 594 # 595 # This makes separate copies of lists (including UserList objects) 596 # dictionaries (including UserDict objects) and tuples, but just copies 597 # references to anything else it finds. 598 # 599 # A special case is any object that has a __semi_deepcopy__() method, 600 # which we invoke to create the copy, which is used by the BuilderDict 601 # class because of its extra initialization argument. 602 # 603 # The dispatch table approach used here is a direct rip-off from the 604 # normal Python copy module. 605 606 _semi_deepcopy_dispatch = d = {} 607
608 -def _semi_deepcopy_dict(x):
609 copy = {} 610 for key, val in x.items(): 611 # The regular Python copy.deepcopy() also deepcopies the key, 612 # as follows: 613 # 614 # copy[semi_deepcopy(key)] = semi_deepcopy(val) 615 # 616 # Doesn't seem like we need to, but we'll comment it just in case. 617 copy[key] = semi_deepcopy(val) 618 return copy
619 d[types.DictionaryType] = _semi_deepcopy_dict 620
621 -def _semi_deepcopy_list(x):
622 return map(semi_deepcopy, x)
623 d[types.ListType] = _semi_deepcopy_list 624
625 -def _semi_deepcopy_tuple(x):
626 return tuple(map(semi_deepcopy, x))
627 d[types.TupleType] = _semi_deepcopy_tuple 628
629 -def _semi_deepcopy_inst(x):
630 if hasattr(x, '__semi_deepcopy__'): 631 return x.__semi_deepcopy__() 632 elif isinstance(x, UserDict): 633 return x.__class__(_semi_deepcopy_dict(x)) 634 elif isinstance(x, UserList): 635 return x.__class__(_semi_deepcopy_list(x)) 636 else: 637 return x
638 d[types.InstanceType] = _semi_deepcopy_inst 639
640 -def semi_deepcopy(x):
641 copier = _semi_deepcopy_dispatch.get(type(x)) 642 if copier: 643 return copier(x) 644 else: 645 return x
646 647 648
649 -class Proxy:
650 """A simple generic Proxy class, forwarding all calls to 651 subject. So, for the benefit of the python newbie, what does 652 this really mean? Well, it means that you can take an object, let's 653 call it 'objA', and wrap it in this Proxy class, with a statement 654 like this 655 656 proxyObj = Proxy(objA), 657 658 Then, if in the future, you do something like this 659 660 x = proxyObj.var1, 661 662 since Proxy does not have a 'var1' attribute (but presumably objA does), 663 the request actually is equivalent to saying 664 665 x = objA.var1 666 667 Inherit from this class to create a Proxy.""" 668
669 - def __init__(self, subject):
670 """Wrap an object as a Proxy object""" 671 self.__subject = subject
672
673 - def __getattr__(self, name):
674 """Retrieve an attribute from the wrapped object. If the named 675 attribute doesn't exist, AttributeError is raised""" 676 return getattr(self.__subject, name)
677
678 - def get(self):
679 """Retrieve the entire wrapped object""" 680 return self.__subject
681
682 - def __cmp__(self, other):
683 if issubclass(other.__class__, self.__subject.__class__): 684 return cmp(self.__subject, other) 685 return cmp(self.__dict__, other.__dict__)
686 687 # attempt to load the windows registry module: 688 can_read_reg = 0 689 try: 690 import _winreg 691 692 can_read_reg = 1 693 hkey_mod = _winreg 694 695 RegOpenKeyEx = _winreg.OpenKeyEx 696 RegEnumKey = _winreg.EnumKey 697 RegEnumValue = _winreg.EnumValue 698 RegQueryValueEx = _winreg.QueryValueEx 699 RegError = _winreg.error 700 701 except ImportError: 702 try: 703 import win32api 704 import win32con 705 can_read_reg = 1 706 hkey_mod = win32con 707 708 RegOpenKeyEx = win32api.RegOpenKeyEx 709 RegEnumKey = win32api.RegEnumKey 710 RegEnumValue = win32api.RegEnumValue 711 RegQueryValueEx = win32api.RegQueryValueEx 712 RegError = win32api.error 713 714 except ImportError:
715 - class _NoError(Exception):
716 pass
717 RegError = _NoError 718 719 if can_read_reg: 720 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT 721 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE 722 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER 723 HKEY_USERS = hkey_mod.HKEY_USERS 724
725 - def RegGetValue(root, key):
726 """This utility function returns a value in the registry 727 without having to open the key first. Only available on 728 Windows platforms with a version of Python that can read the 729 registry. Returns the same thing as 730 SCons.Util.RegQueryValueEx, except you just specify the entire 731 path to the value, and don't have to bother opening the key 732 first. So: 733 734 Instead of: 735 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, 736 r'SOFTWARE\Microsoft\Windows\CurrentVersion') 737 out = SCons.Util.RegQueryValueEx(k, 738 'ProgramFilesDir') 739 740 You can write: 741 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, 742 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir') 743 """ 744 # I would use os.path.split here, but it's not a filesystem 745 # path... 746 p = key.rfind('\\') + 1 747 keyp = key[:p] 748 val = key[p:] 749 k = RegOpenKeyEx(root, keyp) 750 return RegQueryValueEx(k,val)
751 752 if sys.platform == 'win32': 753
754 - def WhereIs(file, path=None, pathext=None, reject=[]):
755 if path is None: 756 try: 757 path = os.environ['PATH'] 758 except KeyError: 759 return None 760 if is_String(path): 761 path = string.split(path, os.pathsep) 762 if pathext is None: 763 try: 764 pathext = os.environ['PATHEXT'] 765 except KeyError: 766 pathext = '.COM;.EXE;.BAT;.CMD' 767 if is_String(pathext): 768 pathext = string.split(pathext, os.pathsep) 769 for ext in pathext: 770 if string.lower(ext) == string.lower(file[-len(ext):]): 771 pathext = [''] 772 break 773 if not is_List(reject) and not is_Tuple(reject): 774 reject = [reject] 775 for dir in path: 776 f = os.path.join(dir, file) 777 for ext in pathext: 778 fext = f + ext 779 if os.path.isfile(fext): 780 try: 781 reject.index(fext) 782 except ValueError: 783 return os.path.normpath(fext) 784 continue 785 return None
786 787 elif os.name == 'os2': 788
789 - def WhereIs(file, path=None, pathext=None, reject=[]):
790 if path is None: 791 try: 792 path = os.environ['PATH'] 793 except KeyError: 794 return None 795 if is_String(path): 796 path = string.split(path, os.pathsep) 797 if pathext is None: 798 pathext = ['.exe', '.cmd'] 799 for ext in pathext: 800 if string.lower(ext) == string.lower(file[-len(ext):]): 801 pathext = [''] 802 break 803 if not is_List(reject) and not is_Tuple(reject): 804 reject = [reject] 805 for dir in path: 806 f = os.path.join(dir, file) 807 for ext in pathext: 808 fext = f + ext 809 if os.path.isfile(fext): 810 try: 811 reject.index(fext) 812 except ValueError: 813 return os.path.normpath(fext) 814 continue 815 return None
816 817 else: 818
819 - def WhereIs(file, path=None, pathext=None, reject=[]):
820 import stat 821 if path is None: 822 try: 823 path = os.environ['PATH'] 824 except KeyError: 825 return None 826 if is_String(path): 827 path = string.split(path, os.pathsep) 828 if not is_List(reject) and not is_Tuple(reject): 829 reject = [reject] 830 for d in path: 831 f = os.path.join(d, file) 832 if os.path.isfile(f): 833 try: 834 st = os.stat(f) 835 except OSError: 836 # os.stat() raises OSError, not IOError if the file 837 # doesn't exist, so in this case we let IOError get 838 # raised so as to not mask possibly serious disk or 839 # network issues. 840 continue 841 if stat.S_IMODE(st[stat.ST_MODE]) & 0111: 842 try: 843 reject.index(f) 844 except ValueError: 845 return os.path.normpath(f) 846 continue 847 return None
848
849 -def PrependPath(oldpath, newpath, sep = os.pathsep):
850 """This prepends newpath elements to the given oldpath. Will only 851 add any particular path once (leaving the first one it encounters 852 and ignoring the rest, to preserve path order), and will 853 os.path.normpath and os.path.normcase all paths to help assure 854 this. This can also handle the case where the given old path 855 variable is a list instead of a string, in which case a list will 856 be returned instead of a string. 857 858 Example: 859 Old Path: "/foo/bar:/foo" 860 New Path: "/biz/boom:/foo" 861 Result: "/biz/boom:/foo:/foo/bar" 862 """ 863 864 orig = oldpath 865 is_list = 1 866 paths = orig 867 if not is_List(orig) and not is_Tuple(orig): 868 paths = string.split(paths, sep) 869 is_list = 0 870 871 if is_List(newpath) or is_Tuple(newpath): 872 newpaths = newpath 873 else: 874 newpaths = string.split(newpath, sep) 875 876 newpaths = newpaths + paths # prepend new paths 877 878 normpaths = [] 879 paths = [] 880 # now we add them only if they are unique 881 for path in newpaths: 882 normpath = os.path.normpath(os.path.normcase(path)) 883 if path and not normpath in normpaths: 884 paths.append(path) 885 normpaths.append(normpath) 886 887 if is_list: 888 return paths 889 else: 890 return string.join(paths, sep)
891
892 -def AppendPath(oldpath, newpath, sep = os.pathsep):
893 """This appends new path elements to the given old path. Will 894 only add any particular path once (leaving the last one it 895 encounters and ignoring the rest, to preserve path order), and 896 will os.path.normpath and os.path.normcase all paths to help 897 assure this. This can also handle the case where the given old 898 path variable is a list instead of a string, in which case a list 899 will be returned instead of a string. 900 901 Example: 902 Old Path: "/foo/bar:/foo" 903 New Path: "/biz/boom:/foo" 904 Result: "/foo/bar:/biz/boom:/foo" 905 """ 906 907 orig = oldpath 908 is_list = 1 909 paths = orig 910 if not is_List(orig) and not is_Tuple(orig): 911 paths = string.split(paths, sep) 912 is_list = 0 913 914 if is_List(newpath) or is_Tuple(newpath): 915 newpaths = newpath 916 else: 917 newpaths = string.split(newpath, sep) 918 919 newpaths = paths + newpaths # append new paths 920 newpaths.reverse() 921 922 normpaths = [] 923 paths = [] 924 # now we add them only of they are unique 925 for path in newpaths: 926 normpath = os.path.normpath(os.path.normcase(path)) 927 if path and not normpath in normpaths: 928 paths.append(path) 929 normpaths.append(normpath) 930 931 paths.reverse() 932 933 if is_list: 934 return paths 935 else: 936 return string.join(paths, sep)
937 938 if sys.platform == 'cygwin':
939 - def get_native_path(path):
940 """Transforms an absolute path into a native path for the system. In 941 Cygwin, this converts from a Cygwin path to a Windows one.""" 942 return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '')
943 else:
944 - def get_native_path(path):
945 """Transforms an absolute path into a native path for the system. 946 Non-Cygwin version, just leave the path alone.""" 947 return path
948 949 display = DisplayEngine() 950
951 -def Split(arg):
952 if is_List(arg) or is_Tuple(arg): 953 return arg 954 elif is_String(arg): 955 return string.split(arg) 956 else: 957 return [arg]
958
959 -class CLVar(UserList):
960 """A class for command-line construction variables. 961 962 This is a list that uses Split() to split an initial string along 963 white-space arguments, and similarly to split any strings that get 964 added. This allows us to Do the Right Thing with Append() and 965 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1 966 arg2') regardless of whether a user adds a list or a string to a 967 command-line construction variable. 968 """
969 - def __init__(self, seq = []):
970 UserList.__init__(self, Split(seq))
971 - def __coerce__(self, other):
972 return (self, CLVar(other))
973 - def __str__(self):
974 return string.join(self.data)
975 976 # A dictionary that preserves the order in which items are added. 977 # Submitted by David Benjamin to ActiveState's Python Cookbook web site: 978 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 979 # Including fixes/enhancements from the follow-on discussions.
980 -class OrderedDict(UserDict):
981 - def __init__(self, dict = None):
982 self._keys = [] 983 UserDict.__init__(self, dict)
984
985 - def __delitem__(self, key):
986 UserDict.__delitem__(self, key) 987 self._keys.remove(key)
988
989 - def __setitem__(self, key, item):
990 UserDict.__setitem__(self, key, item) 991 if key not in self._keys: self._keys.append(key)
992
993 - def clear(self):
994 UserDict.clear(self) 995 self._keys = []
996
997 - def copy(self):
998 dict = OrderedDict() 999 dict.update(self) 1000 return dict
1001
1002 - def items(self):
1003 return zip(self._keys, self.values())
1004
1005 - def keys(self):
1006 return self._keys[:]
1007
1008 - def popitem(self):
1009 try: 1010 key = self._keys[-1] 1011 except IndexError: 1012 raise KeyError('dictionary is empty') 1013 1014 val = self[key] 1015 del self[key] 1016 1017 return (key, val)
1018
1019 - def setdefault(self, key, failobj = None):
1020 UserDict.setdefault(self, key, failobj) 1021 if key not in self._keys: self._keys.append(key)
1022
1023 - def update(self, dict):
1024 for (key, val) in dict.items(): 1025 self.__setitem__(key, val)
1026
1027 - def values(self):
1028 return map(self.get, self._keys)
1029
1030 -class Selector(OrderedDict):
1031 """A callable ordered dictionary that maps file suffixes to 1032 dictionary values. We preserve the order in which items are added 1033 so that get_suffix() calls always return the first suffix added."""
1034 - def __call__(self, env, source):
1035 try: 1036 ext = source[0].suffix 1037 except IndexError: 1038 ext = "" 1039 try: 1040 return self[ext] 1041 except KeyError: 1042 # Try to perform Environment substitution on the keys of 1043 # the dictionary before giving up. 1044 s_dict = {} 1045 for (k,v) in self.items(): 1046 if not k is None: 1047 s_k = env.subst(k) 1048 if s_dict.has_key(s_k): 1049 # We only raise an error when variables point 1050 # to the same suffix. If one suffix is literal 1051 # and a variable suffix contains this literal, 1052 # the literal wins and we don't raise an error. 1053 raise KeyError, (s_dict[s_k][0], k, s_k) 1054 s_dict[s_k] = (k,v) 1055 try: 1056 return s_dict[ext][1] 1057 except KeyError: 1058 try: 1059 return self[None] 1060 except KeyError: 1061 return None
1062 1063 1064 if sys.platform == 'cygwin': 1065 # On Cygwin, os.path.normcase() lies, so just report back the 1066 # fact that the underlying Windows OS is case-insensitive.
1067 - def case_sensitive_suffixes(s1, s2):
1068 return 0
1069 else:
1070 - def case_sensitive_suffixes(s1, s2):
1071 return (os.path.normcase(s1) != os.path.normcase(s2))
1072
1073 -def adjustixes(fname, pre, suf, ensure_suffix=False):
1074 if pre: 1075 path, fn = os.path.split(os.path.normpath(fname)) 1076 if fn[:len(pre)] != pre: 1077 fname = os.path.join(path, pre + fn) 1078 # Only append a suffix if the suffix we're going to add isn't already 1079 # there, and if either we've been asked to ensure the specific suffix 1080 # is present or there's no suffix on it at all. 1081 if suf and fname[-len(suf):] != suf and \ 1082 (ensure_suffix or not splitext(fname)[1]): 1083 fname = fname + suf 1084 return fname
1085 1086 1087 1088 # From Tim Peters, 1089 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1090 # ASPN: Python Cookbook: Remove duplicates from a sequence 1091 # (Also in the printed Python Cookbook.) 1092
1093 -def unique(s):
1094 """Return a list of the elements in s, but without duplicates. 1095 1096 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], 1097 unique("abcabc") some permutation of ["a", "b", "c"], and 1098 unique(([1, 2], [2, 3], [1, 2])) some permutation of 1099 [[2, 3], [1, 2]]. 1100 1101 For best speed, all sequence elements should be hashable. Then 1102 unique() will usually work in linear time. 1103 1104 If not possible, the sequence elements should enjoy a total 1105 ordering, and if list(s).sort() doesn't raise TypeError it's 1106 assumed that they do enjoy a total ordering. Then unique() will 1107 usually work in O(N*log2(N)) time. 1108 1109 If that's not possible either, the sequence elements must support 1110 equality-testing. Then unique() will usually work in quadratic 1111 time. 1112 """ 1113 1114 n = len(s) 1115 if n == 0: 1116 return [] 1117 1118 # Try using a dict first, as that's the fastest and will usually 1119 # work. If it doesn't work, it will usually fail quickly, so it 1120 # usually doesn't cost much to *try* it. It requires that all the 1121 # sequence elements be hashable, and support equality comparison. 1122 u = {} 1123 try: 1124 for x in s: 1125 u[x] = 1 1126 except TypeError: 1127 pass # move on to the next method 1128 else: 1129 return u.keys() 1130 del u 1131 1132 # We can't hash all the elements. Second fastest is to sort, 1133 # which brings the equal elements together; then duplicates are 1134 # easy to weed out in a single pass. 1135 # NOTE: Python's list.sort() was designed to be efficient in the 1136 # presence of many duplicate elements. This isn't true of all 1137 # sort functions in all languages or libraries, so this approach 1138 # is more effective in Python than it may be elsewhere. 1139 try: 1140 t = list(s) 1141 t.sort() 1142 except TypeError: 1143 pass # move on to the next method 1144 else: 1145 assert n > 0 1146 last = t[0] 1147 lasti = i = 1 1148 while i < n: 1149 if t[i] != last: 1150 t[lasti] = last = t[i] 1151 lasti = lasti + 1 1152 i = i + 1 1153 return t[:lasti] 1154 del t 1155 1156 # Brute force is all that's left. 1157 u = [] 1158 for x in s: 1159 if x not in u: 1160 u.append(x) 1161 return u
1162 1163 1164 1165 # From Alex Martelli, 1166 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1167 # ASPN: Python Cookbook: Remove duplicates from a sequence 1168 # First comment, dated 2001/10/13. 1169 # (Also in the printed Python Cookbook.) 1170
1171 -def uniquer(seq, idfun=None):
1172 if idfun is None: 1173 def idfun(x): return x 1174 seen = {} 1175 result = [] 1176 for item in seq: 1177 marker = idfun(item) 1178 # in old Python versions: 1179 # if seen.has_key(marker) 1180 # but in new ones: 1181 if marker in seen: continue 1182 seen[marker] = 1 1183 result.append(item) 1184 return result
1185 1186 # A more efficient implementation of Alex's uniquer(), this avoids the 1187 # idfun() argument and function-call overhead by assuming that all 1188 # items in the sequence are hashable. 1189
1190 -def uniquer_hashables(seq):
1191 seen = {} 1192 result = [] 1193 for item in seq: 1194 #if not item in seen: 1195 if not seen.has_key(item): 1196 seen[item] = 1 1197 result.append(item) 1198 return result
1199 1200 1201 1202 # Much of the logic here was originally based on recipe 4.9 from the 1203 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1204 -class LogicalLines:
1205
1206 - def __init__(self, fileobj):
1207 self.fileobj = fileobj
1208
1209 - def readline(self):
1210 result = [] 1211 while 1: 1212 line = self.fileobj.readline() 1213 if not line: 1214 break 1215 if line[-2:] == '\\\n': 1216 result.append(line[:-2]) 1217 else: 1218 result.append(line) 1219 break 1220 return string.join(result, '')
1221
1222 - def readlines(self):
1223 result = [] 1224 while 1: 1225 line = self.readline() 1226 if not line: 1227 break 1228 result.append(line) 1229 return result
1230 1231 1232
1233 -class UniqueList(UserList):
1234 - def __init__(self, seq = []):
1235 UserList.__init__(self, seq) 1236 self.unique = True
1237 - def __make_unique(self):
1238 if not self.unique: 1239 self.data = uniquer_hashables(self.data) 1240 self.unique = True
1241 - def __lt__(self, other):
1242 self.__make_unique() 1243 return UserList.__lt__(self, other)
1244 - def __le__(self, other):
1245 self.__make_unique() 1246 return UserList.__le__(self, other)
1247 - def __eq__(self, other):
1248 self.__make_unique() 1249 return UserList.__eq__(self, other)
1250 - def __ne__(self, other):
1251 self.__make_unique() 1252 return UserList.__ne__(self, other)
1253 - def __gt__(self, other):
1254 self.__make_unique() 1255 return UserList.__gt__(self, other)
1256 - def __ge__(self, other):
1257 self.__make_unique() 1258 return UserList.__ge__(self, other)
1259 - def __cmp__(self, other):
1260 self.__make_unique() 1261 return UserList.__cmp__(self, other)
1262 - def __len__(self):
1263 self.__make_unique() 1264 return UserList.__len__(self)
1265 - def __getitem__(self, i):
1266 self.__make_unique() 1267 return UserList.__getitem__(self, i)
1268 - def __setitem__(self, i, item):
1269 UserList.__setitem__(self, i, item) 1270 self.unique = False
1271 - def __getslice__(self, i, j):
1272 self.__make_unique() 1273 return UserList.__getslice__(self, i, j)
1274 - def __setslice__(self, i, j, other):
1275 UserList.__setslice__(self, i, j, other) 1276 self.unique = False
1277 - def __add__(self, other):
1278 result = UserList.__add__(self, other) 1279 result.unique = False 1280 return result
1281 - def __radd__(self, other):
1282 result = UserList.__radd__(self, other) 1283 result.unique = False 1284 return result
1285 - def __iadd__(self, other):
1286 result = UserList.__iadd__(self, other) 1287 result.unique = False 1288 return result
1289 - def __mul__(self, other):
1290 result = UserList.__mul__(self, other) 1291 result.unique = False 1292 return result
1293 - def __rmul__(self, other):
1294 result = UserList.__rmul__(self, other) 1295 result.unique = False 1296 return result
1297 - def __imul__(self, other):
1298 result = UserList.__imul__(self, other) 1299 result.unique = False 1300 return result
1301 - def append(self, item):
1302 UserList.append(self, item) 1303 self.unique = False
1304 - def insert(self, i):
1305 UserList.insert(self, i) 1306 self.unique = False
1307 - def count(self, item):
1308 self.__make_unique() 1309 return UserList.count(self, item)
1310 - def index(self, item):
1311 self.__make_unique() 1312 return UserList.index(self, item)
1313 - def reverse(self):
1314 self.__make_unique() 1315 UserList.reverse(self)
1316 - def sort(self, *args, **kwds):
1317 self.__make_unique() 1318 #return UserList.sort(self, *args, **kwds) 1319 return apply(UserList.sort, (self,)+args, kwds)
1320 - def extend(self, other):
1321 UserList.extend(self, other) 1322 self.unique = False
1323 1324 1325
1326 -class Unbuffered:
1327 """ 1328 A proxy class that wraps a file object, flushing after every write, 1329 and delegating everything else to the wrapped object. 1330 """
1331 - def __init__(self, file):
1332 self.file = file
1333 - def write(self, arg):
1334 try: 1335 self.file.write(arg) 1336 self.file.flush() 1337 except IOError: 1338 # Stdout might be connected to a pipe that has been closed 1339 # by now. The most likely reason for the pipe being closed 1340 # is that the user has press ctrl-c. It this is the case, 1341 # then SCons is currently shutdown. We therefore ignore 1342 # IOError's here so that SCons can continue and shutdown 1343 # properly so that the .sconsign is correctly written 1344 # before SCons exits. 1345 pass
1346 - def __getattr__(self, attr):
1347 return getattr(self.file, attr)
1348
1349 -def make_path_relative(path):
1350 """ makes an absolute path name to a relative pathname. 1351 """ 1352 if os.path.isabs(path): 1353 drive_s,path = os.path.splitdrive(path) 1354 1355 import re 1356 if not drive_s: 1357 path=re.compile("/*(.*)").findall(path)[0] 1358 else: 1359 path=path[1:] 1360 1361 assert( not os.path.isabs( path ) ), path 1362 return path
1363 1364 1365 1366 # The original idea for AddMethod() and RenameFunction() come from the 1367 # following post to the ActiveState Python Cookbook: 1368 # 1369 # ASPN: Python Cookbook : Install bound methods in an instance 1370 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 1371 # 1372 # That code was a little fragile, though, so the following changes 1373 # have been wrung on it: 1374 # 1375 # * Switched the installmethod() "object" and "function" arguments, 1376 # so the order reflects that the left-hand side is the thing being 1377 # "assigned to" and the right-hand side is the value being assigned. 1378 # 1379 # * Changed explicit type-checking to the "try: klass = object.__class__" 1380 # block in installmethod() below so that it still works with the 1381 # old-style classes that SCons uses. 1382 # 1383 # * Replaced the by-hand creation of methods and functions with use of 1384 # the "new" module, as alluded to in Alex Martelli's response to the 1385 # following Cookbook post: 1386 # 1387 # ASPN: Python Cookbook : Dynamically added methods to a class 1388 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 1389
1390 -def AddMethod(object, function, name = None):
1391 """ 1392 Adds either a bound method to an instance or an unbound method to 1393 a class. If name is ommited the name of the specified function 1394 is used by default. 1395 Example: 1396 a = A() 1397 def f(self, x, y): 1398 self.z = x + y 1399 AddMethod(f, A, "add") 1400 a.add(2, 4) 1401 print a.z 1402 AddMethod(lambda self, i: self.l[i], a, "listIndex") 1403 print a.listIndex(5) 1404 """ 1405 import new 1406 1407 if name is None: 1408 name = function.func_name 1409 else: 1410 function = RenameFunction(function, name) 1411 1412 try: 1413 klass = object.__class__ 1414 except AttributeError: 1415 # "object" is really a class, so it gets an unbound method. 1416 object.__dict__[name] = new.instancemethod(function, None, object) 1417 else: 1418 # "object" is really an instance, so it gets a bound method. 1419 object.__dict__[name] = new.instancemethod(function, object, klass)
1420
1421 -def RenameFunction(function, name):
1422 """ 1423 Returns a function identical to the specified function, but with 1424 the specified name. 1425 """ 1426 import new 1427 1428 # Compatibility for Python 1.5 and 2.1. Can be removed in favor of 1429 # passing function.func_defaults directly to new.function() once 1430 # we base on Python 2.2 or later. 1431 func_defaults = function.func_defaults 1432 if func_defaults is None: 1433 func_defaults = () 1434 1435 return new.function(function.func_code, 1436 function.func_globals, 1437 name, 1438 func_defaults)
1439 1440 1441 md5 = False
1442 -def MD5signature(s):
1443 return str(s)
1444 1445 try: 1446 import hashlib 1447 except ImportError: 1448 pass 1449 else: 1450 if hasattr(hashlib, 'md5'): 1451 md5 = True
1452 - def MD5signature(s):
1453 m = hashlib.md5() 1454 m.update(str(s)) 1455 return m.hexdigest()
1456
1457 -def MD5collect(signatures):
1458 """ 1459 Collects a list of signatures into an aggregate signature. 1460 1461 signatures - a list of signatures 1462 returns - the aggregate signature 1463 """ 1464 if len(signatures) == 1: 1465 return signatures[0] 1466 else: 1467 return MD5signature(string.join(signatures, ', '))
1468 1469 1470 1471 # From Dinu C. Gherman, 1472 # Python Cookbook, second edition, recipe 6.17, p. 277. 1473 # Also: 1474 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 1475 # ASPN: Python Cookbook: Null Object Design Pattern 1476
1477 -class Null:
1478 """ Null objects always and reliably "do nothging." """ 1479
1480 - def __new__(cls, *args, **kwargs):
1481 if not '_inst' in vars(cls): 1482 #cls._inst = type.__new__(cls, *args, **kwargs) 1483 cls._inst = apply(type.__new__, (cls,) + args, kwargs) 1484 return cls._inst
1485 - def __init__(self, *args, **kwargs):
1486 pass
1487 - def __call__(self, *args, **kwargs):
1488 return self
1489 - def __repr__(self):
1490 return "Null()"
1491 - def __nonzero__(self):
1492 return False
1493 - def __getattr__(self, mname):
1494 return self
1495 - def __setattr__(self, name, value):
1496 return self
1497 - def __delattr__(self, name):
1498 return self
1499 1500 1501 1502 del __revision__ 1503