Better error messages for bad builder creation. (Kevin Quick)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 21 Sep 2004 18:49:05 +0000 (18:49 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 21 Sep 2004 18:49:05 +0000 (18:49 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1092 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Executor.py
src/engine/SCons/ExecutorTests.py
test/BadBuilder.py [new file with mode: 0644]

index 9ba813b4c6840c4682206f7e2dafc11cb665fdb1..502ef2a220de5358d10586dafa13257d36c94e68 100644 (file)
@@ -132,6 +132,9 @@ RELEASE 0.97 - XXX
   - Fix handling when BuildDir() exists but is unwriteable.  Add
     "Stop." to those error messages for consistency.
 
+  - Catch incidents of bad builder creation (without an action) and
+    supply meaningful error messages.
+
   From Christoph Wiedemann:
 
   - Add an Environment.SetDefault() method that only sets values if
index 543ca8ec4683348011548fb622223c90173c7170..c1c7b495a6cd3b1b6608c0ec959ad23614e54d2c 100644 (file)
@@ -329,6 +329,8 @@ def _init_nodes(builder, env, overrides, tlist, slist):
         else:
             executor.add_sources(slist)
     if executor is None:
+        if not builder.action:
+            raise UserError, "Builder %s must have an action to build %s."%(builder.get_name(env or builder.env), map(str,tlist))
         executor = SCons.Executor.Executor(builder.action,
                                            env or builder.env,
                                            [builder.overrides, overrides],
index e4647f5f18e6fc10a017072d4b565e0c335f8956..9c5b3e518f908eeb7c6a8f38608ba208474a3e8a 100644 (file)
@@ -265,6 +265,15 @@ class BuilderTestCase(unittest.TestCase):
         target = builder(env, source='n21')[0]
         assert target.name == 'p-n21.s', target
 
+        builder = SCons.Builder.Builder(misspelled_action="foo",
+                                        suffix = '.s')
+        try:
+            builder(env, target = 'n22', source = 'n22')
+        except SCons.Errors.UserError, e:
+            pass
+        else:
+            raise "Did not catch expected UserError."
+
     def test_mistaken_variables(self):
         """Test keyword arguments that are often mistakes
         """
@@ -393,7 +402,7 @@ class BuilderTestCase(unittest.TestCase):
         env = Environment()
         builder = SCons.Builder.Builder(prefix = 'lib.')
         assert builder.get_prefix(env) == 'lib.'
-        builder = SCons.Builder.Builder(prefix = 'lib')
+        builder = SCons.Builder.Builder(prefix = 'lib', action='')
         assert builder.get_prefix(env) == 'lib'
         tgt = builder(env, target = 'tgt1', source = 'src1')[0]
         assert tgt.path == 'libtgt1', \
@@ -426,7 +435,8 @@ class BuilderTestCase(unittest.TestCase):
                                                   '.in'  : 'out-',
                                                   '.x'   : 'y-',
                                                   '$FOO' : 'foo-',
-                                                  '.zzz' : my_emit})
+                                                  '.zzz' : my_emit},
+                                        action = '')
         tgt = builder(my_env, source = 'f1')[0]
         assert tgt.path == 'default-f1', tgt.path
         tgt = builder(my_env, source = 'f2.c')[0]
