Package SCons :: Package Node
[hide private]
[frames] | no frames]

Source Code for Package SCons.Node

   1  """SCons.Node 
   2   
   3  The Node package for the SCons software construction utility. 
   4   
   5  This is, in many ways, the heart of SCons. 
   6   
   7  A Node is where we encapsulate all of the dependency information about 
   8  any thing that SCons can build, or about any thing which SCons can use 
   9  to build some other thing.  The canonical "thing," of course, is a file, 
  10  but a Node can also represent something remote (like a web page) or 
  11  something completely abstract (like an Alias). 
  12   
  13  Each specific type of "thing" is specifically represented by a subclass 
  14  of the Node base class:  Node.FS.File for files, Node.Alias for aliases, 
  15  etc.  Dependency information is kept here in the base class, and 
  16  information specific to files/aliases/etc. is in the subclass.  The 
  17  goal, if we've done this correctly, is that any type of "thing" should 
  18  be able to depend on any other type of "thing." 
  19   
  20  """ 
  21   
  22  # 
  23  # Copyright (c) 2001 - 2016 The SCons Foundation 
  24  # 
  25  # Permission is hereby granted, free of charge, to any person obtaining 
  26  # a copy of this software and associated documentation files (the 
  27  # "Software"), to deal in the Software without restriction, including 
  28  # without limitation the rights to use, copy, modify, merge, publish, 
  29  # distribute, sublicense, and/or sell copies of the Software, and to 
  30  # permit persons to whom the Software is furnished to do so, subject to 
  31  # the following conditions: 
  32  # 
  33  # The above copyright notice and this permission notice shall be included 
  34  # in all copies or substantial portions of the Software. 
  35  # 
  36  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  37  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  38  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  39  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  40  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  41  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  42  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  43   
  44  __revision__ = "src/engine/SCons/Node/__init__.py rel_2.5.0:3543:937e55cd78f7 2016/04/09 11:29:54 bdbaddog" 
  45   
  46  import collections 
  47  import copy 
  48  from itertools import chain 
  49   
  50  import SCons.Debug 
  51  from SCons.Debug import logInstanceCreation 
  52  import SCons.Executor 
  53  import SCons.Memoize 
  54  import SCons.Util 
  55   
  56  from SCons.Debug import Trace 
  57   
  58  print_duplicate = 0 
