summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-11-09 16:08:23 (GMT)
committerSteven Knight <knight@baldmt.com>2004-11-09 16:08:23 (GMT)
commit4c0421f6c2210f908fdfb1686c473340b15d09c7 (patch)
treef679201e2d6e48f5495027485e42e9b313404e93
parentd0db206cde65dac2cfdd8caa78ba5ef75ee8693f (diff)
downloadSCons-4c0421f6c2210f908fdfb1686c473340b15d09c7.zip
SCons-4c0421f6c2210f908fdfb1686c473340b15d09c7.tar.gz
SCons-4c0421f6c2210f908fdfb1686c473340b15d09c7.tar.bz2
Add explicit support for Builder wrapper functions (pseudo-Builders) in the BUILDERS dictionary.
-rw-r--r--src/CHANGES.txt4
-rw-r--r--src/engine/SCons/Builder.py36
-rw-r--r--src/engine/SCons/BuilderTests.py72
-rw-r--r--src/engine/SCons/Environment.py11
-rw-r--r--src/engine/SCons/EnvironmentTests.py20
-rw-r--r--test/builder-wrappers.py68
6 files changed, 142 insertions, 69 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index d9a397f..5f6968b 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -113,6 +113,10 @@ RELEASE 0.97 - XXX
for another target, to avoid trying to build it again when it comes
up in the target list.
+ - Allow a function with the right calling signature to be put directly
+ in an Environment's BUILDERS dictionary, making for easier creation
+ and use of wrappers (pseudo-Builders) that call other Builders.
+
From Wayne Lee:
- Avoid "maximum recursion limit" errors when removing $(-$) pairs
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index d6b7597..40361d4 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -559,23 +559,15 @@ class BuilderBase:
return tlist, slist
- def _execute(self, env, target=None, source=_null, overwarn={}, executor_kw={}):
- if source is _null:
- source = target
- target = None
-
- if(self.single_source and
- SCons.Util.is_List(source) and
- len(source) > 1 and
- target is None):
+ def _execute(self, env, target, source, overwarn={}, executor_kw={}):
+ # We now assume that target and source are lists or None.
+ if self.single_source and len(source) > 1 and target is None:
result = []
if target is None: target = [None]*len(source)
- for k in range(len(source)):
- t = self._execute(env, target[k], source[k], overwarn)
- if SCons.Util.is_List(t):
- result.extend(t)
- else:
- result.append(t)
+ for tgt, src in zip(target, source):
+ if not tgt is None: tgt = [tgt]
+ if not src is None: src = [src]
+ result.extend(self._execute(env, tgt, src, overwarn))
return result
tlist, slist = self._create_nodes(env, overwarn, target, source)
@@ -588,7 +580,10 @@ class BuilderBase:
return tlist
- def __call__(self, env, target=None, source=_null, chdir=_null, **kw):
+ def __call__(self, env, target=None, source=None, chdir=_null, **kw):
+ # We now assume that target and source are lists or None.
+ # The caller (typically Environment.BuilderWrapper) is
+ # responsible for converting any scalar values to lists.
if chdir is _null:
ekw = self.executor_kw
else:
@@ -709,11 +704,8 @@ class MultiStepBuilder(BuilderBase):
self.sdict = {}
self.cached_src_suffixes = {} # source suffixes keyed on id(env)
- def _execute(self, env, target = None, source = _null, overwarn={}, executor_kw={}):
- if source is _null:
- source = target
- target = None
-
+ def _execute(self, env, target, source, overwarn={}, executor_kw={}):
+ # We now assume that target and source are lists or None.
slist = env.arg2nodes(source, self.source_factory)
final_sources = []
@@ -736,7 +728,7 @@ class MultiStepBuilder(BuilderBase):
for snode in slist:
for srcsuf in src_suffixes:
if str(snode)[-len(srcsuf):] == srcsuf and sdict.has_key(srcsuf):
- tgt = sdict[srcsuf]._execute(env, None, snode, overwarn)
+ tgt = sdict[srcsuf]._execute(env, None, [snode], overwarn)
# If the subsidiary Builder returned more than one target,
# then filter out any sources that this Builder isn't
# capable of building.
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 2075bf0..98e5a58 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -254,7 +254,7 @@ class BuilderTestCase(unittest.TestCase):
n20 = MyNode_without_target_from_source('n20')
flag = 0
try:
- target = builder(env, source=n20)
+ target = builder(env, None, source=n20)
except SCons.Errors.UserError, e:
flag = 1
assert flag, "UserError should be thrown if a source node can't create a target."
@@ -264,7 +264,7 @@ class BuilderTestCase(unittest.TestCase):
source_factory=MyNode,
prefix='p-',
suffix='.s')
- target = builder(env, source='n21')[0]
+ target = builder(env, None, source='n21')[0]
assert target.name == 'p-n21.s', target
builder = SCons.Builder.Builder(misspelled_action="foo",
@@ -412,10 +412,10 @@ class BuilderTestCase(unittest.TestCase):
tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
assert tgt.path == 'libtgt2a tgt2b', \
"Target has unexpected name: %s" % tgt.path
- tgt = builder(env, source = 'src3')[0]
+ tgt = builder(env, target = None, source = 'src3')[0]
assert tgt.path == 'libsrc3', \
"Target has unexpected name: %s" % tgt.path
- tgt = builder(env, source = 'lib/src4')[0]
+ tgt = builder(env, target = None, source = 'lib/src4')[0]
assert tgt.path == os.path.join('lib', 'libsrc4'), \
"Target has unexpected name: %s" % tgt.path
tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
@@ -439,17 +439,17 @@ class BuilderTestCase(unittest.TestCase):
'$FOO' : 'foo-',
'.zzz' : my_emit},
action = '')
- tgt = builder(my_env, source = 'f1')[0]
+ tgt = builder(my_env, target = None, source = 'f1')[0]
assert tgt.path == 'default-f1', tgt.path
- tgt = builder(my_env, source = 'f2.c')[0]
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
assert tgt.path == 'default-f2', tgt.path
- tgt = builder(my_env, source = 'f3.in')[0]
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
assert tgt.path == 'out-f3', tgt.path
- tgt = builder(my_env, source = 'f4.x')[0]
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
assert tgt.path == 'y-f4', tgt.path
- tgt = builder(my_env, source = 'f5.foo')[0]
+ tgt = builder(my_env, target = None, source = 'f5.foo')[0]
assert tgt.path == 'foo-f5', tgt.path
- tgt = builder(my_env, source = 'f6.zzz')[0]
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
assert tgt.path == 'emit-f6', tgt.path
def test_src_suffix(self):
@@ -492,7 +492,7 @@ class BuilderTestCase(unittest.TestCase):
b6 = SCons.Builder.Builder(action = '',
src_suffix='_src.a',
suffix='.b')
- tgt = b6(env, source='foo_src.a')
+ tgt = b6(env, target=None, source='foo_src.a')
assert str(tgt[0]) == 'foo.b', str(tgt[0])
b7 = SCons.Builder.Builder(action = '',
@@ -501,16 +501,16 @@ class BuilderTestCase(unittest.TestCase):
b8 = SCons.Builder.Builder(action = '',
src_builder=b7,
suffix='.c')
- tgt = b8(env, source='foo_source.a')
+ tgt = b8(env, target=None, source='foo_source.a')
assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
src = SCons.Node.FS.default_fs.File('foo_source.a')
- tgt = b8(env, source=src)
+ tgt = b8(env, target=None, source=src)
assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
b9 = SCons.Builder.Builder(action={'_src.a' : 'srcaction'},
suffix='.c')
b9.add_action('_altsrc.b', 'altaction')
- tgt = b9(env, source='foo_altsrc.b')
+ tgt = b9(env, target=None, source='foo_altsrc.b')
assert str(tgt[0]) == 'foo.c', str(tgt[0])
def test_suffix(self):
@@ -530,7 +530,7 @@ class BuilderTestCase(unittest.TestCase):
tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
assert tgt.path == 'tgt4a tgt4b.o', \
"Target has unexpected name: %s" % tgt.path
- tgt = builder(env, source = 'src5')[0]
+ tgt = builder(env, target = None, source = 'src5')[0]
assert tgt.path == 'src5.o', \
"Target has unexpected name: %s" % tgt.path
@@ -551,17 +551,17 @@ class BuilderTestCase(unittest.TestCase):
'$BAR' : '.new',
'.zzz' : my_emit},
action='')
- tgt = builder(my_env, source = 'f1')[0]
+ tgt = builder(my_env, target = None, source = 'f1')[0]
assert tgt.path == 'f1.default', tgt.path
- tgt = builder(my_env, source = 'f2.c')[0]
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
assert tgt.path == 'f2.default', tgt.path
- tgt = builder(my_env, source = 'f3.in')[0]
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
assert tgt.path == 'f3.out', tgt.path
- tgt = builder(my_env, source = 'f4.x')[0]
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
assert tgt.path == 'f4.y', tgt.path
- tgt = builder(my_env, source = 'f5.bar')[0]
+ tgt = builder(my_env, target = None, source = 'f5.bar')[0]
assert tgt.path == 'f5.new', tgt.path
- tgt = builder(my_env, source = 'f6.zzz')[0]
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
assert tgt.path == 'f6.emit', tgt.path
def test_single_source(self):
@@ -589,7 +589,7 @@ class BuilderTestCase(unittest.TestCase):
tgt.prepare()
tgt.build()
assert env['CNT'][0] == 2
- tgts = builder(env, infiles[2:4])
+ tgts = builder(env, None, infiles[2:4])
for t in tgts: t.prepare()
tgts[0].build()
tgts[1].build()
@@ -677,7 +677,7 @@ class BuilderTestCase(unittest.TestCase):
assert str(tgt.sources[1]) == 'test2.foo', str(tgt.sources[1])
assert str(tgt.sources[2]) == 'test3.txt', str(tgt.sources[2])
- tgt = builder2(env, 'aaa.bar')[0]
+ tgt = builder2(env, None, 'aaa.bar')[0]
assert str(tgt) == 'aaa', str(tgt)
assert str(tgt.sources[0]) == 'aaa.foo', str(tgt.sources[0])
assert str(tgt.sources[0].sources[0]) == 'aaa.bar', \
@@ -1184,11 +1184,11 @@ class BuilderTestCase(unittest.TestCase):
'.4b':emit4b},
target_factory=MyNode,
source_factory=MyNode)
- tgt = builder4(env, source='aaa.4a')[0]
+ tgt = builder4(env, None, source='aaa.4a')[0]
assert str(tgt) == 'emit4a-aaa', str(tgt)
- tgt = builder4(env, source='bbb.4b')[0]
+ tgt = builder4(env, None, source='bbb.4b')[0]
assert str(tgt) == 'emit4b-bbb', str(tgt)
- tgt = builder4(env, source='ccc.4c')[0]
+ tgt = builder4(env, None, source='ccc.4c')[0]
assert str(tgt) == 'ccc', str(tgt)
def emit4c(target, source, env):
@@ -1196,7 +1196,7 @@ class BuilderTestCase(unittest.TestCase):
target = map(lambda x: 'emit4c-' + x[:-3], source)
return (target, source)
builder4.add_emitter('.4c', emit4c)
- tgt = builder4(env, source='ccc.4c')[0]
+ tgt = builder4(env, None, source='ccc.4c')[0]
assert str(tgt) == 'emit4c-ccc', str(tgt)
# Test a list of emitter functions.
@@ -1241,43 +1241,43 @@ class BuilderTestCase(unittest.TestCase):
env = Environment()
b = SCons.Builder.Builder(action='foo', suffix='.o')
- tgt = b(env, 'aaa')[0]
+ tgt = b(env, None, 'aaa')[0]
assert str(tgt) == 'aaa.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'aaa', map(str, tgt.sources)
- tgt = b(env, 'bbb.c')[0]
+ tgt = b(env, None, 'bbb.c')[0]
assert str(tgt) == 'bbb.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'bbb.c', map(str, tgt.sources)
- tgt = b(env, 'ccc.x.c')[0]
+ tgt = b(env, None, 'ccc.x.c')[0]
assert str(tgt) == 'ccc.x.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'ccc.x.c', map(str, tgt.sources)
- tgt = b(env, ['d0.c', 'd1.c'])[0]
+ tgt = b(env, None, ['d0.c', 'd1.c'])[0]
assert str(tgt) == 'd0.o', str(tgt)
assert len(tgt.sources) == 2, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'd0.c', map(str, tgt.sources)
assert str(tgt.sources[1]) == 'd1.c', map(str, tgt.sources)
- tgt = b(env, source='eee')[0]
+ tgt = b(env, target = None, source='eee')[0]
assert str(tgt) == 'eee.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'eee', map(str, tgt.sources)
- tgt = b(env, source='fff.c')[0]
+ tgt = b(env, target = None, source='fff.c')[0]
assert str(tgt) == 'fff.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'fff.c', map(str, tgt.sources)
- tgt = b(env, source='ggg.x.c')[0]
+ tgt = b(env, target = None, source='ggg.x.c')[0]
assert str(tgt) == 'ggg.x.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'ggg.x.c', map(str, tgt.sources)
- tgt = b(env, source=['h0.c', 'h1.c'])[0]
+ tgt = b(env, target = None, source=['h0.c', 'h1.c'])[0]
assert str(tgt) == 'h0.o', str(tgt)
assert len(tgt.sources) == 2, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'h0.c', map(str, tgt.sources)
@@ -1285,7 +1285,7 @@ class BuilderTestCase(unittest.TestCase):
w = b(env, target='i0.w', source=['i0.x'])[0]
y = b(env, target='i1.y', source=['i1.z'])[0]
- tgt = b(env, source=[w, y])[0]
+ tgt = b(env, None, source=[w, y])[0]
assert str(tgt) == 'i0.o', str(tgt)
assert len(tgt.sources) == 2, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'i0.w', map(str, tgt.sources)
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index caa3b85..443c6b0 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -149,8 +149,15 @@ class BuilderWrapper:
self.env = env
self.builder = builder
- def __call__(self, *args, **kw):
- return apply(self.builder, (self.env,) + args, kw)
+ def __call__(self, target=None, source=_null, *args, **kw):
+ if source is _null:
+ source = target
+ target = None
+ if not target is None and not SCons.Util.is_List(target):
+ target = [target]
+ if not source is None and not SCons.Util.is_List(source):
+ source = [source]
+ return apply(self.builder, (self.env, target, source) + args, kw)
# This allows a Builder to be executed directly
# through the Environment to which it's attached.
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 27e7e8b..0a0f260 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -87,8 +87,10 @@ class Builder:
def __init__(self, name = None):
self.name = name
- def __call__(self, env, **kw):
+ def __call__(self, env, target=None, source=None, **kw):
global called_it
+ called_it['target'] = target
+ called_it['source'] = source
called_it.update(kw)
def execute(self, target = None, **kw):
@@ -481,21 +483,21 @@ class EnvironmentTestCase(unittest.TestCase):
env.Replace(BUILDERS = { 'builder1' : b1,
'builder2' : b2 })
called_it = {}
- env.builder1(target = 'out1')
- assert called_it['target'] == 'out1', called_it
- assert not called_it.has_key('source')
+ env.builder1('in1')
+ assert called_it['target'] == None, called_it
+ assert called_it['source'] == ['in1'], called_it
called_it = {}
- env.builder2(target = 'out2', xyzzy = 1)
- assert called_it['target'] == 'out2', called_it
+ env.builder2(source = 'in2', xyzzy = 1)
+ assert called_it['target'] == None, called_it
+ assert called_it['source'] == ['in2'], called_it
assert called_it['xyzzy'] == 1, called_it
- assert not called_it.has_key('source')
called_it = {}
env.builder1(foo = 'bar')
assert called_it['foo'] == 'bar', called_it
- assert not called_it.has_key('target')
- assert not called_it.has_key('source')
+ assert called_it['target'] == None, called_it
+ assert called_it['source'] == None, called_it
diff --git a/test/builder-wrappers.py b/test/builder-wrappers.py
new file mode 100644
index 0000000..dbde302
--- /dev/null
+++ b/test/builder-wrappers.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# __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__"
+
+"""
+Test the ability to use a direct Python function to wrap
+calls to other Builder(s).
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+import os.path
+import string
+def cat(target, source, env):
+ fp = open(str(target[0]), 'w')
+ for s in map(str, source):
+ fp.write(open(s).read())
+Cat = Builder(action=cat)
+def Wrapper(env, target, source):
+ if not target:
+ target = [string.replace(str(source[0]), '.in', '.wout')]
+ t1 = 't1-'+str(target[0])
+ source = 's-'+str(source[0])
+ env.Cat(t1, source)
+ t2 = 't2-'+str(target[0])
+ env.Cat(t2, source)
+env = Environment(BUILDERS = {'Cat' : Cat,
+ 'Wrapper' : Wrapper})
+env.Wrapper('f1.out', 'f1.in')
+env.Wrapper('f2.in')
+""")
+
+test.write('s-f1.in', "s-f1.in\n")
+test.write('s-f2.in', "s-f2.in\n")
+
+test.run()
+
+test.must_match('t1-f1.out', "s-f1.in\n")
+test.must_match('t1-f2.wout', "s-f2.in\n")
+test.must_match('t2-f1.out', "s-f1.in\n")
+test.must_match('t2-f2.wout', "s-f2.in\n")
+
+test.pass_test()