Index: scons-2.0.1/engine/SCons/Node/__init__.py |
=================================================================== |
--- scons-2.0.1/engine/SCons/Node/__init__.py (revision 0) |
+++ scons-2.0.1/engine/SCons/Node/__init__.py (revision 0) |
@@ -0,0 +1,1328 @@ |
+"""SCons.Node |
+ |
+The Node package for the SCons software construction utility. |
+ |
+This is, in many ways, the heart of SCons. |
+ |
+A Node is where we encapsulate all of the dependency information about |
+any thing that SCons can build, or about any thing which SCons can use |
+to build some other thing. The canonical "thing," of course, is a file, |
+but a Node can also represent something remote (like a web page) or |
+something completely abstract (like an Alias). |
+ |
+Each specific type of "thing" is specifically represented by a subclass |
+of the Node base class: Node.FS.File for files, Node.Alias for aliases, |
+etc. Dependency information is kept here in the base class, and |
+information specific to files/aliases/etc. is in the subclass. The |
+goal, if we've done this correctly, is that any type of "thing" should |
+be able to depend on any other type of "thing." |
+ |
+""" |
+ |
+# |
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation |
+# |
+# Permission is hereby granted, free of charge, to any person obtaining |
+# a copy of this software and associated documentation files (the |
+# "Software"), to deal in the Software without restriction, including |
+# without limitation the rights to use, copy, modify, merge, publish, |
+# distribute, sublicense, and/or sell copies of the Software, and to |
+# permit persons to whom the Software is furnished to do so, subject to |
+# the following conditions: |
+# |
+# The above copyright notice and this permission notice shall be included |
+# in all copies or substantial portions of the Software. |
+# |
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+ |
+__revision__ = "src/engine/SCons/Node/__init__.py 5134 2010/08/16 23:02:40 bdeegan" |
+ |
+import collections |
+import copy |
+from itertools import chain |
+ |
+from SCons.Debug import logInstanceCreation |
+import SCons.Executor |
+import SCons.Memoize |
+import SCons.Util |
+ |
+from SCons.Debug import Trace |
+ |
+def classname(obj): |
+ return str(obj.__class__).split('.')[-1] |
+ |
+# Node states |
+# |
+# These are in "priority" order, so that the maximum value for any |
+# child/dependency of a node represents the state of that node if |
+# it has no builder of its own. The canonical example is a file |
+# system directory, which is only up to date if all of its children |
+# were up to date. |
+no_state = 0 |
+pending = 1 |
+executing = 2 |
+up_to_date = 3 |
+executed = 4 |
+failed = 5 |
+ |
+StateString = { |
+ 0 : "no_state", |
+ 1 : "pending", |
+ 2 : "executing", |
+ 3 : "up_to_date", |
+ 4 : "executed", |
+ 5 : "failed", |
+} |
+ |
+# controls whether implicit dependencies are cached: |
+implicit_cache = 0 |
+ |
+# controls whether implicit dep changes are ignored: |
+implicit_deps_unchanged = 0 |
+ |
+# controls whether the cached implicit deps are ignored: |
+implicit_deps_changed = 0 |
+ |
+# A variable that can be set to an interface-specific function be called |
+# to annotate a Node with information about its creation. |
+def do_nothing(node): pass |
+ |
+Annotate = do_nothing |
+ |
+# Classes for signature info for Nodes. |
+ |
+class NodeInfoBase(object): |
+ """ |
+ The generic base class for signature information for a Node. |
+ |
+ Node subclasses should subclass NodeInfoBase to provide their own |
+ logic for dealing with their own Node-specific signature information. |
+ """ |
+ current_version_id = 1 |
+ def __init__(self, node=None): |
+ # Create an object attribute from the class attribute so it ends up |
+ # in the pickled data in the .sconsign file. |
+ self._version_id = self.current_version_id |
+ def update(self, node): |
+ try: |
+ field_list = self.field_list |
+ except AttributeError: |
+ return |
+ for f in field_list: |
+ try: |
+ delattr(self, f) |
+ except AttributeError: |
+ pass |
+ try: |
+ func = getattr(node, 'get_' + f) |
+ except AttributeError: |
+ pass |
+ else: |
+ setattr(self, f, func()) |
+ def convert(self, node, val): |
+ pass |
+ def merge(self, other): |
+ self.__dict__.update(other.__dict__) |
+ def format(self, field_list=None, names=0): |
+ if field_list is None: |
+ try: |
+ field_list = self.field_list |
+ except AttributeError: |
+ field_list = sorted(self.__dict__.keys()) |
+ fields = [] |
+ for field in field_list: |
+ try: |
+ f = getattr(self, field) |
+ except AttributeError: |
+ f = None |
+ f = str(f) |
+ if names: |
+ f = field + ': ' + f |
+ fields.append(f) |
+ return fields |
+ |
+class BuildInfoBase(object): |
+ """ |
+ The generic base class for build information for a Node. |
+ |
+ This is what gets stored in a .sconsign file for each target file. |
+ It contains a NodeInfo instance for this node (signature information |
+ that's specific to the type of Node) and direct attributes for the |
+ generic build stuff we have to track: sources, explicit dependencies, |
+ implicit dependencies, and action information. |
+ """ |
+ current_version_id = 1 |
+ def __init__(self, node=None): |
+ # Create an object attribute from the class attribute so it ends up |
+ # in the pickled data in the .sconsign file. |
+ self._version_id = self.current_version_id |
+ self.bsourcesigs = [] |
+ self.bdependsigs = [] |
+ self.bimplicitsigs = [] |
+ self.bactsig = None |
+ def merge(self, other): |
+ self.__dict__.update(other.__dict__) |
+ |
+class Node(object): |
+ """The base Node class, for entities that we know how to |
+ build, or use to build other Nodes. |
+ """ |
+ |
+ if SCons.Memoize.use_memoizer: |
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass |
+ |
+ memoizer_counters = [] |
+ |
+ class Attrs(object): |
+ pass |
+ |
+ def __init__(self): |
+ if __debug__: logInstanceCreation(self, 'Node.Node') |
+ # Note that we no longer explicitly initialize a self.builder |
+ # attribute to None here. That's because the self.builder |
+ # attribute may be created on-the-fly later by a subclass (the |
+ # canonical example being a builder to fetch a file from a |
+ # source code system like CVS or Subversion). |
+ |
+ # Each list of children that we maintain is accompanied by a |
+ # dictionary used to look up quickly whether a node is already |
+ # present in the list. Empirical tests showed that it was |
+ # fastest to maintain them as side-by-side Node attributes in |
+ # this way, instead of wrapping up each list+dictionary pair in |
+ # a class. (Of course, we could always still do that in the |
+ # future if we had a good reason to...). |
+ self.sources = [] # source files used to build node |
+ self.sources_set = set() |
+ self._specific_sources = False |
+ self.depends = [] # explicit dependencies (from Depends) |
+ self.depends_set = set() |
+ self.ignore = [] # dependencies to ignore |
+ self.ignore_set = set() |
+ self.prerequisites = SCons.Util.UniqueList() |
+ self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) |
+ self.waiting_parents = set() |
+ self.waiting_s_e = set() |
+ self.ref_count = 0 |
+ self.wkids = None # Kids yet to walk, when it's an array |
+ |
+ self.env = None |
+ self.state = no_state |
+ self.precious = None |
+ self.noclean = 0 |
+ self.nocache = 0 |
+ self.always_build = None |
+ self.includes = None |
+ self.attributes = self.Attrs() # Generic place to stick information about the Node. |
+ self.side_effect = 0 # true iff this node is a side effect |
+ self.side_effects = [] # the side effects of building this target |
+ self.linked = 0 # is this node linked to the variant directory? |
+ |
+ self.clear_memoized_values() |
+ |
+ # Let the interface in which the build engine is embedded |
+ # annotate this Node with its own info (like a description of |
+ # what line in what file created the node, for example). |
+ Annotate(self) |
+ |
+ def disambiguate(self, must_exist=None): |
+ return self |
+ |
+ def get_suffix(self): |
+ return '' |
+ |
+ memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) |
+ |
+ def get_build_env(self): |
+ """Fetch the appropriate Environment to build this node. |
+ """ |
+ try: |
+ return self._memo['get_build_env'] |
+ except KeyError: |
+ pass |
+ result = self.get_executor().get_build_env() |
+ self._memo['get_build_env'] = result |
+ return result |
+ |
+ def get_build_scanner_path(self, scanner): |
+ """Fetch the appropriate scanner path for this node.""" |
+ return self.get_executor().get_build_scanner_path(scanner) |
+ |
+ def set_executor(self, executor): |
+ """Set the action executor for this node.""" |
+ self.executor = executor |
+ |
+ def get_executor(self, create=1): |
+ """Fetch the action executor for this node. Create one if |
+ there isn't already one, and requested to do so.""" |
+ try: |
+ executor = self.executor |
+ except AttributeError: |
+ if not create: |
+ raise |
+ try: |
+ act = self.builder.action |
+ except AttributeError: |
+ executor = SCons.Executor.Null(targets=[self]) |
+ else: |
+ executor = SCons.Executor.Executor(act, |
+ self.env or self.builder.env, |
+ [self.builder.overrides], |
+ [self], |
+ self.sources) |
+ self.executor = executor |
+ return executor |
+ |
+ def executor_cleanup(self): |
+ """Let the executor clean up any cached information.""" |
+ try: |
+ executor = self.get_executor(create=None) |
+ except AttributeError: |
+ pass |
+ else: |
+ executor.cleanup() |
+ |
+ def reset_executor(self): |
+ "Remove cached executor; forces recompute when needed." |
+ try: |
+ delattr(self, 'executor') |
+ except AttributeError: |
+ pass |
+ |
+ def push_to_cache(self): |
+ """Try to push a node into a cache |
+ """ |
+ pass |
+ |
+ def retrieve_from_cache(self): |
+ """Try to retrieve the node's content from a cache |
+ |
+ This method is called from multiple threads in a parallel build, |
+ so only do thread safe stuff here. Do thread unsafe stuff in |
+ built(). |
+ |
+ Returns true iff the node was successfully retrieved. |
+ """ |
+ return 0 |
+ |
+ # |
+ # Taskmaster interface subsystem |
+ # |
+ |
+ def make_ready(self): |
+ """Get a Node ready for evaluation. |
+ |
+ This is called before the Taskmaster decides if the Node is |
+ up-to-date or not. Overriding this method allows for a Node |
+ subclass to be disambiguated if necessary, or for an implicit |
+ source builder to be attached. |
+ """ |
+ pass |
+ |
+ def prepare(self): |
+ """Prepare for this Node to be built. |
+ |
+ This is called after the Taskmaster has decided that the Node |
+ is out-of-date and must be rebuilt, but before actually calling |
+ the method to build the Node. |
+ |
+ This default implementation checks that explicit or implicit |
+ dependencies either exist or are derived, and initializes the |
+ BuildInfo structure that will hold the information about how |
+ this node is, uh, built. |
+ |
+ (The existence of source files is checked separately by the |
+ Executor, which aggregates checks for all of the targets built |
+ by a specific action.) |
+ |
+ Overriding this method allows for for a Node subclass to remove |
+ the underlying file from the file system. Note that subclass |
+ methods should call this base class method to get the child |
+ check and the BuildInfo structure. |
+ """ |
+ for d in self.depends: |
+ if d.missing(): |
+ msg = "Explicit dependency `%s' not found, needed by target `%s'." |
+ raise SCons.Errors.StopError(msg % (d, self)) |
+ if self.implicit is not None: |
+ for i in self.implicit: |
+ if i.missing(): |
+ msg = "Implicit dependency `%s' not found, needed by target `%s'." |
+ raise SCons.Errors.StopError(msg % (i, self)) |
+ self.binfo = self.get_binfo() |
+ |
+ def build(self, **kw): |
+ """Actually build the node. |
+ |
+ This is called by the Taskmaster after it's decided that the |
+ Node is out-of-date and must be rebuilt, and after the prepare() |
+ method has gotten everything, uh, prepared. |
+ |
+ This method is called from multiple threads in a parallel build, |
+ so only do thread safe stuff here. Do thread unsafe stuff |
+ in built(). |
+ |
+ """ |
+ try: |
+ self.get_executor()(self, **kw) |
+ except SCons.Errors.BuildError, e: |
+ e.node = self |
+ raise |
+ |
+ def built(self): |
+ """Called just after this node is successfully built.""" |
+ |
+ # Clear the implicit dependency caches of any Nodes |
+ # waiting for this Node to be built. |
+ for parent in self.waiting_parents: |
+ parent.implicit = None |
+ |
+ self.clear() |
+ |
+ self.ninfo.update(self) |
+ |
+ def visited(self): |
+ """Called just after this node has been visited (with or |
+ without a build).""" |
+ try: |
+ binfo = self.binfo |
+ except AttributeError: |
+ # Apparently this node doesn't need build info, so |
+ # don't bother calculating or storing it. |
+ pass |
+ else: |
+ self.ninfo.update(self) |
+ self.store_info() |
+ |
+ # |
+ # |
+ # |
+ |
+ def add_to_waiting_s_e(self, node): |
+ self.waiting_s_e.add(node) |
+ |
+ def add_to_waiting_parents(self, node): |
+ """ |
+ Returns the number of nodes added to our waiting parents list: |
+ 1 if we add a unique waiting parent, 0 if not. (Note that the |
+ returned values are intended to be used to increment a reference |
+ count, so don't think you can "clean up" this function by using |
+ True and False instead...) |
+ """ |
+ wp = self.waiting_parents |
+ if node in wp: |
+ return 0 |
+ wp.add(node) |
+ return 1 |
+ |
+ def postprocess(self): |
+ """Clean up anything we don't need to hang onto after we've |
+ been built.""" |
+ self.executor_cleanup() |
+ self.waiting_parents = set() |
+ |
+ def clear(self): |
+ """Completely clear a Node of all its cached state (so that it |
+ can be re-evaluated by interfaces that do continuous integration |
+ builds). |
+ """ |
+ # The del_binfo() call here isn't necessary for normal execution, |
+ # but is for interactive mode, where we might rebuild the same |
+ # target and need to start from scratch. |
+ self.del_binfo() |
+ self.clear_memoized_values() |
+ self.ninfo = self.new_ninfo() |
+ self.executor_cleanup() |
+ try: |
+ delattr(self, '_calculated_sig') |
+ except AttributeError: |
+ pass |
+ self.includes = None |
+ |
+ def clear_memoized_values(self): |
+ self._memo = {} |
+ |
+ def builder_set(self, builder): |
+ self.builder = builder |
+ try: |
+ del self.executor |
+ except AttributeError: |
+ pass |
+ |
+ def has_builder(self): |
+ """Return whether this Node has a builder or not. |
+ |
+ In Boolean tests, this turns out to be a *lot* more efficient |
+ than simply examining the builder attribute directly ("if |
+ node.builder: ..."). When the builder attribute is examined |
+ directly, it ends up calling __getattr__ for both the __len__ |
+ and __nonzero__ attributes on instances of our Builder Proxy |
+ class(es), generating a bazillion extra calls and slowing |
+ things down immensely. |
+ """ |
+ try: |
+ b = self.builder |
+ except AttributeError: |
+ # There was no explicit builder for this Node, so initialize |
+ # the self.builder attribute to None now. |
+ b = self.builder = None |
+ return b is not None |
+ |
+ def set_explicit(self, is_explicit): |
+ self.is_explicit = is_explicit |
+ |
+ def has_explicit_builder(self): |
+ """Return whether this Node has an explicit builder |
+ |
+ This allows an internal Builder created by SCons to be marked |
+ non-explicit, so that it can be overridden by an explicit |
+ builder that the user supplies (the canonical example being |
+ directories).""" |
+ try: |
+ return self.is_explicit |
+ except AttributeError: |
+ self.is_explicit = None |
+ return self.is_explicit |
+ |
+ def get_builder(self, default_builder=None): |
+ """Return the set builder, or a specified default value""" |
+ try: |
+ return self.builder |
+ except AttributeError: |
+ return default_builder |
+ |
+ multiple_side_effect_has_builder = has_builder |
+ |
+ def is_derived(self): |
+ """ |
+ Returns true iff this node is derived (i.e. built). |
+ |
+ This should return true only for nodes whose path should be in |
+ the variant directory when duplicate=0 and should contribute their build |
+ signatures when they are used as source files to other derived files. For |
+ example: source with source builders are not derived in this sense, |
+ and hence should not return true. |
+ """ |
+ return self.has_builder() or self.side_effect |
+ |
+ def alter_targets(self): |
+ """Return a list of alternate targets for this Node. |
+ """ |
+ return [], None |
+ |
+ def get_found_includes(self, env, scanner, path): |
+ """Return the scanned include lines (implicit dependencies) |
+ found in this node. |
+ |
+ The default is no implicit dependencies. We expect this method |
+ to be overridden by any subclass that can be scanned for |
+ implicit dependencies. |
+ """ |
+ return [] |
+ |
+ def get_implicit_deps(self, env, scanner, path): |
+ """Return a list of implicit dependencies for this node. |
+ |
+ This method exists to handle recursive invocation of the scanner |
+ on the implicit dependencies returned by the scanner, if the |
+ scanner's recursive flag says that we should. |
+ """ |
+ if not scanner: |
+ return [] |
+ |
+ # Give the scanner a chance to select a more specific scanner |
+ # for this Node. |
+ #scanner = scanner.select(self) |
+ |
+ nodes = [self] |
+ seen = {} |
+ seen[self] = 1 |
+ deps = [] |
+ while nodes: |
+ n = nodes.pop(0) |
+ d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen] |
+ if d: |
+ deps.extend(d) |
+ for n in d: |
+ seen[n] = 1 |
+ nodes.extend(scanner.recurse_nodes(d)) |
+ |
+ return deps |
+ |
+ def get_env_scanner(self, env, kw={}): |
+ return env.get_scanner(self.scanner_key()) |
+ |
+ def get_target_scanner(self): |
+ return self.builder.target_scanner |
+ |
+ def get_source_scanner(self, node): |
+ """Fetch the source scanner for the specified node |
+ |
+ NOTE: "self" is the target being built, "node" is |
+ the source file for which we want to fetch the scanner. |
+ |
+ Implies self.has_builder() is true; again, expect to only be |
+ called from locations where this is already verified. |
+ |
+ This function may be called very often; it attempts to cache |
+ the scanner found to improve performance. |
+ """ |
+ scanner = None |
+ try: |
+ scanner = self.builder.source_scanner |
+ except AttributeError: |
+ pass |
+ if not scanner: |
+ # The builder didn't have an explicit scanner, so go look up |
+ # a scanner from env['SCANNERS'] based on the node's scanner |
+ # key (usually the file extension). |
+ scanner = self.get_env_scanner(self.get_build_env()) |
+ if scanner: |
+ scanner = scanner.select(node) |
+ return scanner |
+ |
+ def add_to_implicit(self, deps): |
+ if not hasattr(self, 'implicit') or self.implicit is None: |
+ self.implicit = [] |
+ self.implicit_set = set() |
+ self._children_reset() |
+ self._add_child(self.implicit, self.implicit_set, deps) |
+ |
+ def scan(self): |
+ """Scan this node's dependents for implicit dependencies.""" |
+ # Don't bother scanning non-derived files, because we don't |
+ # care what their dependencies are. |
+ # Don't scan again, if we already have scanned. |
+ if self.implicit is not None: |
+ return |
+ self.implicit = [] |
+ self.implicit_set = set() |
+ self._children_reset() |
+ if not self.has_builder(): |
+ return |
+ |
+ build_env = self.get_build_env() |
+ executor = self.get_executor() |
+ |
+ # Here's where we implement --implicit-cache. |
+ if implicit_cache and not implicit_deps_changed: |
+ implicit = self.get_stored_implicit() |
+ if implicit is not None: |
+ # We now add the implicit dependencies returned from the |
+ # stored .sconsign entry to have already been converted |
+ # to Nodes for us. (We used to run them through a |
+ # source_factory function here.) |
+ |
+ # Update all of the targets with them. This |
+ # essentially short-circuits an N*M scan of the |
+ # sources for each individual target, which is a hell |
+ # of a lot more efficient. |
+ for tgt in executor.get_all_targets(): |
+ tgt.add_to_implicit(implicit) |
+ |
+ if implicit_deps_unchanged or self.is_up_to_date(): |
+ return |
+ # one of this node's sources has changed, |
+ # so we must recalculate the implicit deps: |
+ self.implicit = [] |
+ self.implicit_set = set() |
+ |
+ # Have the executor scan the sources. |
+ executor.scan_sources(self.builder.source_scanner) |
+ |
+ # If there's a target scanner, have the executor scan the target |
+ # node itself and associated targets that might be built. |
+ scanner = self.get_target_scanner() |
+ if scanner: |
+ executor.scan_targets(scanner) |
+ |
+ def scanner_key(self): |
+ return None |
+ |
+ def select_scanner(self, scanner): |
+ """Selects a scanner for this Node. |
+ |
+ This is a separate method so it can be overridden by Node |
+ subclasses (specifically, Node.FS.Dir) that *must* use their |
+ own Scanner and don't select one the Scanner.Selector that's |
+ configured for the target. |
+ """ |
+ return scanner.select(self) |
+ |
+ def env_set(self, env, safe=0): |
+ if safe and self.env: |
+ return |
+ self.env = env |
+ |
+ # |
+ # SIGNATURE SUBSYSTEM |
+ # |
+ |
+ NodeInfo = NodeInfoBase |
+ BuildInfo = BuildInfoBase |
+ |
+ def new_ninfo(self): |
+ ninfo = self.NodeInfo(self) |
+ return ninfo |
+ |
+ def get_ninfo(self): |
+ try: |
+ return self.ninfo |
+ except AttributeError: |
+ self.ninfo = self.new_ninfo() |
+ return self.ninfo |
+ |
+ def new_binfo(self): |
+ binfo = self.BuildInfo(self) |
+ return binfo |
+ |
+ def get_binfo(self): |
+ """ |
+ Fetch a node's build information. |
+ |
+ node - the node whose sources will be collected |
+ cache - alternate node to use for the signature cache |
+ returns - the build signature |
+ |
+ This no longer handles the recursive descent of the |
+ node's children's signatures. We expect that they're |
+ already built and updated by someone else, if that's |
+ what's wanted. |
+ """ |
+ try: |
+ return self.binfo |
+ except AttributeError: |
+ pass |
+ |
+ binfo = self.new_binfo() |
+ self.binfo = binfo |
+ |
+ executor = self.get_executor() |
+ ignore_set = self.ignore_set |
+ |
+ if self.has_builder(): |
+ binfo.bact = str(executor) |
+ binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) |
+ |
+ if self._specific_sources: |
+ sources = [] |
+ for s in self.sources: |
+ if s not in ignore_set: |
+ sources.append(s) |
+ else: |
+ sources = executor.get_unignored_sources(self, self.ignore) |
+ seen = set() |
+ bsources = [] |
+ bsourcesigs = [] |
+ for s in sources: |
+ if not s in seen: |
+ seen.add(s) |
+ bsources.append(s) |
+ bsourcesigs.append(s.get_ninfo()) |
+ binfo.bsources = bsources |
+ binfo.bsourcesigs = bsourcesigs |
+ |
+ depends = self.depends |
+ dependsigs = [] |
+ for d in depends: |
+ if d not in ignore_set: |
+ dependsigs.append(d.get_ninfo()) |
+ binfo.bdepends = depends |
+ binfo.bdependsigs = dependsigs |
+ |
+ implicit = self.implicit or [] |
+ implicitsigs = [] |
+ for i in implicit: |
+ if i not in ignore_set: |
+ implicitsigs.append(i.get_ninfo()) |
+ binfo.bimplicit = implicit |
+ binfo.bimplicitsigs = implicitsigs |
+ |
+ return binfo |
+ |
+ def del_binfo(self): |
+ """Delete the build info from this node.""" |
+ try: |
+ delattr(self, 'binfo') |
+ except AttributeError: |
+ pass |
+ |
+ def get_csig(self): |
+ try: |
+ return self.ninfo.csig |
+ except AttributeError: |
+ ninfo = self.get_ninfo() |
+ ninfo.csig = SCons.Util.MD5signature(self.get_contents()) |
+ return self.ninfo.csig |
+ |
+ def get_cachedir_csig(self): |
+ return self.get_csig() |
+ |
+ def store_info(self): |
+ """Make the build signature permanent (that is, store it in the |
+ .sconsign file or equivalent).""" |
+ pass |
+ |
+ def do_not_store_info(self): |
+ pass |
+ |
+ def get_stored_info(self): |
+ return None |
+ |
+ def get_stored_implicit(self): |
+ """Fetch the stored implicit dependencies""" |
+ return None |
+ |
+ # |
+ # |
+ # |
+ |
+ def set_precious(self, precious = 1): |
+ """Set the Node's precious value.""" |
+ self.precious = precious |
+ |
+ def set_noclean(self, noclean = 1): |
+ """Set the Node's noclean value.""" |
+ # Make sure noclean is an integer so the --debug=stree |
+ # output in Util.py can use it as an index. |
+ self.noclean = noclean and 1 or 0 |
+ |
+ def set_nocache(self, nocache = 1): |
+ """Set the Node's nocache value.""" |
+ # Make sure nocache is an integer so the --debug=stree |
+ # output in Util.py can use it as an index. |
+ self.nocache = nocache and 1 or 0 |
+ |
+ def set_always_build(self, always_build = 1): |
+ """Set the Node's always_build value.""" |
+ self.always_build = always_build |
+ |
+ def exists(self): |
+ """Does this node exists?""" |
+ # All node exist by default: |
+ return 1 |
+ |
+ def rexists(self): |
+ """Does this node exist locally or in a repositiory?""" |
+ # There are no repositories by default: |
+ return self.exists() |
+ |
+ def missing(self): |
+ return not self.is_derived() and \ |
+ not self.linked and \ |
+ not self.rexists() |
+ |
+ def remove(self): |
+ """Remove this Node: no-op by default.""" |
+ return None |
+ |
+ def add_dependency(self, depend): |
+ """Adds dependencies.""" |
+ try: |
+ self._add_child(self.depends, self.depends_set, depend) |
+ except TypeError, e: |
+ e = e.args[0] |
+ if SCons.Util.is_List(e): |
+ s = list(map(str, e)) |
+ else: |
+ s = str(e) |
+ 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))) |
+ |
+ def add_prerequisite(self, prerequisite): |
+ """Adds prerequisites""" |
+ self.prerequisites.extend(prerequisite) |
+ self._children_reset() |
+ |
+ def add_ignore(self, depend): |
+ """Adds dependencies to ignore.""" |
+ try: |
+ self._add_child(self.ignore, self.ignore_set, depend) |
+ except TypeError, e: |
+ e = e.args[0] |
+ if SCons.Util.is_List(e): |
+ s = list(map(str, e)) |
+ else: |
+ s = str(e) |
+ 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))) |
+ |
+ def add_source(self, source): |
+ """Adds sources.""" |
+ if self._specific_sources: |
+ return |
+ try: |
+ self._add_child(self.sources, self.sources_set, source) |
+ except TypeError, e: |
+ e = e.args[0] |
+ if SCons.Util.is_List(e): |
+ s = list(map(str, e)) |
+ else: |
+ s = str(e) |
+ 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))) |
+ |
+ def _add_child(self, collection, set, child): |
+ """Adds 'child' to 'collection', first checking 'set' to see if it's |
+ already present.""" |
+ #if type(child) is not type([]): |
+ # child = [child] |
+ #for c in child: |
+ # if not isinstance(c, Node): |
+ # raise TypeError, c |
+ added = None |
+ for c in child: |
+ if c not in set: |
+ set.add(c) |
+ collection.append(c) |
+ added = 1 |
+ if added: |
+ self._children_reset() |
+ |
+ def set_specific_source(self, source): |
+ self.add_source(source) |
+ self._specific_sources = True |
+ |
+ def add_wkid(self, wkid): |
+ """Add a node to the list of kids waiting to be evaluated""" |
+ if self.wkids is not None: |
+ self.wkids.append(wkid) |
+ |
+ def _children_reset(self): |
+ self.clear_memoized_values() |
+ # We need to let the Executor clear out any calculated |
+ # build info that it's cached so we can re-calculate it. |
+ self.executor_cleanup() |
+ |
+ memoizer_counters.append(SCons.Memoize.CountValue('_children_get')) |
+ |
+ def _children_get(self): |
+ try: |
+ return self._memo['children_get'] |
+ except KeyError: |
+ pass |
+ |
+ # The return list may contain duplicate Nodes, especially in |
+ # source trees where there are a lot of repeated #includes |
+ # of a tangle of .h files. Profiling shows, however, that |
+ # eliminating the duplicates with a brute-force approach that |
+ # preserves the order (that is, something like: |
+ # |
+ # u = [] |
+ # for n in list: |
+ # if n not in u: |
+ # u.append(n)" |
+ # |
+ # takes more cycles than just letting the underlying methods |
+ # hand back cached values if a Node's information is requested |
+ # multiple times. (Other methods of removing duplicates, like |
+ # using dictionary keys, lose the order, and the only ordered |
+ # dictionary patterns I found all ended up using "not in" |
+ # internally anyway...) |
+ if self.ignore_set: |
+ if self.implicit is None: |
+ iter = chain(self.sources,self.depends) |
+ else: |
+ iter = chain(self.sources, self.depends, self.implicit) |
+ |
+ children = [] |
+ for i in iter: |
+ if i not in self.ignore_set: |
+ children.append(i) |
+ else: |
+ if self.implicit is None: |
+ children = self.sources + self.depends |
+ else: |
+ children = self.sources + self.depends + self.implicit |
+ |
+ self._memo['children_get'] = children |
+ return children |
+ |
+ def all_children(self, scan=1): |
+ """Return a list of all the node's direct children.""" |
+ if scan: |
+ self.scan() |
+ |
+ # The return list may contain duplicate Nodes, especially in |
+ # source trees where there are a lot of repeated #includes |
+ # of a tangle of .h files. Profiling shows, however, that |
+ # eliminating the duplicates with a brute-force approach that |
+ # preserves the order (that is, something like: |
+ # |
+ # u = [] |
+ # for n in list: |
+ # if n not in u: |
+ # u.append(n)" |
+ # |
+ # takes more cycles than just letting the underlying methods |
+ # hand back cached values if a Node's information is requested |
+ # multiple times. (Other methods of removing duplicates, like |
+ # using dictionary keys, lose the order, and the only ordered |
+ # dictionary patterns I found all ended up using "not in" |
+ # internally anyway...) |
+ if self.implicit is None: |
+ return self.sources + self.depends |
+ else: |
+ return self.sources + self.depends + self.implicit |
+ |
+ def children(self, scan=1): |
+ """Return a list of the node's direct children, minus those |
+ that are ignored by this node.""" |
+ if scan: |
+ self.scan() |
+ return self._children_get() |
+ |
+ def set_state(self, state): |
+ self.state = state |
+ |
+ def get_state(self): |
+ return self.state |
+ |
+ def state_has_changed(self, target, prev_ni): |
+ return (self.state != SCons.Node.up_to_date) |
+ |
+ def get_env(self): |
+ env = self.env |
+ if not env: |
+ import SCons.Defaults |
+ env = SCons.Defaults.DefaultEnvironment() |
+ return env |
+ |
+ def changed_since_last_build(self, target, prev_ni): |
+ """ |
+ |
+ Must be overridden in a specific subclass to return True if this |
+ Node (a dependency) has changed since the last time it was used |
+ to build the specified target. prev_ni is this Node's state (for |
+ example, its file timestamp, length, maybe content signature) |
+ as of the last time the target was built. |
+ |
+ Note that this method is called through the dependency, not the |
+ target, because a dependency Node must be able to use its own |
+ logic to decide if it changed. For example, File Nodes need to |
+ obey if we're configured to use timestamps, but Python Value Nodes |
+ never use timestamps and always use the content. If this method |
+ were called through the target, then each Node's implementation |
+ of this method would have to have more complicated logic to |
+ handle all the different Node types on which it might depend. |
+ """ |
+ raise NotImplementedError |
+ |
+ def Decider(self, function): |
+ SCons.Util.AddMethod(self, function, 'changed_since_last_build') |
+ |
+ def changed(self, node=None): |
+ """ |
+ Returns if the node is up-to-date with respect to the BuildInfo |
+ stored last time it was built. The default behavior is to compare |
+ it against our own previously stored BuildInfo, but the stored |
+ BuildInfo from another Node (typically one in a Repository) |
+ can be used instead. |
+ |
+ Note that we now *always* check every dependency. We used to |
+ short-circuit the check by returning as soon as we detected |
+ any difference, but we now rely on checking every dependency |
+ to make sure that any necessary Node information (for example, |
+ the content signature of an #included .h file) is updated. |
+ """ |
+ t = 0 |
+ if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) |
+ if node is None: |
+ node = self |
+ |
+ result = False |
+ |
+ bi = node.get_stored_info().binfo |
+ then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs |
+ children = self.children() |
+ |
+ diff = len(children) - len(then) |
+ if diff: |
+ # The old and new dependency lists are different lengths. |
+ # This always indicates that the Node must be rebuilt. |
+ # We also extend the old dependency list with enough None |
+ # entries to equal the new dependency list, for the benefit |
+ # of the loop below that updates node information. |
+ then.extend([None] * diff) |
+ if t: Trace(': old %s new %s' % (len(then), len(children))) |
+ result = True |
+ |
+ for child, prev_ni in zip(children, then): |
+ if child.changed_since_last_build(self, prev_ni): |
+ if t: Trace(': %s changed' % child) |
+ result = True |
+ |
+ contents = self.get_executor().get_contents() |
+ if self.has_builder(): |
+ import SCons.Util |
+ newsig = SCons.Util.MD5signature(contents) |
+ if bi.bactsig != newsig: |
+ if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) |
+ result = True |
+ |
+ if not result: |
+ if t: Trace(': up to date') |
+ |
+ if t: Trace('\n') |
+ |
+ return result |
+ |
+ def is_up_to_date(self): |
+ """Default check for whether the Node is current: unknown Node |
+ subtypes are always out of date, so they will always get built.""" |
+ return None |
+ |
+ def children_are_up_to_date(self): |
+ """Alternate check for whether the Node is current: If all of |
+ our children were up-to-date, then this Node was up-to-date, too. |
+ |
+ The SCons.Node.Alias and SCons.Node.Python.Value subclasses |
+ rebind their current() method to this method.""" |
+ # Allow the children to calculate their signatures. |
+ self.binfo = self.get_binfo() |
+ if self.always_build: |
+ return None |
+ state = 0 |
+ for kid in self.children(None): |
+ s = kid.get_state() |
+ if s and (not state or s > state): |
+ state = s |
+ return (state == 0 or state == SCons.Node.up_to_date) |
+ |
+ def is_literal(self): |
+ """Always pass the string representation of a Node to |
+ the command interpreter literally.""" |
+ return 1 |
+ |
+ def render_include_tree(self): |
+ """ |
+ Return a text representation, suitable for displaying to the |
+ user, of the include tree for the sources of this node. |
+ """ |
+ if self.is_derived() and self.env: |
+ env = self.get_build_env() |
+ for s in self.sources: |
+ scanner = self.get_source_scanner(s) |
+ if scanner: |
+ path = self.get_build_scanner_path(scanner) |
+ else: |
+ path = None |
+ def f(node, env=env, scanner=scanner, path=path): |
+ return node.get_found_includes(env, scanner, path) |
+ return SCons.Util.render_tree(s, f, 1) |
+ else: |
+ return None |
+ |
+ def get_abspath(self): |
+ """ |
+ Return an absolute path to the Node. This will return simply |
+ str(Node) by default, but for Node types that have a concept of |
+ relative path, this might return something different. |
+ """ |
+ return str(self) |
+ |
+ def for_signature(self): |
+ """ |
+ Return a string representation of the Node that will always |
+ be the same for this particular Node, no matter what. This |
+ is by contrast to the __str__() method, which might, for |
+ instance, return a relative path for a file Node. The purpose |
+ of this method is to generate a value to be used in signature |
+ calculation for the command line used to build a target, and |
+ we use this method instead of str() to avoid unnecessary |
+ rebuilds. This method does not need to return something that |
+ would actually work in a command line; it can return any kind of |
+ nonsense, so long as it does not change. |
+ """ |
+ return str(self) |
+ |
+ def get_string(self, for_signature): |
+ """This is a convenience function designed primarily to be |
+ used in command generators (i.e., CommandGeneratorActions or |
+ Environment variables that are callable), which are called |
+ with a for_signature argument that is nonzero if the command |
+ generator is being called to generate a signature for the |
+ command line, which determines if we should rebuild or not. |
+ |
+ Such command generators should use this method in preference |
+ to str(Node) when converting a Node to a string, passing |
+ in the for_signature parameter, such that we will call |
+ Node.for_signature() or str(Node) properly, depending on whether |
+ we are calculating a signature or actually constructing a |
+ command line.""" |
+ if for_signature: |
+ return self.for_signature() |
+ return str(self) |
+ |
+ def get_subst_proxy(self): |
+ """ |
+ This method is expected to return an object that will function |
+ exactly like this Node, except that it implements any additional |
+ special features that we would like to be in effect for |
+ Environment variable substitution. The principle use is that |
+ some Nodes would like to implement a __getattr__() method, |
+ but putting that in the Node type itself has a tendency to kill |
+ performance. We instead put it in a proxy and return it from |
+ this method. It is legal for this method to return self |
+ if no new functionality is needed for Environment substitution. |
+ """ |
+ return self |
+ |
+ def explain(self): |
+ if not self.exists(): |
+ return "building `%s' because it doesn't exist\n" % self |
+ |
+ if self.always_build: |
+ return "rebuilding `%s' because AlwaysBuild() is specified\n" % self |
+ |
+ old = self.get_stored_info() |
+ if old is None: |
+ return None |
+ |
+ old = old.binfo |
+ old.prepare_dependencies() |
+ |
+ try: |
+ old_bkids = old.bsources + old.bdepends + old.bimplicit |
+ old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs |
+ except AttributeError: |
+ return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self |
+ |
+ new = self.get_binfo() |
+ |
+ new_bkids = new.bsources + new.bdepends + new.bimplicit |
+ new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs |
+ |
+ osig = dict(zip(old_bkids, old_bkidsigs)) |
+ nsig = dict(zip(new_bkids, new_bkidsigs)) |
+ |
+ # The sources and dependencies we'll want to report are all stored |
+ # as relative paths to this target's directory, but we want to |
+ # report them relative to the top-level SConstruct directory, |
+ # so we only print them after running them through this lambda |
+ # to turn them into the right relative Node and then return |
+ # its string. |
+ def stringify( s, E=self.dir.Entry ) : |
+ if hasattr( s, 'dir' ) : |
+ return str(E(s)) |
+ return str(s) |
+ |
+ lines = [] |
+ |
+ removed = [x for x in old_bkids if not x in new_bkids] |
+ if removed: |
+ removed = list(map(stringify, removed)) |
+ fmt = "`%s' is no longer a dependency\n" |
+ lines.extend([fmt % s for s in removed]) |
+ |
+ for k in new_bkids: |
+ if not k in old_bkids: |
+ lines.append("`%s' is a new dependency\n" % stringify(k)) |
+ elif k.changed_since_last_build(self, osig[k]): |
+ lines.append("`%s' changed\n" % stringify(k)) |
+ |
+ if len(lines) == 0 and old_bkids != new_bkids: |
+ lines.append("the dependency order changed:\n" + |
+ "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + |
+ "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) |
+ |
+ if len(lines) == 0: |
+ def fmt_with_title(title, strlines): |
+ lines = strlines.split('\n') |
+ sep = '\n' + ' '*(15 + len(title)) |
+ return ' '*15 + title + sep.join(lines) + '\n' |
+ if old.bactsig != new.bactsig: |
+ if old.bact == new.bact: |
+ lines.append("the contents of the build action changed\n" + |
+ fmt_with_title('action: ', new.bact)) |
+ else: |
+ lines.append("the build action changed:\n" + |
+ fmt_with_title('old: ', old.bact) + |
+ fmt_with_title('new: ', new.bact)) |
+ |
+ if len(lines) == 0: |
+ return "rebuilding `%s' for unknown reasons\n" % self |
+ |
+ preamble = "rebuilding `%s' because" % self |
+ if len(lines) == 1: |
+ return "%s %s" % (preamble, lines[0]) |
+ else: |
+ lines = ["%s:\n" % preamble] + lines |
+ return ( ' '*11).join(lines) |
+ |
+class NodeList(collections.UserList): |
+ def __str__(self): |
+ return str(list(map(str, self.data))) |
+ |
+def get_children(node, parent): return node.children() |
+def ignore_cycle(node, stack): pass |
+def do_nothing(node, parent): pass |
+ |
+class Walker(object): |
+ """An iterator for walking a Node tree. |
+ |
+ This is depth-first, children are visited before the parent. |
+ The Walker object can be initialized with any node, and |
+ returns the next node on the descent with each get_next() call. |
+ 'kids_func' is an optional function that will be called to |
+ get the children of a node instead of calling 'children'. |
+ 'cycle_func' is an optional function that will be called |
+ when a cycle is detected. |
+ |
+ This class does not get caught in node cycles caused, for example, |
+ by C header file include loops. |
+ """ |
+ def __init__(self, node, kids_func=get_children, |
+ cycle_func=ignore_cycle, |
+ eval_func=do_nothing): |
+ self.kids_func = kids_func |
+ self.cycle_func = cycle_func |
+ self.eval_func = eval_func |
+ node.wkids = copy.copy(kids_func(node, None)) |
+ self.stack = [node] |
+ self.history = {} # used to efficiently detect and avoid cycles |
+ self.history[node] = None |
+ |
+ def get_next(self): |
+ """Return the next node for this walk of the tree. |
+ |
+ This function is intentionally iterative, not recursive, |
+ to sidestep any issues of stack size limitations. |
+ """ |
+ |
+ while self.stack: |
+ if self.stack[-1].wkids: |
+ node = self.stack[-1].wkids.pop(0) |
+ if not self.stack[-1].wkids: |
+ self.stack[-1].wkids = None |
+ if node in self.history: |
+ self.cycle_func(node, self.stack) |
+ else: |
+ node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) |
+ self.stack.append(node) |
+ self.history[node] = None |
+ else: |
+ node = self.stack.pop() |
+ del self.history[node] |
+ if node: |
+ if self.stack: |
+ parent = self.stack[-1] |
+ else: |
+ parent = None |
+ self.eval_func(node, parent) |
+ return node |
+ return None |
+ |
+ def is_done(self): |
+ return not self.stack |
+ |
+ |
+arg2nodes_lookups = [] |
+ |
+# Local Variables: |
+# tab-width:4 |
+# indent-tabs-mode:nil |
+# End: |
+# vim: set expandtab tabstop=4 shiftwidth=4: |
Property changes on: scons-2.0.1/engine/SCons/Node/__init__.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |