summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bin/files1
-rw-r--r--src/CHANGES.txt7
-rw-r--r--src/engine/MANIFEST.in1
-rw-r--r--src/engine/SCons/Builder.py52
-rw-r--r--src/engine/SCons/BuilderTests.py73
-rw-r--r--src/engine/SCons/Executor.py141
-rw-r--r--src/engine/SCons/ExecutorTests.py173
-rw-r--r--src/engine/SCons/Node/FS.py5
-rw-r--r--src/engine/SCons/Node/FSTests.py52
-rw-r--r--src/engine/SCons/Node/NodeTests.py69
-rw-r--r--src/engine/SCons/Node/__init__.py76
-rw-r--r--src/engine/SCons/SConf.py5
-rw-r--r--src/engine/SCons/Sig/SigTests.py2
-rw-r--r--src/engine/SCons/Sig/__init__.py2
14 files changed, 477 insertions, 182 deletions
diff --git a/bin/files b/bin/files
index dec6750..86f297c 100644
--- a/bin/files
+++ b/bin/files
@@ -3,6 +3,7 @@
./SCons/Defaults.py
./SCons/Environment.py
./SCons/Errors.py
+./SCons/Executor.py
./SCons/Job.py
./SCons/Node/Alias.py
./SCons/Node/FS.py
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index e1d842c..509ed4c 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -80,8 +80,11 @@ RELEASE 0.14 - XXX
- Interpolate the null string if an out-of-range subscript is used
for a construction variable.
- - Fix the internal Link function so that it creates properly links or
- copies of files in subsidiary BuildDir directories.
+ - Fix the internal Link function so that it properly links or copies
+ files in subsidiary BuildDir directories.
+
+ - Refactor the internal representation of a single execution instance
+ of an action to eliminate redundant signature calculations.
From Damyan Pepper:
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 24277b4..65d6886 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -4,6 +4,7 @@ SCons/Builder.py
SCons/Defaults.py
SCons/Environment.py
SCons/Errors.py
+SCons/Executor.py
SCons/Job.py
SCons/exitfuncs.py
SCons/Node/__init__.py
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 8b0ac85..414c5de 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -49,6 +49,7 @@ import os.path
from SCons.Errors import InternalError, UserError
import SCons.Action
+import SCons.Executor
import SCons.Node
import SCons.Node.FS
import SCons.Util
@@ -135,12 +136,8 @@ def _init_nodes(builder, env, overrides, tlist, slist):
the proper Builder information.
"""
- for s in slist:
- src_key = s.scanner_key() # the file suffix
- scanner = env.get_scanner(src_key)
- if scanner:
- s.source_scanner = scanner
-
+ # First, figure out if there are any errors in the way the targets
+ # were specified.
for t in tlist:
if t.side_effect:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
@@ -161,14 +158,43 @@ def _init_nodes(builder, env, overrides, tlist, slist):
elif t.sources != slist:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
+ # The targets are fine, so find or make the appropriate Executor to
+ # build this particular list of targets from this particular list of
+ # sources.
+ executor = None
+ if builder.multi:
+ try:
+ executor = tlist[0].get_executor(create = 0)
+ except AttributeError:
+ pass
+ else:
+ executor.add_sources(slist)
+ if executor is None:
+ executor = SCons.Executor.Executor(builder,
+ tlist[0].generate_build_env(env),
+ overrides,
+ tlist,
+ slist)
+
+ # Now set up the relevant information in the target Nodes themselves.
+ for t in tlist:
t.overrides = overrides
t.cwd = SCons.Node.FS.default_fs.getcwd()
t.builder_set(builder)
t.env_set(env)
t.add_source(slist)
+ t.set_executor(executor)
if builder.scanner:
t.target_scanner = builder.scanner
+ # Last, add scanners from the Environment to the source Nodes.
+ for s in slist:
+ src_key = s.scanner_key() # the file suffix
+ scanner = env.get_scanner(src_key)
+ if scanner:
+ s.source_scanner = scanner
+
+
def _adjust_suffix(suff):
if suff and not suff[0] in [ '.', '$' ]:
return '.' + suff
@@ -336,20 +362,6 @@ class BuilderBase:
return tlist
- def get_actions(self):
- return self.action.get_actions()
-
- def get_raw_contents(self, target, source, env):
- """Fetch the "contents" of the builder's action.
- """
- return self.action.get_raw_contents(target, source, env)
-
- def get_contents(self, target, source, env):
- """Fetch the "contents" of the builder's action
- (for signature calculation).
- """
- return self.action.get_contents(target, source, env)
-
def src_suffixes(self, env):
return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
self.src_suffix)
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 6def6d5..f7b329d 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -39,8 +39,8 @@ import TestCmd
import SCons.Action
import SCons.Builder
-import SCons.Errors
import SCons.Environment
+import SCons.Errors
# Initial setup of the common environment for all tests,
# a temporary working directory containing a
@@ -82,6 +82,8 @@ class Environment:
return {}
def autogenerate(self, dir=''):
return {}
+ def __setitem__(self, item, var):
+ self.d[item] = var
def __getitem__(self, item):
return self.d[item]
def has_key(self, item):
@@ -127,6 +129,14 @@ class MyNode:
return self.name
def is_derived(self):
return self.has_builder()
+ def generate_build_env(self, env):
+ return env
+ def get_build_env(self):
+ return self.executor.get_build_env()
+ def set_executor(self, executor):
+ self.executor = executor
+ def get_executor(self, create=1):
+ return self.executor
class BuilderTestCase(unittest.TestCase):
@@ -162,9 +172,10 @@ class BuilderTestCase(unittest.TestCase):
n1 = MyNode("n1");
n2 = MyNode("n2");
builder(env, target = n1, source = n2)
- assert n1.env == env
- assert n1.builder == builder
- assert n1.sources == [n2]
+ assert n1.env == env, n1.env
+ assert n1.builder == builder, n1.builder
+ assert n1.sources == [n2], n1.sources
+ assert n1.executor, "no executor found"
assert not hasattr(n2, 'env')
target = builder(env, target = 'n3', source = 'n4')
@@ -179,6 +190,14 @@ class BuilderTestCase(unittest.TestCase):
assert target.name == 'n8 n9'
assert target.sources[0].name == 'n10 n11'
+ # A test to be uncommented when we freeze the environment
+ # as part of calling the builder.
+ #env1 = Environment(VAR='foo')
+ #target = builder(env1, target = 'n12', source = 'n13')
+ #env1['VAR'] = 'bar'
+ #be = target.get_build_env()
+ #assert be['VAR'] == 'foo', be['VAR']
+
if not hasattr(types, 'UnicodeType'):
uni = str
else:
@@ -230,52 +249,6 @@ class BuilderTestCase(unittest.TestCase):
assert b1 != b3
assert b2 != b3
- def test_get_actions(self):
- """Test fetching the Builder's Action list
- """
- def func():
- pass
- builder = SCons.Builder.Builder(action=SCons.Action.ListAction(["x",
- func,
- "z"]))
- a = builder.get_actions()
- assert len(a) == 3, a
- assert isinstance(a[0], SCons.Action.CommandAction), a[0]
- assert isinstance(a[1], SCons.Action.FunctionAction), a[1]
- assert isinstance(a[2], SCons.Action.CommandAction), a[2]
-
- def test_get_contents(self):
- """Test returning the signature contents of a Builder
- """
-
- class DummyNode:
- def __init__(self, name):
- self.name = name
- def __str__(self):
- return self.name
- def rfile(self):
- return self
- def get_subst_proxy(self):
- return self
-
- target = map(DummyNode, map(lambda x: "__t%d__" % x, range(1, 7)))
- source = map(DummyNode, map(lambda x: "__s%d__" % x, range(1, 7)))
- b1 = SCons.Builder.Builder(action = "foo ${TARGETS[5]}")
- contents = b1.get_contents(target,source,Environment())
- assert contents == "foo __t6__", contents
-
- b1 = SCons.Builder.Builder(action = "bar ${SOURCES[3:5]}")
- contents = b1.get_contents(target,source,Environment())
- assert contents == "bar __s4__ __s5__", contents
-
- b2 = SCons.Builder.Builder(action = Func)
- contents = b2.get_contents(target,source,Environment())
- assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
-
- b3 = SCons.Builder.Builder(action = SCons.Action.ListAction(["foo", Func, "bar"]))
- contents = b3.get_contents(target,source,Environment())
- assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
-
def test_node_factory(self):
"""Test a Builder that creates nodes of a specified class
"""
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
new file mode 100644
index 0000000..2b4e634
--- /dev/null
+++ b/src/engine/SCons/Executor.py
@@ -0,0 +1,141 @@
+"""SCons.Executor
+
+A module for executing actions with specific lists of target and source
+Nodes.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+
+class Executor:
+ """A class for controlling instances of executing an action.
+
+ This largely exists to hold a single association of a builder,
+ environment, environment overrides, targets and sources for later
+ processing as needed.
+ """
+
+ def __init__(self, builder, env, overrides, targets, sources):
+ self.builder = builder
+ self.env = env
+ self.overrides = overrides
+ self.targets = targets
+ self.sources = sources[:]
+
+ def get_build_env(self):
+ """Fetch or create the appropriate build Environment
+ for this Executor.
+ """
+ try:
+ return self.build_env
+ except AttributeError:
+ if self.env is None:
+ # There was no Environment specifically associated with
+ # this set of targets (which kind of implies that it
+ # is--or they are--source files, but who knows...).
+ # So use the environment associated with the Builder
+ # itself.
+ env = self.builder.env
+ overrides = self.builder.overrides
+ else:
+ # The normal case: use the Environment that was
+ # used to specify how these targets will be built.
+ env = self.env
+ overrides = self.overrides
+ self.build_env = env.Override(overrides)
+ return self.build_env
+
+ def get_action_list(self, target):
+ """Fetch or create the appropriate action list (for this target).
+
+ There is an architectural mistake here: we cache the action list
+ for the Executor and re-use it regardless of which target is
+ being asked for. In practice, this doesn't seem to be a problem
+ because executing the action list will update all of the targets
+ involved, so only one target's pre- and post-actions will win,
+ anyway. This is probably a bug we should fix...
+ """
+ try:
+ al = self.action_list
+ except AttributeError:
+ al = self.builder.action.get_actions()
+ self.action_list = al
+ # XXX shouldn't reach into node attributes like this
+ return target.pre_actions + al + target.post_actions
+
+ def __call__(self, target, func):
+ """Actually execute the action list."""
+ action_list = self.get_action_list(target)
+ if not action_list:
+ return
+ env = self.get_build_env()
+ for action in action_list:
+ func(action, self.targets, self.sources, env)
+
+ def add_sources(self, sources):
+ """Add source files to this Executor's list. This is necessary
+ for "multi" Builders that can be called repeatedly to build up
+ a source file list for a given target."""
+ slist = filter(lambda x, s=self.sources: x not in s, sources)
+ self.sources.extend(slist)
+
+ def get_raw_contents(self):
+ """Fetch the raw signature contents. This, along with
+ get_contents(), is the real reason this class exists, so we can
+ compute this once and cache it regardless of how many target or
+ source Nodes there are.
+ """
+ try:
+ return self.raw_contents
+ except AttributeError:
+ action = self.builder.action
+ self.raw_contents = action.get_raw_contents(self.targets,
+ self.sources,
+ self.get_build_env())
+ return self.raw_contents
+
+ def get_contents(self):
+ """Fetch the signature contents. This, along with
+ get_raw_contents(), is the real reason this class exists, so we
+ can compute this once and cache it regardless of how many target
+ or source Nodes there are.
+ """
+ try:
+ return self.contents
+ except AttributeError:
+ action = self.builder.action
+ self.contents = action.get_contents(self.targets,
+ self.sources,
+ self.get_build_env())
+ return self.contents
+
+ def get_timestamp(self):
+ """Fetch a time stamp for this Executor. We don't have one, of
+ course (only files do), but this is the interface used by the
+ timestamp module.
+ """
+ return None
diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py
new file mode 100644
index 0000000..6af5794
--- /dev/null
+++ b/src/engine/SCons/ExecutorTests.py
@@ -0,0 +1,173 @@
+#
+# __COPYRIGHT__
+#
+# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import string
+import sys
+import unittest
+
+import SCons.Executor
+
+
+class MyEnvironment:
+ def __init__(self, **kw):
+ self._dict = {}
+ self._dict.update(kw)
+ def Override(self, overrides):
+ d = self._dict.copy()
+ d.update(overrides)
+ return d
+
+class MyAction:
+ actions = ['action1', 'action2']
+ def get_actions(self):
+ return self.actions
+ def get_raw_contents(self, target, source, env):
+ return string.join(['RAW'] + self.actions + target + source)
+ def get_contents(self, target, source, env):
+ return string.join(self.actions + target + source)
+
+class MyBuilder:
+ def __init__(self, env, overrides):
+ self.env = env
+ self.overrides = overrides
+ self.action = MyAction()
+
+class MyNode:
+ def __init__(self, pre, post):
+ self.pre_actions = pre
+ self.post_actions = post
+
+
+class ExecutorTestCase(unittest.TestCase):
+
+ def test__init__(self):
+ """Test creating an Executor"""
+ source_list = ['s1', 's2']
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', source_list)
+ assert x.builder == 'b', x.builder
+ assert x.env == 'e', x.env
+ assert x.overrides == 'o', x.overrides
+ assert x.targets == 't', x.targets
+ source_list.append('s3')
+ assert x.sources == ['s1', 's2'], x.sources
+
+ def test_get_build_env(self):
+ """Test fetching and generating a build environment"""
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ x.build_env = 'eee'
+ be = x.get_build_env()
+ assert be == 'eee', be
+
+ x = SCons.Executor.Executor('b',
+ MyEnvironment(X='xxx'),
+ {'O':'ooo'},
+ 't',
+ ['s1', 's2'])
+ be = x.get_build_env()
+ assert be == {'O':'ooo', 'X':'xxx'}, be
+
+ env = MyEnvironment(Y='yyy')
+ over = {'O':'ooo'}
+ x = SCons.Executor.Executor(MyBuilder(env, over), None, {}, 't', 's')
+ be = x.get_build_env()
+ assert be == {'O':'ooo', 'Y':'yyy'}, be
+
+ def test_get_action_list(self):
+ """Test fetching and generating an action list"""
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', 's')
+ x.action_list = ['aaa']
+ al = x.get_action_list(MyNode([], []))
+ assert al == ['aaa'], al
+ al = x.get_action_list(MyNode(['PRE'], ['POST']))
+ assert al == ['PRE', 'aaa', 'POST'], al
+
+ x = SCons.Executor.Executor(MyBuilder('e', 'o'), None, {}, 't', 's')
+ al = x.get_action_list(MyNode(['pre'], ['post']))
+ assert al == ['pre', 'action1', 'action2', 'post'], al
+
+ def test__call__(self):
+ """Test calling an Executor"""
+ actions = []
+ env = MyEnvironment(CALL='call')
+ b = MyBuilder(env, {})
+ x = SCons.Executor.Executor(b, None, {}, ['t1', 't2'], ['s1', 's2'])
+ def func(action, target, source, env, a=actions):
+ a.append(action)
+ assert target == ['t1', 't2'], target
+ assert source == ['s1', 's2'], source
+ assert env == {'CALL':'call'}, env
+ x(MyNode(['pre'], ['post']), func)
+ assert actions == ['pre', 'action1', 'action2', 'post'], actions
+
+ def test_add_sources(self):
+ """Test adding sources to an Executor"""
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ assert x.sources == ['s1', 's2'], x.sources
+ x.add_sources(['s1', 's2'])
+ assert x.sources == ['s1', 's2'], x.sources
+ x.add_sources(['s3', 's1', 's4'])
+ assert x.sources == ['s1', 's2', 's3', 's4'], x.sources
+
+ def test_get_raw_contents(self):
+ """Test fetching the raw signatures contents"""
+ env = MyEnvironment(RC='raw contents')
+
+ x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ x.raw_contents = 'raw raw raw'
+ rc = x.get_raw_contents()
+ assert rc == 'raw raw raw', rc
+
+ x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ rc = x.get_raw_contents()
+ assert rc == 'RAW action1 action2 t s', rc
+
+ def test_get_contents(self):
+ """Test fetching the signatures contents"""
+ env = MyEnvironment(C='contents')
+
+ x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ x.contents = 'contents'
+ c = x.get_contents()
+ assert c == 'contents', c
+
+ x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ c = x.get_contents()
+ assert c == 'action1 action2 t s', c
+
+ def test_get_timetstamp(self):
+ """Test fetching the "timestamp" """
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ ts = x.get_timestamp()
+ assert ts is None, ts
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ ExecutorTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index b4641a7..c715ade 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -1073,9 +1073,8 @@ class File(Entry):
return self.fs.Rsearchall(pathlist, clazz=Dir, must_exist=0,
cwd=self.cwd)
- def generate_build_env(self):
- env = SCons.Node.Node.generate_build_env(self)
-
+ def generate_build_env(self, env):
+ """Generate an appropriate Environment to build this File."""
return env.Override({'Dir' : self.Dir,
'File' : self.File,
'RDirs' : self.RDirs})
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 1f58264..13f171e 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -46,28 +46,6 @@ except NameError :
True = 1 ; False = 0
-class Builder:
- def __init__(self, factory):
- self.factory = factory
-
- def get_actions(self):
- class Action:
- def __call__(self, targets, sources, env):
- global built_it
- built_it = 1
- return 0
- def show(self, string):
- pass
- def strfunction(self, targets, sources, env):
- return ""
- return [Action()]
-
- def targets(self, t):
- return [t]
-
- def source_factory(self, name):
- return self.factory(name)
-
scanner_count = 0
class Scanner:
@@ -95,6 +73,34 @@ class Environment:
def Override(self, overrides):
return self
+class Action:
+ def __call__(self, targets, sources, env):
+ global built_it
+ built_it = 1
+ return 0
+ def show(self, string):
+ pass
+ def strfunction(self, targets, sources, env):
+ return ""
+ def get_actions(self):
+ return [self]
+
+class Builder:
+ def __init__(self, factory):
+ self.factory = factory
+ self.env = Environment()
+ self.overrides = {}
+ self.action = Action()
+
+ def get_actions(self):
+ return [self]
+
+ def targets(self, t):
+ return [t]
+
+ def source_factory(self, name):
+ return self.factory(name)
+
class BuildDirTestCase(unittest.TestCase):
def runTest(self):
"""Test build dir functionality"""
@@ -916,7 +922,7 @@ class FSTestCase(unittest.TestCase):
test.write("remove_me", "\n")
assert os.path.exists(test.workpath("remove_me"))
f1 = fs.File(test.workpath("remove_me"))
- f1.builder = 1
+ f1.builder = Builder(fs.File)
f1.env_set(Environment())
f1.prepare()
assert not os.path.exists(test.workpath("remove_me"))
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 2cf6d4b..ff57871 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -53,6 +53,9 @@ class MyAction:
self.order = built_order
return 0
+ def get_actions(self):
+ return [self]
+
class MyNonGlobalAction:
def __init__(self):
self.order = 0
@@ -71,11 +74,24 @@ class MyNonGlobalAction:
self.order = built_order
return 0
+ def get_actions(self):
+ return [self]
+
+class Environment:
+ def Dictionary(self, *args):
+ return {}
+ def Override(self, overrides):
+ return overrides
+
class Builder:
+ def __init__(self):
+ self.env = Environment()
+ self.overrides = {}
+ self.action = MyAction()
def targets(self, t):
return [t]
def get_actions(self):
- return [MyAction()]
+ return [self.action]
def get_contents(self, target, source, env):
return 7
@@ -86,6 +102,7 @@ class NoneBuilder(Builder):
class ListBuilder(Builder):
def __init__(self, *nodes):
+ Builder.__init__(self)
self.nodes = nodes
def execute(self, target, source, env):
if hasattr(self, 'status'):
@@ -107,12 +124,6 @@ class ExceptBuilder2:
def execute(self, target, source, env):
raise "foo"
-class Environment:
- def Dictionary(self, *args):
- return {}
- def Override(self, overrides):
- return overrides
-
class Scanner:
called = None
def __call__(self, node):
@@ -154,7 +165,7 @@ class NodeTestCase(unittest.TestCase):
node.sources = ["yyy", "zzz"]
node.build()
assert built_it
- assert built_target[0] == node, built_target[0]
+ assert built_target == [node], built_target
assert built_source == ["yyy", "zzz"], built_source
built_it = None
@@ -163,10 +174,10 @@ class NodeTestCase(unittest.TestCase):
node.env_set(Environment())
node.path = "qqq"
node.sources = ["rrr", "sss"]
- node.overrides = { "foo" : 1, "bar" : 2 }
+ node.builder.overrides = { "foo" : 1, "bar" : 2 }
node.build()
assert built_it
- assert built_target[0] == node, built_target[0]
+ assert built_target == [node], built_target
assert built_source == ["rrr", "sss"], built_source
assert built_args["foo"] == 1, built_args
assert built_args["bar"] == 2, built_args
@@ -184,6 +195,17 @@ class NodeTestCase(unittest.TestCase):
fff.sources = ["hhh", "iii"]
ggg.sources = ["hhh", "iii"]
# [Charles C. 1/7/2002] Uhhh, why are there no asserts here?
+ # [SK, 15 May 2003] I dunno, let's add some...
+ built_it = None
+ fff.build()
+ assert built_it
+ assert built_target == [fff], built_target
+ assert built_source == ["hhh", "iii"], built_source
+ built_it = None
+ ggg.build()
+ assert built_it
+ assert built_target == [ggg], built_target
+ assert built_source == ["hhh", "iii"], built_source
built_it = None
jjj = MyNode("jjj")
@@ -285,31 +307,6 @@ class NodeTestCase(unittest.TestCase):
assert t == [], t
assert m == None, m
- def test_builder_sig_adapter(self):
- """Test the node's adapter for builder signatures
- """
- node = SCons.Node.Node()
- node.builder_set(Builder())
- node.env_set(Environment())
- c = node.builder_sig_adapter().get_contents()
- assert c == 7, c
-
- class ListBuilder:
- def __init__(self, targets):
- self.tgt = targets
- def targets(self, t):
- return self.tgt
- def get_contents(self, target, source, env):
- assert target == self.tgt
- return 8
-
- node1 = SCons.Node.Node()
- node2 = SCons.Node.Node()
- node.builder_set(ListBuilder([node1, node2]))
- node.env_set(Environment())
- c = node.builder_sig_adapter().get_contents()
- assert c == 8, c
-
def test_current(self):
"""Test the default current() method
"""
@@ -605,7 +602,7 @@ class NodeTestCase(unittest.TestCase):
"""Test Scanner functionality
"""
node = MyNode("nnn")
- node.builder = 1
+ node.builder = Builder()
node.env_set(Environment())
s = Scanner()
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 5231f90..09c930b 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -122,21 +122,36 @@ class Node:
# what line in what file created the node, for example).
Annotate(self)
- def generate_build_env(self):
+ def generate_build_env(self, env):
"""Generate the appropriate Environment to build this node."""
- if self.env is None:
- # The node itself doesn't have an associated Environment
- # (which kind of implies it's a source code file, but who
- # knows...). Regardless of why, use the environment (if
- # any) associated with the Builder itself.
- env = self.builder.env
- overrides = self.builder.overrides
- else:
- # The normal case: use the Environment used to specify how
- # this Node is to be built.
- env = self.env
- overrides = self.overrides
- return env.Override(overrides)
+ return env
+
+ def get_build_env(self):
+ """Fetch the appropriate Environment to build this node."""
+ executor = self.get_executor()
+ return executor.get_build_env()
+
+ 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
+ import SCons.Builder
+ env = self.generate_build_env(self.builder.env)
+ executor = SCons.Executor.Executor(self.builder,
+ env,
+ self.builder.overrides,
+ [self],
+ self.sources)
+ self.executor = executor
+ return executor
def _for_each_action(self, func):
"""Call a function for each action required to build a node.
@@ -147,15 +162,8 @@ class Node:
to do different things."""
if not self.has_builder():
return
- action_list = self.pre_actions + \
- self.builder.get_actions() + \
- self.post_actions
- if not action_list:
- return
- targets = self.builder.targets(self)
- env = self.generate_build_env()
- for action in action_list:
- func(action, targets, self.sources, env)
+ executor = self.get_executor()
+ executor(self, func)
def build(self):
"""Actually build the node.
@@ -249,24 +257,6 @@ class Node:
"""
return [], None
- def builder_sig_adapter(self):
- """Create an adapter for calculating a builder's signature.
-
- The underlying signature class will call get_contents()
- to fetch the signature of a builder, but the actual
- content of that signature depends on the node and the
- environment (for construction variable substitution),
- so this adapter provides the right glue between the two.
- """
- class Adapter:
- def __init__(self, node):
- self.node = node
- def get_contents(self):
- return self.node.builder.get_contents(self.node.builder.targets(self.node), self.node.sources, self.node.generate_build_env())
- def get_timestamp(self):
- return None
- return Adapter(self)
-
def get_found_includes(self, env, scanner, target):
"""Return the scanned include lines (implicit dependencies)
found in this node.
@@ -351,7 +341,7 @@ class Node:
self.implicit = []
self.del_bsig()
- build_env = self.generate_build_env()
+ build_env = self.get_build_env()
for child in self.children(scan=0):
self._add_child(self.implicit,
@@ -589,7 +579,7 @@ class Node:
user, of the include tree for the sources of this node.
"""
if self.has_builder() and self.env:
- env = self.generate_build_env()
+ env = self.get_build_env()
for s in self.sources:
def f(node, env=env, scanner=s.source_scanner, target=self):
return node.get_found_includes(env, scanner, target)
diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py
index 64cd5f9..830b94a 100644
--- a/src/engine/SCons/SConf.py
+++ b/src/engine/SCons/SConf.py
@@ -166,7 +166,6 @@ class SConf:
except:
pass
-
for n in nodes:
state = n.get_state()
if (state != SCons.Node.executed and
@@ -193,6 +192,7 @@ class SConf:
#target = self.confdir.File("conftest_" + str(_ac_build_counter))
f = "conftest_" + str(_ac_build_counter)
target = os.path.join(str(self.confdir), f)
+ self.env['SCONF_TEXT'] = text
if text != None:
source = self.confdir.File(f + extension)
sourceNode = self.env.SConfSourceBuilder(target=source,
@@ -200,7 +200,6 @@ class SConf:
nodesToBeBuilt.append(sourceNode)
else:
source = None
- self.env['SCONF_TEXT'] = text
node = builder(target = target, source = source)
nodesToBeBuilt.append(node)
@@ -301,7 +300,7 @@ class SConf:
# We record errors in the cache. Only non-exisiting targets may
# have recorded errors
needs_rebuild = target[0].exists()
- buildSig = target[0].builder.get_contents(target, source, env)
+ buildSig = target[0].builder.action.get_contents(target, source, env)
for node in source:
if node.get_state() != SCons.Node.up_to_date:
# if any of the sources has changed, we cannot use our cache
diff --git a/src/engine/SCons/Sig/SigTests.py b/src/engine/SCons/Sig/SigTests.py
index 7e1cc40..bb3efb8 100644
--- a/src/engine/SCons/Sig/SigTests.py
+++ b/src/engine/SCons/Sig/SigTests.py
@@ -138,7 +138,7 @@ class DummyNode:
def store_timestamp(self):
pass
- def builder_sig_adapter(self):
+ def get_executor(self):
class Adapter:
def get_contents(self):
return 111
diff --git a/src/engine/SCons/Sig/__init__.py b/src/engine/SCons/Sig/__init__.py
index cd6fe7e..cf7a86f 100644
--- a/src/engine/SCons/Sig/__init__.py
+++ b/src/engine/SCons/Sig/__init__.py
@@ -282,7 +282,7 @@ class Calculator:
sigs = map(lambda n, c=self: n.calc_signature(c), children)
if node.has_builder():
- sigs.append(self.module.signature(node.builder_sig_adapter()))
+ sigs.append(self.module.signature(node.get_executor()))
bsig = self.module.collect(filter(lambda x: not x is None, sigs))