OLD | NEW |
(Empty) | |
| 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, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons 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 5134 2010/08/16 23:02:40 bdeeg
an" |
| 45 |
| 46 import collections |
| 47 import copy |
| 48 from itertools import chain |
| 49 |
| 50 from SCons.Debug import logInstanceCreation |
| 51 import SCons.Executor |
| 52 import SCons.Memoize |
| 53 import SCons.Util |
| 54 |
| 55 from SCons.Debug import Trace |
| 56 |
| 57 def classname(obj): |
| 58 return str(obj.__class__).split('.')[-1] |
| 59 |
| 60 # Node states |
| 61 # |
| 62 # These are in "priority" order, so that the maximum value for any |
| 63 # child/dependency of a node represents the state of that node if |
| 64 # it has no builder of its own. The canonical example is a file |
| 65 # system directory, which is only up to date if all of its children |
| 66 # were up to date. |
| 67 no_state = 0 |
| 68 pending = 1 |
| 69 executing = 2 |
| 70 up_to_date = 3 |
| 71 executed = 4 |
| 72 failed = 5 |
| 73 |
| 74 StateString = { |
| 75 0 : "no_state", |
| 76 1 : "pending", |
| 77 2 : "executing", |
| 78 3 : "up_to_date", |
| 79 4 : "executed", |
| 80 5 : "failed", |
| 81 } |
| 82 |
| 83 # controls whether implicit dependencies are cached: |
| 84 implicit_cache = 0 |
| 85 |
| 86 # controls whether implicit dep changes are ignored: |
| 87 implicit_deps_unchanged = 0 |
| 88 |
| 89 # controls whether the cached implicit deps are ignored: |
| 90 implicit_deps_changed = 0 |
| 91 |
| 92 # A variable that can be set to an interface-specific function be called |
| 93 # to annotate a Node with information about its creation. |
| 94 def do_nothing(node): pass |
| 95 |
| 96 Annotate = do_nothing |
| 97 |
| 98 # Classes for signature info for Nodes. |
| 99 |
| 100 class NodeInfoBase(object): |
| 101 """ |
| 102 The generic base class for signature information for a Node. |
| 103 |
| 104 Node subclasses should subclass NodeInfoBase to provide their own |
| 105 logic for dealing with their own Node-specific signature information. |
| 106 """ |
| 107 current_version_id = 1 |
| 108 def __init__(self, node=None): |
| 109 # Create an object attribute from the class attribute so it ends up |
| 110 # in the pickled data in the .sconsign file. |
| 111 self._version_id = self.current_version_id |
| 112 def update(self, node): |
| 113 try: |
| 114 field_list = self.field_list |
| 115 except AttributeError: |
| 116 return |
| 117 for f in field_list: |
| 118 try: |
| 119 delattr(self, f) |
| 120 except AttributeError: |
| 121 pass |
| 122 try: |
| 123 func = getattr(node, 'get_' + f) |
| 124 except AttributeError: |
| 125 pass |
| 126 else: |
| 127 setattr(self, f, func()) |
| 128 def convert(self, node, val): |
| 129 pass |
| 130 def merge(self, other): |
| 131 self.__dict__.update(other.__dict__) |
| 132 def format(self, field_list=None, names=0): |
| 133 if field_list is None: |
| 134 try: |
| 135 field_list = self.field_list |
| 136 except AttributeError: |
| 137 field_list = sorted(self.__dict__.keys()) |
| 138 fields = [] |
| 139 for field in field_list: |
| 140 try: |
| 141 f = getattr(self, field) |
| 142 except AttributeError: |
| 143 f = None |
| 144 f = str(f) |
| 145 if names: |
| 146 f = field + ': ' + f |
| 147 fields.append(f) |
| 148 return fields |
| 149 |
| 150 class BuildInfoBase(object): |
| 151 """ |
| 152 The generic base class for build information for a Node. |
| 153 |
| 154 This is what gets stored in a .sconsign file for each target file. |
| 155 It contains a NodeInfo instance for this node (signature information |
| 156 that's specific to the type of Node) and direct attributes for the |
| 157 generic build stuff we have to track: sources, explicit dependencies, |
| 158 implicit dependencies, and action information. |
| 159 """ |
| 160 current_version_id = 1 |
| 161 def __init__(self, node=None): |
| 162 # Create an object attribute from the class attribute so it ends up |
| 163 # in the pickled data in the .sconsign file. |
| 164 self._version_id = self.current_version_id |
| 165 self.bsourcesigs = [] |
| 166 self.bdependsigs = [] |
| 167 self.bimplicitsigs = [] |
| 168 self.bactsig = None |
| 169 def merge(self, other): |
| 170 self.__dict__.update(other.__dict__) |
| 171 |
| 172 class Node(object): |
| 173 """The base Node class, for entities that we know how to |
| 174 build, or use to build other Nodes. |
| 175 """ |
| 176 |
| 177 if SCons.Memoize.use_memoizer: |
| 178 __metaclass__ = SCons.Memoize.Memoized_Metaclass |
| 179 |
| 180 memoizer_counters = [] |
| 181 |
| 182 class Attrs(object): |
| 183 pass |
| 184 |
| 185 def __init__(self): |
| 186 if __debug__: logInstanceCreation(self, 'Node.Node') |
| 187 # Note that we no longer explicitly initialize a self.builder |
| 188 # attribute to None here. That's because the self.builder |
| 189 # attribute may be created on-the-fly later by a subclass (the |
| 190 # canonical example being a builder to fetch a file from a |
| 191 # source code system like CVS or Subversion). |
| 192 |
| 193 # Each list of children that we maintain is accompanied by a |
| 194 # dictionary used to look up quickly whether a node is already |
| 195 # present in the list. Empirical tests showed that it was |
| 196 # fastest to maintain them as side-by-side Node attributes in |
| 197 # this way, instead of wrapping up each list+dictionary pair in |
| 198 # a class. (Of course, we could always still do that in the |
| 199 # future if we had a good reason to...). |
| 200 self.sources = [] # source files used to build node |
| 201 self.sources_set = set() |
| 202 self._specific_sources = False |
| 203 self.depends = [] # explicit dependencies (from Depends) |
| 204 self.depends_set = set() |
| 205 self.ignore = [] # dependencies to ignore |
| 206 self.ignore_set = set() |
| 207 self.prerequisites = SCons.Util.UniqueList() |
| 208 self.implicit = None # implicit (scanned) dependencies (None means no
t scanned yet) |
| 209 self.waiting_parents = set() |
| 210 self.waiting_s_e = set() |
| 211 self.ref_count = 0 |
| 212 self.wkids = None # Kids yet to walk, when it's an array |
| 213 |
| 214 self.env = None |
| 215 self.state = no_state |
| 216 self.precious = None |
| 217 self.noclean = 0 |
| 218 self.nocache = 0 |
| 219 self.always_build = None |
| 220 self.includes = None |
| 221 self.attributes = self.Attrs() # Generic place to stick information abou
t the Node. |
| 222 self.side_effect = 0 # true iff this node is a side effect |
| 223 self.side_effects = [] # the side effects of building this target |
| 224 self.linked = 0 # is this node linked to the variant directory? |
| 225 |
| 226 self.clear_memoized_values() |
| 227 |
| 228 # Let the interface in which the build engine is embedded |
| 229 # annotate this Node with its own info (like a description of |
| 230 # what line in what file created the node, for example). |
| 231 Annotate(self) |
| 232 |
| 233 def disambiguate(self, must_exist=None): |
| 234 return self |
| 235 |
| 236 def get_suffix(self): |
| 237 return '' |
| 238 |
| 239 memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) |
| 240 |
| 241 def get_build_env(self): |
| 242 """Fetch the appropriate Environment to build this node. |
| 243 """ |
| 244 try: |
| 245 return self._memo['get_build_env'] |
| 246 except KeyError: |
| 247 pass |
| 248 result = self.get_executor().get_build_env() |
| 249 self._memo['get_build_env'] = result |
| 250 return result |
| 251 |
| 252 def get_build_scanner_path(self, scanner): |
| 253 """Fetch the appropriate scanner path for this node.""" |
| 254 return self.get_executor().get_build_scanner_path(scanner) |
| 255 |
| 256 def set_executor(self, executor): |
| 257 """Set the action executor for this node.""" |
| 258 self.executor = executor |
| 259 |
| 260 def get_executor(self, create=1): |
| 261 """Fetch the action executor for this node. Create one if |
| 262 there isn't already one, and requested to do so.""" |
| 263 try: |
| 264 executor = self.executor |
| 265 except AttributeError: |
| 266 if not create: |
| 267 raise |
| 268 try: |
| 269 act = self.builder.action |
| 270 except AttributeError: |
| 271 executor = SCons.Executor.Null(targets=[self]) |
| 272 else: |
| 273 executor = SCons.Executor.Executor(act, |
| 274 self.env or self.builder.env, |
| 275 [self.builder.overrides], |
| 276 [self], |
| 277 self.sources) |
| 278 self.executor = executor |
| 279 return executor |
| 280 |
| 281 def executor_cleanup(self): |
| 282 """Let the executor clean up any cached information.""" |
| 283 try: |
| 284 executor = self.get_executor(create=None) |
| 285 except AttributeError: |
| 286 pass |
| 287 else: |
| 288 executor.cleanup() |
| 289 |
| 290 def reset_executor(self): |
| 291 "Remove cached executor; forces recompute when needed." |
| 292 try: |
| 293 delattr(self, 'executor') |
| 294 except AttributeError: |
| 295 pass |
| 296 |
| 297 def push_to_cache(self): |
| 298 """Try to push a node into a cache |
| 299 """ |
| 300 pass |
| 301 |
| 302 def retrieve_from_cache(self): |
| 303 """Try to retrieve the node's content from a cache |
| 304 |
| 305 This method is called from multiple threads in a parallel build, |
| 306 so only do thread safe stuff here. Do thread unsafe stuff in |
| 307 built(). |
| 308 |
| 309 Returns true iff the node was successfully retrieved. |
| 310 """ |
| 311 return 0 |
| 312 |
| 313 # |
| 314 # Taskmaster interface subsystem |
| 315 # |
| 316 |
| 317 def make_ready(self): |
| 318 """Get a Node ready for evaluation. |
| 319 |
| 320 This is called before the Taskmaster decides if the Node is |
| 321 up-to-date or not. Overriding this method allows for a Node |
| 322 subclass to be disambiguated if necessary, or for an implicit |
| 323 source builder to be attached. |
| 324 """ |
| 325 pass |
| 326 |
| 327 def prepare(self): |
| 328 """Prepare for this Node to be built. |
| 329 |
| 330 This is called after the Taskmaster has decided that the Node |
| 331 is out-of-date and must be rebuilt, but before actually calling |
| 332 the method to build the Node. |
| 333 |
| 334 This default implementation checks that explicit or implicit |
| 335 dependencies either exist or are derived, and initializes the |
| 336 BuildInfo structure that will hold the information about how |
| 337 this node is, uh, built. |
| 338 |
| 339 (The existence of source files is checked separately by the |
| 340 Executor, which aggregates checks for all of the targets built |
| 341 by a specific action.) |
| 342 |
| 343 Overriding this method allows for for a Node subclass to remove |
| 344 the underlying file from the file system. Note that subclass |
| 345 methods should call this base class method to get the child |
| 346 check and the BuildInfo structure. |
| 347 """ |
| 348 for d in self.depends: |
| 349 if d.missing(): |
| 350 msg = "Explicit dependency `%s' not found, needed by target `%s'
." |
| 351 raise SCons.Errors.StopError(msg % (d, self)) |
| 352 if self.implicit is not None: |
| 353 for i in self.implicit: |
| 354 if i.missing(): |
| 355 msg = "Implicit dependency `%s' not found, needed by target
`%s'." |
| 356 raise SCons.Errors.StopError(msg % (i, self)) |
| 357 self.binfo = self.get_binfo() |
| 358 |
| 359 def build(self, **kw): |
| 360 """Actually build the node. |
| 361 |
| 362 This is called by the Taskmaster after it's decided that the |
| 363 Node is out-of-date and must be rebuilt, and after the prepare() |
| 364 method has gotten everything, uh, prepared. |
| 365 |
| 366 This method is called from multiple threads in a parallel build, |
| 367 so only do thread safe stuff here. Do thread unsafe stuff |
| 368 in built(). |
| 369 |
| 370 """ |
| 371 try: |
| 372 self.get_executor()(self, **kw) |
| 373 except SCons.Errors.BuildError, e: |
| 374 e.node = self |
| 375 raise |
| 376 |
| 377 def built(self): |
| 378 """Called just after this node is successfully built.""" |
| 379 |
| 380 # Clear the implicit dependency caches of any Nodes |
| 381 # waiting for this Node to be built. |
| 382 for parent in self.waiting_parents: |
| 383 parent.implicit = None |
| 384 |
| 385 self.clear() |
| 386 |
| 387 self.ninfo.update(self) |
| 388 |
| 389 def visited(self): |
| 390 """Called just after this node has been visited (with or |
| 391 without a build).""" |
| 392 try: |
| 393 binfo = self.binfo |
| 394 except AttributeError: |
| 395 # Apparently this node doesn't need build info, so |
| 396 # don't bother calculating or storing it. |
| 397 pass |
| 398 else: |
| 399 self.ninfo.update(self) |
| 400 self.store_info() |
| 401 |
| 402 # |
| 403 # |
| 404 # |
| 405 |
| 406 def add_to_waiting_s_e(self, node): |
| 407 self.waiting_s_e.add(node) |
| 408 |
| 409 def add_to_waiting_parents(self, node): |
| 410 """ |
| 411 Returns the number of nodes added to our waiting parents list: |
| 412 1 if we add a unique waiting parent, 0 if not. (Note that the |
| 413 returned values are intended to be used to increment a reference |
| 414 count, so don't think you can "clean up" this function by using |
| 415 True and False instead...) |
| 416 """ |
| 417 wp = self.waiting_parents |
| 418 if node in wp: |
| 419 return 0 |
| 420 wp.add(node) |
| 421 return 1 |
| 422 |
| 423 def postprocess(self): |
| 424 """Clean up anything we don't need to hang onto after we've |
| 425 been built.""" |
| 426 self.executor_cleanup() |
| 427 self.waiting_parents = set() |
| 428 |
| 429 def clear(self): |
| 430 """Completely clear a Node of all its cached state (so that it |
| 431 can be re-evaluated by interfaces that do continuous integration |
| 432 builds). |
| 433 """ |
| 434 # The del_binfo() call here isn't necessary for normal execution, |
| 435 # but is for interactive mode, where we might rebuild the same |
| 436 # target and need to start from scratch. |
| 437 self.del_binfo() |
| 438 self.clear_memoized_values() |
| 439 self.ninfo = self.new_ninfo() |
| 440 self.executor_cleanup() |
| 441 try: |
| 442 delattr(self, '_calculated_sig') |
| 443 except AttributeError: |
| 444 pass |
| 445 self.includes = None |
| 446 |
| 447 def clear_memoized_values(self): |
| 448 self._memo = {} |
| 449 |
| 450 def builder_set(self, builder): |
| 451 self.builder = builder |
| 452 try: |
| 453 del self.executor |
| 454 except AttributeError: |
| 455 pass |
| 456 |
| 457 def has_builder(self): |
| 458 """Return whether this Node has a builder or not. |
| 459 |
| 460 In Boolean tests, this turns out to be a *lot* more efficient |
| 461 than simply examining the builder attribute directly ("if |
| 462 node.builder: ..."). When the builder attribute is examined |
| 463 directly, it ends up calling __getattr__ for both the __len__ |
| 464 and __nonzero__ attributes on instances of our Builder Proxy |
| 465 class(es), generating a bazillion extra calls and slowing |
| 466 things down immensely. |
| 467 """ |
| 468 try: |
| 469 b = self.builder |
| 470 except AttributeError: |
| 471 # There was no explicit builder for this Node, so initialize |
| 472 # the self.builder attribute to None now. |
| 473 b = self.builder = None |
| 474 return b is not None |
| 475 |
| 476 def set_explicit(self, is_explicit): |
| 477 self.is_explicit = is_explicit |
| 478 |
| 479 def has_explicit_builder(self): |
| 480 """Return whether this Node has an explicit builder |
| 481 |
| 482 This allows an internal Builder created by SCons to be marked |
| 483 non-explicit, so that it can be overridden by an explicit |
| 484 builder that the user supplies (the canonical example being |
| 485 directories).""" |
| 486 try: |
| 487 return self.is_explicit |
| 488 except AttributeError: |
| 489 self.is_explicit = None |
| 490 return self.is_explicit |
| 491 |
| 492 def get_builder(self, default_builder=None): |
| 493 """Return the set builder, or a specified default value""" |
| 494 try: |
| 495 return self.builder |
| 496 except AttributeError: |
| 497 return default_builder |
| 498 |
| 499 multiple_side_effect_has_builder = has_builder |
| 500 |
| 501 def is_derived(self): |
| 502 """ |
| 503 Returns true iff this node is derived (i.e. built). |
| 504 |
| 505 This should return true only for nodes whose path should be in |
| 506 the variant directory when duplicate=0 and should contribute their build |
| 507 signatures when they are used as source files to other derived files. Fo
r |
| 508 example: source with source builders are not derived in this sense, |
| 509 and hence should not return true. |
| 510 """ |
| 511 return self.has_builder() or self.side_effect |
| 512 |
| 513 def alter_targets(self): |
| 514 """Return a list of alternate targets for this Node. |
| 515 """ |
| 516 return [], None |
| 517 |
| 518 def get_found_includes(self, env, scanner, path): |
| 519 """Return the scanned include lines (implicit dependencies) |
| 520 found in this node. |
| 521 |
| 522 The default is no implicit dependencies. We expect this method |
| 523 to be overridden by any subclass that can be scanned for |
| 524 implicit dependencies. |
| 525 """ |
| 526 return [] |
| 527 |
| 528 def get_implicit_deps(self, env, scanner, path): |
| 529 """Return a list of implicit dependencies for this node. |
| 530 |
| 531 This method exists to handle recursive invocation of the scanner |
| 532 on the implicit dependencies returned by the scanner, if the |
| 533 scanner's recursive flag says that we should. |
| 534 """ |
| 535 if not scanner: |
| 536 return [] |
| 537 |
| 538 # Give the scanner a chance to select a more specific scanner |
| 539 # for this Node. |
| 540 #scanner = scanner.select(self) |
| 541 |
| 542 nodes = [self] |
| 543 seen = {} |
| 544 seen[self] = 1 |
| 545 deps = [] |
| 546 while nodes: |
| 547 n = nodes.pop(0) |
| 548 d = [x for x in n.get_found_includes(env, scanner, path) if x not in
seen] |
| 549 if d: |
| 550 deps.extend(d) |
| 551 for n in d: |
| 552 seen[n] = 1 |
| 553 nodes.extend(scanner.recurse_nodes(d)) |
| 554 |
| 555 return deps |
| 556 |
| 557 def get_env_scanner(self, env, kw={}): |
| 558 return env.get_scanner(self.scanner_key()) |
| 559 |
| 560 def get_target_scanner(self): |
| 561 return self.builder.target_scanner |
| 562 |
| 563 def get_source_scanner(self, node): |
| 564 """Fetch the source scanner for the specified node |
| 565 |
| 566 NOTE: "self" is the target being built, "node" is |
| 567 the source file for which we want to fetch the scanner. |
| 568 |
| 569 Implies self.has_builder() is true; again, expect to only be |
| 570 called from locations where this is already verified. |
| 571 |
| 572 This function may be called very often; it attempts to cache |
| 573 the scanner found to improve performance. |
| 574 """ |
| 575 scanner = None |
| 576 try: |
| 577 scanner = self.builder.source_scanner |
| 578 except AttributeError: |
| 579 pass |
| 580 if not scanner: |
| 581 # The builder didn't have an explicit scanner, so go look up |
| 582 # a scanner from env['SCANNERS'] based on the node's scanner |
| 583 # key (usually the file extension). |
| 584 scanner = self.get_env_scanner(self.get_build_env()) |
| 585 if scanner: |
| 586 scanner = scanner.select(node) |
| 587 return scanner |
| 588 |
| 589 def add_to_implicit(self, deps): |
| 590 if not hasattr(self, 'implicit') or self.implicit is None: |
| 591 self.implicit = [] |
| 592 self.implicit_set = set() |
| 593 self._children_reset() |
| 594 self._add_child(self.implicit, self.implicit_set, deps) |
| 595 |
| 596 def scan(self): |
| 597 """Scan this node's dependents for implicit dependencies.""" |
| 598 # Don't bother scanning non-derived files, because we don't |
| 599 # care what their dependencies are. |
| 600 # Don't scan again, if we already have scanned. |
| 601 if self.implicit is not None: |
| 602 return |
| 603 self.implicit = [] |
| 604 self.implicit_set = set() |
| 605 self._children_reset() |
| 606 if not self.has_builder(): |
| 607 return |
| 608 |
| 609 build_env = self.get_build_env() |
| 610 executor = self.get_executor() |
| 611 |
| 612 # Here's where we implement --implicit-cache. |
| 613 if implicit_cache and not implicit_deps_changed: |
| 614 implicit = self.get_stored_implicit() |
| 615 if implicit is not None: |
| 616 # We now add the implicit dependencies returned from the |
| 617 # stored .sconsign entry to have already been converted |
| 618 # to Nodes for us. (We used to run them through a |
| 619 # source_factory function here.) |
| 620 |
| 621 # Update all of the targets with them. This |
| 622 # essentially short-circuits an N*M scan of the |
| 623 # sources for each individual target, which is a hell |
| 624 # of a lot more efficient. |
| 625 for tgt in executor.get_all_targets(): |
| 626 tgt.add_to_implicit(implicit) |
| 627 |
| 628 if implicit_deps_unchanged or self.is_up_to_date(): |
| 629 return |
| 630 # one of this node's sources has changed, |
| 631 # so we must recalculate the implicit deps: |
| 632 self.implicit = [] |
| 633 self.implicit_set = set() |
| 634 |
| 635 # Have the executor scan the sources. |
| 636 executor.scan_sources(self.builder.source_scanner) |
| 637 |
| 638 # If there's a target scanner, have the executor scan the target |
| 639 # node itself and associated targets that might be built. |
| 640 scanner = self.get_target_scanner() |
| 641 if scanner: |
| 642 executor.scan_targets(scanner) |
| 643 |
| 644 def scanner_key(self): |
| 645 return None |
| 646 |
| 647 def select_scanner(self, scanner): |
| 648 """Selects a scanner for this Node. |
| 649 |
| 650 This is a separate method so it can be overridden by Node |
| 651 subclasses (specifically, Node.FS.Dir) that *must* use their |
| 652 own Scanner and don't select one the Scanner.Selector that's |
| 653 configured for the target. |
| 654 """ |
| 655 return scanner.select(self) |
| 656 |
| 657 def env_set(self, env, safe=0): |
| 658 if safe and self.env: |
| 659 return |
| 660 self.env = env |
| 661 |
| 662 # |
| 663 # SIGNATURE SUBSYSTEM |
| 664 # |
| 665 |
| 666 NodeInfo = NodeInfoBase |
| 667 BuildInfo = BuildInfoBase |
| 668 |
| 669 def new_ninfo(self): |
| 670 ninfo = self.NodeInfo(self) |
| 671 return ninfo |
| 672 |
| 673 def get_ninfo(self): |
| 674 try: |
| 675 return self.ninfo |
| 676 except AttributeError: |
| 677 self.ninfo = self.new_ninfo() |
| 678 return self.ninfo |
| 679 |
| 680 def new_binfo(self): |
| 681 binfo = self.BuildInfo(self) |
| 682 return binfo |
| 683 |
| 684 def get_binfo(self): |
| 685 """ |
| 686 Fetch a node's build information. |
| 687 |
| 688 node - the node whose sources will be collected |
| 689 cache - alternate node to use for the signature cache |
| 690 returns - the build signature |
| 691 |
| 692 This no longer handles the recursive descent of the |
| 693 node's children's signatures. We expect that they're |
| 694 already built and updated by someone else, if that's |
| 695 what's wanted. |
| 696 """ |
| 697 try: |
| 698 return self.binfo |
| 699 except AttributeError: |
| 700 pass |
| 701 |
| 702 binfo = self.new_binfo() |
| 703 self.binfo = binfo |
| 704 |
| 705 executor = self.get_executor() |
| 706 ignore_set = self.ignore_set |
| 707 |
| 708 if self.has_builder(): |
| 709 binfo.bact = str(executor) |
| 710 binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) |
| 711 |
| 712 if self._specific_sources: |
| 713 sources = [] |
| 714 for s in self.sources: |
| 715 if s not in ignore_set: |
| 716 sources.append(s) |
| 717 else: |
| 718 sources = executor.get_unignored_sources(self, self.ignore) |
| 719 seen = set() |
| 720 bsources = [] |
| 721 bsourcesigs = [] |
| 722 for s in sources: |
| 723 if not s in seen: |
| 724 seen.add(s) |
| 725 bsources.append(s) |
| 726 bsourcesigs.append(s.get_ninfo()) |
| 727 binfo.bsources = bsources |
| 728 binfo.bsourcesigs = bsourcesigs |
| 729 |
| 730 depends = self.depends |
| 731 dependsigs = [] |
| 732 for d in depends: |
| 733 if d not in ignore_set: |
| 734 dependsigs.append(d.get_ninfo()) |
| 735 binfo.bdepends = depends |
| 736 binfo.bdependsigs = dependsigs |
| 737 |
| 738 implicit = self.implicit or [] |
| 739 implicitsigs = [] |
| 740 for i in implicit: |
| 741 if i not in ignore_set: |
| 742 implicitsigs.append(i.get_ninfo()) |
| 743 binfo.bimplicit = implicit |
| 744 binfo.bimplicitsigs = implicitsigs |
| 745 |
| 746 return binfo |
| 747 |
| 748 def del_binfo(self): |
| 749 """Delete the build info from this node.""" |
| 750 try: |
| 751 delattr(self, 'binfo') |
| 752 except AttributeError: |
| 753 pass |
| 754 |
| 755 def get_csig(self): |
| 756 try: |
| 757 return self.ninfo.csig |
| 758 except AttributeError: |
| 759 ninfo = self.get_ninfo() |
| 760 ninfo.csig = SCons.Util.MD5signature(self.get_contents()) |
| 761 return self.ninfo.csig |
| 762 |
| 763 def get_cachedir_csig(self): |
| 764 return self.get_csig() |
| 765 |
| 766 def store_info(self): |
| 767 """Make the build signature permanent (that is, store it in the |
| 768 .sconsign file or equivalent).""" |
| 769 pass |
| 770 |
| 771 def do_not_store_info(self): |
| 772 pass |
| 773 |
| 774 def get_stored_info(self): |
| 775 return None |
| 776 |
| 777 def get_stored_implicit(self): |
| 778 """Fetch the stored implicit dependencies""" |
| 779 return None |
| 780 |
| 781 # |
| 782 # |
| 783 # |
| 784 |
| 785 def set_precious(self, precious = 1): |
| 786 """Set the Node's precious value.""" |
| 787 self.precious = precious |
| 788 |
| 789 def set_noclean(self, noclean = 1): |
| 790 """Set the Node's noclean value.""" |
| 791 # Make sure noclean is an integer so the --debug=stree |
| 792 # output in Util.py can use it as an index. |
| 793 self.noclean = noclean and 1 or 0 |
| 794 |
| 795 def set_nocache(self, nocache = 1): |
| 796 """Set the Node's nocache value.""" |
| 797 # Make sure nocache is an integer so the --debug=stree |
| 798 # output in Util.py can use it as an index. |
| 799 self.nocache = nocache and 1 or 0 |
| 800 |
| 801 def set_always_build(self, always_build = 1): |
| 802 """Set the Node's always_build value.""" |
| 803 self.always_build = always_build |
| 804 |
| 805 def exists(self): |
| 806 """Does this node exists?""" |
| 807 # All node exist by default: |
| 808 return 1 |
| 809 |
| 810 def rexists(self): |
| 811 """Does this node exist locally or in a repositiory?""" |
| 812 # There are no repositories by default: |
| 813 return self.exists() |
| 814 |
| 815 def missing(self): |
| 816 return not self.is_derived() and \ |
| 817 not self.linked and \ |
| 818 not self.rexists() |
| 819 |
| 820 def remove(self): |
| 821 """Remove this Node: no-op by default.""" |
| 822 return None |
| 823 |
| 824 def add_dependency(self, depend): |
| 825 """Adds dependencies.""" |
| 826 try: |
| 827 self._add_child(self.depends, self.depends_set, depend) |
| 828 except TypeError, e: |
| 829 e = e.args[0] |
| 830 if SCons.Util.is_List(e): |
| 831 s = list(map(str, e)) |
| 832 else: |
| 833 s = str(e) |
| 834 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))) |
| 835 |
| 836 def add_prerequisite(self, prerequisite): |
| 837 """Adds prerequisites""" |
| 838 self.prerequisites.extend(prerequisite) |
| 839 self._children_reset() |
| 840 |
| 841 def add_ignore(self, depend): |
| 842 """Adds dependencies to ignore.""" |
| 843 try: |
| 844 self._add_child(self.ignore, self.ignore_set, depend) |
| 845 except TypeError, e: |
| 846 e = e.args[0] |
| 847 if SCons.Util.is_List(e): |
| 848 s = list(map(str, e)) |
| 849 else: |
| 850 s = str(e) |
| 851 raise SCons.Errors.UserError("attempted to ignore a non-Node depende
ncy of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) |
| 852 |
| 853 def add_source(self, source): |
| 854 """Adds sources.""" |
| 855 if self._specific_sources: |
| 856 return |
| 857 try: |
| 858 self._add_child(self.sources, self.sources_set, source) |
| 859 except TypeError, e: |
| 860 e = e.args[0] |
| 861 if SCons.Util.is_List(e): |
| 862 s = list(map(str, e)) |
| 863 else: |
| 864 s = str(e) |
| 865 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))) |
| 866 |
| 867 def _add_child(self, collection, set, child): |
| 868 """Adds 'child' to 'collection', first checking 'set' to see if it's |
| 869 already present.""" |
| 870 #if type(child) is not type([]): |
| 871 # child = [child] |
| 872 #for c in child: |
| 873 # if not isinstance(c, Node): |
| 874 # raise TypeError, c |
| 875 added = None |
| 876 for c in child: |
| 877 if c not in set: |
| 878 set.add(c) |
| 879 collection.append(c) |
| 880 added = 1 |
| 881 if added: |
| 882 self._children_reset() |
| 883 |
| 884 def set_specific_source(self, source): |
| 885 self.add_source(source) |
| 886 self._specific_sources = True |
| 887 |
| 888 def add_wkid(self, wkid): |
| 889 """Add a node to the list of kids waiting to be evaluated""" |
| 890 if self.wkids is not None: |
| 891 self.wkids.append(wkid) |
| 892 |
| 893 def _children_reset(self): |
| 894 self.clear_memoized_values() |
| 895 # We need to let the Executor clear out any calculated |
| 896 # build info that it's cached so we can re-calculate it. |
| 897 self.executor_cleanup() |
| 898 |
| 899 memoizer_counters.append(SCons.Memoize.CountValue('_children_get')) |
| 900 |
| 901 def _children_get(self): |
| 902 try: |
| 903 return self._memo['children_get'] |
| 904 except KeyError: |
| 905 pass |
| 906 |
| 907 # The return list may contain duplicate Nodes, especially in |
| 908 # source trees where there are a lot of repeated #includes |
| 909 # of a tangle of .h files. Profiling shows, however, that |
| 910 # eliminating the duplicates with a brute-force approach that |
| 911 # preserves the order (that is, something like: |
| 912 # |
| 913 # u = [] |
| 914 # for n in list: |
| 915 # if n not in u: |
| 916 # u.append(n)" |
| 917 # |
| 918 # takes more cycles than just letting the underlying methods |
| 919 # hand back cached values if a Node's information is requested |
| 920 # multiple times. (Other methods of removing duplicates, like |
| 921 # using dictionary keys, lose the order, and the only ordered |
| 922 # dictionary patterns I found all ended up using "not in" |
| 923 # internally anyway...) |
| 924 if self.ignore_set: |
| 925 if self.implicit is None: |
| 926 iter = chain(self.sources,self.depends) |
| 927 else: |
| 928 iter = chain(self.sources, self.depends, self.implicit) |
| 929 |
| 930 children = [] |
| 931 for i in iter: |
| 932 if i not in self.ignore_set: |
| 933 children.append(i) |
| 934 else: |
| 935 if self.implicit is None: |
| 936 children = self.sources + self.depends |
| 937 else: |
| 938 children = self.sources + self.depends + self.implicit |
| 939 |
| 940 self._memo['children_get'] = children |
| 941 return children |
| 942 |
| 943 def all_children(self, scan=1): |
| 944 """Return a list of all the node's direct children.""" |
| 945 if scan: |
| 946 self.scan() |
| 947 |
| 948 # The return list may contain duplicate Nodes, especially in |
| 949 # source trees where there are a lot of repeated #includes |
| 950 # of a tangle of .h files. Profiling shows, however, that |
| 951 # eliminating the duplicates with a brute-force approach that |
| 952 # preserves the order (that is, something like: |
| 953 # |
| 954 # u = [] |
| 955 # for n in list: |
| 956 # if n not in u: |
| 957 # u.append(n)" |
| 958 # |
| 959 # takes more cycles than just letting the underlying methods |
| 960 # hand back cached values if a Node's information is requested |
| 961 # multiple times. (Other methods of removing duplicates, like |
| 962 # using dictionary keys, lose the order, and the only ordered |
| 963 # dictionary patterns I found all ended up using "not in" |
| 964 # internally anyway...) |
| 965 if self.implicit is None: |
| 966 return self.sources + self.depends |
| 967 else: |
| 968 return self.sources + self.depends + self.implicit |
| 969 |
| 970 def children(self, scan=1): |
| 971 """Return a list of the node's direct children, minus those |
| 972 that are ignored by this node.""" |
| 973 if scan: |
| 974 self.scan() |
| 975 return self._children_get() |
| 976 |
| 977 def set_state(self, state): |
| 978 self.state = state |
| 979 |
| 980 def get_state(self): |
| 981 return self.state |
| 982 |
| 983 def state_has_changed(self, target, prev_ni): |
| 984 return (self.state != SCons.Node.up_to_date) |
| 985 |
| 986 def get_env(self): |
| 987 env = self.env |
| 988 if not env: |
| 989 import SCons.Defaults |
| 990 env = SCons.Defaults.DefaultEnvironment() |
| 991 return env |
| 992 |
| 993 def changed_since_last_build(self, target, prev_ni): |
| 994 """ |
| 995 |
| 996 Must be overridden in a specific subclass to return True if this |
| 997 Node (a dependency) has changed since the last time it was used |
| 998 to build the specified target. prev_ni is this Node's state (for |
| 999 example, its file timestamp, length, maybe content signature) |
| 1000 as of the last time the target was built. |
| 1001 |
| 1002 Note that this method is called through the dependency, not the |
| 1003 target, because a dependency Node must be able to use its own |
| 1004 logic to decide if it changed. For example, File Nodes need to |
| 1005 obey if we're configured to use timestamps, but Python Value Nodes |
| 1006 never use timestamps and always use the content. If this method |
| 1007 were called through the target, then each Node's implementation |
| 1008 of this method would have to have more complicated logic to |
| 1009 handle all the different Node types on which it might depend. |
| 1010 """ |
| 1011 raise NotImplementedError |
| 1012 |
| 1013 def Decider(self, function): |
| 1014 SCons.Util.AddMethod(self, function, 'changed_since_last_build') |
| 1015 |
| 1016 def changed(self, node=None): |
| 1017 """ |
| 1018 Returns if the node is up-to-date with respect to the BuildInfo |
| 1019 stored last time it was built. The default behavior is to compare |
| 1020 it against our own previously stored BuildInfo, but the stored |
| 1021 BuildInfo from another Node (typically one in a Repository) |
| 1022 can be used instead. |
| 1023 |
| 1024 Note that we now *always* check every dependency. We used to |
| 1025 short-circuit the check by returning as soon as we detected |
| 1026 any difference, but we now rely on checking every dependency |
| 1027 to make sure that any necessary Node information (for example, |
| 1028 the content signature of an #included .h file) is updated. |
| 1029 """ |
| 1030 t = 0 |
| 1031 if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) |
| 1032 if node is None: |
| 1033 node = self |
| 1034 |
| 1035 result = False |
| 1036 |
| 1037 bi = node.get_stored_info().binfo |
| 1038 then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs |
| 1039 children = self.children() |
| 1040 |
| 1041 diff = len(children) - len(then) |
| 1042 if diff: |
| 1043 # The old and new dependency lists are different lengths. |
| 1044 # This always indicates that the Node must be rebuilt. |
| 1045 # We also extend the old dependency list with enough None |
| 1046 # entries to equal the new dependency list, for the benefit |
| 1047 # of the loop below that updates node information. |
| 1048 then.extend([None] * diff) |
| 1049 if t: Trace(': old %s new %s' % (len(then), len(children))) |
| 1050 result = True |
| 1051 |
| 1052 for child, prev_ni in zip(children, then): |
| 1053 if child.changed_since_last_build(self, prev_ni): |
| 1054 if t: Trace(': %s changed' % child) |
| 1055 result = True |
| 1056 |
| 1057 contents = self.get_executor().get_contents() |
| 1058 if self.has_builder(): |
| 1059 import SCons.Util |
| 1060 newsig = SCons.Util.MD5signature(contents) |
| 1061 if bi.bactsig != newsig: |
| 1062 if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) |
| 1063 result = True |
| 1064 |
| 1065 if not result: |
| 1066 if t: Trace(': up to date') |
| 1067 |
| 1068 if t: Trace('\n') |
| 1069 |
| 1070 return result |
| 1071 |
| 1072 def is_up_to_date(self): |
| 1073 """Default check for whether the Node is current: unknown Node |
| 1074 subtypes are always out of date, so they will always get built.""" |
| 1075 return None |
| 1076 |
| 1077 def children_are_up_to_date(self): |
| 1078 """Alternate check for whether the Node is current: If all of |
| 1079 our children were up-to-date, then this Node was up-to-date, too. |
| 1080 |
| 1081 The SCons.Node.Alias and SCons.Node.Python.Value subclasses |
| 1082 rebind their current() method to this method.""" |
| 1083 # Allow the children to calculate their signatures. |
| 1084 self.binfo = self.get_binfo() |
| 1085 if self.always_build: |
| 1086 return None |
| 1087 state = 0 |
| 1088 for kid in self.children(None): |
| 1089 s = kid.get_state() |
| 1090 if s and (not state or s > state): |
| 1091 state = s |
| 1092 return (state == 0 or state == SCons.Node.up_to_date) |
| 1093 |
| 1094 def is_literal(self): |
| 1095 """Always pass the string representation of a Node to |
| 1096 the command interpreter literally.""" |
| 1097 return 1 |
| 1098 |
| 1099 def render_include_tree(self): |
| 1100 """ |
| 1101 Return a text representation, suitable for displaying to the |
| 1102 user, of the include tree for the sources of this node. |
| 1103 """ |
| 1104 if self.is_derived() and self.env: |
| 1105 env = self.get_build_env() |
| 1106 for s in self.sources: |
| 1107 scanner = self.get_source_scanner(s) |
| 1108 if scanner: |
| 1109 path = self.get_build_scanner_path(scanner) |
| 1110 else: |
| 1111 path = None |
| 1112 def f(node, env=env, scanner=scanner, path=path): |
| 1113 return node.get_found_includes(env, scanner, path) |
| 1114 return SCons.Util.render_tree(s, f, 1) |
| 1115 else: |
| 1116 return None |
| 1117 |
| 1118 def get_abspath(self): |
| 1119 """ |
| 1120 Return an absolute path to the Node. This will return simply |
| 1121 str(Node) by default, but for Node types that have a concept of |
| 1122 relative path, this might return something different. |
| 1123 """ |
| 1124 return str(self) |
| 1125 |
| 1126 def for_signature(self): |
| 1127 """ |
| 1128 Return a string representation of the Node that will always |
| 1129 be the same for this particular Node, no matter what. This |
| 1130 is by contrast to the __str__() method, which might, for |
| 1131 instance, return a relative path for a file Node. The purpose |
| 1132 of this method is to generate a value to be used in signature |
| 1133 calculation for the command line used to build a target, and |
| 1134 we use this method instead of str() to avoid unnecessary |
| 1135 rebuilds. This method does not need to return something that |
| 1136 would actually work in a command line; it can return any kind of |
| 1137 nonsense, so long as it does not change. |
| 1138 """ |
| 1139 return str(self) |
| 1140 |
| 1141 def get_string(self, for_signature): |
| 1142 """This is a convenience function designed primarily to be |
| 1143 used in command generators (i.e., CommandGeneratorActions or |
| 1144 Environment variables that are callable), which are called |
| 1145 with a for_signature argument that is nonzero if the command |
| 1146 generator is being called to generate a signature for the |
| 1147 command line, which determines if we should rebuild or not. |
| 1148 |
| 1149 Such command generators should use this method in preference |
| 1150 to str(Node) when converting a Node to a string, passing |
| 1151 in the for_signature parameter, such that we will call |
| 1152 Node.for_signature() or str(Node) properly, depending on whether |
| 1153 we are calculating a signature or actually constructing a |
| 1154 command line.""" |
| 1155 if for_signature: |
| 1156 return self.for_signature() |
| 1157 return str(self) |
| 1158 |
| 1159 def get_subst_proxy(self): |
| 1160 """ |
| 1161 This method is expected to return an object that will function |
| 1162 exactly like this Node, except that it implements any additional |
| 1163 special features that we would like to be in effect for |
| 1164 Environment variable substitution. The principle use is that |
| 1165 some Nodes would like to implement a __getattr__() method, |
| 1166 but putting that in the Node type itself has a tendency to kill |
| 1167 performance. We instead put it in a proxy and return it from |
| 1168 this method. It is legal for this method to return self |
| 1169 if no new functionality is needed for Environment substitution. |
| 1170 """ |
| 1171 return self |
| 1172 |
| 1173 def explain(self): |
| 1174 if not self.exists(): |
| 1175 return "building `%s' because it doesn't exist\n" % self |
| 1176 |
| 1177 if self.always_build: |
| 1178 return "rebuilding `%s' because AlwaysBuild() is specified\n" % self |
| 1179 |
| 1180 old = self.get_stored_info() |
| 1181 if old is None: |
| 1182 return None |
| 1183 |
| 1184 old = old.binfo |
| 1185 old.prepare_dependencies() |
| 1186 |
| 1187 try: |
| 1188 old_bkids = old.bsources + old.bdepends + old.bimplicit |
| 1189 old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs |
| 1190 except AttributeError: |
| 1191 return "Cannot explain why `%s' is being rebuilt: No previous build
information found\n" % self |
| 1192 |
| 1193 new = self.get_binfo() |
| 1194 |
| 1195 new_bkids = new.bsources + new.bdepends + new.bimplicit |
| 1196 new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs |
| 1197 |
| 1198 osig = dict(zip(old_bkids, old_bkidsigs)) |
| 1199 nsig = dict(zip(new_bkids, new_bkidsigs)) |
| 1200 |
| 1201 # The sources and dependencies we'll want to report are all stored |
| 1202 # as relative paths to this target's directory, but we want to |
| 1203 # report them relative to the top-level SConstruct directory, |
| 1204 # so we only print them after running them through this lambda |
| 1205 # to turn them into the right relative Node and then return |
| 1206 # its string. |
| 1207 def stringify( s, E=self.dir.Entry ) : |
| 1208 if hasattr( s, 'dir' ) : |
| 1209 return str(E(s)) |
| 1210 return str(s) |
| 1211 |
| 1212 lines = [] |
| 1213 |
| 1214 removed = [x for x in old_bkids if not x in new_bkids] |
| 1215 if removed: |
| 1216 removed = list(map(stringify, removed)) |
| 1217 fmt = "`%s' is no longer a dependency\n" |
| 1218 lines.extend([fmt % s for s in removed]) |
| 1219 |
| 1220 for k in new_bkids: |
| 1221 if not k in old_bkids: |
| 1222 lines.append("`%s' is a new dependency\n" % stringify(k)) |
| 1223 elif k.changed_since_last_build(self, osig[k]): |
| 1224 lines.append("`%s' changed\n" % stringify(k)) |
| 1225 |
| 1226 if len(lines) == 0 and old_bkids != new_bkids: |
| 1227 lines.append("the dependency order changed:\n" + |
| 1228 "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids)
)) + |
| 1229 "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)
))) |
| 1230 |
| 1231 if len(lines) == 0: |
| 1232 def fmt_with_title(title, strlines): |
| 1233 lines = strlines.split('\n') |
| 1234 sep = '\n' + ' '*(15 + len(title)) |
| 1235 return ' '*15 + title + sep.join(lines) + '\n' |
| 1236 if old.bactsig != new.bactsig: |
| 1237 if old.bact == new.bact: |
| 1238 lines.append("the contents of the build action changed\n" + |
| 1239 fmt_with_title('action: ', new.bact)) |
| 1240 else: |
| 1241 lines.append("the build action changed:\n" + |
| 1242 fmt_with_title('old: ', old.bact) + |
| 1243 fmt_with_title('new: ', new.bact)) |
| 1244 |
| 1245 if len(lines) == 0: |
| 1246 return "rebuilding `%s' for unknown reasons\n" % self |
| 1247 |
| 1248 preamble = "rebuilding `%s' because" % self |
| 1249 if len(lines) == 1: |
| 1250 return "%s %s" % (preamble, lines[0]) |
| 1251 else: |
| 1252 lines = ["%s:\n" % preamble] + lines |
| 1253 return ( ' '*11).join(lines) |
| 1254 |
| 1255 class NodeList(collections.UserList): |
| 1256 def __str__(self): |
| 1257 return str(list(map(str, self.data))) |
| 1258 |
| 1259 def get_children(node, parent): return node.children() |
| 1260 def ignore_cycle(node, stack): pass |
| 1261 def do_nothing(node, parent): pass |
| 1262 |
| 1263 class Walker(object): |
| 1264 """An iterator for walking a Node tree. |
| 1265 |
| 1266 This is depth-first, children are visited before the parent. |
| 1267 The Walker object can be initialized with any node, and |
| 1268 returns the next node on the descent with each get_next() call. |
| 1269 'kids_func' is an optional function that will be called to |
| 1270 get the children of a node instead of calling 'children'. |
| 1271 'cycle_func' is an optional function that will be called |
| 1272 when a cycle is detected. |
| 1273 |
| 1274 This class does not get caught in node cycles caused, for example, |
| 1275 by C header file include loops. |
| 1276 """ |
| 1277 def __init__(self, node, kids_func=get_children, |
| 1278 cycle_func=ignore_cycle, |
| 1279 eval_func=do_nothing): |
| 1280 self.kids_func = kids_func |
| 1281 self.cycle_func = cycle_func |
| 1282 self.eval_func = eval_func |
| 1283 node.wkids = copy.copy(kids_func(node, None)) |
| 1284 self.stack = [node] |
| 1285 self.history = {} # used to efficiently detect and avoid cycles |
| 1286 self.history[node] = None |
| 1287 |
| 1288 def get_next(self): |
| 1289 """Return the next node for this walk of the tree. |
| 1290 |
| 1291 This function is intentionally iterative, not recursive, |
| 1292 to sidestep any issues of stack size limitations. |
| 1293 """ |
| 1294 |
| 1295 while self.stack: |
| 1296 if self.stack[-1].wkids: |
| 1297 node = self.stack[-1].wkids.pop(0) |
| 1298 if not self.stack[-1].wkids: |
| 1299 self.stack[-1].wkids = None |
| 1300 if node in self.history: |
| 1301 self.cycle_func(node, self.stack) |
| 1302 else: |
| 1303 node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) |
| 1304 self.stack.append(node) |
| 1305 self.history[node] = None |
| 1306 else: |
| 1307 node = self.stack.pop() |
| 1308 del self.history[node] |
| 1309 if node: |
| 1310 if self.stack: |
| 1311 parent = self.stack[-1] |
| 1312 else: |
| 1313 parent = None |
| 1314 self.eval_func(node, parent) |
| 1315 return node |
| 1316 return None |
| 1317 |
| 1318 def is_done(self): |
| 1319 return not self.stack |
| 1320 |
| 1321 |
| 1322 arg2nodes_lookups = [] |
| 1323 |
| 1324 # Local Variables: |
| 1325 # tab-width:4 |
| 1326 # indent-tabs-mode:nil |
| 1327 # End: |
| 1328 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |