summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-03-03 19:39:25 (GMT)
committerSteven Knight <knight@baldmt.com>2003-03-03 19:39:25 (GMT)
commit57847f39e8598008ce3acddbcfb5a117e6981b3d (patch)
treeb6fc19421b03826488e03d795c4c258002f4ede7
parent1eac1ba89777cce14d1ed50322ee163e8b38fd1e (diff)
downloadSCons-57847f39e8598008ce3acddbcfb5a117e6981b3d.zip
SCons-57847f39e8598008ce3acddbcfb5a117e6981b3d.tar.gz
SCons-57847f39e8598008ce3acddbcfb5a117e6981b3d.tar.bz2
Add an Exit() function for explicit termination of SCons.
-rw-r--r--doc/man/scons.112
-rw-r--r--src/CHANGES.txt2
-rw-r--r--src/engine/SCons/Errors.py6
-rw-r--r--src/engine/SCons/ErrorsTests.py7
-rw-r--r--src/engine/SCons/Script/SConscript.py4
-rw-r--r--src/engine/SCons/Script/__init__.py12
-rw-r--r--src/engine/SCons/Taskmaster.py8
-rw-r--r--src/engine/SCons/TaskmasterTests.py13
-rw-r--r--test/Exit.py139
9 files changed, 197 insertions, 6 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index f9a6ea9..51e3013 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -3078,6 +3078,18 @@ EnsureSConsVersion(0,9)
.EE
.TP
+.RI Exit([ value ])
+This tells
+.B scons
+to exit immediately
+with the specified
+.IR value .
+A default exit value of
+.B 0
+(zero)
+is used if no value is specified.
+
+.TP
.RI Export( vars )
This tells
.B scons
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 2824e8f..3db1264 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -62,6 +62,8 @@ RELEASE 0.12 - XXX
- Raise an internal error if we attempt to push a file to CacheDir()
with a build signature of None.
+ - Add an explicit Exit() function for terminating early.
+
From Lachlan O'Dea:
- Add SharedObject() support to the masm tool.
diff --git a/src/engine/SCons/Errors.py b/src/engine/SCons/Errors.py
index 0028d95..9743674 100644
--- a/src/engine/SCons/Errors.py
+++ b/src/engine/SCons/Errors.py
@@ -49,3 +49,9 @@ class UserError(Exception):
class StopError(Exception):
def __init__(self, args=None):
self.args = args
+
+class ExplicitExit(Exception):
+ def __init__(self, node=None, status=None, *args):
+ self.node = node
+ self.status = status
+ self.args = args
diff --git a/src/engine/SCons/ErrorsTests.py b/src/engine/SCons/ErrorsTests.py
index 9d5010d..b128026 100644
--- a/src/engine/SCons/ErrorsTests.py
+++ b/src/engine/SCons/ErrorsTests.py
@@ -51,6 +51,13 @@ class ErrorsTestCase(unittest.TestCase):
except SCons.Errors.UserError, e:
assert e.args == "test user error"
+ def test_ExplicitExit(self):
+ """Test the ExplicitExit exception."""
+ try:
+ raise SCons.Errors.ExplicitExit, "node"
+ except SCons.Errors.ExplicitExit, e:
+ assert e.node == "node"
+
if __name__ == "__main__":
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index dfcfe5b..d9da651 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -418,6 +418,9 @@ def AddPostAction(files, action):
for n in nodes:
n.add_post_action(SCons.Action.Action(action))
+def Exit(value=0):
+ sys.exit(value)
+
def BuildDefaultGlobals():
"""
Create a dictionary containing all the default globals for
@@ -439,6 +442,7 @@ def BuildDefaultGlobals():
globals['EnsurePythonVersion'] = EnsurePythonVersion
globals['EnsureSConsVersion'] = EnsureSConsVersion
globals['Environment'] = SCons.Environment.Environment
+ globals['Exit'] = Exit
globals['Export'] = Export
globals['File'] = SCons.Node.FS.default_fs.File
globals['FindFile'] = FindFile
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index b14e50d..ca0d50e 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -86,16 +86,16 @@ class BuildTask(SCons.Taskmaster.Task):
command_time = command_time+finish_time-start_time
print "Command execution time: %f seconds"%(finish_time-start_time)
- def do_failed(self):
+ def do_failed(self, status=2):
global exit_status
if ignore_errors:
SCons.Taskmaster.Task.executed(self)
elif keep_going_on_error:
SCons.Taskmaster.Task.fail_continue(self)
- exit_status = 2
+ exit_status = status
else:
SCons.Taskmaster.Task.fail_stop(self)
- exit_status = 2
+ exit_status = status
def executed(self):
t = self.targets[0]
@@ -129,6 +129,7 @@ class BuildTask(SCons.Taskmaster.Task):
def failed(self):
e = sys.exc_value
+ status = 2
if sys.exc_type == SCons.Errors.BuildError:
sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
if e.errstr == 'Exception':
@@ -142,12 +143,15 @@ class BuildTask(SCons.Taskmaster.Task):
if not keep_going_on_error:
s = s + ' Stop.'
sys.stderr.write("scons: *** %s\n" % s)
+ elif sys.exc_type == SCons.Errors.ExplicitExit:
+ status = e.status
+ sys.stderr.write("scons: *** [%s] Explicit exit, status %s\n" % (e.node, e.status))
else:
if e is None:
e = sys.exc_type
sys.stderr.write("scons: *** %s\n" % e)
- self.do_failed()
+ self.do_failed(status)
class CleanTask(SCons.Taskmaster.Task):
"""An SCons clean task."""
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
index 68e251c..2ab076b 100644
--- a/src/engine/SCons/Taskmaster.py
+++ b/src/engine/SCons/Taskmaster.py
@@ -83,6 +83,8 @@ class Task:
self.targets[0].build()
except KeyboardInterrupt:
raise
+ except SystemExit:
+ raise SCons.Errors.ExplicitExit(self.targets[0], sys.exc_value.code)
except SCons.Errors.UserError:
raise
except SCons.Errors.BuildError:
@@ -215,6 +217,12 @@ class Taskmaster:
try:
children = node.children()
+ except SystemExit:
+ e = SCons.Errors.ExplicitExit(node, sys.exc_value.code)
+ self.exception_set(SCons.Errors.ExplicitExit, e)
+ self.candidates.pop()
+ self.ready = node
+ break
except:
# We had a problem just trying to figure out the
# children (like a child couldn't be linked in to a
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
index 2f9383a..ea9b8b4 100644
--- a/src/engine/SCons/TaskmasterTests.py
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -392,14 +392,23 @@ class TaskmasterTestCase(unittest.TestCase):
def test_children_errors(self):
"""Test errors when fetching the children of a node.
"""
- class MyNode(Node):
+ class StopNode(Node):
def children(self):
raise SCons.Errors.StopError, "stop!"
- n1 = MyNode("n1")
+ class ExitNode(Node):
+ def children(self):
+ sys.exit(77)
+ n1 = StopNode("n1")
tm = SCons.Taskmaster.Taskmaster([n1])
t = tm.next_task()
assert tm.exc_type == SCons.Errors.StopError, "Did not record StopError on node"
assert str(tm.exc_value) == "stop!", "Unexpected exc_value `%s'" % tm.exc_value
+ n2 = ExitNode("n2")
+ tm = SCons.Taskmaster.Taskmaster([n2])
+ t = tm.next_task()
+ assert tm.exc_type == SCons.Errors.ExplicitExit, "Did not record ExplicitExit on node"
+ assert tm.exc_value.node == n2, tm.exc_value.node
+ assert tm.exc_value.status == 77, tm.exc_value.status
def test_cycle_detection(self):
"""Test detecting dependency cycles
diff --git a/test/Exit.py b/test/Exit.py
new file mode 100644
index 0000000..60aa6ea
--- /dev/null
+++ b/test/Exit.py
@@ -0,0 +1,139 @@
+#!/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 explicit Exit() function.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('subdir')
+
+test.write('SConstruct', """\
+print "SConstruct"
+Exit()
+""")
+
+test.run(stdout = """\
+scons: Reading SConscript files ...
+SConstruct
+""")
+
+test.write('SConstruct', """\
+print "SConstruct"
+Exit(7)
+""")
+
+test.run(status = 7, stdout = """\
+scons: Reading SConscript files ...
+SConstruct
+""")
+
+test.write('SConstruct', """\
+print "SConstruct"
+SConscript('subdir/SConscript')
+""")
+
+test.write(['subdir', 'SConscript'], """\
+print "subdir/SConscript"
+Exit()
+""")
+
+test.run(stdout = """\
+scons: Reading SConscript files ...
+SConstruct
+subdir/SConscript
+""")
+
+test.write(['subdir', 'SConscript'], """\
+print "subdir/SConscript"
+Exit(17)
+""")
+
+test.run(status = 17, stdout = """\
+scons: Reading SConscript files ...
+SConstruct
+subdir/SConscript
+""")
+
+test.write('SConstruct', """\
+SConscript('subdir/SConscript')
+""")
+
+test.write(['subdir', 'SConscript'], """\
+def exit_builder(env, source, target):
+ target = str(target[0])
+ source = map(str, source)
+ f = open(target, "wb")
+ for src in source:
+ f.write(open(src, "rb").read())
+ f.close()
+ Exit(27)
+env = Environment(BUILDERS = {'my_exit' : Builder(action=exit_builder)})
+env.my_exit('foo.out', 'foo.in')
+""")
+
+test.write(['subdir', 'foo.in'], "subdir/foo.in\n")
+
+test.run(status = 27,
+ stdout = test.wrap_stdout("""\
+exit_builder("subdir/foo.out", "subdir/foo.in")
+"""),
+ stderr = "scons: *** [subdir/foo.out] Explicit exit, status 27\n")
+
+test.fail_test(test.read(['subdir', 'foo.out']) != "subdir/foo.in\n")
+
+test.write('SConstruct', """\
+def exit_scanner(node, env, target):
+ Exit(37)
+
+exitscan = Scanner(function = exit_scanner, skeys = ['.k'])
+
+def cat(env, source, target):
+ target = str(target[0])
+ source = map(str, source)
+
+ outf = open(target, 'wb')
+ for src in source:
+ outf.write(open(src, "rb").read())
+ outf.close()
+
+env = Environment(BUILDERS={'Cat':Builder(action=cat)})
+env.Append(SCANNERS = [exitscan])
+
+env.Cat('foo', 'foo.k')
+""")
+
+test.write('foo.k', "foo.k\n")
+
+test.run(status = 37,
+ stdout = test.wrap_stdout(""),
+ stderr = "scons: *** [foo] Explicit exit, status 37\n")
+
+#
+test.pass_test()