@@ -448,7 +458,7 @@ class BuilderTestCase(unittest.TestCase):
         """
         env = Environment(XSUFFIX = '.x', YSUFFIX = '.y')
 
-        b1 = SCons.Builder.Builder(src_suffix = '.c')
+        b1 = SCons.Builder.Builder(src_suffix = '.c', action='')
         assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
 
         tgt = b1(env, target = 'tgt2', source = 'src2')[0]
@@ -483,7 +493,7 @@ class BuilderTestCase(unittest.TestCase):
         env = Environment()
         builder = SCons.Builder.Builder(suffix = '.o')
         assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
-        builder = SCons.Builder.Builder(suffix = 'o')
+        builder = SCons.Builder.Builder(suffix = 'o', action='')
         assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
         tgt = builder(env, target = 'tgt3', source = 'src3')[0]
         assert tgt.path == 'tgt3.o', \
@@ -510,7 +520,8 @@ class BuilderTestCase(unittest.TestCase):
                                                   '.in'  : '.out',
                                                   '.x'   : '.y',
                                                   '$BAR' : '.new',
-                                                  '.zzz' : my_emit})
+                                                  '.zzz' : my_emit},
+                                        action='')
         tgt = builder(my_env, source = 'f1')[0]
         assert tgt.path == 'f1.default', tgt.path
         tgt = builder(my_env, source = 'f2.c')[0]
@@ -809,7 +820,8 @@ class BuilderTestCase(unittest.TestCase):
         sscan = TestScanner()
         env = Environment()
         builder = SCons.Builder.Builder(target_scanner=tscan,
-                                        source_scanner=sscan)
+                                        source_scanner=sscan,
+                                        action='')
         tgt = builder(env, target='foo2', source='bar')[0]
         assert tgt.target_scanner == tscan, tgt.target_scanner
         assert tgt.source_scanner == sscan, tgt.source_scanner
index bb10ea0d3300a8a4b6763367a82c8212c0daaff9..b9d9897d4fe22c71d119b436a5ee5610f7496a67 100644 (file)
@@ -50,6 +50,8 @@ class Executor:
         self.overridelist = overridelist
         self.targets = targets
         self.sources = sources[:]
+        if not action:
+            raise SCons.Errors.UserError, "Executor must have an action."
 
     def get_build_env(self):
         """Fetch or create the appropriate build Environment
index c391543c7f2e704c713af31276e21f2883348b45..cfa2dcdeb37d78e042dd691c783bb87ea2129157 100644 (file)
@@ -81,6 +81,12 @@ class ExecutorTestCase(unittest.TestCase):
         assert x.targets == 't', x.targets
         source_list.append('s3')
         assert x.sources == ['s1', 's2'], x.sources
+        try:
+            x = SCons.Executor.Executor(None, 'e', ['o'], 't', source_list)
+        except SCons.Errors.UserError:
+            pass
+        else:
+            raise "Did not catch expected UserError"
 
     def test_get_build_env(self):
         """Test fetching and generating a build environment"""
diff --git a/test/BadBuilder.py b/test/BadBuilder.py
new file mode 100644 (file)
index 0000000..4667a21
--- /dev/null
@@ -0,0 +1,99 @@
+#!/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 catch Builder creation with poorly specified Actions.
+"""
+
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+sconstruct = """
+def buildop(env, source, target):
+    outf = open(str(target[0]), 'wb')
+    inpf = open(str(source[0]), 'r')
+    for line in inpf.readlines():
+        if line.find(str(target[0])) == -1:
+            outf.write(line)
+    inpf.close()
+    outf.close()
+b1 = Builder(action=buildop, src_suffix='.a', suffix='.b')
+%s
+env=Environment(tools=[], BUILDERS={'b1':b1, 'b2':b2})
+foo_b = env.b1(source='foo.a')
+env.b2(source=foo_b)
+"""
+
+test.write('foo.a', """\
+foo.c
+foo.b
+built
+""")
+
+### Gross mistake in Builder spec
+
+test.write('SConstruct', sconstruct % '\
+b2 = Builder(act__ion=buildop, src_suffix=".b", suffix=".c")')
+
+test.run(arguments='.',
+         stderr="""\
+
+scons: *** Builder b2 must have an action to build ['foo.c'].
+File "SConstruct", line 14, in ?
+""",
+status = 2)
+
+### Subtle mistake in Builder spec
+
+test.write('SConstruct', sconstruct % '\
+b2 = Builder(actoin=buildop, src_suffix=".b", suffix=".c")')
+
+test.run(arguments='test2',
+         stderr="""\
+
+scons: *** Builder b2 must have an action to build ['foo.c'].
+File "SConstruct", line 14, in ?
+""",
+status = 2)
+
+### Missing action in Builder spec
+
+test.write('SConstruct', sconstruct % '\
+b2 = Builder(src_suffix=".b", suffix=".c")')
+
+test.run(arguments='test2',
+         stderr="""\
+
+scons: *** Builder b2 must have an action to build ['foo.c'].
+File "SConstruct", line 14, in ?
+""",
+status = 2)
+
+
+test.pass_test()