Print --debug={tree,dtree,includes} even if the build has an error.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 3 Dec 2004 16:06:04 +0000 (16:06 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 3 Dec 2004 16:06:04 +0000 (16:06 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1181 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Script/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
test/option--debug.py

index ebfc13c288ed7320065e3aa27014d1ce40996886..021d1d4e33e00dc4b1c9e2f10896287903b2735d 100644 (file)
@@ -147,6 +147,9 @@ RELEASE 0.97 - XXX
   - Add specific exceptions to try:-except: blocks without any listed,
     so that they won't catch and mask keyboard interrupts.
 
+  - Make --debug={tree,dtree,stree} print something even when there's
+    a build failure.
+
   From Wayne Lee:
 
   - Avoid "maximum recursion limit" errors when removing $(-$) pairs
index 7d139a1fceaf446f65e91d1b93891ece2f4324de..71c2f039f89376fbe1500aa092b432431de5184a 100644 (file)
@@ -121,25 +121,6 @@ class BuildTask(SCons.Taskmaster.Task):
         else:
             SCons.Taskmaster.Task.executed(self)
 
-        # print the tree here instead of in execute() because
-        # this method is serialized, but execute isn't:
-        if print_tree and self.top:
-            print
-            SCons.Util.print_tree(self.targets[0], get_all_children)
-        if print_stree and self.top:
-            print
-            SCons.Util.print_tree(self.targets[0], get_all_children,
-                                  showtags=2)
-        if print_dtree and self.top:
-            print
-            SCons.Util.print_tree(self.targets[0], get_derived_children)
-        if print_includes and self.top:
-            t = self.targets[0]
-            tree = t.render_include_tree()
-            if tree:
-                print
-                print tree
-
     def failed(self):
         # Handle the failure of a build task.  The primary purpose here
         # is to display the various types of Errors and Exceptions
@@ -182,6 +163,25 @@ class BuildTask(SCons.Taskmaster.Task):
 
         self.exc_clear()
 
+    def postprocess(self):
+        if self.top:
+            t = self.targets[0]
+            if print_tree:
+                print
+                SCons.Util.print_tree(t, get_all_children)
+            if print_stree:
+                print
+                SCons.Util.print_tree(t, get_all_children, showtags=2)
+            if print_dtree:
+                print
+                SCons.Util.print_tree(t, get_derived_children)
+            if print_includes:
+                tree = t.render_include_tree()
+                if tree:
+                    print
+                    print tree
+        SCons.Taskmaster.Task.postprocess(self)
+
     def make_ready(self):
         """Make a task ready for execution"""
         SCons.Taskmaster.Task.make_ready(self)
index 6bd5e2132ecf100747cdde721364e2746e27b501..b528f44e92e2ce15b847d45fc15700ebf5272875 100644 (file)
@@ -152,8 +152,16 @@ class Task:
         for t in self.targets:
             t.set_state(SCons.Node.failed)
         self.tm.failed(self.node)
+        next_top = self.tm.next_top_level_candidate()
         self.tm.stop()
 
+        if next_top:
+            # We're stopping because of a build failure, but give the
+            # calling Task class a chance to postprocess() the top-level
+            # target under which the build failure occurred.
+            self.targets = [next_top]
+            self.top = 1
+
     def fail_continue(self):
         """Explicit continue-the-build failure.
 
@@ -426,6 +434,14 @@ class Taskmaster:
 
         return not self.ready and (self.pending or self.executing)
 
+    def next_top_level_candidate(self):
+        candidates = self.candidates[:]
+        candidates.reverse()
+        for c in candidates:
+            if c in self.targets:
+                return c
+        return None
+
     def stop(self):
         """Stop the current build completely."""
         self.candidates = []
index cc4ba0cd91143b5be85078a6f674ecc037be878c..a3e42192b652026266fd855eda2edf1eabd277f1 100644 (file)
@@ -625,6 +625,20 @@ class TaskmasterTestCase(unittest.TestCase):
         tm = MyTM()
         assert tm.is_blocked()
 
+    def test_next_top_level_candidate(self):
+        """Test the next_top_level_candidate() method
+        """
+        n1 = Node("n1")
+        n2 = Node("n2", [n1])
+        n3 = Node("n3", [n2])
+
+        tm = SCons.Taskmaster.Taskmaster([n3])
+        t = tm.next_task()
+        assert tm.executing == [n1], tm.executing
+        t.fail_stop()
+        assert t.targets == [n3], t.targets
+        assert t.top == 1, t.top
+
     def test_stop(self):
         """Test the stop() method
 
index 47bc058d9c01c2138b04b2ddc3cc7290d9173ee8..f0114e554c04519fe640246b7f7d47cb43886dd4 100644 (file)
@@ -126,24 +126,61 @@ test.fail_test(string.find(test.stdout(), stree2) == -1)
 
 
 
-tree = """
+dtree = """
 +-foo.xxx
   +-foo.ooo
   +-bar.ooo
 """
 
 test.run(arguments = "--debug=dtree foo.xxx")
-test.fail_test(string.find(test.stdout(), tree) == -1)
+test.fail_test(string.find(test.stdout(), dtree) == -1)
 
-tree = """
+includes = """
 +-foo.c
   +-foo.h
     +-bar.h
 """
 test.run(arguments = "--debug=includes foo.ooo")
+test.fail_test(string.find(test.stdout(), includes) == -1)
+
+# Make sure we print the debug stuff even if there's a build failure.
+test.write('bar.h', """
+#ifndef BAR_H
+#define BAR_H
+#include "foo.h"
+#endif
+THIS SHOULD CAUSE A BUILD FAILURE
+""")
+
+test.run(arguments = "--debug=tree foo.xxx",
+         status = 2,
+         stderr = None)
 test.fail_test(string.find(test.stdout(), tree) == -1)
 
-# these shouldn't print out anything in particular, but
+test.run(arguments = "--debug=dtree foo.xxx",
+         status = 2,
+         stderr = None)
+test.fail_test(string.find(test.stdout(), dtree) == -1)
+
+# In an ideal world, --debug=includes would also work when there's a build
+# failure, but this would require even more complicated logic to scan
+# all of the intermediate nodes that get skipped when the build failure
+# occurs.  On the YAGNI theory, we're just not going to worry about this
+# until it becomes an issue that someone actually cares enough about.
+#test.run(arguments = "--debug=includes foo.xxx",
+#         status = 2,
+#         stderr = None)
+#test.fail_test(string.find(test.stdout(), includes) == -1)
+
+# Restore bar.h to something good.
+test.write('bar.h', """
+#ifndef BAR_H
+#define BAR_H
+#include "foo.h"
+#endif
+""")
+
+# These shouldn't print out anything in particular, but
 # they shouldn't crash either:
 test.run(arguments = "--debug=includes .")
 test.run(arguments = "--debug=includes foo.c")