summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-06-25 04:10:24 (GMT)
committerSteven Knight <knight@baldmt.com>2004-06-25 04:10:24 (GMT)
commitc2bb425dcb2907f50a485469b69e83884fed6fb4 (patch)
treeeec003c4e6e332651cf70c8896612b17b7acb290 /src
parent5f1ca10deda557947d8669098fdce1852b38b81f (diff)
downloadSCons-c2bb425dcb2907f50a485469b69e83884fed6fb4.zip
SCons-c2bb425dcb2907f50a485469b69e83884fed6fb4.tar.gz
SCons-c2bb425dcb2907f50a485469b69e83884fed6fb4.tar.bz2
Officially support target_factory and source_factory when creating a Builder.
Diffstat (limited to 'src')
-rw-r--r--src/CHANGES.txt4
-rw-r--r--src/engine/SCons/Action.py22
-rw-r--r--src/engine/SCons/ActionTests.py36
-rw-r--r--src/engine/SCons/Builder.py2
-rw-r--r--src/engine/SCons/Environment.py5
-rw-r--r--src/engine/SCons/Node/FS.py48
-rw-r--r--src/engine/SCons/Node/FSTests.py82
-rw-r--r--src/engine/SCons/Node/NodeTests.py8
-rw-r--r--src/engine/SCons/Node/__init__.py2
9 files changed, 130 insertions, 79 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index ee75857..f2ee12d 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -138,6 +138,10 @@ RELEASE 0.96 - XXX
- Slim down the internal Sig.Calculator class by eliminating methods
whose functionality is now covered by Node methods.
+ - Document use of the target_factory and source_factory keyword
+ arguments when creating Builder objects. Enhance Dir Nodes so that
+ they can be created with user-specified Builder objects.
+
From Gary Oberbrunner:
- Add a --debug=presub option to print actions prior to substitution.
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index c8d62cd..6a90c76 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -191,25 +191,29 @@ def _do_create_action(act, *args, **kw):
return apply(ListAction, (listCmdActions,)+args, kw)
return None
-def Action(act, strfunction=_null, varlist=[]):
+def Action(act, strfunction=_null, varlist=[], presub=_null):
"""A factory for action objects."""
if SCons.Util.is_List(act):
- acts = map(lambda x, s=strfunction, v=varlist:
- _do_create_action(x, strfunction=s, varlist=v),
+ acts = map(lambda x, s=strfunction, v=varlist, ps=presub:
+ _do_create_action(x, strfunction=s, varlist=v, presub=ps),
act)
acts = filter(lambda x: not x is None, acts)
if len(acts) == 1:
return acts[0]
else:
- return ListAction(acts, strfunction=strfunction, varlist=varlist)
+ return ListAction(acts, strfunction=strfunction, varlist=varlist, presub=presub)
else:
- return _do_create_action(act, strfunction=strfunction, varlist=varlist)
+ return _do_create_action(act, strfunction=strfunction, varlist=varlist, presub=presub)
class ActionBase:
"""Base class for actions that create output objects."""
- def __init__(self, strfunction=_null, **kw):
+ def __init__(self, strfunction=_null, presub=_null, **kw):
if not strfunction is _null:
self.strfunction = strfunction
+ if presub is _null:
+ self.presub = print_actions_presub
+ else:
+ self.presub = presub
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
@@ -223,12 +227,12 @@ class ActionBase:
target = [target]
if not SCons.Util.is_List(source):
source = [source]
- if presub is _null: presub = print_actions_presub
+ if presub is _null: presub = self.presub
if show is _null: show = print_actions
if execute is _null: execute = execute_actions
if presub:
t = string.join(map(str, target), 'and')
- l = string.join(self.presub(env), '\n ')
+ l = string.join(self.presub_lines(env), '\n ')
out = "Building %s with action(s):\n %s\n" % (t, l)
sys.stdout.write(out)
if show and self.strfunction:
@@ -243,7 +247,7 @@ class ActionBase:
else:
return 0
- def presub(self, env):
+ def presub_lines(self, env):
# CommandGeneratorAction needs a real environment
# in order to return the proper string here, since
# it may call LazyCmdGenerator, which looks up a key
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index f699e61..9a89623 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -392,11 +392,27 @@ class ActionBaseTestCase(unittest.TestCase):
result = a("out", "in", env)
assert result == 0, result
s = sio.getvalue()
+ assert s == 'execfunc("out", "in")\n', s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env, presub=1)
+ assert result == 0, result
+ s = sio.getvalue()
+ assert s == 'Building out with action(s):\n execfunc(env, target, source)\nexecfunc("out", "in")\n', s
+
+ a2 = SCons.Action.Action(execfunc)
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a2("out", "in", env)
+ assert result == 0, result
+ s = sio.getvalue()
assert s == 'Building out with action(s):\n execfunc(env, target, source)\nexecfunc("out", "in")\n', s
sio = StringIO.StringIO()
sys.stdout = sio
- result = a("out", "in", env, presub=0)
+ result = a2("out", "in", env, presub=0)
assert result == 0, result
s = sio.getvalue()
assert s == 'execfunc("out", "in")\n', s
@@ -428,36 +444,36 @@ class ActionBaseTestCase(unittest.TestCase):
SCons.Action.print_actions_presub = save_print_actions_presub
SCons.Action.execute_actions = save_execute_actions
- def test_presub(self):
- """Test the presub() method
+ def test_presub_lines(self):
+ """Test the presub_lines() method
"""
env = Environment()
a = SCons.Action.Action("x")
- s = a.presub(env)
+ s = a.presub_lines(env)
assert s == ['x'], s
a = SCons.Action.Action(["y", "z"])
- s = a.presub(env)
+ s = a.presub_lines(env)
assert s == ['y', 'z'], s
def func():
pass
a = SCons.Action.Action(func)
- s = a.presub(env)
+ s = a.presub_lines(env)
assert s == ["func(env, target, source)"], s
def gen(target, source, env, for_signature):
return 'generat' + env.get('GEN', 'or')
a = SCons.Action.Action(SCons.Action.CommandGenerator(gen))
- s = a.presub(env)
+ s = a.presub_lines(env)
assert s == ["generator"], s
- s = a.presub(Environment(GEN = 'ed'))
+ s = a.presub_lines(Environment(GEN = 'ed'))
assert s == ["generated"], s
a = SCons.Action.Action("$ACT")
- s = a.presub(env)
+ s = a.presub_lines(env)
assert s == [''], s
- s = a.presub(Environment(ACT = 'expanded action'))
+ s = a.presub_lines(Environment(ACT = 'expanded action'))
assert s == ['expanded action'], s
def test_get_actions(self):
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 733a26a..1dcf84c 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -285,7 +285,7 @@ def _init_nodes(builder, env, overrides, tlist, slist):
if t.side_effect:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
if t.has_builder():
- if not t.env is env:
+ if not t.env is None and not t.env is env:
t_contents = t.builder.action.get_contents(tlist, slist, t.env)
contents = t.builder.action.get_contents(tlist, slist, env)
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 5766523..080232a 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -1172,10 +1172,7 @@ class Base:
targets = self.arg2nodes(target, self.fs.Entry)
for side_effect in side_effects:
- # A builder of 1 means the node is supposed to appear
- # buildable without actually having a builder, so we allow
- # it to be a side effect as well.
- if side_effect.has_builder() and side_effect.builder != 1:
+ if side_effect.multiple_side_effect_has_builder():
raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
side_effect.add_source(targets)
side_effect.side_effect = 1
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 2f115c9..52a75ce 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -176,10 +176,24 @@ Unlink = SCons.Action.Action(UnlinkFunc, None)
def MkdirFunc(target, source, env):
t = target[0]
- t.fs.mkdir(t.path)
+ if not t.fs.exists(t.path):
+ t.fs.mkdir(t.path)
return 0
-Mkdir = SCons.Action.Action(MkdirFunc, None)
+Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None)
+
+MkdirBuilder = None
+
+def get_MkdirBuilder():
+ global MkdirBuilder
+ if MkdirBuilder is None:
+ import SCons.Builder
+ import SCons.Defaults
+ env = SCons.Defaults.DefaultEnvironment()
+ MkdirBuilder = SCons.Builder.Builder(action = Mkdir,
+ env = env,
+ explain = None)
+ return MkdirBuilder
def CacheRetrieveFunc(target, source, env):
t = target[0]
@@ -1118,7 +1132,7 @@ class Dir(Base):
self.entries['.'] = self
self.entries['..'] = self.dir
self.cwd = self
- self.builder = 1
+ self.builder = get_MkdirBuilder()
self.searched = 0
self._sconsign = None
self.build_dirs = []
@@ -1238,7 +1252,13 @@ class Dir(Base):
def build(self, **kw):
"""A null "builder" for directories."""
- pass
+ global MkdirBuilder
+ if not self.builder is MkdirBuilder:
+ apply(SCons.Node.Node.build, [self,], kw)
+
+ def multiple_side_effect_has_builder(self):
+ global MkdirBuilder
+ return not self.builder is MkdirBuilder and self.has_builder()
def alter_targets(self):
"""Return any corresponding targets in a build directory.
@@ -1262,6 +1282,8 @@ class Dir(Base):
def current(self, calc=None):
"""If all of our children were up-to-date, then this
directory was up-to-date, too."""
+ if not self.builder is MkdirBuilder and not self.exists():
+ return 0
state = 0
for kid in self.children():
s = kid.get_state()
@@ -1299,16 +1321,6 @@ class Dir(Base):
return self.srcdir
return Base.srcnode(self)
- 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:
- executor = DummyExecutor()
- self.executor = executor
- return executor
-
def get_timestamp(self):
"""Return the latest timestamp from among our children"""
stamp = 0
@@ -1482,8 +1494,12 @@ class File(Base):
listDirs.reverse()
for dirnode in listDirs:
try:
- Mkdir(dirnode, [], None)
- # The Mkdir() action may or may not have actually
+ # Don't call dirnode.build(), call the base Node method
+ # directly because we definitely *must* create this
+ # directory. The dirnode.build() method will suppress
+ # the build if it's the default builder.
+ SCons.Node.Node.build(dirnode)
+ # The build() action may or may not have actually
# created the directory, depending on whether the -n
# option was used or not. Delete the _exists and
# _rexists attributes so they can be reevaluated.
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index d4137c1..bbc64ef 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -281,8 +281,33 @@ class BuildDirTestCase(unittest.TestCase):
f8.rfile().path
# Verify the Mkdir and Link actions are called
+ d9 = fs.Dir('build/var2/new_dir')
f9 = fs.File('build/var2/new_dir/test9.out')
+ class MkdirAction(Action):
+ def __init__(self, dir_made):
+ self.dir_made = dir_made
+ def __call__(self, target, source, env, errfunc):
+ self.dir_made.extend(target)
+
+ save_Link = SCons.Node.FS.Link
+ link_made = []
+ def link_func(target, source, env, link_made=link_made):
+ link_made.append(target)
+ SCons.Node.FS.Link = link_func
+
+ try:
+ dir_made = []
+ d9.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
+ f9.exists()
+ expect = os.path.join('build', 'var2', 'new_dir')
+ assert dir_made[0].path == expect, dir_made[0].path
+ expect = os.path.join('build', 'var2', 'new_dir', 'test9.out')
+ assert link_made[0].path == expect, link_made[0].path
+ assert f9.linked
+ finally:
+ SCons.Node.FS.Link = save_Link
+
# Test for an interesting pathological case...we have a source
# file in a build path, but not in a source path. This can
# happen if you switch from duplicate=1 to duplicate=0, then
@@ -312,29 +337,6 @@ class BuildDirTestCase(unittest.TestCase):
var2_new_dir = os.path.normpath('build/var2/new_dir')
assert bdt == [var1_new_dir, var2_new_dir], bdt
- save_Mkdir = SCons.Node.FS.Mkdir
- dir_made = []
- def mkdir_func(target, source, env, dir_made=dir_made):
- dir_made.append(target)
- SCons.Node.FS.Mkdir = mkdir_func
-
- save_Link = SCons.Node.FS.Link
- link_made = []
- def link_func(target, source, env, link_made=link_made):
- link_made.append(target)
- SCons.Node.FS.Link = link_func
-
- try:
- f9.exists()
- expect = os.path.join('build', 'var2', 'new_dir')
- assert dir_made[0].path == expect, dir_made[0].path
- expect = os.path.join('build', 'var2', 'new_dir', 'test9.out')
- assert link_made[0].path == expect, link_made[0].path
- assert f9.linked
- finally:
- SCons.Node.FS.Mkdir = save_Mkdir
- SCons.Node.FS.Link = save_Link
-
# Test that an IOError trying to Link a src file
# into a BuildDir ends up throwing a StopError.
fIO = fs.File("build/var2/IOError")
@@ -754,7 +756,7 @@ class FSTestCase(unittest.TestCase):
d1.builder_set(Builder(fs.File))
d1.env_set(Environment())
d1.build()
- assert not built_it
+ assert built_it
built_it = None
assert not built_it
@@ -1497,22 +1499,24 @@ class prepareTestCase(unittest.TestCase):
exc_caught = 1
assert exc_caught, "Should have caught a StopError."
- save_Mkdir = SCons.Node.FS.Mkdir
- dir_made = []
- def mkdir_func(target, source, env, dir_made=dir_made):
- dir_made.append(target)
- SCons.Node.FS.Mkdir = mkdir_func
+ class MkdirAction(Action):
+ def __init__(self, dir_made):
+ self.dir_made = dir_made
+ def __call__(self, target, source, env, errfunc):
+ self.dir_made.extend(target)
- file = fs.File(os.path.join("new_dir", "xyz"))
- try:
- file.set_state(SCons.Node.up_to_date)
- file.prepare()
- assert dir_made == [], dir_made
- file.set_state(0)
- file.prepare()
- assert dir_made[0].path == "new_dir", dir_made[0].path
- finally:
- SCons.Node.FS.Mkdir = save_Mkdir
+ dir_made = []
+ new_dir = fs.Dir("new_dir")
+ new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
+ xyz = fs.File(os.path.join("new_dir", "xyz"))
+
+ xyz.set_state(SCons.Node.up_to_date)
+ xyz.prepare()
+ assert dir_made == [], dir_made
+ xyz.set_state(0)
+ xyz.prepare()
+ print "dir_made[0] =", dir_made[0]
+ assert dir_made[0].path == "new_dir", dir_made[0]
dir = fs.Dir("dir")
dir.prepare()
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index cd7aa18..72ddd74 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -304,6 +304,14 @@ class NodeTestCase(unittest.TestCase):
n1.builder_set(Builder())
assert n1.has_builder() == 1
+ def test_multiple_side_effect_has_builder(self):
+ """Test the multiple_side_effect_has_builder() method
+ """
+ n1 = SCons.Node.Node()
+ assert n1.multiple_side_effect_has_builder() == 0
+ n1.builder_set(Builder())
+ assert n1.multiple_side_effect_has_builder() == 1
+
def test_is_derived(self):
"""Test the is_derived() method
"""
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 51e6628..603762e 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -299,6 +299,8 @@ class Node:
b = self.builder
return not b is None
+ multiple_side_effect_has_builder = has_builder
+
def is_derived(self):
"""
Returns true iff this node is derived (i.e. built).