summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Script
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-07-27 06:49:12 (GMT)
committerSteven Knight <knight@baldmt.com>2003-07-27 06:49:12 (GMT)
commit60f78848e0e58a6002942ef73b8518023e2f8aa7 (patch)
tree71836c065f1c744cf8f1d7f691a11bc6b75ec8ef /src/engine/SCons/Script
parente9390044311958512a370110b6f6473c320ab522 (diff)
downloadSCons-60f78848e0e58a6002942ef73b8518023e2f8aa7.zip
SCons-60f78848e0e58a6002942ef73b8518023e2f8aa7.tar.gz
SCons-60f78848e0e58a6002942ef73b8518023e2f8aa7.tar.bz2
Record and print an internal stack trace when Taskmaster detects errors for easier debugging. (Gary Oberbrunner) Better debugging of Python exceptions (like TypeErrors) in SConscript files.
Diffstat (limited to 'src/engine/SCons/Script')
-rw-r--r--src/engine/SCons/Script/SConscript.py26
-rw-r--r--src/engine/SCons/Script/__init__.py53
2 files changed, 61 insertions, 18 deletions
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index 313a3f2..2984160 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -309,6 +309,30 @@ def SConscript(*ls, **kw):
else:
return tuple(results)
+def is_our_exec_statement(line):
+ return not line is None and line[:12] == "exec _file_ "
+
+def SConscript_exception(file=sys.stderr):
+ """Print an exception stack trace just for the SConscript file(s).
+ This will show users who have Python errors where the problem is,
+ without cluttering the output with all of the internal calls leading
+ up to where we exec the SConscript."""
+ stack = traceback.extract_tb(sys.exc_traceback)
+ last_text = ""
+ i = 0
+ for frame in stack:
+ if is_our_exec_statement(last_text):
+ break
+ i = i + 1
+ last_text = frame[3]
+ type = str(sys.exc_type)
+ if type[:11] == "exceptions.":
+ type = type[11:]
+ file.write('%s: %s:\n' % (type, sys.exc_value))
+ for fname, line, func, text in stack[i:]:
+ file.write(' File "%s", line %d:\n' % (fname, line))
+ file.write(' %s\n' % text)
+
def annotate(node):
"""Annotate a node with the stack frame describing the
SConscript file and line number that created it."""
@@ -319,7 +343,7 @@ def annotate(node):
# magic "exec _file_ " string, then this frame describes the
# SConscript file and line number that caused this node to be
# created. Record the tuple and carry on.
- if not last_text is None and last_text[:12] == "exec _file_ ":
+ if is_our_exec_statement(last_text):
node.creator = frame
return
last_text = frame[3]
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index c171a91..a413054 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -133,28 +133,41 @@ class BuildTask(SCons.Taskmaster.Task):
print tree
def failed(self):
- e = sys.exc_value
+ # Handle the failure of a build task. The primary purpose here
+ # is to display the various types of Errors and Exceptions
+ # appropriately.
status = 2
- if sys.exc_type == SCons.Errors.BuildError:
+ e = sys.exc_value
+ t = sys.exc_type
+ tb = None
+ if t is SCons.Errors.TaskmasterException:
+ # The Taskmaster received an Error or Exception while trying
+ # to process or build the Nodes and dependencies, which it
+ # wrapped up for us in the object recorded as the value of
+ # the Exception, so process the wrapped info instead of the
+ # TaskmasterException itself.
+ t = e.type
+ tb = e.traceback
+ e = e.value
+
+ if t == SCons.Errors.BuildError:
sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
if e.errstr == 'Exception':
traceback.print_exception(e.args[0], e.args[1], e.args[2])
- elif sys.exc_type == SCons.Errors.UserError:
- # We aren't being called out of a user frame, so
- # don't try to walk the stack, just print the error.
- sys.stderr.write("\nscons: *** %s\n" % e)
- elif sys.exc_type == SCons.Errors.StopError:
- s = str(e)
- if not keep_going_on_error:
- s = s + ' Stop.'
- sys.stderr.write("scons: *** %s\n" % s)
- elif sys.exc_type == SCons.Errors.ExplicitExit:
+ elif t == 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)
+ e = t
+ s = str(e)
+ if t == SCons.Errors.StopError and not keep_going_on_error:
+ s = s + ' Stop.'
+ sys.stderr.write("scons: *** %s\n" % s)
+
+ if tb:
+ sys.stderr.write("scons: internal stack trace:\n")
+ traceback.print_tb(tb, file=sys.stderr)
self.do_failed(status)
@@ -290,11 +303,11 @@ def _scons_internal_warning(e):
sys.stderr.write("\nscons: warning: %s\n" % e)
sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
-def _scons_other_errors():
+def _scons_internal_error():
"""Handle all errors but user errors. Print out a message telling
the user what to do in this case and print a normal trace.
"""
- print 'other errors'
+ print 'internal error'
traceback.print_exc()
sys.exit(2)
@@ -984,12 +997,18 @@ def main():
sys.exit(2)
except SyntaxError, e:
_scons_syntax_error(e)
+ except SCons.Errors.InternalError:
+ _scons_internal_error()
except SCons.Errors.UserError, e:
_scons_user_error(e)
except SCons.Errors.ConfigureDryRunError, e:
_scons_configure_dryrun_error(e)
except:
- _scons_other_errors()
+ # An exception here is likely a builtin Python exception Python
+ # code in an SConscript file. Show them precisely what the
+ # problem was and where it happened.
+ SCons.Script.SConscript.SConscript_exception()
+ sys.exit(2)
if print_time:
total_time = time.time()-start_time