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

Source Code for Package SCons.Node

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