- 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
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
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)
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.
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 = []
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
-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")