Merged revisions 1907-1940,1942-1967 via svnmerge from
[scons.git] / src / engine / SCons / Node / __init__.py
index e73e5f389b166912d99b8d5bec340be0a6dee994..f550b5b7cebbc29c8c8c957722aabc5e40bb02e8 100644 (file)
@@ -44,7 +44,7 @@ be able to depend on any other type of "thing."
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-
+import SCons.compat
 
 import copy
 import string
@@ -52,6 +52,7 @@ import UserList
 
 from SCons.Debug import logInstanceCreation
 import SCons.Executor
+import SCons.Memoize
 import SCons.SConsign
 import SCons.Util
 
@@ -68,7 +69,6 @@ executing = 2
 up_to_date = 3
 executed = 4
 failed = 5
-stack = 6 # nodes that are in the current Taskmaster execution stack
 
 StateString = {
     0 : "0",
@@ -77,7 +77,6 @@ StateString = {
     3 : "up_to_date",
     4 : "executed",
     5 : "failed",
-    6 : "stack",
 }
 
 # controls whether implicit dependencies are cached:
@@ -97,15 +96,14 @@ Annotate = do_nothing
 
 # Classes for signature info for Nodes.
 
-class NodeInfo:
+class NodeInfoBase:
     """
-    A generic class for signature information for a Node.
+    The generic base class for signature information for a Node.
 
-    We actually expect that modules containing Node subclasses will also
-    subclass NodeInfo, to provide their own logic for dealing with their
-    own Node-specific signature information.
+    Node subclasses should subclass NodeInfoBase to provide their own
+    logic for dealing with their own Node-specific signature information.
     """
-    def __init__(self):
+    def __init__(self, node):
         """A null initializer so that subclasses have a superclass
         initialization method to call for future use.
         """
@@ -117,10 +115,26 @@ class NodeInfo:
     def merge(self, other):
         for key, val in other.__dict__.items():
             self.__dict__[key] = val
+    def prepare_dependencies(self):
+        pass
+    def format(self):
+        try:
+            field_list = self.field_list
+        except AttributeError:
+            field_list = self.__dict__.keys()
+            field_list.sort()
+        fields = []
+        for field in field_list:
+            try:
+                f = getattr(self, field)
+            except AttributeError:
+                f = None
+            fields.append(str(f))
+        return string.join(fields, " ")
 
-class BuildInfo:
+class BuildInfoBase:
     """
-    The generic build information for a Node.
+    The generic base clasee 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
@@ -129,7 +143,7 @@ class BuildInfo:
     implicit dependencies, and action information.
     """
     def __init__(self, node):
-        self.ninfo = node.new_ninfo()
+        self.ninfo = node.NodeInfo(node)
         self.bsourcesigs = []
         self.bdependsigs = []
         self.bimplicitsigs = []
@@ -153,6 +167,8 @@ class Node:
     if SCons.Memoize.use_memoizer:
         __metaclass__ = SCons.Memoize.Memoized_Metaclass
 
+    memoizer_counters = []
+
     class Attrs:
         pass
 
@@ -178,33 +194,40 @@ class Node:
         self.ignore = []        # dependencies to ignore
         self.ignore_dict = {}
         self.implicit = None    # implicit (scanned) dependencies (None means not scanned yet)
-        self.waiting_parents = []
+        self.waiting_parents = {}
+        self.waiting_s_e = {}
+        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.found_includes = {}
         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.pre_actions = []
-        self.post_actions = []
-        self.linked = 0 # is this node linked to the build directory? 
+        self.linked = 0 # is this node linked to the build 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 ''
 
     def get_build_env(self):
         """Fetch the appropriate Environment to build this node.
-        __cacheable__"""
+        """
         return self.get_executor().get_build_env()
 
     def get_build_scanner_path(self, scanner):
@@ -262,7 +285,7 @@ class Node:
         Returns true iff the node was successfully retrieved.
         """
         return 0
-        
+
     def build(self, **kw):
         """Actually build the node.
 
@@ -282,10 +305,10 @@ class Node:
 
         # Clear the implicit dependency caches of any Nodes
         # waiting for this Node to be built.
-        for parent in self.waiting_parents:
+        for parent in self.waiting_parents.keys():
             parent.implicit = None
             parent.del_binfo()
-        
+
         try:
             new = self.binfo
         except AttributeError:
@@ -297,7 +320,7 @@ class Node:
         # Reset this Node's cached state since it was just built and
         # various state has changed.
         self.clear()
-        
+
         if new:
             # It had build info, so it should be stored in the signature
             # cache.  However, if the build info included a content
@@ -309,25 +332,42 @@ class Node:
                 self.binfo = new
             self.store_info(self.binfo)
 
+    def add_to_waiting_s_e(self, node):
+        self.waiting_s_e[node] = 1
+
     def add_to_waiting_parents(self, node):
-        self.waiting_parents.append(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 wp.has_key(node):
+            result = 0
+        else:
+            result = 1
+        wp[node] = 1
+        return result
 
     def call_for_all_waiting_parents(self, func):
         func(self)
-        for parent in self.waiting_parents:
+        for parent in self.waiting_parents.keys():
             parent.call_for_all_waiting_parents(func)
 
     def postprocess(self):
         """Clean up anything we don't need to hang onto after we've
         been built."""
         self.executor_cleanup()
+        self.waiting_parents = {}
 
     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).
-        __reset_cache__
         """
+        self.clear_memoized_values()
         self.executor_cleanup()
         self.del_binfo()
         try:
@@ -338,7 +378,8 @@ class Node:
         self.found_includes = {}
         self.implicit = None
 
-        self.waiting_parents = []
+    def clear_memoized_values(self):
+        self._memo = {}
 
     def visited(self):
         """Called just after this node has been visited
@@ -346,7 +387,6 @@ class Node:
         pass
 
     def builder_set(self, builder):
-        "__cache_reset__"
         self.builder = builder
 
     def has_builder(self):
@@ -403,7 +443,6 @@ class Node:
         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.
-        __cacheable__
         """
         return self.has_builder() or self.side_effect
 
@@ -442,7 +481,7 @@ class Node:
 
         # Give the scanner a chance to select a more specific scanner
         # for this Node.
-        scanner = scanner.select(self)
+        #scanner = scanner.select(self)
 
         nodes = [self]
         seen = {}
@@ -460,9 +499,12 @@ class Node:
 
         return deps
 
-    def get_scanner(self, env, kw={}):
+    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
 
@@ -484,7 +526,7 @@ class Node:
             # 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_scanner(self.get_build_env())
+            scanner = self.get_env_scanner(self.get_build_env())
         if scanner:
             scanner = scanner.select(node)
         return scanner
@@ -514,7 +556,7 @@ class Node:
         # Here's where we implement --implicit-cache.
         if implicit_cache and not implicit_deps_changed:
             implicit = self.get_stored_implicit()
-            if implicit:
+            if implicit is not None:
                 factory = build_env.get_factory(self.builder.source_factory)
                 nodes = []
                 for i in implicit:
@@ -549,13 +591,23 @@ class Node:
 
         # If there's a target scanner, have the executor scan the target
         # node itself and associated targets that might be built.
-        scanner = self.builder.target_scanner
+        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
@@ -565,36 +617,49 @@ class Node:
     # SIGNATURE SUBSYSTEM
     #
 
+    NodeInfo = NodeInfoBase
+    BuildInfo = BuildInfoBase
+
     def calculator(self):
         import SCons.Defaults
         
         env = self.env or SCons.Defaults.DefaultEnvironment()
         return env.get_calculator()
 
+    memoizer_counters.append(SCons.Memoize.CountValue('calc_signature'))
+
     def calc_signature(self, calc=None):
         """
         Select and calculate the appropriate build signature for a node.
-        __cacheable__
 
         self - the node
         calc - the signature calculation module
         returns - the signature
         """
+        try:
+            return self._memo['calc_signature']
+        except KeyError:
+            pass
         if self.is_derived():
             import SCons.Defaults
 
             env = self.env or SCons.Defaults.DefaultEnvironment()
             if env.use_build_signature():
-                return self.get_bsig(calc)
+                result = self.get_bsig(calc)
+            else:
+                result = self.get_csig(calc)
         elif not self.rexists():
-            return None
-        return self.get_csig(calc)
+            result = None
+        else:
+            result = self.get_csig(calc)
+        self._memo['calc_signature'] = result
+        return result
 
     def new_ninfo(self):
-        return NodeInfo()
+        return self.NodeInfo(self)
 
     def new_binfo(self):
-        return BuildInfo(self)
+        return self.BuildInfo(self)
 
     def get_binfo(self):
         try:
@@ -623,7 +688,6 @@ class Node:
         node's children's signatures.  We expect that they're
         already built and updated by someone else, if that's
         what's wanted.
-        __cacheable__
         """
 
         if calc is None:
@@ -638,7 +702,7 @@ class Node:
         def calc_signature(node, calc=calc):
             return node.calc_signature(calc)
 
-        sources = executor.process_sources(None, self.ignore)
+        sources = executor.get_unignored_sources(self.ignore)
         sourcesigs = executor.process_sources(calc_signature, self.ignore)
 
         depends = self.depends
@@ -708,6 +772,18 @@ class Node:
         """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
@@ -716,14 +792,13 @@ class Node:
         """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):
-        """__cacheable__"""
         return not self.is_derived() and \
                not self.is_pseudo_derived() and \
                not self.linked and \
@@ -806,7 +881,7 @@ class Node:
             self.wkids.append(wkid)
 
     def _children_reset(self):
-        "__cache_reset__"
+        self.clear_memoized_values()
         # We need to let the Executor clear out any calculated
         # bsig info that it's cached so we can re-calculate it.
         self.executor_cleanup()
@@ -837,11 +912,17 @@ class Node:
         else:
             return self.sources + self.depends + self.implicit
 
+    memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
+
     def _children_get(self):
-        "__cacheable__"
+        try:
+            return self._memo['children_get']
+        except KeyError:
+            pass
         children = self._all_children_get()
         if self.ignore:
             children = filter(self.do_not_ignore, children)
+        self._memo['children_get'] = children
         return children
 
     def all_children(self, scan=1):
@@ -965,31 +1046,27 @@ class Node:
         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
-
-        def dictify(result, kids, sigs):
-            for k, s in zip(kids, sigs):
-                result[k] = s
+        old.prepare_dependencies()
 
         try:
-            osig = {}
-            dictify(osig, old.bsources, old.bsourcesigs)
-            dictify(osig, old.bdepends, old.bdependsigs)
-            dictify(osig, old.bimplicit, old.bimplicitsigs)
+            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()
 
-        nsig = {}
-        dictify(nsig, new.bsources, new.bsourcesigs)
-        dictify(nsig, new.bdepends, new.bdependsigs)
-        dictify(nsig, new.bimplicit, new.bimplicitsigs)
+        new_bkids    = new.bsources    + new.bdepends    + new.bimplicit
+        new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs
 
-        old_bkids = old.bsources + old.bdepends + old.bimplicit
-        new_bkids = new.bsources + new.bdepends + new.bimplicit
+        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
@@ -1056,14 +1133,6 @@ else:
 del l
 del ul
 
-if SCons.Memoize.use_old_memoization():
-    _Base = Node
-    class Node(SCons.Memoize.Memoizer, _Base):
-        def __init__(self, *args, **kw):
-            apply(_Base.__init__, (self,)+args, kw)
-            SCons.Memoize.Memoizer.__init__(self)
-
-
 def get_children(node, parent): return node.children()
 def ignore_cycle(node, stack): pass
 def do_nothing(node, parent): pass