__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
+import SCons.compat
import copy
import string
from SCons.Debug import logInstanceCreation
import SCons.Executor
+import SCons.Memoize
import SCons.SConsign
import SCons.Util
up_to_date = 3
executed = 4
failed = 5
-stack = 6 # nodes that are in the current Taskmaster execution stack
StateString = {
0 : "0",
3 : "up_to_date",
4 : "executed",
5 : "failed",
- 6 : "stack",
}
# controls whether implicit dependencies are cached:
# 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.
"""
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
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 = []
if SCons.Memoize.use_memoizer:
__metaclass__ = SCons.Memoize.Memoized_Metaclass
+ memoizer_counters = []
+
class Attrs:
pass
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):
Returns true iff the node was successfully retrieved.
"""
return 0
-
+
def build(self, **kw):
"""Actually build the 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:
# 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
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:
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
pass
def builder_set(self, builder):
- "__cache_reset__"
self.builder = builder
def has_builder(self):
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
# 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 = {}
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
# 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
# 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:
# 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
# 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:
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:
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
"""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
"""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 \
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()
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):
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
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