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