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