Add an Exit() function for explicit termination of SCons.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 3 Mar 2003 19:39:25 +0000 (19:39 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 3 Mar 2003 19:39:25 +0000 (19:39 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@607 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Errors.py
src/engine/SCons/ErrorsTests.py
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
test/Exit.py [new file with mode: 0644]

index f9a6ea984a82d634d0ea48c851a2f6b29948d428..51e3013acfbc28c42c231075cb7dadebcb2c4043 100644 (file)
@@ -3077,6 +3077,18 @@ actual SCons version is not late enough.
 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 
index 2824e8f0f116f9143f3844f1b70889ead5799162..3db1264d8de16317ee041be084758d17fe109c07 100644 (file)
@@ -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.
index 0028d95901f46995e36a979c6bf0f17bad6b0d66..9743674726699747aa621ae045a41b0818afe6b1 100644 (file)
@@ -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
index 9d5010da51a40c20a0655a4c7bb240ca9075be7d..b12802658e2efd9ac3a575ba43f4d6669cf21e38 100644 (file)
@@ -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__":
index dfcfe5b7d4c4868b5ed148681e68bcc193bd47ac..d9da651f3408c706e45e625654e2eabaf5819230 100644 (file)
@@ -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
index b14e50d0e818f9114837de47cfa7f5788a190fca..ca0d50e8ce60fb3009944637059f075ffd15d885 100644 (file)
@@ -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."""
index 68e251c506ce9524dcd37670da65e4f3d0016689..2ab076bb50391c195aff692491363fd6776c3fe5 100644 (file)
@@ -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
index 2f9383a5a97f7106ad7ac4e86cc5f9935fa0e5ae..ea9b8b4480f5d644f6240afe9abdf39421287900 100644 (file)
@@ -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 (file)
index 0000000..60aa6ea
--- /dev/null
@@ -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()