blob: 1a85a917794bddc91bb92f8bcbb1f4c387390880 [file] [log] [blame]
# 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())