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