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  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation 
   7  # 
   8  # Permission is hereby granted, free of charge, to any person obtaining 
   9  # a copy of this software and associated documentation files (the 
  10  # "Software"), to deal in the Software without restriction, including 
  11  # without limitation the rights to use, copy, modify, merge, publish, 
  12  # distribute, sublicense, and/or sell copies of the Software, and to 
  13  # permit persons to whom the Software is furnished to do so, subject to 
  14  # the following conditions: 
  15  # 
  16  # The above copyright notice and this permission notice shall be included 
  17  # in all copies or substantial portions of the Software. 
  18  # 
  19  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  20  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  21  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  22  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  23  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  24  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  25  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  26   
  27  __revision__ = "src/engine/SCons/Util.py 5023 2010/06/14 22:05:46 scons" 
  28   
  29  import os 
  30  import sys 
  31  import copy 
  32  import re 
  33  import types 
  34   
  35  from collections import UserDict, UserList, UserString 
  36   
  37  # Don't "from types import ..." these because we need to get at the 
  38  # types module later to look for UnicodeType. 
  39  InstanceType    = types.InstanceType 
  40  MethodType      = types.MethodType 
  41  FunctionType    = types.FunctionType 
  42  try: unicode 
  43  except NameError: UnicodeType = None 
  44  else:             UnicodeType = unicode 
  45   
