| # Status: being written afresh by Vladimir Prus |
| |
| # Copyright 2007 Vladimir Prus |
| # Distributed under the Boost Software License, Version 1.0. |
| # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) |
| |
| # This file is supposed to implement error reporting for Boost.Build. |
| # Experience with jam version has shown that printing full backtrace |
| # on each error is buffling. Further, for errors printed after parsing -- |
| # during target building, the stacktrace does not even mention what |
| # target is being built. |
| |
| # This module implements explicit contexts -- where other code can |
| # communicate which projects/targets are being built, and error |
| # messages will show those contexts. For programming errors, |
| # Python assertions are to be used. |
| |
| import bjam |
| import traceback |
| import sys |
| |
| def format(message, prefix=""): |
| parts = message.split("\n") |
| return "\n".join(prefix+p for p in parts) |
| |
| |
| class Context: |
| |
| def __init__(self, message, nested=None): |
| self.message_ = message |
| self.nested_ = nested |
| |
| def report(self, indent=""): |
| print indent + " -", self.message_ |
| if self.nested_: |
| print indent + " declared at:" |
| for n in self.nested_: |
| n.report(indent + " ") |
| |
| class JamfileContext: |
| |
| def __init__(self): |
| raw = bjam.backtrace() |
| self.raw_ = raw |
| |
| def report(self, indent=""): |
| for r in self.raw_: |
| print indent + " - %s:%s" % (r[0], r[1]) |
| |
| class ExceptionWithUserContext(Exception): |
| |
| def __init__(self, message, context, |
| original_exception=None, original_tb=None, stack=None): |
| Exception.__init__(self, message) |
| self.context_ = context |
| self.original_exception_ = original_exception |
| self.original_tb_ = original_tb |
| self.stack_ = stack |
| |
| def report(self): |
| print "error:", self.message |
| if self.original_exception_: |
| print format(self.original_exception_.message, " ") |
| print |
| print " error context (most recent first):" |
| for c in self.context_[::-1]: |
| c.report() |
| print |
| if "--stacktrace" in bjam.variable("ARGV"): |
| if self.original_tb_: |
| traceback.print_tb(self.original_tb_) |
| elif self.stack_: |
| for l in traceback.format_list(self.stack_): |
| print l, |
| else: |
| print " use the '--stacktrace' option to get Python stacktrace" |
| print |
| |
| def user_error_checkpoint(callable): |
| def wrapper(self, *args): |
| errors = self.manager().errors() |
| try: |
| return callable(self, *args) |
| except ExceptionWithUserContext, e: |
| raise |
| except Exception, e: |
| errors.handle_stray_exception(e) |
| finally: |
| errors.pop_user_context() |
| |
| return wrapper |
| |
| class Errors: |
| |
| def __init__(self): |
| self.contexts_ = [] |
| |
| def push_user_context(self, message, nested=None): |
| self.contexts_.append(Context(message, nested)) |
| |
| def pop_user_context(self): |
| del self.contexts_[-1] |
| |
| def push_jamfile_context(self): |
| self.contexts_.append(JamfileContext()) |
| |
| def pop_jamfile_context(self): |
| del self.contexts_[-1] |
| |
| def capture_user_context(self): |
| return self.contexts_[:] |
| |
| def handle_stray_exception(self, e): |
| raise ExceptionWithUserContext("unexpected exception", self.contexts_[:], |
| e, sys.exc_info()[2]) |
| def __call__(self, message): |
| raise ExceptionWithUserContext(message, self.contexts_[:], |
| stack=traceback.extract_stack()) |
| |
| |
| |
| |