Handle moved SCons source tree. (Kevin Quick)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 11 Nov 2004 11:42:53 +0000 (11:42 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 11 Nov 2004 11:42:53 +0000 (11:42 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1155 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Script/SConscript.py
test/SConscript.py
test/errors.py
test/exceptions.py

index 3b63109c6fe02a814d1a0127906a9cf81ac8ccd6..4c23967e8418ea0582fd23b6a4a9d2338c77ed08 100644 (file)
@@ -270,6 +270,9 @@ RELEASE 0.97 - XXX
 
   - 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.
index 6adb399a6ac1d30cc8e0a1c3576a2fb15a78924f..4bc8bd31b5dc6295069ed1b8704ef43f0d8a552f 100644 (file)
@@ -169,6 +169,9 @@ def Return(*vars):
     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()
@@ -236,12 +239,14 @@ def _SConscript(fs, *files, **kw):
                     # 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,
@@ -268,51 +273,38 @@ def _SConscript(fs, *files, **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."""
     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
index 4aaed3ba41d254d34f24ec788fdabbe7af4d20b5..08e41a6fc8909a0be5749615689dad30e085d1ac 100644 (file)
@@ -43,26 +43,26 @@ SConscript('SConscript')
 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)
@@ -75,7 +75,7 @@ import UserList
 x7 = "SConstruct x7"
 x8 = "SConstruct x8"
 x9 = SConscript('SConscript6', UserList.UserList(["x7", "x8"]))
-assert x9 == "SConscript6 x9"
+assert x9 == "SConscript6 x9", x9
 
 SConscript('SConscript7')
 """)
@@ -91,8 +91,8 @@ print "SConscript " + os.getcwd()
 
 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"
@@ -102,8 +102,8 @@ Return("x3 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")
@@ -112,15 +112,15 @@ 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")
 """)
@@ -128,8 +128,8 @@ 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")
@@ -139,8 +139,8 @@ Return("x5")
 
 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")
@@ -165,8 +165,8 @@ A = Action("A")
 
 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")
 """)
index 1652f88c597c8d32ffb1e61a4f2b5295f73d0967..571bffc98f01a929e3be7df993e87bb10948d204 100644 (file)
@@ -56,15 +56,18 @@ env.foo('foo.out', 'foo.in')
 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)
index 7b45a4ba752f294a45ccaf31e35d439f016ab24a..584d4f19415121b6d7c1dcc90cd6f5e9c0092257 100644 (file)
@@ -46,11 +46,12 @@ test.write('foo.in', "foo.in\n")
 
 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
 """