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