46 -def dictify(keys, values, result={}):
47 for k, v in zip(keys, values): 48 result[k] = v 49 return result
50 51 _altsep = os.altsep 52 if _altsep is None and sys.platform == 'win32': 53 # My ActivePython 2.0.1 doesn't set os.altsep! What gives? 54 _altsep = '/' 55 if _altsep:
56 - def rightmost_separator(path, sep):
57 return max(path.rfind(sep), path.rfind(_altsep))
58 else:
59 - def rightmost_separator(path, sep):
60 return path.rfind(sep)
61 62 # First two from the Python Cookbook, just for completeness. 63 # (Yeah, yeah, YAGNI...)
64 -def containsAny(str, set):
65 """Check whether sequence str contains ANY of the items in set.""" 66 for c in set: 67 if c in str: return 1 68 return 0
69
70 -def containsAll(str, set):
71 """Check whether sequence str contains ALL of the items in set.""" 72 for c in set: 73 if c not in str: return 0 74 return 1
75
76 -def containsOnly(str, set):
77 """Check whether sequence str contains ONLY items in set.""" 78 for c in str: 79 if c not in set: return 0 80 return 1
81
82 -def splitext(path):
83 "Same as os.path.splitext() but faster." 84 sep = rightmost_separator(path, os.sep) 85 dot = path.rfind('.') 86 # An ext is only real if it has at least one non-digit char 87 if dot > sep and not containsOnly(path[dot:], "0123456789."): 88 return path[:dot],path[dot:] 89 else: 90 return path,""
91
92 -def updrive(path):
93 """ 94 Make the drive letter (if any) upper case. 95 This is useful because Windows is inconsitent on the case 96 of the drive letter, which can cause inconsistencies when 97 calculating command signatures. 98 """ 99 drive, rest = os.path.splitdrive(path) 100 if drive: 101 path = drive.upper() + rest 102 return path
103
104 -class NodeList(UserList):
105 """This class is almost exactly like a regular list of Nodes 106 (actually it can hold any object), with one important difference. 107 If you try to get an attribute from this list, it will return that 108 attribute from every item in the list. For example: 109 110 >>> someList = NodeList([ ' foo ', ' bar ' ]) 111 >>> someList.strip() 112 [ 'foo', 'bar' ] 113 """
114 - def __nonzero__(self):
115 return len(self.data) != 0
116
117 - def __str__(self):
118 return ' '.join(map(str, self.data))
119
120 - def __iter__(self):
121 return iter(self.data)
122
123 - def __call__(self, *args, **kwargs):
124 result = [x(*args, **kwargs) for x in self.data] 125 return self.__class__(result)
126
127 - def __getattr__(self, name):
128 result = [getattr(x, name) for x in self.data] 129 return self.__class__(result)
130 131 132 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') 133
134 -def get_environment_var(varstr):
135 """Given a string, first determine if it looks like a reference 136 to a single environment variable, like "$FOO" or "${FOO}". 137 If so, return that variable with no decorations ("FOO"). 138 If not, return None.""" 139 mo=_get_env_var.match(to_String(varstr)) 140 if mo: 141 var = mo.group(1) 142 if var[0] == '{': 143 return var[1:-1] 144 else: 145 return var 146 else: 147 return None
148
149 -class DisplayEngine(object):
150 print_it = True
151 - def __call__(self, text, append_newline=1):
152 if not self.print_it: 153 return 154 if append_newline: text = text + '\n' 155 try: 156 sys.stdout.write(unicode(text)) 157 except IOError: 158 # Stdout might be connected to a pipe that has been closed 159 # by now. The most likely reason for the pipe being closed 160 # is that the user has press ctrl-c. It this is the case, 161 # then SCons is currently shutdown. We therefore ignore 162 # IOError's here so that SCons can continue and shutdown 163 # properly so that the .sconsign is correctly written 164 # before SCons exits. 165 pass
166
167 - def set_mode(self, mode):
168 self.print_it = mode
169
170 -def render_tree(root, child_func, prune=0, margin=[0], visited={}):
171 """ 172 Render a tree of nodes into an ASCII tree view. 173 root - the root node of the tree 174 child_func - the function called to get the children of a node 175 prune - don't visit the same node twice 176 margin - the format of the left margin to use for children of root. 177 1 results in a pipe, and 0 results in no pipe. 178 visited - a dictionary of visited nodes in the current branch if not prune, 179 or in the whole tree if prune. 180 """ 181 182 rname = str(root) 183 184 children = child_func(root) 185 retval = "" 186 for pipe in margin[:-1]: 187 if pipe: 188 retval = retval + "| " 189 else: 190 retval = retval + " " 191 192 if rname in visited: 193 return retval + "+-[" + rname + "]\n" 194 195 retval = retval + "+-" + rname + "\n" 196 if not prune: 197 visited = copy.copy(visited) 198 visited[rname] = 1 199 200 for i in range(len(children)): 201 margin.append(i<len(children)-1) 202 retval = retval + render_tree(children[i], child_func, prune, margin, visited 203 ) 204 margin.pop() 205 206 return retval
207 208 IDX = lambda N: N and 1 or 0 209 262 margins = list(map(MMM, margin[:-1])) 263 264 children = child_func(root) 265 266 if prune and rname in visited and children: 267 sys.stdout.write(''.join(tags + margins + ['+-[', rname, ']']) + u'\n') 268 return 269 270 sys.stdout.write(''.join(tags + margins + ['+-', rname]) + u'\n') 271 272 visited[rname] = 1 273 274 if children: 275 margin.append(1) 276 idx = IDX(showtags) 277 for C in children[:-1]: 278 print_tree(C, child_func, prune, idx, margin, visited) 279 margin[-1] = 0 280 print_tree(children[-1], child_func, prune, idx, margin, visited) 281 margin.pop() 282 283 284 285 # Functions for deciding if things are like various types, mainly to 286 # handle UserDict, UserList and UserString like their underlying types. 287 # 288 # Yes, all of this manual testing breaks polymorphism, and the real 289 # Pythonic way to do all of this would be to just try it and handle the 290 # exception, but handling the exception when it's not the right type is 291 # often too slow. 292 293 # We are using the following trick to speed up these 294 # functions. Default arguments are used to take a snapshot of the 295 # the global functions and constants used by these functions. This 296 # transforms accesses to global variable into local variables 297 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL). 298 299 DictTypes = (dict, UserDict) 300 ListTypes = (list, UserList) 301 SequenceTypes = (list, tuple, UserList) 302 303 # Note that profiling data shows a speed-up when comparing 304 # explicitely with str and unicode instead of simply comparing 305 # with basestring. (at least on Python 2.5.1) 306 StringTypes = (str, unicode, UserString) 307 308 # Empirically, it is faster to check explicitely for str and 309 # unicode than for basestring. 310 BaseStringTypes = (str, unicode) 311
312 -def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
313 return isinstance(obj, DictTypes)
314
315 -def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
316 return isinstance(obj, ListTypes)
317
318 -def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
319 return isinstance(obj, SequenceTypes)
320
321 -def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
322 return isinstance(obj, tuple)
323
324 -def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
325 return isinstance(obj, StringTypes)
326
327 -def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
328 # Profiling shows that there is an impressive speed-up of 2x 329 # when explicitely checking for strings instead of just not 330 # sequence when the argument (i.e. obj) is already a string. 331 # But, if obj is a not string then it is twice as fast to 332 # check only for 'not sequence'. The following code therefore 333 # assumes that the obj argument is a string must of the time. 334 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
335
336 -def do_flatten(sequence, result, isinstance=isinstance, 337 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
338 for item in sequence: 339 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 340 result.append(item) 341 else: 342 do_flatten(item, result)
343
344 -def flatten(obj, isinstance=isinstance, StringTypes=StringTypes, 345 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
346 """Flatten a sequence to a non-nested list. 347 348 Flatten() converts either a single scalar or a nested sequence 349 to a non-nested list. Note that flatten() considers strings 350 to be scalars instead of sequences like Python would. 351 """ 352 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes): 353 return [obj] 354 result = [] 355 for item in obj: 356 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 357 result.append(item) 358 else: 359 do_flatten(item, result) 360 return result
361
362 -def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes, 363 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
364 """Flatten a sequence to a non-nested list. 365 366 Same as flatten(), but it does not handle the single scalar 367 case. This is slightly more efficient when one knows that 368 the sequence to flatten can not be a scalar. 369 """ 370 result = [] 371 for item in sequence: 372 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 373 result.append(item) 374 else: 375 do_flatten(item, result) 376 return result
377 378 # Generic convert-to-string functions that abstract away whether or 379 # not the Python we're executing has Unicode support. The wrapper 380 # to_String_for_signature() will use a for_signature() method if the 381 # specified object has one. 382 #
383 -def to_String(s, 384 isinstance=isinstance, str=str, 385 UserString=UserString, BaseStringTypes=BaseStringTypes):
386 if isinstance(s,BaseStringTypes): 387 # Early out when already a string! 388 return s 389 elif isinstance(s, UserString): 390 # s.data can only be either a unicode or a regular 391 # string. Please see the UserString initializer. 392 return s.data 393 else: 394 return str(s)
395
396 -def to_String_for_subst(s, 397 isinstance=isinstance, str=str, to_String=to_String, 398 BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes, 399 UserString=UserString):
400 401 # Note that the test cases are sorted by order of probability. 402 if isinstance(s, BaseStringTypes): 403 return s 404 elif isinstance(s, SequenceTypes): 405 l = [] 406 for e in s: 407 l.append(to_String_for_subst(e)) 408 return ' '.join( s ) 409 elif isinstance(s, UserString): 410 # s.data can only be either a unicode or a regular 411 # string. Please see the UserString initializer. 412 return s.data 413 else: 414 return str(s)
415
416 -def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst, 417 AttributeError=AttributeError):
418 try: 419 f = obj.for_signature 420 except AttributeError: 421 return to_String_for_subst(obj) 422 else: 423 return f()
424 425 426 # The SCons "semi-deep" copy. 427 # 428 # This makes separate copies of lists (including UserList objects) 429 # dictionaries (including UserDict objects) and tuples, but just copies 430 # references to anything else it finds. 431 # 432 # A special case is any object that has a __semi_deepcopy__() method, 433 # which we invoke to create the copy, which is used by the BuilderDict 434 # class because of its extra initialization argument. 435 # 436 # The dispatch table approach used here is a direct rip-off from the 437 # normal Python copy module. 438 439 _semi_deepcopy_dispatch = d = {} 440
441 -def _semi_deepcopy_dict(x):
442 copy = {} 443 for key, val in x.items(): 444 # The regular Python copy.deepcopy() also deepcopies the key, 445 # as follows: 446 # 447 # copy[semi_deepcopy(key)] = semi_deepcopy(val) 448 # 449 # Doesn't seem like we need to, but we'll comment it just in case. 450 copy[key] = semi_deepcopy(val) 451 return copy
452 d[dict] = _semi_deepcopy_dict 453
454 -def _semi_deepcopy_list(x):
455 return list(map(semi_deepcopy, x))
456 d[list] = _semi_deepcopy_list 457
458 -def _semi_deepcopy_tuple(x):
459 return tuple(map(semi_deepcopy, x))
460 d[tuple] = _semi_deepcopy_tuple 461
462 -def _semi_deepcopy_inst(x):
463 if hasattr(x, '__semi_deepcopy__'): 464 return x.__semi_deepcopy__() 465 elif isinstance(x, UserDict): 466 return x.__class__(_semi_deepcopy_dict(x)) 467 elif isinstance(x, UserList): 468 return x.__class__(_semi_deepcopy_list(x)) 469 else: 470 return x
471 d[InstanceType] = _semi_deepcopy_inst 472
473 -def semi_deepcopy(x):
474 copier = _semi_deepcopy_dispatch.get(type(x)) 475 if copier: 476 return copier(x) 477 else: 478 return x
479 480 481
482 -class Proxy(object):
483 """A simple generic Proxy class, forwarding all calls to 484 subject. So, for the benefit of the python newbie, what does 485 this really mean? Well, it means that you can take an object, let's 486 call it 'objA', and wrap it in this Proxy class, with a statement 487 like this 488 489 proxyObj = Proxy(objA), 490 491 Then, if in the future, you do something like this 492 493 x = proxyObj.var1, 494 495 since Proxy does not have a 'var1' attribute (but presumably objA does), 496 the request actually is equivalent to saying 497 498 x = objA.var1 499 500 Inherit from this class to create a Proxy. 501 502 Note that, with new-style classes, this does *not* work transparently 503 for Proxy subclasses that use special .__*__() method names, because 504 those names are now bound to the class, not the individual instances. 505 You now need to know in advance which .__*__() method names you want 506 to pass on to the underlying Proxy object, and specifically delegate 507 their calls like this: 508 509 class Foo(Proxy): 510 __str__ = Delegate('__str__') 511 """ 512
513 - def __init__(self, subject):
514 """Wrap an object as a Proxy object""" 515 self._subject = subject
516
517 - def __getattr__(self, name):
518 """Retrieve an attribute from the wrapped object. If the named 519 attribute doesn't exist, AttributeError is raised""" 520 return getattr(self._subject, name)
521
522 - def get(self):
523 """Retrieve the entire wrapped object""" 524 return self._subject
525
526 - def __cmp__(self, other):
527 if issubclass(other.__class__, self._subject.__class__): 528 return cmp(self._subject, other) 529 return cmp(self.__dict__, other.__dict__)
530
531 -class Delegate(object):
532 """A Python Descriptor class that delegates attribute fetches 533 to an underlying wrapped subject of a Proxy. Typical use: 534 535 class Foo(Proxy): 536 __str__ = Delegate('__str__') 537 """
538 - def __init__(self, attribute):
539 self.attribute = attribute
540 - def __get__(self, obj, cls):
541 if isinstance(obj, cls): 542 return getattr(obj._subject, self.attribute) 543 else: 544 return self
545 546 # attempt to load the windows registry module: 547 can_read_reg = 0 548 try: 549 import winreg 550 551 can_read_reg = 1 552 hkey_mod = winreg 553 554 RegOpenKeyEx = winreg.OpenKeyEx 555 RegEnumKey = winreg.EnumKey 556 RegEnumValue = winreg.EnumValue 557 RegQueryValueEx = winreg.QueryValueEx 558 RegError = winreg.error 559 560 except ImportError: 561 try: 562 import win32api 563 import win32con 564 can_read_reg = 1 565 hkey_mod = win32con 566 567 RegOpenKeyEx = win32api.RegOpenKeyEx 568 RegEnumKey = win32api.RegEnumKey 569 RegEnumValue = win32api.RegEnumValue 570 RegQueryValueEx = win32api.RegQueryValueEx 571 RegError = win32api.error 572 573 except ImportError:
574 - class _NoError(Exception):
575 pass
576 RegError = _NoError 577 578 if can_read_reg: 579 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT 580 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE 581 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER 582 HKEY_USERS = hkey_mod.HKEY_USERS 583
584 - def RegGetValue(root, key):
585 """This utility function returns a value in the registry 586 without having to open the key first. Only available on 587 Windows platforms with a version of Python that can read the 588 registry. Returns the same thing as 589 SCons.Util.RegQueryValueEx, except you just specify the entire 590 path to the value, and don't have to bother opening the key 591 first. So: 592 593 Instead of: 594 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, 595 r'SOFTWARE\Microsoft\Windows\CurrentVersion') 596 out = SCons.Util.RegQueryValueEx(k, 597 'ProgramFilesDir') 598 599 You can write: 600 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, 601 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir') 602 """ 603 # I would use os.path.split here, but it's not a filesystem 604 # path... 605 p = key.rfind('\\') + 1 606 keyp = key[:p-1] # -1 to omit trailing slash 607 val = key[p:] 608 k = RegOpenKeyEx(root, keyp) 609 return RegQueryValueEx(k,val)
610 else: 611 try: 612 e = WindowsError 613 except NameError: 614 # Make sure we have a definition of WindowsError so we can 615 # run platform-independent tests of Windows functionality on 616 # platforms other than Windows. (WindowsError is, in fact, an 617 # OSError subclass on Windows.)
618 - class WindowsError(OSError):
619 pass
620 import builtins 621 builtins.WindowsError = WindowsError 622 else: 623 del e 624 625 HKEY_CLASSES_ROOT = None 626 HKEY_LOCAL_MACHINE = None 627 HKEY_CURRENT_USER = None 628 HKEY_USERS = None 629
630 - def RegGetValue(root, key):
631 raise WindowsError
632
633 - def RegOpenKeyEx(root, key):
634 raise WindowsError
635 636 if sys.platform == 'win32': 637
638 - def WhereIs(file, path=None, pathext=None, reject=[]):
639 if path is None: 640 try: 641 path = os.environ['PATH'] 642 except KeyError: 643 return None 644 if is_String(path): 645 path = path.split(os.pathsep) 646 if pathext is None: 647 try: 648 pathext = os.environ['PATHEXT'] 649 except KeyError: 650 pathext = '.COM;.EXE;.BAT;.CMD' 651 if is_String(pathext): 652 pathext = pathext.split(os.pathsep) 653 for ext in pathext: 654 if ext.lower() == file[-len(ext):].lower(): 655 pathext = [''] 656 break 657 if not is_List(reject) and not is_Tuple(reject): 658 reject = [reject] 659 for dir in path: 660 f = os.path.join(dir, file) 661 for ext in pathext: 662 fext = f + ext 663 if os.path.isfile(fext): 664 try: 665 reject.index(fext) 666 except ValueError: 667 return os.path.normpath(fext) 668 continue 669 return None
670 671 elif os.name == 'os2': 672
673 - def WhereIs(file, path=None, pathext=None, reject=[]):
674 if path is None: 675 try: 676 path = os.environ['PATH'] 677 except KeyError: 678 return None 679 if is_String(path): 680 path = path.split(os.pathsep) 681 if pathext is None: 682 pathext = ['.exe', '.cmd'] 683 for ext in pathext: 684 if ext.lower() == file[-len(ext):].lower(): 685 pathext = [''] 686 break 687 if not is_List(reject) and not is_Tuple(reject): 688 reject = [reject] 689 for dir in path: 690 f = os.path.join(dir, file) 691 for ext in pathext: 692 fext = f + ext 693 if os.path.isfile(fext): 694 try: 695 reject.index(fext) 696 except ValueError: 697 return os.path.normpath(fext) 698 continue 699 return None
700 701 else: 702
703 - def WhereIs(file, path=None, pathext=None, reject=[]):
704 import stat 705 if path is None: 706 try: 707 path = os.environ['PATH'] 708 except KeyError: 709 return None 710 if is_String(path): 711 path = path.split(os.pathsep) 712 if not is_List(reject) and not is_Tuple(reject): 713 reject = [reject] 714 for d in path: 715 f = os.path.join(d, file) 716 if os.path.isfile(f): 717 try: 718 st = os.stat(f) 719 except OSError: 720 # os.stat() raises OSError, not IOError if the file 721 # doesn't exist, so in this case we let IOError get 722 # raised so as to not mask possibly serious disk or 723 # network issues. 724 continue 725 if stat.S_IMODE(st[stat.ST_MODE]) & 0111: 726 try: 727 reject.index(f) 728 except ValueError: 729 return os.path.normpath(f) 730 continue 731 return None
732
733 -def PrependPath(oldpath, newpath, sep = os.pathsep, 734 delete_existing=1, canonicalize=None):
735 """This prepends newpath elements to the given oldpath. Will only 736 add any particular path once (leaving the first one it encounters 737 and ignoring the rest, to preserve path order), and will 738 os.path.normpath and os.path.normcase all paths to help assure 739 this. This can also handle the case where the given old path 740 variable is a list instead of a string, in which case a list will 741 be returned instead of a string. 742 743 Example: 744 Old Path: "/foo/bar:/foo" 745 New Path: "/biz/boom:/foo" 746 Result: "/biz/boom:/foo:/foo/bar" 747 748 If delete_existing is 0, then adding a path that exists will 749 not move it to the beginning; it will stay where it is in the 750 list. 751 752 If canonicalize is not None, it is applied to each element of 753 newpath before use. 754 """ 755 756 orig = oldpath 757 is_list = 1 758 paths = orig 759 if not is_List(orig) and not is_Tuple(orig): 760 paths = paths.split(sep) 761 is_list = 0 762 763 if is_String(newpath): 764 newpaths = newpath.split(sep) 765 elif not is_List(newpath) and not is_Tuple(newpath): 766 newpaths = [ newpath ] # might be a Dir 767 else: 768 newpaths = newpath 769 770 if canonicalize: 771 newpaths=list(map(canonicalize, newpaths)) 772 773 if not delete_existing: 774 # First uniquify the old paths, making sure to 775 # preserve the first instance (in Unix/Linux, 776 # the first one wins), and remembering them in normpaths. 777 # Then insert the new paths at the head of the list 778 # if they're not already in the normpaths list. 779 result = [] 780 normpaths = [] 781 for path in paths: 782 if not path: 783 continue 784 normpath = os.path.normpath(os.path.normcase(path)) 785 if normpath not in normpaths: 786 result.append(path) 787 normpaths.append(normpath) 788 newpaths.reverse() # since we're inserting at the head 789 for path in newpaths: 790 if not path: 791 continue 792 normpath = os.path.normpath(os.path.normcase(path)) 793 if normpath not in normpaths: 794 result.insert(0, path) 795 normpaths.append(normpath) 796 paths = result 797 798 else: 799 newpaths = newpaths + paths # prepend new paths 800 801 normpaths = [] 802 paths = [] 803 # now we add them only if they are unique 804 for path in newpaths: 805 normpath = os.path.normpath(os.path.normcase(path)) 806 if path and not normpath in normpaths: 807 paths.append(path) 808 normpaths.append(normpath) 809 810 if is_list: 811 return paths 812 else: 813 return sep.join(paths)
814
815 -def AppendPath(oldpath, newpath, sep = os.pathsep, 816 delete_existing=1, canonicalize=None):
817 """This appends new path elements to the given old path. Will 818 only add any particular path once (leaving the last one it 819 encounters and ignoring the rest, to preserve path order), and 820 will os.path.normpath and os.path.normcase all paths to help 821 assure this. This can also handle the case where the given old 822 path variable is a list instead of a string, in which case a list 823 will be returned instead of a string. 824 825 Example: 826 Old Path: "/foo/bar:/foo" 827 New Path: "/biz/boom:/foo" 828 Result: "/foo/bar:/biz/boom:/foo" 829 830 If delete_existing is 0, then adding a path that exists 831 will not move it to the end; it will stay where it is in the list. 832 833 If canonicalize is not None, it is applied to each element of 834 newpath before use. 835 """ 836 837 orig = oldpath 838 is_list = 1 839 paths = orig 840 if not is_List(orig) and not is_Tuple(orig): 841 paths = paths.split(sep) 842 is_list = 0 843 844 if is_String(newpath): 845 newpaths = newpath.split(sep) 846 elif not is_List(newpath) and not is_Tuple(newpath): 847 newpaths = [ newpath ] # might be a Dir 848 else: 849 newpaths = newpath 850 851 if canonicalize: 852 newpaths=list(map(canonicalize, newpaths)) 853 854 if not delete_existing: 855 # add old paths to result, then 856 # add new paths if not already present 857 # (I thought about using a dict for normpaths for speed, 858 # but it's not clear hashing the strings would be faster 859 # than linear searching these typically short lists.) 860 result = [] 861 normpaths = [] 862 for path in paths: 863 if not path: 864 continue 865 result.append(path) 866 normpaths.append(os.path.normpath(os.path.normcase(path))) 867 for path in newpaths: 868 if not path: 869 continue 870 normpath = os.path.normpath(os.path.normcase(path)) 871 if normpath not in normpaths: 872 result.append(path) 873 normpaths.append(normpath) 874 paths = result 875 else: 876 # start w/ new paths, add old ones if not present, 877 # then reverse. 878 newpaths = paths + newpaths # append new paths 879 newpaths.reverse() 880 881 normpaths = [] 882 paths = [] 883 # now we add them only if they are unique 884 for path in newpaths: 885 normpath = os.path.normpath(os.path.normcase(path)) 886 if path and not normpath in normpaths: 887 paths.append(path) 888 normpaths.append(normpath) 889 paths.reverse() 890 891 if is_list: 892 return paths 893 else: 894 return sep.join(paths)
895 896 if sys.platform == 'cygwin':
897 - def get_native_path(path):
898 """Transforms an absolute path into a native path for the system. In 899 Cygwin, this converts from a Cygwin path to a Windows one.""" 900 return os.popen('cygpath -w ' + path).read().replace('\n', '')
901 else:
902 - def get_native_path(path):
903 """Transforms an absolute path into a native path for the system. 904 Non-Cygwin version, just leave the path alone.""" 905 return path
906 907 display = DisplayEngine() 908
909 -def Split(arg):
910 if is_List(arg) or is_Tuple(arg): 911 return arg 912 elif is_String(arg): 913 return arg.split() 914 else: 915 return [arg]
916
917 -class CLVar(UserList):
918 """A class for command-line construction variables. 919 920 This is a list that uses Split() to split an initial string along 921 white-space arguments, and similarly to split any strings that get 922 added. This allows us to Do the Right Thing with Append() and 923 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1 924 arg2') regardless of whether a user adds a list or a string to a 925 command-line construction variable. 926 """
927 - def __init__(self, seq = []):
928 UserList.__init__(self, Split(seq))
929 - def __add__(self, other):
930 return UserList.__add__(self, CLVar(other))
931 - def __radd__(self, other):
932 return UserList.__radd__(self, CLVar(other))
933 - def __coerce__(self, other):
934 return (self, CLVar(other))
935 - def __str__(self):
936 return ' '.join(self.data)
937 938 # A dictionary that preserves the order in which items are added. 939 # Submitted by David Benjamin to ActiveState's Python Cookbook web site: 940 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 941 # Including fixes/enhancements from the follow-on discussions.
942 -class OrderedDict(UserDict):
943 - def __init__(self, dict = None):
944 self._keys = [] 945 UserDict.__init__(self, dict)
946
947 - def __delitem__(self, key):
948 UserDict.__delitem__(self, key) 949 self._keys.remove(key)
950
951 - def __setitem__(self, key, item):
952 UserDict.__setitem__(self, key, item) 953 if key not in self._keys: self._keys.append(key)
954
955 - def clear(self):
956 UserDict.clear(self) 957 self._keys = []
958
959 - def copy(self):
960 dict = OrderedDict() 961 dict.update(self) 962 return dict
963
964 - def items(self):
965 return list(zip(self._keys, list(self.values())))
966
967 - def keys(self):
968 return self._keys[:]
969
970 - def popitem(self):
971 try: 972 key = self._keys[-1] 973 except IndexError: 974 raise KeyError('dictionary is empty') 975 976 val = self[key] 977 del self[key] 978 979 return (key, val)
980
981 - def setdefault(self, key, failobj = None):
982 UserDict.setdefault(self, key, failobj) 983 if key not in self._keys: self._keys.append(key)
984
985 - def update(self, dict):
986 for (key, val) in dict.items(): 987 self.__setitem__(key, val)
988
989 - def values(self):
990 return list(map(self.get, self._keys))
991
992 -class Selector(OrderedDict):
993 """A callable ordered dictionary that maps file suffixes to 994 dictionary values. We preserve the order in which items are added 995 so that get_suffix() calls always return the first suffix added."""
996 - def __call__(self, env, source, ext=None):
997 if ext is None: 998 try: 999 ext = source[0].suffix 1000 except IndexError: 1001 ext = "" 1002 try: 1003 return self[ext] 1004 except KeyError: 1005 # Try to perform Environment substitution on the keys of 1006 # the dictionary before giving up. 1007 s_dict = {} 1008 for (k,v) in self.items(): 1009 if k is not None: 1010 s_k = env.subst(k) 1011 if s_k in s_dict: 1012 # We only raise an error when variables point 1013 # to the same suffix. If one suffix is literal 1014 # and a variable suffix contains this literal, 1015 # the literal wins and we don't raise an error. 1016 raise KeyError(s_dict[s_k][0], k, s_k) 1017 s_dict[s_k] = (k,v) 1018 try: 1019 return s_dict[ext][1] 1020 except KeyError: 1021 try: 1022 return self[None] 1023 except KeyError: 1024 return None
1025 1026 1027 if sys.platform == 'cygwin': 1028 # On Cygwin, os.path.normcase() lies, so just report back the 1029 # fact that the underlying Windows OS is case-insensitive.
1030 - def case_sensitive_suffixes(s1, s2):
1031 return 0
1032 else:
1033 - def case_sensitive_suffixes(s1, s2):
1034 return (os.path.normcase(s1) != os.path.normcase(s2))
1035
1036 -def adjustixes(fname, pre, suf, ensure_suffix=False):
1037 if pre: 1038 path, fn = os.path.split(os.path.normpath(fname)) 1039 if fn[:len(pre)] != pre: 1040 fname = os.path.join(path, pre + fn) 1041 # Only append a suffix if the suffix we're going to add isn't already 1042 # there, and if either we've been asked to ensure the specific suffix 1043 # is present or there's no suffix on it at all. 1044 if suf and fname[-len(suf):] != suf and \ 1045 (ensure_suffix or not splitext(fname)[1]): 1046 fname = fname + suf 1047 return fname
1048 1049 1050 1051 # From Tim Peters, 1052 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1053 # ASPN: Python Cookbook: Remove duplicates from a sequence 1054 # (Also in the printed Python Cookbook.) 1055
1056 -def unique(s):
1057 """Return a list of the elements in s, but without duplicates. 1058 1059 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], 1060 unique("abcabc") some permutation of ["a", "b", "c"], and 1061 unique(([1, 2], [2, 3], [1, 2])) some permutation of 1062 [[2, 3], [1, 2]]. 1063 1064 For best speed, all sequence elements should be hashable. Then 1065 unique() will usually work in linear time. 1066 1067 If not possible, the sequence elements should enjoy a total 1068 ordering, and if list(s).sort() doesn't raise TypeError it's 1069 assumed that they do enjoy a total ordering. Then unique() will 1070 usually work in O(N*log2(N)) time. 1071 1072 If that's not possible either, the sequence elements must support 1073 equality-testing. Then unique() will usually work in quadratic 1074 time. 1075 """ 1076 1077 n = len(s) 1078 if n == 0: 1079 return [] 1080 1081 # Try using a dict first, as that's the fastest and will usually 1082 # work. If it doesn't work, it will usually fail quickly, so it 1083 # usually doesn't cost much to *try* it. It requires that all the 1084 # sequence elements be hashable, and support equality comparison. 1085 u = {} 1086 try: 1087 for x in s: 1088 u[x] = 1 1089 except TypeError: 1090 pass # move on to the next method 1091 else: 1092 return list(u.keys()) 1093 del u 1094 1095 # We can't hash all the elements. Second fastest is to sort, 1096 # which brings the equal elements together; then duplicates are 1097 # easy to weed out in a single pass. 1098 # NOTE: Python's list.sort() was designed to be efficient in the 1099 # presence of many duplicate elements. This isn't true of all 1100 # sort functions in all languages or libraries, so this approach 1101 # is more effective in Python than it may be elsewhere. 1102 try: 1103 t = sorted(s) 1104 except TypeError: 1105 pass # move on to the next method 1106 else: 1107 assert n > 0 1108 last = t[0] 1109 lasti = i = 1 1110 while i < n: 1111 if t[i] != last: 1112 t[lasti] = last = t[i] 1113 lasti = lasti + 1 1114 i = i + 1 1115 return t[:lasti] 1116 del t 1117 1118 # Brute force is all that's left. 1119 u = [] 1120 for x in s: 1121 if x not in u: 1122 u.append(x) 1123 return u
1124 1125 1126 1127 # From Alex Martelli, 1128 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1129 # ASPN: Python Cookbook: Remove duplicates from a sequence 1130 # First comment, dated 2001/10/13. 1131 # (Also in the printed Python Cookbook.) 1132
1133 -def uniquer(seq, idfun=None):
1134 if idfun is None: 1135 def idfun(x): return x 1136 seen = {} 1137 result = [] 1138 for item in seq: 1139 marker = idfun(item) 1140 # in old Python versions: 1141 # if seen.has_key(marker) 1142 # but in new ones: 1143 if marker in seen: continue 1144 seen[marker] = 1 1145 result.append(item) 1146 return result
1147 1148 # A more efficient implementation of Alex's uniquer(), this avoids the 1149 # idfun() argument and function-call overhead by assuming that all 1150 # items in the sequence are hashable. 1151
1152 -def uniquer_hashables(seq):
1153 seen = {} 1154 result = [] 1155 for item in seq: 1156 #if not item in seen: 1157 if item not in seen: 1158 seen[item] = 1 1159 result.append(item) 1160 return result
1161 1162 1163 1164 # Much of the logic here was originally based on recipe 4.9 from the 1165 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1166 -class LogicalLines(object):
1167
1168 - def __init__(self, fileobj):
1169 self.fileobj = fileobj
1170
1171 - def readline(self):
1172 result = [] 1173 while True: 1174 line = self.fileobj.readline() 1175 if not line: 1176 break 1177 if line[-2:] == '\\\n': 1178 result.append(line[:-2]) 1179 else: 1180 result.append(line) 1181 break 1182 return ''.join(result)
1183
1184 - def readlines(self):
1185 result = [] 1186 while True: 1187 line = self.readline() 1188 if not line: 1189 break 1190 result.append(line) 1191 return result
1192 1193 1194
1195 -class UniqueList(UserList):
1196 - def __init__(self, seq = []):
1197 UserList.__init__(self, seq) 1198 self.unique = True
1199 - def __make_unique(self):
1200 if not self.unique: 1201 self.data = uniquer_hashables(self.data) 1202 self.unique = True
1203 - def __lt__(self, other):
1204 self.__make_unique() 1205 return UserList.__lt__(self, other)
1206 - def __le__(self, other):
1207 self.__make_unique() 1208 return UserList.__le__(self, other)
1209 - def __eq__(self, other):
1210 self.__make_unique() 1211 return UserList.__eq__(self, other)
1212 - def __ne__(self, other):
1213 self.__make_unique() 1214 return UserList.__ne__(self, other)
1215 - def __gt__(self, other):
1216 self.__make_unique() 1217 return UserList.__gt__(self, other)
1218 - def __ge__(self, other):
1219 self.__make_unique() 1220 return UserList.__ge__(self, other)
1221 - def __cmp__(self, other):
1222 self.__make_unique() 1223 return UserList.__cmp__(self, other)
1224 - def __len__(self):
1225 self.__make_unique() 1226 return UserList.__len__(self)
1227 - def __getitem__(self, i):
1228 self.__make_unique() 1229 return UserList.__getitem__(self, i)
1230 - def __setitem__(self, i, item):
1231 UserList.__setitem__(self, i, item) 1232 self.unique = False
1233 - def __getslice__(self, i, j):
1234 self.__make_unique() 1235 return UserList.__getslice__(self, i, j)
1236 - def __setslice__(self, i, j, other):
1237 UserList.__setslice__(self, i, j, other) 1238 self.unique = False
1239 - def __add__(self, other):
1240 result = UserList.__add__(self, other) 1241 result.unique = False 1242 return result
1243 - def __radd__(self, other):
1244 result = UserList.__radd__(self, other) 1245 result.unique = False 1246 return result
1247 - def __iadd__(self, other):
1248 result = UserList.__iadd__(self, other) 1249 result.unique = False 1250 return result
1251 - def __mul__(self, other):
1252 result = UserList.__mul__(self, other) 1253 result.unique = False 1254 return result
1255 - def __rmul__(self, other):
1256 result = UserList.__rmul__(self, other) 1257 result.unique = False 1258 return result
1259 - def __imul__(self, other):
1260 result = UserList.__imul__(self, other) 1261 result.unique = False 1262 return result
1263 - def append(self, item):
1264 UserList.append(self, item) 1265 self.unique = False
1266 - def insert(self, i):
1267 UserList.insert(self, i) 1268 self.unique = False
1269 - def count(self, item):
1270 self.__make_unique() 1271 return UserList.count(self, item)
1272 - def index(self, item):
1273 self.__make_unique() 1274 return UserList.index(self, item)
1275 - def reverse(self):
1276 self.__make_unique() 1277 UserList.reverse(self)
1278 - def sort(self, *args, **kwds):
1279 self.__make_unique() 1280 return UserList.sort(self, *args, **kwds)
1281 - def extend(self, other):
1282 UserList.extend(self, other) 1283 self.unique = False
1284 1285
1286 -class Unbuffered(object):
1287 """ 1288 A proxy class that wraps a file object, flushing after every write, 1289 and delegating everything else to the wrapped object. 1290 """
1291 - def __init__(self, file):
1292 self.file = file 1293 self.softspace = 0 ## backward compatibility; not supported in Py3k
1294 - def write(self, arg):
1295 try: 1296 self.file.write(arg) 1297 self.file.flush() 1298 except IOError: 1299 # Stdout might be connected to a pipe that has been closed 1300 # by now. The most likely reason for the pipe being closed 1301 # is that the user has press ctrl-c. It this is the case, 1302 # then SCons is currently shutdown. We therefore ignore 1303 # IOError's here so that SCons can continue and shutdown 1304 # properly so that the .sconsign is correctly written 1305 # before SCons exits. 1306 pass
1307 - def __getattr__(self, attr):
1308 return getattr(self.file, attr)
1309
1310 -def make_path_relative(path):
1311 """ makes an absolute path name to a relative pathname. 1312 """ 1313 if os.path.isabs(path): 1314 drive_s,path = os.path.splitdrive(path) 1315 1316 import re 1317 if not drive_s: 1318 path=re.compile("/*(.*)").findall(path)[0] 1319 else: 1320 path=path[1:] 1321 1322 assert( not os.path.isabs( path ) ), path 1323 return path
1324 1325 1326 1327 # The original idea for AddMethod() and RenameFunction() come from the 1328 # following post to the ActiveState Python Cookbook: 1329 # 1330 # ASPN: Python Cookbook : Install bound methods in an instance 1331 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 1332 # 1333 # That code was a little fragile, though, so the following changes 1334 # have been wrung on it: 1335 # 1336 # * Switched the installmethod() "object" and "function" arguments, 1337 # so the order reflects that the left-hand side is the thing being 1338 # "assigned to" and the right-hand side is the value being assigned. 1339 # 1340 # * Changed explicit type-checking to the "try: klass = object.__class__" 1341 # block in installmethod() below so that it still works with the 1342 # old-style classes that SCons uses. 1343 # 1344 # * Replaced the by-hand creation of methods and functions with use of 1345 # the "new" module, as alluded to in Alex Martelli's response to the 1346 # following Cookbook post: 1347 # 1348 # ASPN: Python Cookbook : Dynamically added methods to a class 1349 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 1350
1351 -def AddMethod(obj, function, name=None):
1352 """ 1353 Adds either a bound method to an instance or an unbound method to 1354 a class. If name is ommited the name of the specified function 1355 is used by default. 1356 Example: 1357 a = A() 1358 def f(self, x, y): 1359 self.z = x + y 1360 AddMethod(f, A, "add") 1361 a.add(2, 4) 1362 print a.z 1363 AddMethod(lambda self, i: self.l[i], a, "listIndex") 1364 print a.listIndex(5) 1365 """ 1366 if name is None: 1367 name = function.func_name 1368 else: 1369 function = RenameFunction(function, name) 1370 1371 if hasattr(obj, '__class__') and obj.__class__ is not type: 1372 # "obj" is an instance, so it gets a bound method. 1373 setattr(obj, name, MethodType(function, obj, obj.__class__)) 1374 else: 1375 # "obj" is a class, so it gets an unbound method. 1376 setattr(obj, name, MethodType(function, None, obj))
1377
1378 -def RenameFunction(function, name):
1379 """ 1380 Returns a function identical to the specified function, but with 1381 the specified name. 1382 """ 1383 return FunctionType(function.func_code, 1384 function.func_globals, 1385 name, 1386 function.func_defaults)
1387 1388 1389 md5 = False
1390 -def MD5signature(s):
1391 return str(s)
1392
1393 -def MD5filesignature(fname, chunksize=65536):
1394 f = open(fname, "rb") 1395 result = f.read() 1396 f.close() 1397 return result
1398 1399 try: 1400 import hashlib 1401 except ImportError: 1402 pass 1403 else: 1404 if hasattr(hashlib, 'md5'): 1405 md5 = True
1406 - def MD5signature(s):
1407 m = hashlib.md5() 1408 m.update(str(s)) 1409 return m.hexdigest()
1410
1411 - def MD5filesignature(fname, chunksize=65536):
1412 m = hashlib.md5() 1413 f = open(fname, "rb") 1414 while True: 1415 blck = f.read(chunksize) 1416 if not blck: 1417 break 1418 m.update(str(blck)) 1419 f.close() 1420 return m.hexdigest()
1421
1422 -def MD5collect(signatures):
1423 """ 1424 Collects a list of signatures into an aggregate signature. 1425 1426 signatures - a list of signatures 1427 returns - the aggregate signature 1428 """ 1429 if len(signatures) == 1: 1430 return signatures[0] 1431 else: 1432 return MD5signature(', '.join(signatures))
1433 1434 1435
1436 -def silent_intern(x):
1437 """ 1438 Perform sys.intern() on the passed argument and return the result. 1439 If the input is ineligible (e.g. a unicode string) the original argument is 1440 returned and no exception is thrown. 1441 """ 1442 try: 1443 return sys.intern(x) 1444 except TypeError: 1445 return x
1446 1447 1448 1449 # From Dinu C. Gherman, 1450 # Python Cookbook, second edition, recipe 6.17, p. 277. 1451 # Also: 1452 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 1453 # ASPN: Python Cookbook: Null Object Design Pattern 1454 1455 #TODO??? class Null(object):
1456 -class Null(object):
1457 """ Null objects always and reliably "do nothing." """
1458 - def __new__(cls, *args, **kwargs):
1459 if not '_instance' in vars(cls): 1460 cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) 1461 return cls._instance
1462 - def __init__(self, *args, **kwargs):
1463 pass
1464 - def __call__(self, *args, **kwargs):
1465 return self
1466 - def __repr__(self):
1467 return "Null(0x%08X)" % id(self)
1468 - def __nonzero__(self):
1469 return False
1470 - def __getattr__(self, name):
1471 return self
1472 - def __setattr__(self, name, value):
1473 return self
1474 - def __delattr__(self, name):
1475 return self
1476
1477 -class NullSeq(Null):
1478 - def __len__(self):
1479 return 0
1480 - def __iter__(self):
1481 return iter(())
1482 - def __getitem__(self, i):
1483 return self
1484 - def __delitem__(self, i):
1485 return self
1486 - def __setitem__(self, i, v):
1487 return self
1488 1489 1490 del __revision__ 1491 1492 # Local Variables: 1493 # tab-width:4 1494 # indent-tabs-mode:nil 1495 # End: 1496 # vim: set expandtab tabstop=4 shiftwidth=4: 1497