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 - 2015 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.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 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, scanner, path):
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 if not scanner: 927 return [] 928 929 # Give the scanner a chance to select a more specific scanner 930 # for this Node. 931 #scanner = scanner.select(self) 932 933 nodes = [self] 934 seen = {} 935 seen[self] = 1 936 deps = [] 937 while nodes: 938 n = nodes.pop(0) 939 d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen] 940 if d: 941 deps.extend(d) 942 for n in d: 943 seen[n] = 1 944 nodes.extend(scanner.recurse_nodes(d)) 945 946 return deps
947
948 - def get_env_scanner(self, env, kw={}):
949 return env.get_scanner(self.scanner_key())
950
951 - def get_target_scanner(self):
952 return self.builder.target_scanner
953
954 - def get_source_scanner(self, node):
955 """Fetch the source scanner for the specified node 956 957 NOTE: "self" is the target being built, "node" is 958 the source file for which we want to fetch the scanner. 959 960 Implies self.has_builder() is true; again, expect to only be 961 called from locations where this is already verified. 962 963 This function may be called very often; it attempts to cache 964 the scanner found to improve performance. 965 """ 966 scanner = None 967 try: 968 scanner = self.builder.source_scanner 969 except AttributeError: 970 pass 971 if not scanner: 972 # The builder didn't have an explicit scanner, so go look up 973 # a scanner from env['SCANNERS'] based on the node's scanner 974 # key (usually the file extension). 975 scanner = self.get_env_scanner(self.get_build_env()) 976 if scanner: 977 scanner = scanner.select(node) 978 return scanner
979
980 - def add_to_implicit(self, deps):
981 if not hasattr(self, 'implicit') or self.implicit is None: 982 self.implicit = [] 983 self.implicit_set = set() 984 self._children_reset() 985 self._add_child(self.implicit, self.implicit_set, deps)
986
987 - def scan(self):
988 """Scan this node's dependents for implicit dependencies.""" 989 # Don't bother scanning non-derived files, because we don't 990 # care what their dependencies are. 991 # Don't scan again, if we already have scanned. 992 if self.implicit is not None: 993 return 994 self.implicit = [] 995 self.implicit_set = set() 996 self._children_reset() 997 if not self.has_builder(): 998 return 999 1000 build_env = self.get_build_env() 1001 executor = self.get_executor() 1002 1003 # Here's where we implement --implicit-cache. 1004 if implicit_cache and not implicit_deps_changed: 1005 implicit = self.get_stored_implicit() 1006 if implicit is not None: 1007 # We now add the implicit dependencies returned from the 1008 # stored .sconsign entry to have already been converted 1009 # to Nodes for us. (We used to run them through a 1010 # source_factory function here.) 1011 1012 # Update all of the targets with them. This 1013 # essentially short-circuits an N*M scan of the 1014 # sources for each individual target, which is a hell 1015 # of a lot more efficient. 1016 for tgt in executor.get_all_targets(): 1017 tgt.add_to_implicit(implicit) 1018 1019 if implicit_deps_unchanged or self.is_up_to_date(): 1020 return 1021 # one of this node's sources has changed, 1022 # so we must recalculate the implicit deps for all targets 1023 for tgt in executor.get_all_targets(): 1024 tgt.implicit = [] 1025 tgt.implicit_set = set() 1026 1027 # Have the executor scan the sources. 1028 executor.scan_sources(self.builder.source_scanner) 1029 1030 # If there's a target scanner, have the executor scan the target 1031 # node itself and associated targets that might be built. 1032 scanner = self.get_target_scanner() 1033 if scanner: 1034 executor.scan_targets(scanner)
1035
1036 - def scanner_key(self):
1037 return None
1038
1039 - def select_scanner(self, scanner):
1040 """Selects a scanner for this Node. 1041 1042 This is a separate method so it can be overridden by Node 1043 subclasses (specifically, Node.FS.Dir) that *must* use their 1044 own Scanner and don't select one the Scanner.Selector that's 1045 configured for the target. 1046 """ 1047 return scanner.select(self)
1048
1049 - def env_set(self, env, safe=0):
1050 if safe and self.env: 1051 return 1052 self.env = env
1053 1054 # 1055 # SIGNATURE SUBSYSTEM 1056 # 1057 1058 NodeInfo = NodeInfoBase 1059 BuildInfo = BuildInfoBase 1060
1061 - def new_ninfo(self):
1062 ninfo = self.NodeInfo() 1063 return ninfo
1064
1065 - def get_ninfo(self):
1066 try: 1067 return self.ninfo 1068 except AttributeError: 1069 self.ninfo = self.new_ninfo() 1070 return self.ninfo
1071
1072 - def new_binfo(self):
1073 binfo = self.BuildInfo() 1074 return binfo
1075
1076 - def get_binfo(self):
1077 """ 1078 Fetch a node's build information. 1079 1080 node - the node whose sources will be collected 1081 cache - alternate node to use for the signature cache 1082 returns - the build signature 1083 1084 This no longer handles the recursive descent of the 1085 node's children's signatures. We expect that they're 1086 already built and updated by someone else, if that's 1087 what's wanted. 1088 """ 1089 try: 1090 return self.binfo 1091 except AttributeError: 1092 pass 1093 1094 binfo = self.new_binfo() 1095 self.binfo = binfo 1096 1097 executor = self.get_executor() 1098 ignore_set = self.ignore_set 1099 1100 if self.has_builder(): 1101 binfo.bact = str(executor) 1102 binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) 1103 1104 if self._specific_sources: 1105 sources = [] 1106 for s in self.sources: 1107 if s not in ignore_set: 1108 sources.append(s) 1109 else: 1110 sources = executor.get_unignored_sources(self, self.ignore) 1111 seen = set() 1112 bsources = [] 1113 bsourcesigs = [] 1114 for s in sources: 1115 if not s in seen: 1116 seen.add(s) 1117 bsources.append(s) 1118 bsourcesigs.append(s.get_ninfo()) 1119 binfo.bsources = bsources 1120 binfo.bsourcesigs = bsourcesigs 1121 1122 depends = self.depends 1123 dependsigs = [] 1124 for d in depends: 1125 if d not in ignore_set: 1126 dependsigs.append(d.get_ninfo()) 1127 binfo.bdepends = depends 1128 binfo.bdependsigs = dependsigs 1129 1130 implicit = self.implicit or [] 1131 implicitsigs = [] 1132 for i in implicit: 1133 if i not in ignore_set: 1134 implicitsigs.append(i.get_ninfo()) 1135 binfo.bimplicit = implicit 1136 binfo.bimplicitsigs = implicitsigs 1137 1138 return binfo
1139
1140 - def del_binfo(self):
1141 """Delete the build info from this node.""" 1142 try: 1143 delattr(self, 'binfo') 1144 except AttributeError: 1145 pass
1146
1147 - def get_csig(self):
1148 try: 1149 return self.ninfo.csig 1150 except AttributeError: 1151 ninfo = self.get_ninfo() 1152 ninfo.csig = SCons.Util.MD5signature(self.get_contents()) 1153 return self.ninfo.csig
1154
1155 - def get_cachedir_csig(self):
1156 return self.get_csig()
1157
1158 - def get_stored_info(self):
1159 return None
1160
1161 - def get_stored_implicit(self):
1162 """Fetch the stored implicit dependencies""" 1163 return None
1164 1165 # 1166 # 1167 # 1168
1169 - def set_precious(self, precious = 1):
1170 """Set the Node's precious value.""" 1171 self.precious = precious
1172
1173 - def set_pseudo(self, pseudo = True):
1174 """Set the Node's precious value.""" 1175 self.pseudo = pseudo
1176
1177 - def set_noclean(self, noclean = 1):
1178 """Set the Node's noclean value.""" 1179 # Make sure noclean is an integer so the --debug=stree 1180 # output in Util.py can use it as an index. 1181 self.noclean = noclean and 1 or 0
1182
1183 - def set_nocache(self, nocache = 1):
1184 """Set the Node's nocache value.""" 1185 # Make sure nocache is an integer so the --debug=stree 1186 # output in Util.py can use it as an index. 1187 self.nocache = nocache and 1 or 0
1188
1189 - def set_always_build(self, always_build = 1):
1190 """Set the Node's always_build value.""" 1191 self.always_build = always_build
1192
1193 - def exists(self):
1194 """Does this node exists?""" 1195 return _exists_map[self._func_exists](self)
1196
1197 - def rexists(self):
1198 """Does this node exist locally or in a repositiory?""" 1199 # There are no repositories by default: 1200 return _rexists_map[self._func_rexists](self)
1201
1202 - def get_contents(self):
1203 """Fetch the contents of the entry.""" 1204 return _get_contents_map[self._func_get_contents](self)
1205
1206 - def missing(self):
1207 return not self.is_derived() and \ 1208 not self.linked and \ 1209 not self.rexists()
1210
1211 - def remove(self):
1212 """Remove this Node: no-op by default.""" 1213 return None
1214
1215 - def add_dependency(self, depend):
1216 """Adds dependencies.""" 1217 try: 1218 self._add_child(self.depends, self.depends_set, depend) 1219 except TypeError, e: 1220 e = e.args[0] 1221 if SCons.Util.is_List(e): 1222 s = list(map(str, e)) 1223 else: 1224 s = str(e) 1225 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)))
1226
1227 - def add_prerequisite(self, prerequisite):
1228 """Adds prerequisites""" 1229 if self.prerequisites is None: 1230 self.prerequisites = SCons.Util.UniqueList() 1231 self.prerequisites.extend(prerequisite) 1232 self._children_reset()
1233
1234 - def add_ignore(self, depend):
1235 """Adds dependencies to ignore.""" 1236 try: 1237 self._add_child(self.ignore, self.ignore_set, depend) 1238 except TypeError, e: 1239 e = e.args[0] 1240 if SCons.Util.is_List(e): 1241 s = list(map(str, e)) 1242 else: 1243 s = str(e) 1244 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)))
1245
1246 - def add_source(self, source):
1247 """Adds sources.""" 1248 if self._specific_sources: 1249 return 1250 try: 1251 self._add_child(self.sources, self.sources_set, source) 1252 except TypeError, e: 1253 e = e.args[0] 1254 if SCons.Util.is_List(e): 1255 s = list(map(str, e)) 1256 else: 1257 s = str(e) 1258 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)))
1259
1260 - def _add_child(self, collection, set, child):
1261 """Adds 'child' to 'collection', first checking 'set' to see if it's 1262 already present.""" 1263 #if type(child) is not type([]): 1264 # child = [child] 1265 #for c in child: 1266 # if not isinstance(c, Node): 1267 # raise TypeError, c 1268 added = None 1269 for c in child: 1270 if c not in set: 1271 set.add(c) 1272 collection.append(c) 1273 added = 1 1274 if added: 1275 self._children_reset()
1276
1277 - def set_specific_source(self, source):
1278 self.add_source(source) 1279 self._specific_sources = True
1280
1281 - def add_wkid(self, wkid):
1282 """Add a node to the list of kids waiting to be evaluated""" 1283 if self.wkids is not None: 1284 self.wkids.append(wkid)
1285
1286 - def _children_reset(self):
1287 self.clear_memoized_values() 1288 # We need to let the Executor clear out any calculated 1289 # build info that it's cached so we can re-calculate it. 1290 self.executor_cleanup()
1291 1292 @SCons.Memoize.CountMethodCall
1293 - def _children_get(self):
1294 try: 1295 return self._memo['_children_get'] 1296 except KeyError: 1297 pass 1298 1299 # The return list may contain duplicate Nodes, especially in 1300 # source trees where there are a lot of repeated #includes 1301 # of a tangle of .h files. Profiling shows, however, that 1302 # eliminating the duplicates with a brute-force approach that 1303 # preserves the order (that is, something like: 1304 # 1305 # u = [] 1306 # for n in list: 1307 # if n not in u: 1308 # u.append(n)" 1309 # 1310 # takes more cycles than just letting the underlying methods 1311 # hand back cached values if a Node's information is requested 1312 # multiple times. (Other methods of removing duplicates, like 1313 # using dictionary keys, lose the order, and the only ordered 1314 # dictionary patterns I found all ended up using "not in" 1315 # internally anyway...) 1316 if self.ignore_set: 1317 iter = chain.from_iterable(filter(None, [self.sources, self.depends, self.implicit])) 1318 1319 children = [] 1320 for i in iter: 1321 if i not in self.ignore_set: 1322 children.append(i) 1323 else: 1324 children = self.all_children(scan=0) 1325 1326 self._memo['_children_get'] = children 1327 return children
1328
1329 - def all_children(self, scan=1):
1330 """Return a list of all the node's direct children.""" 1331 if scan: 1332 self.scan() 1333 1334 # The return list may contain duplicate Nodes, especially in 1335 # source trees where there are a lot of repeated #includes 1336 # of a tangle of .h files. Profiling shows, however, that 1337 # eliminating the duplicates with a brute-force approach that 1338 # preserves the order (that is, something like: 1339 # 1340 # u = [] 1341 # for n in list: 1342 # if n not in u: 1343 # u.append(n)" 1344 # 1345 # takes more cycles than just letting the underlying methods 1346 # hand back cached values if a Node's information is requested 1347 # multiple times. (Other methods of removing duplicates, like 1348 # using dictionary keys, lose the order, and the only ordered 1349 # dictionary patterns I found all ended up using "not in" 1350 # internally anyway...) 1351 return list(chain.from_iterable(filter(None, [self.sources, self.depends, self.implicit])))
1352
1353 - def children(self, scan=1):
1354 """Return a list of the node's direct children, minus those 1355 that are ignored by this node.""" 1356 if scan: 1357 self.scan() 1358 return self._children_get()
1359
1360 - def set_state(self, state):
1361 self.state = state
1362
1363 - def get_state(self):
1364 return self.state
1365
1366 - def get_env(self):
1367 env = self.env 1368 if not env: 1369 import SCons.Defaults 1370 env = SCons.Defaults.DefaultEnvironment() 1371 return env
1372
1373 - def Decider(self, function):
1374 foundkey = None 1375 for k, v in _decider_map.iteritems(): 1376 if v == function: 1377 foundkey = k 1378 break 1379 if not foundkey: 1380 foundkey = len(_decider_map) 1381 _decider_map[foundkey] = function 1382 self.changed_since_last_build = foundkey
1383
1384 - def Tag(self, key, value):
1385 """ Add a user-defined tag. """ 1386 if not self._tags: 1387 self._tags = {} 1388 self._tags[key] = value
1389
1390 - def GetTag(self, key):
1391 """ Return a user-defined tag. """ 1392 if not self._tags: 1393 return None 1394 return self._tags.get(key, None)
1395
1396 - def changed(self, node=None, allowcache=False):
1397 """ 1398 Returns if the node is up-to-date with respect to the BuildInfo 1399 stored last time it was built. The default behavior is to compare 1400 it against our own previously stored BuildInfo, but the stored 1401 BuildInfo from another Node (typically one in a Repository) 1402 can be used instead. 1403 1404 Note that we now *always* check every dependency. We used to 1405 short-circuit the check by returning as soon as we detected 1406 any difference, but we now rely on checking every dependency 1407 to make sure that any necessary Node information (for example, 1408 the content signature of an #included .h file) is updated. 1409 1410 The allowcache option was added for supporting the early 1411 release of the executor/builder structures, right after 1412 a File target was built. When set to true, the return 1413 value of this changed method gets cached for File nodes. 1414 Like this, the executor isn't needed any longer for subsequent 1415 calls to changed(). 1416 1417 @see: FS.File.changed(), FS.File.release_target_info() 1418 """ 1419 t = 0 1420 if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) 1421 if node is None: 1422 node = self 1423 1424 result = False 1425 1426 bi = node.get_stored_info().binfo 1427 then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs 1428 children = self.children() 1429 1430 diff = len(children) - len(then) 1431 if diff: 1432 # The old and new dependency lists are different lengths. 1433 # This always indicates that the Node must be rebuilt. 1434 # We also extend the old dependency list with enough None 1435 # entries to equal the new dependency list, for the benefit 1436 # of the loop below that updates node information. 1437 then.extend([None] * diff) 1438 if t: Trace(': old %s new %s' % (len(then), len(children))) 1439 result = True 1440 1441 for child, prev_ni in zip(children, then): 1442 if _decider_map[child.changed_since_last_build](child, self, prev_ni): 1443 if t: Trace(': %s changed' % child) 1444 result = True 1445 1446 contents = self.get_executor().get_contents() 1447 if self.has_builder(): 1448 import SCons.Util 1449 newsig = SCons.Util.MD5signature(contents) 1450 if bi.bactsig != newsig: 1451 if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) 1452 result = True 1453 1454 if not result: 1455 if t: Trace(': up to date') 1456 1457 if t: Trace('\n') 1458 1459 return result
1460
1461 - def is_up_to_date(self):
1462 """Default check for whether the Node is current: unknown Node 1463 subtypes are always out of date, so they will always get built.""" 1464 return None
1465
1466 - def children_are_up_to_date(self):
1467 """Alternate check for whether the Node is current: If all of 1468 our children were up-to-date, then this Node was up-to-date, too. 1469 1470 The SCons.Node.Alias and SCons.Node.Python.Value subclasses 1471 rebind their current() method to this method.""" 1472 # Allow the children to calculate their signatures. 1473 self.binfo = self.get_binfo() 1474 if self.always_build: 1475 return None 1476 state = 0 1477 for kid in self.children(None): 1478 s = kid.get_state() 1479 if s and (not state or s > state): 1480 state = s 1481 return (state == 0 or state == SCons.Node.up_to_date)
1482
1483 - def is_literal(self):
1484 """Always pass the string representation of a Node to 1485 the command interpreter literally.""" 1486 return 1
1487
1488 - def render_include_tree(self):
1489 """ 1490 Return a text representation, suitable for displaying to the 1491 user, of the include tree for the sources of this node. 1492 """ 1493 if self.is_derived(): 1494 env = self.get_build_env() 1495 if env: 1496 for s in self.sources: 1497 scanner = self.get_source_scanner(s) 1498 if scanner: 1499 path = self.get_build_scanner_path(scanner) 1500 else: 1501 path = None 1502 def f(node, env=env, scanner=scanner, path=path): 1503 return node.get_found_includes(env, scanner, path)
1504 return SCons.Util.render_tree(s, f, 1) 1505 else: 1506 return None
1507
1508 - def get_abspath(self):
1509 """ 1510 Return an absolute path to the Node. This will return simply 1511 str(Node) by default, but for Node types that have a concept of 1512 relative path, this might return something different. 1513 """ 1514 return str(self)
1515
1516 - def for_signature(self):
1517 """ 1518 Return a string representation of the Node that will always 1519 be the same for this particular Node, no matter what. This 1520 is by contrast to the __str__() method, which might, for 1521 instance, return a relative path for a file Node. The purpose 1522 of this method is to generate a value to be used in signature 1523 calculation for the command line used to build a target, and 1524 we use this method instead of str() to avoid unnecessary 1525 rebuilds. This method does not need to return something that 1526 would actually work in a command line; it can return any kind of 1527 nonsense, so long as it does not change. 1528 """ 1529 return str(self)
1530
1531 - def get_string(self, for_signature):
1532 """This is a convenience function designed primarily to be 1533 used in command generators (i.e., CommandGeneratorActions or 1534 Environment variables that are callable), which are called 1535 with a for_signature argument that is nonzero if the command 1536 generator is being called to generate a signature for the 1537 command line, which determines if we should rebuild or not. 1538 1539 Such command generators should use this method in preference 1540 to str(Node) when converting a Node to a string, passing 1541 in the for_signature parameter, such that we will call 1542 Node.for_signature() or str(Node) properly, depending on whether 1543 we are calculating a signature or actually constructing a 1544 command line.""" 1545 if for_signature: 1546 return self.for_signature() 1547 return str(self)
1548
1549 - def get_subst_proxy(self):
1550 """ 1551 This method is expected to return an object that will function 1552 exactly like this Node, except that it implements any additional 1553 special features that we would like to be in effect for 1554 Environment variable substitution. The principle use is that 1555 some Nodes would like to implement a __getattr__() method, 1556 but putting that in the Node type itself has a tendency to kill 1557 performance. We instead put it in a proxy and return it from 1558 this method. It is legal for this method to return self 1559 if no new functionality is needed for Environment substitution. 1560 """ 1561 return self
1562
1563 - def explain(self):
1564 if not self.exists(): 1565 return "building `%s' because it doesn't exist\n" % self 1566 1567 if self.always_build: 1568 return "rebuilding `%s' because AlwaysBuild() is specified\n" % self 1569 1570 old = self.get_stored_info() 1571 if old is None: 1572 return None 1573 1574 old = old.binfo 1575 old.prepare_dependencies() 1576 1577 try: 1578 old_bkids = old.bsources + old.bdepends + old.bimplicit 1579 old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs 1580 except AttributeError: 1581 return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self 1582 1583 new = self.get_binfo() 1584 1585 new_bkids = new.bsources + new.bdepends + new.bimplicit 1586 new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs 1587 1588 osig = dict(zip(old_bkids, old_bkidsigs)) 1589 nsig = dict(zip(new_bkids, new_bkidsigs)) 1590 1591 # The sources and dependencies we'll want to report are all stored 1592 # as relative paths to this target's directory, but we want to 1593 # report them relative to the top-level SConstruct directory, 1594 # so we only print them after running them through this lambda 1595 # to turn them into the right relative Node and then return 1596 # its string. 1597 def stringify( s, E=self.dir.Entry ) : 1598 if hasattr( s, 'dir' ) : 1599 return str(E(s)) 1600 return str(s)
1601 1602 lines = [] 1603 1604 removed = [x for x in old_bkids if not x in new_bkids] 1605 if removed: 1606 removed = list(map(stringify, removed)) 1607 fmt = "`%s' is no longer a dependency\n" 1608 lines.extend([fmt % s for s in removed]) 1609 1610 for k in new_bkids: 1611 if not k in old_bkids: 1612 lines.append("`%s' is a new dependency\n" % stringify(k)) 1613 elif _decider_map[k.changed_since_last_build](k, self, osig[k]): 1614 lines.append("`%s' changed\n" % stringify(k)) 1615 1616 if len(lines) == 0 and old_bkids != new_bkids: 1617 lines.append("the dependency order changed:\n" + 1618 "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + 1619 "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) 1620 1621 if len(lines) == 0: 1622 def fmt_with_title(title, strlines): 1623 lines = strlines.split('\n') 1624 sep = '\n' + ' '*(15 + len(title)) 1625 return ' '*15 + title + sep.join(lines) + '\n' 1626 if old.bactsig != new.bactsig: 1627 if old.bact == new.bact: 1628 lines.append("the contents of the build action changed\n" + 1629 fmt_with_title('action: ', new.bact)) 1630 else: 1631 lines.append("the build action changed:\n" + 1632 fmt_with_title('old: ', old.bact) + 1633 fmt_with_title('new: ', new.bact)) 1634 1635 if len(lines) == 0: 1636 return "rebuilding `%s' for unknown reasons\n" % self 1637 1638 preamble = "rebuilding `%s' because" % self 1639 if len(lines) == 1: 1640 return "%s %s" % (preamble, lines[0]) 1641 else: 1642 lines = ["%s:\n" % preamble] + lines 1643 return ( ' '*11).join(lines) 1644
1645 -class NodeList(collections.UserList):
1646 - def __str__(self):
1647 return str(list(map(str, self.data)))
1648
1649 -def get_children(node, parent): return node.children()
1650 -def ignore_cycle(node, stack): pass
1651 -def do_nothing(node, parent): pass
1652
1653 -class Walker(object):
1654 """An iterator for walking a Node tree. 1655 1656 This is depth-first, children are visited before the parent. 1657 The Walker object can be initialized with any node, and 1658 returns the next node on the descent with each get_next() call. 1659 'kids_func' is an optional function that will be called to 1660 get the children of a node instead of calling 'children'. 1661 'cycle_func' is an optional function that will be called 1662 when a cycle is detected. 1663 1664 This class does not get caught in node cycles caused, for example, 1665 by C header file include loops. 1666 """
1667 - def __init__(self, node, kids_func=get_children, 1668 cycle_func=ignore_cycle, 1669 eval_func=do_nothing):
1670 self.kids_func = kids_func 1671 self.cycle_func = cycle_func 1672 self.eval_func = eval_func 1673 node.wkids = copy.copy(kids_func(node, None)) 1674 self.stack = [node] 1675 self.history = {} # used to efficiently detect and avoid cycles 1676 self.history[node] = None
1677
1678 - def get_next(self):
1679 """Return the next node for this walk of the tree. 1680 1681 This function is intentionally iterative, not recursive, 1682 to sidestep any issues of stack size limitations. 1683 """ 1684 1685 while self.stack: 1686 if self.stack[-1].wkids: 1687 node = self.stack[-1].wkids.pop(0) 1688 if not self.stack[-1].wkids: 1689 self.stack[-1].wkids = None 1690 if node in self.history: 1691 self.cycle_func(node, self.stack) 1692 else: 1693 node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) 1694 self.stack.append(node) 1695 self.history[node] = None 1696 else: 1697 node = self.stack.pop() 1698 del self.history[node] 1699 if node: 1700 if self.stack: 1701 parent = self.stack[-1] 1702 else: 1703 parent = None 1704 self.eval_func(node, parent) 1705 return node 1706 return None
1707
1708 - def is_done(self):
1709 return not self.stack
1710 1711 1712 arg2nodes_lookups = [] 1713 1714 # Local Variables: 1715 # tab-width:4 1716 # indent-tabs-mode:nil 1717 # End: 1718 # vim: set expandtab tabstop=4 shiftwidth=4: 1719