- Fix hard-coding of JDK path names in various Java tests.
+ - Handle Python stack traces consistently (stop at the SConscript stack
+ frame, by default) even if the Python source code isn't available.
+
From Levi Stephen:
- Allow $JARCHDIR to be expanded to other construction variables.
else:
stack[-1].retval = tuple(retval)
+
+stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :)
+
def _SConscript(fs, *files, **kw):
top = fs.Top
sd = fs.SConstruct_dir.rdir()
# directory can be easily imported.
sys.path = [ f.dir.get_abspath() ] + sys.path
- # This is the magic line that actually reads up and
- # executes the stuff in the SConscript file. We
- # look for the "exec _file_ " from the beginning
- # of this line to find the right stack frame (the
- # next one) describing the SConscript file and line
- # number that creates a node.
+ # This is the magic line that actually reads up
+ # and executes the stuff in the SConscript file.
+ # The locals for this frame contain the special
+ # bottom-of-the-stack marker so that any
+ # exceptions that occur when processing this
+ # SConscript can base the printed frames at this
+ # level and not show SCons internals as well.
+ stack[-1].globals.update({stack_bottom:1})
exec _file_ in stack[-1].globals
else:
SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning,
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."""
exc_type, exc_value, exc_tb = sys.exc_info()
- stack = traceback.extract_tb(exc_tb)
- last_text = ""
- found = 0
- i = 0
- for frame in stack:
- if is_our_exec_statement(last_text):
- found = 1
- break
- i = i + 1
- last_text = frame[3]
- if not found:
+ tb = exc_tb
+ while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
+ tb = tb.tb_next
+ if not tb:
# We did not find our exec statement, so this was actually a bug
# in SCons itself. Show the whole stack.
- i = 0
+ tb = exc_tb
+ stack = traceback.extract_tb(tb)
type = str(exc_type)
if type[:11] == "exceptions.":
type = type[11:]
file.write('%s: %s:\n' % (type, exc_value))
- for fname, line, func, text in stack[i:]:
+ for fname, line, func, text in stack:
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."""
- stack = traceback.extract_stack()
- last_text = ""
- for frame in stack:
- # If the script text of the previous frame begins with the
- # 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 is_our_exec_statement(last_text):
- node.creator = frame
- return
- last_text = frame[3]
+ tb = exc_tb = sys.exc_info()[2]
+ while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
+ tb = tb.tb_next
+ if not tb:
+ # We did not find any exec of an SConscript file: what?!
+ raise InternalError, "could not find SConscript stack frame"
+ node.creator = traceback.extract_stack(tb)[0]
# The following line would cause each Node to be annotated using the
# above function. Unfortunately, this is a *huge* performance hit, so
x1 = "SConstruct x1"
x2 = "SConstruct x2"
x3,x4 = SConscript('SConscript1', "x1 x2")
-assert x3 == "SConscript1 x3"
-assert x4 == "SConscript1 x4"
+assert x3 == "SConscript1 x3", x3
+assert x4 == "SConscript1 x4", x4
(x3,x4) = SConscript('SConscript2', ["x1","x2"])
-assert x3 == "SConscript2 x3"
-assert x4 == "SConscript2 x4"
+assert x3 == "SConscript2 x3", x3
+assert x4 == "SConscript2 x4", x4
Export("x1 x2")
SConscript('SConscript3')
Import("x1 x2")
-assert x1 == "SConscript3 x1"
-assert x2 == "SConscript3 x2"
+assert x1 == "SConscript3 x1", x1
+assert x2 == "SConscript3 x2", x2
x1 = "SConstruct x1"
x2 = "SConstruct x2"
Export("x1","x2")
SConscript('SConscript4')
Import("x1"," x2")
-assert x1 == "SConscript4 x1"
-assert x2 == "SConscript4 x2"
+assert x1 == "SConscript4 x1", x1
+assert x2 == "SConscript4 x2", x2
subdir = Dir('subdir')
script = File('SConscript', subdir)
x7 = "SConstruct x7"
x8 = "SConstruct x8"
x9 = SConscript('SConscript6', UserList.UserList(["x7", "x8"]))
-assert x9 == "SConscript6 x9"
+assert x9 == "SConscript6 x9", x9
SConscript('SConscript7')
""")
test.write('SConscript1', """
Import("x1 x2")
-assert x1 == "SConstruct x1"
-assert x2 == "SConstruct x2"
+assert x1 == "SConstruct x1", x1
+assert x2 == "SConstruct x2", x2
x3 = "SConscript1 x3"
x4 = "SConscript1 x4"
test.write('SConscript2', """\
Import("x1","x2")
-assert x1 == "SConstruct x1"
-assert x2 == "SConstruct x2"
+assert x1 == "SConstruct x1", x1
+assert x2 == "SConstruct x2", x2
x3 = "SConscript2 x3"
x4 = "SConscript2 x4"
Return("x3","x4")
test.write('SConscript3', """\
Import("x1 x2")
-assert x1 == "SConstruct x1"
-assert x2 == "SConstruct x2"
+assert x1 == "SConstruct x1", x1
+assert x2 == "SConstruct x2", x2
x1 = "SConscript3 x1"
x2 = "SConscript3 x2"
x5 = SConscript('SConscript31', "x1")
Import("x6")
-assert x5 == "SConscript31 x5"
-assert x6 == "SConscript31 x6"
+assert x5 == "SConscript31 x5", x5
+assert x6 == "SConscript31 x6", x6
Export("x1 x2")
""")
test.write('SConscript31', """\
Import("x1 x2")
-assert x1 == "SConscript3 x1"
-assert x2 == "SConstruct x2"
+assert x1 == "SConscript3 x1", x1
+assert x2 == "SConstruct x2", x2
x5 = "SConscript31 x5"
x6 = "SConscript31 x6"
Export("x6")
test.write('SConscript4', """\
Import("x1", "x2")
-assert x1 == "SConstruct x1"
-assert x2 == "SConstruct x2"
+assert x1 == "SConstruct x1", x1
+assert x2 == "SConstruct x2", x2
x1 = "SConscript4 x1"
x2 = "SConscript4 x2"
Export("x1", "x2")
test.write('SConscript6', """\
Import("x7 x8")
-assert x7 == "SConstruct x7"
-assert x8 == "SConstruct x8"
+assert x7 == "SConstruct x7", x7
+assert x8 == "SConstruct x8", x8
x9 = "SConscript6 x9"
Return("x9")
""")
env.exit('exit.out', 'exit.in')
""")
+# print_exception doesn't always show a source line if the source file
+# no longer exists or that line in the source file no longer exists,
+# so make sure the proper variations are supported in the following
+# regexp.
stderr = """scons: \*\*\* \[exit.out\] Exception
Traceback \((most recent call|innermost) last\):
- File ".+", line \d+, in \S+
+( File ".+", line \d+, in \S+
[^\n]+
- File ".+", line \d+, in \S+
+)*( File ".+", line \d+, in \S+
+)*( File ".+", line \d+, in \S+
[^\n]+
- File ".+", line \d+, in \S+
- [^\n]+
-\S.+
+)*\S.+
"""
test.run(arguments='foo.out exit.out', stderr=stderr, status=2)
expected_stderr = """scons: \*\*\* \[foo.out\] Exception
Traceback \((most recent call|innermost) last\):
- File ".+", line \d+, in \S+
+( File ".+", line \d+, in \S+
[^\n]+
- File ".+", line \d+, in \S+
+)*( File ".+", line \d+, in \S+
+)*( File ".+", line \d+, in \S+
[^\n]+
- File "SConstruct", line 3, in func
+)* File "SConstruct", line 3, in func
raise "func exception"
func exception
"""