59 60 -def classname(obj):
61 return str(obj.__class__).split('.')[-1]
62 63 # Set to false if we're doing a dry run. There's more than one of these 64 # little treats 65 do_store_info = True 66 67 # Node states 68 # 69 # These are in "priority" order, so that the maximum value for any 70 # child/dependency of a node represents the state of that node if 71 # it has no builder of its own. The canonical example is a file 72 # system directory, which is only up to date if all of its children 73 # were up to date. 74 no_state = 0 75 pending = 1 76 executing = 2 77 up_to_date = 3 78 executed = 4 79 failed = 5 80 81 StateString = { 82 0 : "no_state", 83 1 : "pending", 84 2 : "executing", 85 3 : "up_to_date", 86 4 : "executed", 87 5 : "failed", 88 } 89 90 # controls whether implicit dependencies are cached: 91 implicit_cache = 0 92 93 # controls whether implicit dep changes are ignored: 94 implicit_deps_unchanged = 0 95 96 # controls whether the cached implicit deps are ignored: 97 implicit_deps_changed = 0
98 99 # A variable that can be set to an interface-specific function be called 100 # to annotate a Node with information about its creation. 101 -def do_nothing(node): pass
102 103 Annotate = do_nothing 104 105 # Gets set to 'True' if we're running in interactive mode. Is 106 # currently used to release parts of a target's info during 107 # clean builds and update runs (see release_target_info). 108 interactive = False
109 110 -def is_derived_none(node):
111 raise NotImplementedError
112
113 -def is_derived_node(node):
114 """ 115 Returns true if this node is derived (i.e. built). 116 """ 117 return node.has_builder() or node.side_effect
118 119 _is_derived_map = {0 : is_derived_none, 120 1 : is_derived_node}
121 122 -def exists_none(node):
123 raise NotImplementedError
124
125 -def exists_always(node):
126 return 1
127
128 -def exists_base(node):
129 return node.stat() is not None
130
131 -def exists_entry(node):
132 """Return if the Entry exists. Check the file system to see 133 what we should turn into first. Assume a file if there's no 134 directory.""" 135 node.disambiguate() 136 return _exists_map[node._func_exists](node)
137
138 -def exists_file(node):
139 # Duplicate from source path if we are set up to do this. 140 if node.duplicate and not node.is_derived() and not node.linked: 141 src = node.srcnode() 142 if src is not node: 143 # At this point, src is meant to be copied in a variant directory. 144 src = src.rfile() 145 if src.get_abspath() != node.get_abspath(): 146 if src.exists(): 147 node.do_duplicate(src) 148 # Can't return 1 here because the duplication might 149 # not actually occur if the -n option is being used. 150 else: 151 # The source file does not exist. Make sure no old 152 # copy remains in the variant directory. 153 if print_duplicate: 154 print "dup: no src for %s, unlinking old variant copy"%self 155 if exists_base(node) or node.islink(): 156 node.fs.unlink(node.get_internal_path()) 157 # Return None explicitly because the Base.exists() call 158 # above will have cached its value if the file existed. 159 return None 160 return exists_base(node)
161 162 _exists_map = {0 : exists_none, 163 1 : exists_always, 164 2 : exists_base, 165 3 : exists_entry, 166 4 : exists_file}
167 168 169 -def rexists_none(node):
170 raise NotImplementedError
171
172 -def rexists_node(node):
173 return node.exists()
174
175 -def rexists_base(node):
176 return node.rfile().exists()
177 178 _rexists_map = {0 : rexists_none, 179 1 : rexists_node, 180 2 : rexists_base}
181 182 -def get_contents_none(node):
183 raise NotImplementedError
184
185 -def get_contents_entry(node):
186 """Fetch the contents of the entry. Returns the exact binary 187 contents of the file.""" 188 try: 189 node = node.disambiguate(must_exist=1) 190 except SCons.Errors.UserError: 191 # There was nothing on disk with which to disambiguate 192 # this entry. Leave it as an Entry, but return a null 193 # string so calls to get_contents() in emitters and the 194 # like (e.g. in qt.py) don't have to disambiguate by hand 195 # or catch the exception. 196 return '' 197 else: 198 return _get_contents_map[node._func_get_contents](node)
199
200 -def get_contents_dir(node):
201 """Return content signatures and names of all our children 202 separated by new-lines. Ensure that the nodes are sorted.""" 203 contents = [] 204 for n in sorted(node.children(), key=lambda t: t.name): 205 contents.append('%s %s\n' % (n.get_csig(), n.name)) 206 return ''.join(contents)
207
208 -def get_contents_file(node):
209 if not node.rexists(): 210 return '' 211 fname = node.rfile().get_abspath() 212 try: 213 contents = open(fname, "rb").read() 214 except EnvironmentError, e: 215 if not e.filename: 216 e.filename = fname 217 raise 218 return contents 219 220 _get_contents_map = {0 : get_contents_none, 221 1 : get_contents_entry, 222 2 : get_contents_dir, 223 3 : get_contents_file}
224 225 -def target_from_source_none(node, prefix, suffix, splitext):
226 raise NotImplementedError
227
228 -def target_from_source_base(node, prefix, suffix, splitext):
229 return node.dir.Entry(prefix + splitext(node.name)[0] + suffix)
230 231 _target_from_source_map = {0 : target_from_source_none, 232 1 : target_from_source_base}
233 234 # 235 # The new decider subsystem for Nodes 236 # 237 # We would set and overwrite the changed_since_last_build function 238 # before, but for being able to use slots (less memory!) we now have 239 # a dictionary of the different decider functions. Then in the Node 240 # subclasses we simply store the index to the decider that should be 241 # used by it. 242 # 243 244 # 245 # First, the single decider functions 246 # 247 -def changed_since_last_build_node(node, target, prev_ni):
248 """ 249 250 Must be overridden in a specific subclass to return True if this 251 Node (a dependency) has changed since the last time it was used 252 to build the specified target. prev_ni is this Node's state (for 253 example, its file timestamp, length, maybe content signature) 254 as of the last time the target was built. 255 256 Note that this method is called through the dependency, not the 257 target, because a dependency Node must be able to use its own 258 logic to decide if it changed. For example, File Nodes need to 259 obey if we're configured to use timestamps, but Python Value Nodes 260 never use timestamps and always use the content. If this method 261 were called through the target, then each Node's implementation 262 of this method would have to have more complicated logic to 263 handle all the different Node types on which it might depend. 264 """ 265 raise NotImplementedError
266
267 -def changed_since_last_build_alias(node, target, prev_ni):
268 cur_csig = node.get_csig() 269 try: 270 return cur_csig != prev_ni.csig 271 except AttributeError: 272 return 1
273
274 -def changed_since_last_build_entry(node, target, prev_ni):
275 node.disambiguate() 276 return _decider_map[node.changed_since_last_build](node, target, prev_ni)
277
278 -def changed_since_last_build_state_changed(node, target, prev_ni):
279 return (node.state != SCons.Node.up_to_date)
280
281 -def decide_source(node, target, prev_ni):
282 return target.get_build_env().decide_source(node, target, prev_ni)
283
284 -def decide_target(node, target, prev_ni):
285 return target.get_build_env().decide_target(node, target, prev_ni)
286
287 -def changed_since_last_build_python(node, target, prev_ni):
288 cur_csig = node.get_csig() 289 try: 290 return cur_csig != prev_ni.csig 291 except AttributeError: 292 return 1
293 294 295 # 296 # Now, the mapping from indices to decider functions 297 # 298 _decider_map = {0 : changed_since_last_build_node, 299 1 : changed_since_last_build_alias, 300 2 : changed_since_last_build_entry, 301 3 : changed_since_last_build_state_changed, 302 4 : decide_source, 303 5 : decide_target, 304 6 : changed_since_last_build_python} 305 306 do_store_info = True
307 308 # 309 # The new store_info subsystem for Nodes 310 # 311 # We would set and overwrite the store_info function 312 # before, but for being able to use slots (less memory!) we now have 313 # a dictionary of the different functions. Then in the Node 314 # subclasses we simply store the index to the info method that should be 315 # used by it. 316 # 317 318 # 319 # First, the single info functions 320 # 321 322 -def store_info_pass(node):
323 pass
324
325 -def store_info_file(node):
326 # Merge our build information into the already-stored entry. 327 # This accommodates "chained builds" where a file that's a target 328 # in one build (SConstruct file) is a source in a different build. 329 # See test/chained-build.py for the use case. 330 if do_store_info: 331 node.dir.sconsign().store_info(node.name, node)
332 333 334 store_info_map = {0 : store_info_pass, 335 1 : store_info_file}
336 337 # Classes for signature info for Nodes. 338 339 -class NodeInfoBase(object):
340 """ 341 The generic base class for signature information for a Node. 342 343 Node subclasses should subclass NodeInfoBase to provide their own 344 logic for dealing with their own Node-specific signature information. 345 """ 346 __slots__ = ('__weakref__',) 347 current_version_id = 2
348 - def update(self, node):
349 try: 350 field_list = self.field_list 351 except AttributeError: 352 return 353 for f in field_list: 354 try: 355 delattr(self, f) 356 except AttributeError: 357 pass 358 try: 359 func = getattr(node, 'get_' + f) 360 except AttributeError: 361 pass 362 else: 363 setattr(self, f, func())
364 - def convert(self, node, val):
365 pass
366 - def merge(self, other):
367 """ 368 Merge the fields of another object into this object. Already existing 369 information is overwritten by the other instance's data. 370 WARNING: If a '__dict__' slot is added, it should be updated instead of 371 replaced. 372 """ 373 state = other.__getstate__() 374 self.__setstate__(state)
375 - def format(self, field_list=None, names=0):
376 if field_list is None: 377 try: 378 field_list = self.field_list 379 except AttributeError: 380 field_list = getattr(self, '__dict__', {}).keys() 381 for obj in type(self).mro(): 382 for slot in getattr(obj, '__slots__', ()): 383 if slot not in ('__weakref__', '__dict__'): 384 field_list.append(slot) 385 field_list.sort() 386 fields = [] 387 for field in field_list: 388 try: 389 f = getattr(self, field) 390 except AttributeError: 391 f = None 392 f = str(f) 393 if names: 394 f = field + ': ' + f 395 fields.append(f) 396 return fields
397
398 - def __getstate__(self):
399 """ 400 Return all fields that shall be pickled. Walk the slots in the class 401 hierarchy and add those to the state dictionary. If a '__dict__' slot is 402 available, copy all entries to the dictionary. Also include the version 403 id, which is fixed for all instances of a class. 404 """ 405 state = getattr(self, '__dict__', {}).copy() 406 for obj in type(self).mro(): 407 for name in getattr(obj,'__slots__',()): 408 if hasattr(self, name): 409 state[name] = getattr(self, name) 410 411 state['_version_id'] = self.current_version_id 412 try: 413 del state['__weakref__'] 414 except KeyError: 415 pass 416 return state
417
418 - def __setstate__(self, state):
419 """ 420 Restore the attributes from a pickled state. The version is discarded. 421 """ 422 # TODO check or discard version 423 del state['_version_id'] 424 425 for key, value in state.items(): 426 if key not in ('__weakref__',): 427 setattr(self, key, value)
428
429 430 -class BuildInfoBase(object):
431 """ 432 The generic base class for build information for a Node. 433 434 This is what gets stored in a .sconsign file for each target file. 435 It contains a NodeInfo instance for this node (signature information 436 that's specific to the type of Node) and direct attributes for the 437 generic build stuff we have to track: sources, explicit dependencies, 438 implicit dependencies, and action information. 439 """ 440 __slots__ = ("bsourcesigs", "bdependsigs", "bimplicitsigs", "bactsig", 441 "bsources", "bdepends", "bact", "bimplicit", "__weakref__") 442 current_version_id = 2
443 - def __init__(self):
444 # Create an object attribute from the class attribute so it ends up 445 # in the pickled data in the .sconsign file. 446 self.bsourcesigs = [] 447 self.bdependsigs = [] 448 self.bimplicitsigs = [] 449 self.bactsig = None
450 - def merge(self, other):
451 """ 452 Merge the fields of another object into this object. Already existing 453 information is overwritten by the other instance's data. 454 WARNING: If a '__dict__' slot is added, it should be updated instead of 455 replaced. 456 """ 457 state = other.__getstate__() 458 self.__setstate__(state)
459
460 - def __getstate__(self):
461 """ 462 Return all fields that shall be pickled. Walk the slots in the class 463 hierarchy and add those to the state dictionary. If a '__dict__' slot is 464 available, copy all entries to the dictionary. Also include the version 465 id, which is fixed for all instances of a class. 466 """ 467 state = getattr(self, '__dict__', {}).copy() 468 for obj in type(self).mro(): 469 for name in getattr(obj,'__slots__',()): 470 if hasattr(self, name): 471 state[name] = getattr(self, name) 472 473 state['_version_id'] = self.current_version_id 474 try: 475 del state['__weakref__'] 476 except KeyError: 477 pass 478 return state
479
480 - def __setstate__(self, state):
481 """ 482 Restore the attributes from a pickled state. 483 """ 484 # TODO check or discard version 485 del state['_version_id'] 486 for key, value in state.items(): 487 if key not in ('__weakref__',): 488 setattr(self, key, value)
489
490 -class Node(object):
491 """The base Node class, for entities that we know how to 492 build, or use to build other Nodes. 493 """ 494 495 __slots__ = ['sources', 496 'sources_set', 497 '_specific_sources', 498 'depends', 499 'depends_set', 500 'ignore', 501 'ignore_set', 502 'prerequisites', 503 'implicit', 504 'waiting_parents', 505 'waiting_s_e', 506 'ref_count', 507 'wkids', 508 'env', 509 'state', 510 'precious', 511 'noclean', 512 'nocache', 513 'cached', 514 'always_build', 515 'includes', 516 'attributes', 517 'side_effect', 518 'side_effects', 519 'linked', 520 '_memo', 521 'executor', 522 'binfo', 523 'ninfo', 524 'builder', 525 'is_explicit', 526 'implicit_set', 527 'changed_since_last_build', 528 'store_info', 529 'pseudo', 530 '_tags', 531 '_func_is_derived', 532 '_func_exists', 533 '_func_rexists', 534 '_func_get_contents', 535 '_func_target_from_source'] 536
537 - class Attrs(object):
538 __slots__ = ('shared', '__dict__')
539 540
541 - def __init__(self):
542 if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.Node') 543 # Note that we no longer explicitly initialize a self.builder 544 # attribute to None here. That's because the self.builder 545 # attribute may be created on-the-fly later by a subclass (the 546 # canonical example being a builder to fetch a file from a 547 # source code system like CVS or Subversion). 548 549 # Each list of children that we maintain is accompanied by a 550 # dictionary used to look up quickly whether a node is already 551 # present in the list. Empirical tests showed that it was 552 # fastest to maintain them as side-by-side Node attributes in 553 # this way, instead of wrapping up each list+dictionary pair in 554 # a class. (Of course, we could always still do that in the 555 # future if we had a good reason to...). 556 self.sources = [] # source files used to build node 557 self.sources_set = set() 558 self._specific_sources = False 559 self.depends = [] # explicit dependencies (from Depends) 560 self.depends_set = set() 561 self.ignore = [] # dependencies to ignore 562 self.ignore_set = set() 563 self.prerequisites = None 564 self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) 565 self.waiting_parents = set() 566 self.waiting_s_e = set() 567 self.ref_count = 0 568 self.wkids = None # Kids yet to walk, when it's an array 569 570 self.env = None 571 self.state = no_state 572 self.precious = None 573 self.pseudo = False 574 self.noclean = 0 575 self.nocache = 0 576 self.cached = 0 # is this node pulled from cache? 577 self.always_build = None 578 self.includes = None 579 self.attributes = self.Attrs() # Generic place to stick information about the Node. 580 self.side_effect = 0 # true iff this node is a side effect 581 self.side_effects = [] # the side effects of building this target 582 self.linked = 0 # is this node linked to the variant directory? 583 self.changed_since_last_build = 0 584 self.store_info = 0 585 self._tags = None 586 self._func_is_derived = 1 587 self._func_exists = 1 588 self._func_rexists = 1 589 self._func_get_contents = 0 590 self._func_target_from_source = 0 591 592 self.clear_memoized_values() 593 594 # Let the interface in which the build engine is embedded 595 # annotate this Node with its own info (like a description of 596 # what line in what file created the node, for example). 597 Annotate(self)
598
599 - def disambiguate(self, must_exist=None):
600 return self
601
602 - def get_suffix(self):
603 return ''
604 605 @SCons.Memoize.CountMethodCall
606 - def get_build_env(self):
607 """Fetch the appropriate Environment to build this node. 608 """ 609 try: 610 return self._memo['get_build_env'] 611 except KeyError: 612 pass 613 result = self.get_executor().get_build_env() 614 self._memo['get_build_env'] = result 615 return result
616
617 - def get_build_scanner_path(self, scanner):
618 """Fetch the appropriate scanner path for this node.""" 619 return self.get_executor().get_build_scanner_path(scanner)
620
621 - def set_executor(self, executor):
622 """Set the action executor for this node.""" 623 self.executor = executor
624
625 - def get_executor(self, create=1):
626 """Fetch the action executor for this node. Create one if 627 there isn't already one, and requested to do so.""" 628 try: 629 executor = self.executor 630 except AttributeError: 631 if not create: 632 raise 633 try: 634 act = self.builder.action 635 except AttributeError: 636 executor = SCons.Executor.Null(targets=[self]) 637 else: 638 executor = SCons.Executor.Executor(act, 639 self.env or self.builder.env, 640 [self.builder.overrides], 641 [self], 642 self.sources) 643 self.executor = executor 644 return executor
645
646 - def executor_cleanup(self):
647 """Let the executor clean up any cached information.""" 648 try: 649 executor = self.get_executor(create=None) 650 except AttributeError: 651 pass 652 else: 653 if executor is not None: 654 executor.cleanup()
655
656 - def reset_executor(self):
657 "Remove cached executor; forces recompute when needed." 658 try: 659 delattr(self, 'executor') 660 except AttributeError: 661 pass
662
663 - def push_to_cache(self):
664 """Try to push a node into a cache 665 """ 666 pass
667
668 - def retrieve_from_cache(self):
669 """Try to retrieve the node's content from a cache 670 671 This method is called from multiple threads in a parallel build, 672 so only do thread safe stuff here. Do thread unsafe stuff in 673 built(). 674 675 Returns true if the node was successfully retrieved. 676 """ 677 return 0
678 679 # 680 # Taskmaster interface subsystem 681 # 682
683 - def make_ready(self):
684 """Get a Node ready for evaluation. 685 686 This is called before the Taskmaster decides if the Node is 687 up-to-date or not. Overriding this method allows for a Node 688 subclass to be disambiguated if necessary, or for an implicit 689 source builder to be attached. 690 """ 691 pass
692
693 - def prepare(self):
694 """Prepare for this Node to be built. 695 696 This is called after the Taskmaster has decided that the Node 697 is out-of-date and must be rebuilt, but before actually calling 698 the method to build the Node. 699 700 This default implementation checks that explicit or implicit 701 dependencies either exist or are derived, and initializes the 702 BuildInfo structure that will hold the information about how 703 this node is, uh, built. 704 705 (The existence of source files is checked separately by the 706 Executor, which aggregates checks for all of the targets built 707 by a specific action.) 708 709 Overriding this method allows for for a Node subclass to remove 710 the underlying file from the file system. Note that subclass 711 methods should call this base class method to get the child 712 check and the BuildInfo structure. 713 """ 714 if self.depends is not None: 715 for d in self.depends: 716 if d.missing(): 717 msg = "Explicit dependency `%s' not found, needed by target `%s'." 718 raise SCons.Errors.StopError(msg % (d, self)) 719 if self.implicit is not None: 720 for i in self.implicit: 721 if i.missing(): 722 msg = "Implicit dependency `%s' not found, needed by target `%s'." 723 raise SCons.Errors.StopError(msg % (i, self)) 724 self.binfo = self.get_binfo()
725
726 - def build(self, **kw):
727 """Actually build the node. 728 729 This is called by the Taskmaster after it's decided that the 730 Node is out-of-date and must be rebuilt, and after the prepare() 731 method has gotten everything, uh, prepared. 732 733 This method is called from multiple threads in a parallel build, 734 so only do thread safe stuff here. Do thread unsafe stuff 735 in built(). 736 737 """ 738 try: 739 self.get_executor()(self, **kw) 740 except SCons.Errors.BuildError, e: 741 e.node = self 742 raise
743
744 - def built(self):
745 """Called just after this node is successfully built.""" 746 747 # Clear the implicit dependency caches of any Nodes 748 # waiting for this Node to be built. 749 for parent in self.waiting_parents: 750 parent.implicit = None 751 752 self.clear() 753 754 if self.pseudo: 755 if self.exists(): 756 raise SCons.Errors.UserError("Pseudo target " + str(self) + " must not exist") 757 else: 758 if not self.exists() and do_store_info: 759 SCons.Warnings.warn(SCons.Warnings.TargetNotBuiltWarning, 760 "Cannot find target " + str(self) + " after building") 761 self.ninfo.update(self)
762
763 - def visited(self):
764 """Called just after this node has been visited (with or 765 without a build).""" 766 try: 767 binfo = self.binfo 768 except AttributeError: 769 # Apparently this node doesn't need build info, so 770 # don't bother calculating or storing it. 771 pass 772 else: 773 self.ninfo.update(self) 774 SCons.Node.store_info_map[self.store_info](self)
775
776 - def release_target_info(self):
777 """Called just after this node has been marked 778 up-to-date or was built completely. 779 780 This is where we try to release as many target node infos 781 as possible for clean builds and update runs, in order 782 to minimize the overall memory consumption. 783 784 By purging attributes that aren't needed any longer after 785 a Node (=File) got built, we don't have to care that much how 786 many KBytes a Node actually requires...as long as we free 787 the memory shortly afterwards. 788 789 @see: built() and File.release_target_info() 790 """ 791 pass
792 793 # 794 # 795 # 796
797 - def add_to_waiting_s_e(self, node):
798 self.waiting_s_e.add(node)
799
800 - def add_to_waiting_parents(self, node):
801 """ 802 Returns the number of nodes added to our waiting parents list: 803 1 if we add a unique waiting parent, 0 if not. (Note that the 804 returned values are intended to be used to increment a reference 805 count, so don't think you can "clean up" this function by using 806 True and False instead...) 807 """ 808 wp = self.waiting_parents 809 if node in wp: 810 return 0 811 wp.add(node) 812 return 1
813
814 - def postprocess(self):
815 """Clean up anything we don't need to hang onto after we've 816 been built.""" 817 self.executor_cleanup() 818 self.waiting_parents = set()
819
820 - def clear(self):
821 """Completely clear a Node of all its cached state (so that it 822 can be re-evaluated by interfaces that do continuous integration 823 builds). 824 """ 825 # The del_binfo() call here isn't necessary for normal execution, 826 # but is for interactive mode, where we might rebuild the same 827 # target and need to start from scratch. 828 self.del_binfo() 829 self.clear_memoized_values() 830 self.ninfo = self.new_ninfo() 831 self.executor_cleanup() 832 try: 833 delattr(self, '_calculated_sig') 834 except AttributeError: 835 pass 836 self.includes = None
837
838 - def clear_memoized_values(self):
839 self._memo = {}
840
841 - def builder_set(self, builder):
842 self.builder = builder 843 try: 844 del self.executor 845 except AttributeError: 846 pass
847
848 - def has_builder(self):
849 """Return whether this Node has a builder or not. 850 851 In Boolean tests, this turns out to be a *lot* more efficient 852 than simply examining the builder attribute directly ("if 853 node.builder: ..."). When the builder attribute is examined 854 directly, it ends up calling __getattr__ for both the __len__ 855 and __nonzero__ attributes on instances of our Builder Proxy 856 class(es), generating a bazillion extra calls and slowing 857 things down immensely. 858 """ 859 try: 860 b = self.builder 861 except AttributeError: 862 # There was no explicit builder for this Node, so initialize 863 # the self.builder attribute to None now. 864 b = self.builder = None 865 return b is not None
866
867 - def set_explicit(self, is_explicit):
869
870 - def has_explicit_builder(self):
871 """Return whether this Node has an explicit builder 872 873 This allows an internal Builder created by SCons to be marked 874 non-explicit, so that it can be overridden by an explicit 875 builder that the user supplies (the canonical example being 876 directories).""" 877 try: 878 return self.is_explicit 879 except AttributeError: 880 self.is_explicit = None 881 return self.is_explicit
882
883 - def get_builder(self, default_builder=None):
884 """Return the set builder, or a specified default value""" 885 try: 886 return self.builder 887 except AttributeError: 888 return default_builder
889 890 multiple_side_effect_has_builder = has_builder 891
892 - def is_derived(self):
893 """ 894 Returns true if this node is derived (i.e. built). 895 896 This should return true only for nodes whose path should be in 897 the variant directory when duplicate=0 and should contribute their build 898 signatures when they are used as source files to other derived files. For 899 example: source with source builders are not derived in this sense, 900 and hence should not return true. 901 """ 902 return _is_derived_map[self._func_is_derived](self)
903
904 - def alter_targets(self):
905 """Return a list of alternate targets for this Node. 906 """ 907 return [], None
908
909 - def get_found_includes(self, env, scanner, path):
910 """Return the scanned include lines (implicit dependencies) 911 found in this node. 912 913 The default is no implicit dependencies. We expect this method 914 to be overridden by any subclass that can be scanned for 915 implicit dependencies. 916 """ 917 return []
918
919 - def get_implicit_deps(self, env, initial_scanner, path_func, kw = {}):
920 """Return a list of implicit dependencies for this node. 921 922 This method exists to handle recursive invocation of the scanner 923 on the implicit dependencies returned by the scanner, if the 924 scanner's recursive flag says that we should. 925 """ 926 nodes = [self] 927 seen = {} 928 seen[self] = 1 929 dependencies = [] 930 931 root_node_scanner = self._get_scanner(env, initial_scanner, None, kw) 932 933 while nodes: 934 node = nodes.pop(0) 935 936 scanner = node._get_scanner(env, initial_scanner, root_node_scanner, kw) 937 938 if not scanner: 939 continue 940 941 path = path_func(scanner) 942 943 included_deps = [x for x in node.get_found_includes(env, scanner, path) if x not in seen] 944 if included_deps: 945 dependencies.extend(included_deps) 946 for dep in included_deps: 947 seen[dep] = 1 948 nodes.extend(scanner.recurse_nodes(included_deps)) 949 950 return dependencies
951
952 - def _get_scanner(self, env, initial_scanner, root_node_scanner, kw):
953 if not initial_scanner: 954 # handle implicit scanner case 955 scanner = self.get_env_scanner(env, kw) 956 if scanner: 957 scanner = scanner.select(self) 958 else: 959 # handle explicit scanner case 960 scanner = initial_scanner.select(self) 961 962 if not scanner: 963 # no scanner could be found for the given node's scanner key; 964 # thus, make an attempt at using a default. 965 scanner = root_node_scanner 966 967 return scanner
968
969 - def get_env_scanner(self, env, kw={}):
970 return env.get_scanner(self.scanner_key())
971
972 - def get_target_scanner(self):
973 return self.builder.target_scanner
974
975 - def get_source_scanner(self, node):
976 """Fetch the source scanner for the specified node 977 978 NOTE: "self" is the target being built, "node" is 979 the source file for which we want to fetch the scanner. 980 981 Implies self.has_builder() is true; again, expect to only be 982 called from locations where this is already verified. 983 984 This function may be called very often; it attempts to cache 985 the scanner found to improve performance. 986 """ 987 scanner = None 988 try: 989 scanner = self.builder.source_scanner 990 except AttributeError: 991 pass 992 if not scanner: 993 # The builder didn't have an explicit scanner, so go look up 994 # a scanner from env['SCANNERS'] based on the node's scanner 995 # key (usually the file extension). 996 scanner = self.get_env_scanner(self.get_build_env()) 997 if scanner: 998 scanner = scanner.select(node) 999 return scanner
1000
1001 - def add_to_implicit(self, deps):
1002 if not hasattr(self, 'implicit') or self.implicit is None: 1003 self.implicit = [] 1004 self.implicit_set = set() 1005 self._children_reset() 1006 self._add_child(self.implicit, self.implicit_set, deps)
1007
1008 - def scan(self):
1009 """Scan this node's dependents for implicit dependencies.""" 1010 # Don't bother scanning non-derived files, because we don't 1011 # care what their dependencies are. 1012 # Don't scan again, if we already have scanned. 1013 if self.implicit is not None: 1014 return 1015 self.implicit = [] 1016 self.implicit_set = set() 1017 self._children_reset() 1018 if not self.has_builder(): 1019 return 1020 1021 build_env = self.get_build_env() 1022 executor = self.get_executor() 1023 1024 # Here's where we implement --implicit-cache. 1025 if implicit_cache and not implicit_deps_changed: 1026 implicit = self.get_stored_implicit() 1027 if implicit is not None: 1028 # We now add the implicit dependencies returned from the 1029 # stored .sconsign entry to have already been converted 1030 # to Nodes for us. (We used to run them through a 1031 # source_factory function here.) 1032 1033 # Update all of the targets with them. This 1034 # essentially short-circuits an N*M scan of the 1035 # sources for each individual target, which is a hell 1036 # of a lot more efficient. 1037 for tgt in executor.get_all_targets(): 1038 tgt.add_to_implicit(implicit) 1039 1040 if implicit_deps_unchanged or self.is_up_to_date(): 1041 return 1042 # one of this node's sources has changed, 1043 # so we must recalculate the implicit deps for all targets 1044 for tgt in executor.get_all_targets(): 1045 tgt.implicit = [] 1046 tgt.implicit_set = set() 1047 1048 # Have the executor scan the sources. 1049 executor.scan_sources(self.builder.source_scanner) 1050 1051 # If there's a target scanner, have the executor scan the target 1052 # node itself and associated targets that might be built. 1053 scanner = self.get_target_scanner() 1054 if scanner: 1055 executor.scan_targets(scanner)
1056
1057 - def scanner_key(self):
1058 return None
1059
1060 - def select_scanner(self, scanner):
1061 """Selects a scanner for this Node. 1062 1063 This is a separate method so it can be overridden by Node 1064 subclasses (specifically, Node.FS.Dir) that *must* use their 1065 own Scanner and don't select one the Scanner.Selector that's 1066 configured for the target. 1067 """ 1068 return scanner.select(self)
1069
1070 - def env_set(self, env, safe=0):
1071 if safe and self.env: 1072 return 1073 self.env = env
1074 1075 # 1076 # SIGNATURE SUBSYSTEM 1077 # 1078 1079 NodeInfo = NodeInfoBase 1080 BuildInfo = BuildInfoBase 1081
1082 - def new_ninfo(self):
1083 ninfo = self.NodeInfo() 1084 return ninfo
1085
1086 - def get_ninfo(self):
1087 try: 1088 return self.ninfo 1089 except AttributeError: 1090 self.ninfo = self.new_ninfo() 1091 return self.ninfo
1092
1093 - def new_binfo(self):
1094 binfo = self.BuildInfo() 1095 return binfo
1096
1097 - def get_binfo(self):
1098 """ 1099 Fetch a node's build information. 1100 1101 node - the node whose sources will be collected 1102 cache - alternate node to use for the signature cache 1103 returns - the build signature 1104 1105 This no longer handles the recursive descent of the 1106 node's children's signatures. We expect that they're 1107 already built and updated by someone else, if that's 1108 what's wanted. 1109 """ 1110 try: 1111 return self.binfo 1112 except AttributeError: 1113 pass 1114 1115 binfo = self.new_binfo() 1116 self.binfo = binfo 1117 1118 executor = self.get_executor() 1119 ignore_set = self.ignore_set 1120 1121 if self.has_builder(): 1122 binfo.bact = str(executor) 1123 binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) 1124 1125 if self._specific_sources: 1126 sources = [] 1127 for s in self.sources: 1128 if s not in ignore_set: 1129 sources.append(s) 1130 else: 1131 sources = executor.get_unignored_sources(self, self.ignore) 1132 seen = set() 1133 bsources = [] 1134 bsourcesigs = [] 1135 for s in sources: 1136 if not s in seen: 1137 seen.add(s) 1138 bsources.append(s) 1139 bsourcesigs.append(s.get_ninfo()) 1140 binfo.bsources = bsources 1141 binfo.bsourcesigs = bsourcesigs 1142 1143 depends = self.depends 1144 dependsigs = [] 1145 for d in depends: 1146 if d not in ignore_set: 1147 dependsigs.append(d.get_ninfo()) 1148 binfo.bdepends = depends 1149 binfo.bdependsigs = dependsigs 1150 1151 implicit = self.implicit or [] 1152 implicitsigs = [] 1153 for i in implicit: 1154 if i not in ignore_set: 1155 implicitsigs.append(i.get_ninfo()) 1156 binfo.bimplicit = implicit 1157 binfo.bimplicitsigs = implicitsigs 1158 1159 return binfo
1160
1161 - def del_binfo(self):
1162 """Delete the build info from this node.""" 1163 try: 1164 delattr(self, 'binfo') 1165 except AttributeError: 1166 pass
1167
1168 - def get_csig(self):
1169 try: 1170 return self.ninfo.csig 1171 except AttributeError: 1172 ninfo = self.get_ninfo() 1173 ninfo.csig = SCons.Util.MD5signature(self.get_contents()) 1174 return self.ninfo.csig
1175
1176 - def get_cachedir_csig(self):
1177 return self.get_csig()
1178
1179 - def get_stored_info(self):
1180 return None
1181
1182 - def get_stored_implicit(self):
1183 """Fetch the stored implicit dependencies""" 1184 return None
1185 1186 # 1187 # 1188 # 1189
1190 - def set_precious(self, precious = 1):
1191 """Set the Node's precious value.""" 1192 self.precious = precious
1193
1194 - def set_pseudo(self, pseudo = True):
1195 """Set the Node's precious value.""" 1196 self.pseudo = pseudo
1197
1198 - def set_noclean(self, noclean = 1):
1199 """Set the Node's noclean value.""" 1200 # Make sure noclean is an integer so the --debug=stree 1201 # output in Util.py can use it as an index. 1202 self.noclean = noclean and 1 or 0
1203
1204 - def set_nocache(self, nocache = 1):
1205 """Set the Node's nocache value.""" 1206 # Make sure nocache is an integer so the --debug=stree 1207 # output in Util.py can use it as an index. 1208 self.nocache = nocache and 1 or 0
1209
1210 - def set_always_build(self, always_build = 1):
1211 """Set the Node's always_build value.""" 1212 self.always_build = always_build
1213
1214 - def exists(self):
1215 """Does this node exists?""" 1216 return _exists_map[self._func_exists](self)
1217
1218 - def rexists(self):
1219 """Does this node exist locally or in a repositiory?""" 1220 # There are no repositories by default: 1221 return _rexists_map[self._func_rexists](self)
1222
1223 - def get_contents(self):
1224 """Fetch the contents of the entry.""" 1225 return _get_contents_map[self._func_get_contents](self)
1226
1227 - def missing(self):
1228 return not self.is_derived() and \ 1229 not self.linked and \ 1230 not self.rexists()
1231
1232 - def remove(self):
1233 """Remove this Node: no-op by default.""" 1234 return None
1235
1236 - def add_dependency(self, depend):
1237 """Adds dependencies.""" 1238 try: 1239 self._add_child(self.depends, self.depends_set, depend) 1240 except TypeError, e: 1241 e = e.args[0] 1242 if SCons.Util.is_List(e): 1243 s = list(map(str, e)) 1244 else: 1245 s = str(e) 1246 raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
1247
1248 - def add_prerequisite(self, prerequisite):
1249 """Adds prerequisites""" 1250 if self.prerequisites is None: 1251 self.prerequisites = SCons.Util.UniqueList() 1252 self.prerequisites.extend(prerequisite) 1253 self._children_reset()
1254
1255 - def add_ignore(self, depend):
1256 """Adds dependencies to ignore.""" 1257 try: 1258 self._add_child(self.ignore, self.ignore_set, depend) 1259 except TypeError, e: 1260 e = e.args[0] 1261 if SCons.Util.is_List(e): 1262 s = list(map(str, e)) 1263 else: 1264 s = str(e) 1265 raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
1266
1267 - def add_source(self, source):
1268 """Adds sources.""" 1269 if self._specific_sources: 1270 return 1271 try: 1272 self._add_child(self.sources, self.sources_set, source) 1273 except TypeError, e: 1274 e = e.args[0] 1275 if SCons.Util.is_List(e): 1276 s = list(map(str, e)) 1277 else: 1278 s = str(e) 1279 raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
1280
1281 - def _add_child(self, collection, set, child):
1282 """Adds 'child' to 'collection', first checking 'set' to see if it's 1283 already present.""" 1284 added = None 1285 for c in child: 1286 if c not in set: 1287 set.add(c) 1288 collection.append(c) 1289 added = 1 1290 if added: 1291 self._children_reset()
1292
1293 - def set_specific_source(self, source):
1294 self.add_source(source) 1295 self._specific_sources = True
1296
1297 - def add_wkid(self, wkid):
1298 """Add a node to the list of kids waiting to be evaluated""" 1299 if self.wkids is not None: 1300 self.wkids.append(wkid)
1301
1302 - def _children_reset(self):
1303 self.clear_memoized_values() 1304 # We need to let the Executor clear out any calculated 1305 # build info that it's cached so we can re-calculate it. 1306 self.executor_cleanup()
1307 1308 @SCons.Memoize.CountMethodCall
1309 - def _children_get(self):
1310 try: 1311 return self._memo['_children_get'] 1312 except KeyError: 1313 pass 1314 1315 # The return list may contain duplicate Nodes, especially in 1316 # source trees where there are a lot of repeated #includes 1317 # of a tangle of .h files. Profiling shows, however, that 1318 # eliminating the duplicates with a brute-force approach that 1319 # preserves the order (that is, something like: 1320 # 1321 # u = [] 1322 # for n in list: 1323 # if n not in u: 1324 # u.append(n)" 1325 # 1326 # takes more cycles than just letting the underlying methods 1327 # hand back cached values if a Node's information is requested 1328 # multiple times. (Other methods of removing duplicates, like 1329 # using dictionary keys, lose the order, and the only ordered 1330 # dictionary patterns I found all ended up using "not in" 1331 # internally anyway...) 1332 if self.ignore_set: 1333 iter = chain.from_iterable(filter(None, [self.sources, self.depends, self.implicit])) 1334 1335 children = [] 1336 for i in iter: 1337 if i not in self.ignore_set: 1338 children.append(i) 1339 else: 1340 children = self.all_children(scan=0) 1341 1342 self._memo['_children_get'] = children 1343 return children
1344
1345 - def all_children(self, scan=1):
1346 """Return a list of all the node's direct children.""" 1347 if scan: 1348 self.scan() 1349 1350 # The return list may contain duplicate Nodes, especially in 1351 # source trees where there are a lot of repeated #includes 1352 # of a tangle of .h files. Profiling shows, however, that 1353 # eliminating the duplicates with a brute-force approach that 1354 # preserves the order (that is, something like: 1355 # 1356 # u = [] 1357 # for n in list: 1358 # if n not in u: 1359 # u.append(n)" 1360 # 1361 # takes more cycles than just letting the underlying methods 1362 # hand back cached values if a Node's information is requested 1363 # multiple times. (Other methods of removing duplicates, like 1364 # using dictionary keys, lose the order, and the only ordered 1365 # dictionary patterns I found all ended up using "not in" 1366 # internally anyway...) 1367 return list(chain.from_iterable(filter(None, [self.sources, self.depends, self.implicit])))
1368
1369 - def children(self, scan=1):
1370 """Return a list of the node's direct children, minus those 1371 that are ignored by this node.""" 1372 if scan: 1373 self.scan() 1374 return self._children_get()
1375
1376 - def set_state(self, state):
1377 self.state = state
1378
1379 - def get_state(self):
1380 return self.state
1381
1382 - def get_env(self):
1383 env = self.env 1384 if not env: 1385 import SCons.Defaults 1386 env = SCons.Defaults.DefaultEnvironment() 1387 return env
1388
1389 - def Decider(self, function):
1390 foundkey = None 1391 for k, v in _decider_map.iteritems(): 1392 if v == function: 1393 foundkey = k 1394 break 1395 if not foundkey: 1396 foundkey = len(_decider_map) 1397 _decider_map[foundkey] = function 1398 self.changed_since_last_build = foundkey
1399
1400 - def Tag(self, key, value):
1401 """ Add a user-defined tag. """ 1402 if not self._tags: 1403 self._tags = {} 1404 self._tags[key] = value
1405
1406 - def GetTag(self, key):
1407 """ Return a user-defined tag. """ 1408 if not self._tags: 1409 return None 1410 return self._tags.get(key, None)
1411
1412 - def changed(self, node=None, allowcache=False):
1413 """ 1414 Returns if the node is up-to-date with respect to the BuildInfo 1415 stored last time it was built. The default behavior is to compare 1416 it against our own previously stored BuildInfo, but the stored 1417 BuildInfo from another Node (typically one in a Repository) 1418 can be used instead. 1419 1420 Note that we now *always* check every dependency. We used to 1421 short-circuit the check by returning as soon as we detected 1422 any difference, but we now rely on checking every dependency 1423 to make sure that any necessary Node information (for example, 1424 the content signature of an #included .h file) is updated. 1425 1426 The allowcache option was added for supporting the early 1427 release of the executor/builder structures, right after 1428 a File target was built. When set to true, the return 1429 value of this changed method gets cached for File nodes. 1430 Like this, the executor isn't needed any longer for subsequent 1431 calls to changed(). 1432 1433 @see: FS.File.changed(), FS.File.release_target_info() 1434 """ 1435 t = 0 1436 if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) 1437 if node is None: 1438 node = self 1439 1440 result = False 1441 1442 bi = node.get_stored_info().binfo 1443 then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs 1444 children = self.children() 1445 1446 diff = len(children) - len(then) 1447 if diff: 1448 # The old and new dependency lists are different lengths. 1449 # This always indicates that the Node must be rebuilt. 1450 # We also extend the old dependency list with enough None 1451 # entries to equal the new dependency list, for the benefit 1452 # of the loop below that updates node information. 1453 then.extend([None] * diff) 1454 if t: Trace(': old %s new %s' % (len(then), len(children))) 1455 result = True 1456 1457 for child, prev_ni in zip(children, then): 1458 if _decider_map[child.changed_since_last_build](child, self, prev_ni): 1459 if t: Trace(': %s changed' % child) 1460 result = True 1461 1462 contents = self.get_executor().get_contents() 1463 if self.has_builder(): 1464 import SCons.Util 1465 newsig = SCons.Util.MD5signature(contents) 1466 if bi.bactsig != newsig: 1467 if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) 1468 result = True 1469 1470 if not result: 1471 if t: Trace(': up to date') 1472 1473 if t: Trace('\n') 1474 1475 return result
1476
1477 - def is_up_to_date(self):
1478 """Default check for whether the Node is current: unknown Node 1479 subtypes are always out of date, so they will always get built.""" 1480 return None
1481
1482 - def children_are_up_to_date(self):
1483 """Alternate check for whether the Node is current: If all of 1484 our children were up-to-date, then this Node was up-to-date, too. 1485 1486 The SCons.Node.Alias and SCons.Node.Python.Value subclasses 1487 rebind their current() method to this method.""" 1488 # Allow the children to calculate their signatures. 1489 self.binfo = self.get_binfo() 1490 if self.always_build: 1491 return None 1492 state = 0 1493 for kid in self.children(None): 1494 s = kid.get_state() 1495 if s and (not state or s > state): 1496 state = s 1497 return (state == 0 or state == SCons.Node.up_to_date)
1498
1499 - def is_literal(self):
1500 """Always pass the string representation of a Node to 1501 the command interpreter literally.""" 1502 return 1
1503
1504 - def render_include_tree(self):
1505 """ 1506 Return a text representation, suitable for displaying to the 1507 user, of the include tree for the sources of this node. 1508 """ 1509 if self.is_derived(): 1510 env = self.get_build_env() 1511 if env: 1512 for s in self.sources: 1513 scanner = self.get_source_scanner(s) 1514 if scanner: 1515 path = self.get_build_scanner_path(scanner) 1516 else: 1517 path = None 1518 def f(node, env=env, scanner=scanner, path=path): 1519 return node.get_found_includes(env, scanner, path)
1520 return SCons.Util.render_tree(s, f, 1) 1521 else: 1522 return None
1523
1524 - def get_abspath(self):
1525 """ 1526 Return an absolute path to the Node. This will return simply 1527 str(Node) by default, but for Node types that have a concept of 1528 relative path, this might return something different. 1529 """ 1530 return str(self)
1531
1532 - def for_signature(self):
1533 """ 1534 Return a string representation of the Node that will always 1535 be the same for this particular Node, no matter what. This 1536 is by contrast to the __str__() method, which might, for 1537 instance, return a relative path for a file Node. The purpose 1538 of this method is to generate a value to be used in signature 1539 calculation for the command line used to build a target, and 1540 we use this method instead of str() to avoid unnecessary 1541 rebuilds. This method does not need to return something that 1542 would actually work in a command line; it can return any kind of 1543 nonsense, so long as it does not change. 1544 """ 1545 return str(self)
1546
1547 - def get_string(self, for_signature):
1548 """This is a convenience function designed primarily to be 1549 used in command generators (i.e., CommandGeneratorActions or 1550 Environment variables that are callable), which are called 1551 with a for_signature argument that is nonzero if the command 1552 generator is being called to generate a signature for the 1553 command line, which determines if we should rebuild or not. 1554 1555 Such command generators should use this method in preference 1556 to str(Node) when converting a Node to a string, passing 1557 in the for_signature parameter, such that we will call 1558 Node.for_signature() or str(Node) properly, depending on whether 1559 we are calculating a signature or actually constructing a 1560 command line.""" 1561 if for_signature: 1562 return self.for_signature() 1563 return str(self)
1564
1565 - def get_subst_proxy(self):
1566 """ 1567 This method is expected to return an object that will function 1568 exactly like this Node, except that it implements any additional 1569 special features that we would like to be in effect for 1570 Environment variable substitution. The principle use is that 1571 some Nodes would like to implement a __getattr__() method, 1572 but putting that in the Node type itself has a tendency to kill 1573 performance. We instead put it in a proxy and return it from 1574 this method. It is legal for this method to return self 1575 if no new functionality is needed for Environment substitution. 1576 """ 1577 return self
1578
1579 - def explain(self):
1580 if not self.exists(): 1581 return "building `%s' because it doesn't exist\n" % self 1582 1583 if self.always_build: 1584 return "rebuilding `%s' because AlwaysBuild() is specified\n" % self 1585 1586 old = self.get_stored_info() 1587 if old is None: 1588 return None 1589 1590 old = old.binfo 1591 old.prepare_dependencies() 1592 1593 try: 1594 old_bkids = old.bsources + old.bdepends + old.bimplicit 1595 old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs 1596 except AttributeError: 1597 return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self 1598 1599 new = self.get_binfo() 1600 1601 new_bkids = new.bsources + new.bdepends + new.bimplicit 1602 new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs 1603 1604 osig = dict(zip(old_bkids, old_bkidsigs)) 1605 nsig = dict(zip(new_bkids, new_bkidsigs)) 1606 1607 # The sources and dependencies we'll want to report are all stored 1608 # as relative paths to this target's directory, but we want to 1609 # report them relative to the top-level SConstruct directory, 1610 # so we only print them after running them through this lambda 1611 # to turn them into the right relative Node and then return 1612 # its string. 1613 def stringify( s, E=self.dir.Entry ) : 1614 if hasattr( s, 'dir' ) : 1615 return str(E(s)) 1616 return str(s)
1617 1618 lines = [] 1619 1620 removed = [x for x in old_bkids if not x in new_bkids] 1621 if removed: 1622 removed = list(map(stringify, removed)) 1623 fmt = "`%s' is no longer a dependency\n" 1624 lines.extend([fmt % s for s in removed]) 1625 1626 for k in new_bkids: 1627 if not k in old_bkids: 1628 lines.append("`%s' is a new dependency\n" % stringify(k)) 1629 elif _decider_map[k.changed_since_last_build](k, self, osig[k]): 1630 lines.append("`%s' changed\n" % stringify(k)) 1631 1632 if len(lines) == 0 and old_bkids != new_bkids: 1633 lines.append("the dependency order changed:\n" + 1634 "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + 1635 "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) 1636 1637 if len(lines) == 0: 1638 def fmt_with_title(title, strlines): 1639 lines = strlines.split('\n') 1640 sep = '\n' + ' '*(15 + len(title)) 1641 return ' '*15 + title + sep.join(lines) + '\n' 1642 if old.bactsig != new.bactsig: 1643 if old.bact == new.bact: 1644 lines.append("the contents of the build action changed\n" + 1645 fmt_with_title('action: ', new.bact)) 1646 else: 1647 lines.append("the build action changed:\n" + 1648 fmt_with_title('old: ', old.bact) + 1649 fmt_with_title('new: ', new.bact)) 1650 1651 if len(lines) == 0: 1652 return "rebuilding `%s' for unknown reasons\n" % self 1653 1654 preamble = "rebuilding `%s' because" % self 1655 if len(lines) == 1: 1656 return "%s %s" % (preamble, lines[0]) 1657 else: 1658 lines = ["%s:\n" % preamble] + lines 1659 return ( ' '*11).join(lines) 1660
1661 -class NodeList(collections.UserList):
1662 - def __str__(self):
1663 return str(list(map(str, self.data)))
1664
1665 -def get_children(node, parent): return node.children()
1666 -def ignore_cycle(node, stack): pass
1667 -def do_nothing(node, parent): pass
1668
1669 -class Walker(object):
1670 """An iterator for walking a Node tree. 1671 1672 This is depth-first, children are visited before the parent. 1673 The Walker object can be initialized with any node, and 1674 returns the next node on the descent with each get_next() call. 1675 'kids_func' is an optional function that will be called to 1676 get the children of a node instead of calling 'children'. 1677 'cycle_func' is an optional function that will be called 1678 when a cycle is detected. 1679 1680 This class does not get caught in node cycles caused, for example, 1681 by C header file include loops. 1682 """
1683 - def __init__(self, node, kids_func=get_children, 1684 cycle_func=ignore_cycle, 1685 eval_func=do_nothing):
1686 self.kids_func = kids_func 1687 self.cycle_func = cycle_func 1688 self.eval_func = eval_func 1689 node.wkids = copy.copy(kids_func(node, None)) 1690 self.stack = [node] 1691 self.history = {} # used to efficiently detect and avoid cycles 1692 self.history[node] = None
1693
1694 - def get_next(self):
1695 """Return the next node for this walk of the tree. 1696 1697 This function is intentionally iterative, not recursive, 1698 to sidestep any issues of stack size limitations. 1699 """ 1700 1701 while self.stack: 1702 if self.stack[-1].wkids: 1703 node = self.stack[-1].wkids.pop(0) 1704 if not self.stack[-1].wkids: 1705 self.stack[-1].wkids = None 1706 if node in self.history: 1707 self.cycle_func(node, self.stack) 1708 else: 1709 node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) 1710 self.stack.append(node) 1711 self.history[node] = None 1712 else: 1713 node = self.stack.pop() 1714 del self.history[node] 1715 if node: 1716 if self.stack: 1717 parent = self.stack[-1] 1718 else: 1719 parent = None 1720 self.eval_func(node, parent) 1721 return node 1722 return None
1723
1724 - def is_done(self):
1725 return not self.stack
1726 1727 1728 arg2nodes_lookups = [] 1729 1730 # Local Variables: 1731 # tab-width:4 1732 # indent-tabs-mode:nil 1733 # End: 1734 # vim: set expandtab tabstop=4 shiftwidth=4: 1735