diff options
| author | Steven Knight <knight@baldmt.com> | 2003-07-27 06:49:12 (GMT) |
|---|---|---|
| committer | Steven Knight <knight@baldmt.com> | 2003-07-27 06:49:12 (GMT) |
| commit | 60f78848e0e58a6002942ef73b8518023e2f8aa7 (patch) | |
| tree | 71836c065f1c744cf8f1d7f691a11bc6b75ec8ef /src/engine/SCons/Script | |
| parent | e9390044311958512a370110b6f6473c320ab522 (diff) | |
| download | SCons-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.py | 26 | ||||
| -rw-r--r-- | src/engine/SCons/Script/__init__.py | 53 |
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 |
