Print tracebacks for errors other than UserError and StopError. (Gary Oberbrunner)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 18 Sep 2004 12:41:15 +0000 (12:41 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 18 Sep 2004 12:41:15 +0000 (12:41 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1081 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Script/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
test/option--debug.py

index d4f2103e470475fc2da0e1c9cbfc2c05074977e1..c63a23a777b70ca303a7f8dbcde6391d5c726f1a 100644 (file)
@@ -561,6 +561,11 @@ Building myprog.o with action(s):
 ...
 .EE
 
+.TP
+--debug=stacktrace
+Prints an internal Python stack trace
+when encountering an otherwise unexplained error.
+
 .TP
 --debug=time
 Prints various time profiling information: the time spent
index b035acbf57f7374fc50a5b702197338b6cc7076f..1965c6191bd1a138edcb6ab6b8e1f0049a57be88 100644 (file)
@@ -73,6 +73,9 @@ RELEASE 0.97 - XXX
   - Add a PRINT_CMD_LINE_FUNC construction variable to allow people
     to filter (or log) command-line output.
 
+  - Print an internal Python stack trace in response to an otherwise
+    unexplained error when --debug=stacktrace is specified.
+
   From Kevin Quick:
 
   - Fix the Builder name returned from ListBuilders and other instances
index dfee76774d8d51bab242d1d25d3f1d7236ea1f86..160fae30f70378dcb44457e98b31252e28ce73d3 100644 (file)
@@ -141,8 +141,12 @@ class BuildTask(SCons.Taskmaster.Task):
         # is to display the various types of Errors and Exceptions
         # appropriately.
         status = 2
-        t, e = self.exc_info()[:2]
-        tb = None
+        exc_info = self.exc_info()
+        try:
+            t, e, tb = exc_info
+        except ValueError:
+            t, e = exc_info
+            tb = None
         if t is None:
             # The Taskmaster didn't record an exception for this Task;
             # see if the sys module has one.
@@ -166,7 +170,7 @@ class BuildTask(SCons.Taskmaster.Task):
                 s = s + '  Stop.'
             sys.stderr.write("scons: *** %s\n" % s)
 
-            if tb:
+            if tb and print_stacktrace:
                 sys.stderr.write("scons: internal stack trace:\n")
                 traceback.print_tb(tb, file=sys.stderr)
 
@@ -240,6 +244,7 @@ print_dtree = 0
 print_explanations = 0
 print_includes = 0
 print_objects = 0
+print_stacktrace = 0
 print_time = 0
 print_tree = 0
 memory_stats = None
@@ -400,7 +405,8 @@ def _set_globals(options):
     global repositories, keep_going_on_error, ignore_errors
     global print_count, print_dtree
     global print_explanations, print_includes
-    global print_objects, print_time, print_tree
+    global print_objects, print_stacktrace
+    global print_time, print_tree
     global memory_outf, memory_stats
 
     if options.repository:
@@ -423,6 +429,8 @@ def _set_globals(options):
                 print_objects = 1
             elif options.debug == "presub":
                 SCons.Action.print_actions_presub = 1
+            elif options.debug == "stacktrace":
+                print_stacktrace = 1
             elif options.debug == "time":
                 print_time = 1
             elif options.debug == "tree":
@@ -520,7 +528,8 @@ class OptParser(OptionParser):
 
         debug_options = ["count", "dtree", "explain",
                          "includes", "memory", "objects",
-                         "pdb", "presub", "time", "tree"]
+                         "pdb", "presub", "stacktrace",
+                         "time", "tree"]
 
         def opt_debug(option, opt, value, parser, debug_options=debug_options):
             if value in debug_options:
index dab50264ccdc96b6bef9799151cb19a27246d1eb..84eb08dcffafeab5bb8246b71646cf530d755ddb 100644 (file)
@@ -461,5 +461,10 @@ class Taskmaster:
         self.pending = []
 
     def exception_raise(self, exception):
-        exc_type, exc_value = exception[:2]
-        raise exc_type, exc_value
+        exc = exception[:]
+        try:
+            exc_type, exc_value, exc_traceback = exc
+        except ValueError:
+            exc_type, exc_value = exc
+            exc_traceback = None
+        raise exc_type, exc_value, exc_traceback
index 8865e09b9ce11ed81fa60df1e2fdadf1ca14829e..a908192821450fa80c392a93085fcf0bf5aaf637 100644 (file)
@@ -921,7 +921,25 @@ class TaskmasterTestCase(unittest.TestCase):
         else:
             assert 0, "did not catch expected exception"
 
-        t.exception_set(("exception 3", "XYZZY"))
+        try:
+            1/0
+        except:
+            tb = sys.exc_info()[2]
+        t.exception_set(("exception 3", "arg", tb))
+        try:
+            t.exception_raise()
+        except:
+            exc_type, exc_value, exc_tb = sys.exc_info()
+            assert exc_type == 'exception 3', exc_type
+            assert exc_value == "arg", exc_value
+            import traceback
+            x = traceback.extract_tb(tb)[-1]
+            y = traceback.extract_tb(exc_tb)[-1]
+            assert x == y, "x = %s, y = %s" % (x, y)
+        else:
+            assert 0, "did not catch expected exception"
+
+        t.exception_set(("exception 4", "XYZZY"))
         def fw_exc(exc):
             raise 'exception_forwarded', exc
         tm.exception_raise = fw_exc 
@@ -930,7 +948,7 @@ class TaskmasterTestCase(unittest.TestCase):
         except:
             exc_type, exc_value = sys.exc_info()[:2]
             assert exc_type == 'exception_forwarded', exc_type
-            assert exc_value[0] == "exception 3", exc_value[0]
+            assert exc_value[0] == "exception 4", exc_value[0]
             assert exc_value[1] == "XYZZY", exc_value[1]
         else:
             assert 0, "did not catch expected exception"
index 64cc424e8d0cbb5efbaa4550c8fb283a3afa41cb..e3d99605b6bca09ac4ef69a4f40116d0466e5517 100644 (file)
@@ -384,4 +384,49 @@ test.must_match('file16.out', "file16.in\n")
 test.must_match('file17.out', "file17.in\n")
 test.must_match('file18.out', "file18.in\n")
 
+############################
+# test --debug=stacktrace
+
+test.write('SConstruct', """\
+def kfile_scan(node, env, target):
+    raise "kfile_scan error"
+
+kscan = Scanner(name = 'kfile',
+                function = kfile_scan,
+                skeys = ['.k'])
+
+env = Environment()
+env.Append(SCANNERS = [kscan])
+
+env.Command('foo', 'foo.k', Copy('$TARGET', '$SOURCE'))
+""")
+
+test.write('foo.k', "foo.k\n")
+
+test.run(status = 2, stderr = "scons: *** kfile_scan error\n")
+
+test.run(arguments = "--debug=stacktrace",
+         status = 2,
+         stderr = None)
+
+stderr = test.stderr()
+
+lines = [
+    "scons: *** kfile_scan error",
+    "scons: internal stack trace:",
+    'raise "kfile_scan error"',
+]
+
+missing = []
+for line in lines:
+    if string.find(stderr, line) == -1:
+        missing.append(line)
+
+if missing:
+    print "STDERR is missing the following lines:"
+    print "\t" + string.join(lines, "\n\t")
+    print "STDERR ====="
+    print stderr
+    test.fail_test(1)
+
 test.pass_test()