Handle build errors.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 2 Oct 2001 03:15:32 +0000 (03:15 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 2 Oct 2001 03:15:32 +0000 (03:15 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@78 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Errors.py
src/engine/SCons/ErrorsTests.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/script/scons.py
test/builderrors.py [new file with mode: 0644]
test/multiline.py

index d354c5737c8150985d98b42933410e06a5c66485..c90bc40fc9f3ddbd011b27e4a40d71e9d8cabacb 100644 (file)
@@ -55,7 +55,7 @@ class Builder:
     def execute(self, **kw):
        """Execute a builder's action to create an output object.
        """
-       apply(self.action.execute, (), kw)
+       return apply(self.action.execute, (), kw)
 
 
 
@@ -99,6 +99,7 @@ class CommandAction(ActionBase):
        cmd = self.command % kw
        if print_actions:
            self.show(cmd)
+       ret = 0
        if execute_actions:
            pid = os.fork()
            if not pid:
@@ -112,7 +113,10 @@ class CommandAction(ActionBase):
                os.execvpe(args[0], args, ENV)
            else:
                # Parent process.
-               os.waitpid(pid, 0)
+               pid, stat = os.waitpid(pid, 0)
+               ret = stat >> 8
+       return ret
+
 
 
 class FunctionAction(ActionBase):
@@ -124,7 +128,7 @@ class FunctionAction(ActionBase):
        # if print_actions:
        # XXX:  WHAT SHOULD WE PRINT HERE?
        if execute_actions:
-           self.function(kw)
+           return self.function(kw)
 
 class ListAction(ActionBase):
     """Class for lists of other actions."""
@@ -133,4 +137,7 @@ class ListAction(ActionBase):
 
     def execute(self, **kw):
        for l in self.list:
-           apply(l.execute, (), kw)
+           r = apply(l.execute, (), kw)
+           if r != 0:
+               return r
+       return 0
index 0ea81f0bdbed84c095487b059866eaefc53f0315..965df6e13466b4c1b0c49892ffe9f17ac3aaa8f7 100644 (file)
@@ -77,23 +77,34 @@ class BuilderTestCase(unittest.TestCase):
        """Test execution of simple Builder objects
        
        One Builder is a string that executes an external command,
-       and one is an internal Python function.
+       one is an internal Python function, one is a list
+       containing one of each.
        """
-       cmd = "python %s %s xyzzy" % (act_py, outfile)
-       builder = SCons.Builder.Builder(action = cmd)
-       builder.execute()
+       cmd1 = "python %s %s xyzzy" % (act_py, outfile)
+       builder = SCons.Builder.Builder(action = cmd1)
+       r = builder.execute()
+       assert r == 0
        assert test.read(outfile, 'r') == "act.py: xyzzy\n"
 
-       def function(kw):
-           import os, string, sys
+       def function1(kw):
            f = open(kw['out'], 'w')
-           f.write("function\n")
+           f.write("function1\n")
            f.close()
-           return not None
-
-       builder = SCons.Builder.Builder(action = function)
-       builder.execute(out = outfile)
-       assert test.read(outfile, 'r') == "function\n"
+           return 1
+
+       builder = SCons.Builder.Builder(action = function1)
+       r = builder.execute(out = outfile)
+       assert r == 1
+       assert test.read(outfile, 'r') == "function1\n"
+
+       cmd2 = "python %s %s syzygy" % (act_py, outfile)
+       def function2(kw):
+           open(kw['out'], 'a').write("function2\n")
+           return 2
+       builder = SCons.Builder.Builder(action = [cmd2, function2])
+       r = builder.execute(out = outfile)
+       assert r == 2
+       assert test.read(outfile, 'r') == "act.py: syzygy\nfunction2\n"
 
     def test_insuffix(self):
        """Test Builder creation with a specified input suffix
index ac438375106febceffb3e95dde526282924441ae..8599aee27c5c8c32e0435b8639495d90a8246740 100644 (file)
@@ -9,6 +9,12 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
 
+class BuildError(Exception):
+    def __init__(self, node=None, stat=0, *args):
+        self.node = node
+        self.stat = stat
+        self.args = args
+
 class InternalError(Exception):
     def __init__(self, args=None):
         self.args = args
index d39a28707717c2cae46b199e31d1ef1439b8c6a8..4747a43107b9b6ce7b58a99ccf3729a1e55a064d 100644 (file)
@@ -6,6 +6,14 @@ import SCons.Errors
 
 
 class ErrorsTestCase(unittest.TestCase):
+    def test_BuildError(self):
+        """Test the BuildError exception."""
+        try:
+            raise SCons.Errors.BuildError(node = "n", stat = 7)
+        except SCons.Errors.BuildError, e:
+            assert e.node == "n"
+            assert e.stat == 7
+
     def test_InternalError(self):
        """Test the InternalError exception."""
         try:
index 8e7e2a6f84237f054e7f03edb78eb3009b1d615a..1cffd8c8d2f0c6e2d07ab33fd3152f73de5799e4 100644 (file)
@@ -6,17 +6,22 @@ import unittest
 
 import SCons.Node.FS
 
+
+
 built_it = None
 
 class Builder:
     def execute(self, **kw):
         global built_it
         built_it = 1
+        return 0
 
 class Environment:
     def Dictionary(self, *args):
        pass
 
+
+
 class FSTestCase(unittest.TestCase):
     def runTest(self):
         """Test FS (file system) Node operations
index 738c7ccd741b259bfd721596686617c547775a27..26cf6bdaaeaac17f8ddec79386f5a3d0e22af05c 100644 (file)
@@ -4,6 +4,7 @@ import os
 import sys
 import unittest
 
+import SCons.Errors
 import SCons.Node
 
 
@@ -14,6 +15,11 @@ class Builder:
     def execute(self, **kw):
        global built_it
        built_it = 1
+        return 0
+
+class FailBuilder:
+    def execute(self, **kw):
+        return 1
 
 class Environment:
     def Dictionary(self, *args):
@@ -23,6 +29,19 @@ class Environment:
 
 class NodeTestCase(unittest.TestCase):
 
+    def test_BuildException(self):
+       """Test throwing an exception on build failure.
+       """
+       node = SCons.Node.Node()
+       node.builder_set(FailBuilder())
+       node.env_set(Environment())
+       try:
+           node.build()
+       except SCons.Errors.BuildError:
+           pass
+       else:
+           raise TestFailed, "did not catch expected BuildError"
+
     def test_build(self):
        """Test building a node
        """
index 4f414ec934824d5c421c68b25af060b73a783fc9..de1571231b378278c8e9abd1064f651444aade5d 100644 (file)
@@ -8,6 +8,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
 
+from SCons.Errors import BuildError
 import string
 
 
@@ -25,8 +26,11 @@ class Node:
 
     def build(self):
        sources_str = string.join(map(lambda x: str(x), self.sources))
-       self.builder.execute(ENV = self.env.Dictionary('ENV'),
-                            target = str(self), source = sources_str)
+       stat = self.builder.execute(ENV = self.env.Dictionary('ENV'),
+                                   target = str(self), source = sources_str)
+       if stat != 0:
+           raise BuildError(node = self, stat = stat)
+       return stat
 
     def builder_set(self, builder):
        self.builder = builder
index fbb7341eeaf7e5aba73f07426d4306c77cb95de8..e0e958b7638f5afa1ef2bdf33fa3df770cee3915 100644 (file)
@@ -32,7 +32,11 @@ class Task:
         self.target = target
 
     def execute(self):
-        self.target.build()
+        try:
+            self.target.build()
+        except BuildError, e:
+            sys.stderr.write("scons: *** [%s] Error %d\n" % (e.node, e.stat))
+            raise
 
 
 
@@ -62,7 +66,7 @@ class Taskmaster:
         pass
 
     def failed(self, task):
-        pass
+        self.num_iterated = len(self.targets)
 
 
 # Global variables
diff --git a/test/builderrors.py b/test/builderrors.py
new file mode 100644 (file)
index 0000000..d11ab24
--- /dev/null
@@ -0,0 +1,84 @@
+
+#!/usr/bin/env python
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('one', 'two', 'three')
+
+test.write('build.py', r"""
+import sys
+exitval = int(sys.argv[1])
+if exitval == 0:
+    contents = open(sys.argv[3], 'r').read()
+    file = open(sys.argv[2], 'w')
+    file.write(contents)
+    file.close()
+sys.exit(exitval)
+""")
+
+test.write(['one', 'SConstruct'], """
+B0 = Builder(name = 'B0', action = "python ../build.py 0 %(target)s %(source)s")
+B1 = Builder(name = 'B1', action = "python ../build.py 1 %(target)s %(source)s")
+env = Environment(BUILDERS = [B0, B1])
+env.B1(target = 'f1.out', source = 'f1.in')
+env.B0(target = 'f2.out', source = 'f2.in')
+env.B0(target = 'f3.out', source = 'f3.in')
+""")
+
+test.write(['one', 'f1.in'], "one/f1.in\n")
+test.write(['one', 'f2.in'], "one/f2.in\n")
+test.write(['one', 'f3.in'], "one/f3.in\n")
+
+test.run(chdir = 'one', arguments = "f1.out f2.out f3.out",
+        stderr = "scons: *** [f1.out] Error 1\n")
+
+test.fail_test(os.path.exists(test.workpath('f1.out')))
+test.fail_test(os.path.exists(test.workpath('f2.out')))
+test.fail_test(os.path.exists(test.workpath('f3.out')))
+
+test.write(['two', 'SConstruct'], """
+B0 = Builder(name = 'B0', action = "python ../build.py 0 %(target)s %(source)s")
+B1 = Builder(name = 'B1', action = "python ../build.py 1 %(target)s %(source)s")
+env = Environment(BUILDERS = [B0, B1])
+env.B0(target = 'f1.out', source = 'f1.in')
+env.B1(target = 'f2.out', source = 'f2.in')
+env.B0(target = 'f3.out', source = 'f3.in')
+""")
+
+test.write(['two', 'f1.in'], "two/f1.in\n")
+test.write(['two', 'f2.in'], "two/f2.in\n")
+test.write(['two', 'f3.in'], "two/f3.in\n")
+
+test.run(chdir = 'two', arguments = "f1.out f2.out f3.out",
+        stderr = "scons: *** [f2.out] Error 1\n")
+
+test.fail_test(test.read(['two', 'f1.out']) != "two/f1.in\n")
+test.fail_test(os.path.exists(test.workpath('f2.out')))
+test.fail_test(os.path.exists(test.workpath('f3.out')))
+
+test.write(['three', 'SConstruct'], """
+B0 = Builder(name = 'B0', action = "python ../build.py 0 %(target)s %(source)s")
+B1 = Builder(name = 'B1', action = "python ../build.py 1 %(target)s %(source)s")
+env = Environment(BUILDERS = [B0, B1])
+env.B0(target = 'f1.out', source = 'f1.in')
+env.B0(target = 'f2.out', source = 'f2.in')
+env.B1(target = 'f3.out', source = 'f3.in')
+""")
+
+test.write(['three', 'f1.in'], "three/f1.in\n")
+test.write(['three', 'f2.in'], "three/f2.in\n")
+test.write(['three', 'f3.in'], "three/f3.in\n")
+
+test.run(chdir = 'three', arguments = "f1.out f2.out f3.out",
+        stderr = "scons: *** [f3.out] Error 1\n")
+
+test.fail_test(test.read(['three', 'f1.out']) != "three/f1.in\n")
+test.fail_test(test.read(['three', 'f2.out']) != "three/f2.in\n")
+test.fail_test(os.path.exists(test.workpath('f3.out')))
+
+test.pass_test()
index 5c51fac13b2e4399489bcfae5403ea23b0641485..7bdb586c0e862eec576854ef3bdebf5102c635c2 100644 (file)
@@ -13,6 +13,7 @@ contents = open(sys.argv[2], 'r').read()
 file = open(sys.argv[1], 'w')
 file.write(contents)
 file.close()
+sys.exit(0)
 """)
 
 test.write('SConstruct', """