summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2001-10-02 03:15:32 (GMT)
committerSteven Knight <knight@baldmt.com>2001-10-02 03:15:32 (GMT)
commit321aa365f5770b70be13631f717be8ecfeaf70f2 (patch)
tree683c28579fef2fe9a942693357dd5ca9a46e2514
parent97446895b5e0d92057123a962f45856009ff6bb9 (diff)
downloadSCons-321aa365f5770b70be13631f717be8ecfeaf70f2.zip
SCons-321aa365f5770b70be13631f717be8ecfeaf70f2.tar.gz
SCons-321aa365f5770b70be13631f717be8ecfeaf70f2.tar.bz2
Handle build errors.
-rw-r--r--src/engine/SCons/Builder.py15
-rw-r--r--src/engine/SCons/BuilderTests.py35
-rw-r--r--src/engine/SCons/Errors.py6
-rw-r--r--src/engine/SCons/ErrorsTests.py8
-rw-r--r--src/engine/SCons/Node/FSTests.py5
-rw-r--r--src/engine/SCons/Node/NodeTests.py19
-rw-r--r--src/engine/SCons/Node/__init__.py8
-rw-r--r--src/script/scons.py8
-rw-r--r--test/builderrors.py84
-rw-r--r--test/multiline.py1
10 files changed, 169 insertions, 20 deletions
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index d354c57..c90bc40 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -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
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 0ea81f0..965df6e 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -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
diff --git a/src/engine/SCons/Errors.py b/src/engine/SCons/Errors.py
index ac43837..8599aee 100644
--- a/src/engine/SCons/Errors.py
+++ b/src/engine/SCons/Errors.py
@@ -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
diff --git a/src/engine/SCons/ErrorsTests.py b/src/engine/SCons/ErrorsTests.py
index d39a287..4747a43 100644
--- a/src/engine/SCons/ErrorsTests.py
+++ b/src/engine/SCons/ErrorsTests.py
@@ -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:
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 8e7e2a6..1cffd8c 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -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
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 738c7cc..26cf6bd 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -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
"""
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 4f414ec..de15712 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -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
diff --git a/src/script/scons.py b/src/script/scons.py
index fbb7341..e0e958b 100644
--- a/src/script/scons.py
+++ b/src/script/scons.py
@@ -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
index 0000000..d11ab24
--- /dev/null
+++ b/test/builderrors.py
@@ -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()
diff --git a/test/multiline.py b/test/multiline.py
index 5c51fac..7bdb586 100644
--- a/test/multiline.py
+++ b/test/multiline.py
@@ -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', """