diff options
| author | Steven Knight <knight@baldmt.com> | 2003-01-27 03:55:51 (GMT) |
|---|---|---|
| committer | Steven Knight <knight@baldmt.com> | 2003-01-27 03:55:51 (GMT) |
| commit | 078be7b37ba947064d07ac85969f1bd5f3c8ae27 (patch) | |
| tree | bb2dcbd756279f2004eeb24e3ba47bd32511e3c2 /src | |
| parent | fb4152bf88a71d44c6ec7627d63dae6b93ee348a (diff) | |
| download | SCons-078be7b37ba947064d07ac85969f1bd5f3c8ae27.zip SCons-078be7b37ba947064d07ac85969f1bd5f3c8ae27.tar.gz SCons-078be7b37ba947064d07ac85969f1bd5f3c8ae27.tar.bz2 | |
Provide a better error message when a BuildDir() is read-only.
Diffstat (limited to 'src')
| -rw-r--r-- | src/CHANGES.txt | 3 | ||||
| -rw-r--r-- | src/engine/SCons/Node/FS.py | 6 | ||||
| -rw-r--r-- | src/engine/SCons/Node/FSTests.py | 22 | ||||
| -rw-r--r-- | src/engine/SCons/Script/__init__.py | 18 | ||||
| -rw-r--r-- | src/engine/SCons/Taskmaster.py | 39 | ||||
| -rw-r--r-- | src/engine/SCons/TaskmasterTests.py | 55 |
6 files changed, 128 insertions, 15 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 49346b4..ce88f01 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -42,6 +42,9 @@ RELEASE 0.11 - XXX - Fix adding a prefix to a file when the target isn't specified. (Bug reported by Esa Ilari Vuokko.) + - Clean up error messages from problems duplicating into read-only + BuildDir directories or into read-only files. + From Steve Leblanc: - Fix the output of -c -n when directories are involved, so it diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 7b77736..0978b57 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -971,7 +971,11 @@ class File(Entry): Unlink(self, None, None) except OSError: pass - Link(self, src, None) + try: + Link(self, src, None) + except IOError, e: + desc = "Cannot duplicate `%s' in `%s': %s." % (src, self.dir, e.strerror) + raise SCons.Errors.StopError, desc self.linked = 1 # The Link() action may or may not have actually # created the file, depending on whether the -n diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 51dcc1b..83bdccf 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -288,6 +288,28 @@ class BuildDirTestCase(unittest.TestCase): finally: SCons.Node.FS.Mkdir = save_Mkdir SCons.Node.FS.Link = save_Link + + # Test that an IOError trying to Link a src file + # into a BuildDir ends up throwing a StopError. + fIO = fs.File("build/var2/IOError") + + save_Link = SCons.Node.FS.Link + def Link_IOError(target, source, env): + raise IOError, "Link_IOError" + SCons.Node.FS.Link = Link_IOError + + test.write(['work', 'src', 'IOError'], "work/src/IOError\n") + + try: + exc_caught = 0 + try: + fIO.exists() + except SCons.Errors.StopError: + exc_caught = 1 + assert exc_caught, "Should have caught a StopError" + + finally: + SCons.Node.FS.Link = save_Link # Test to see if Link() works... test.subdir('src','build') diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 9257266..a14c7ae 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -65,7 +65,6 @@ import SCons.Taskmaster from SCons.Util import display import SCons.Warnings - # # Task control. # @@ -642,8 +641,6 @@ class OptParser(OptionParser): def _main(): - import SCons.Node - targets = [] # Enable deprecated warnings by default. @@ -753,8 +750,19 @@ def _main(): display("scons: Reading SConscript files ...") try: start_time = time.time() - for script in scripts: - SCons.Script.SConscript.SConscript(script) + try: + for script in scripts: + SCons.Script.SConscript.SConscript(script) + except SCons.Errors.StopError, e: + # We had problems reading an SConscript file, such as it + # couldn't be copied in to the BuildDir. Since we're just + # reading SConscript files and haven't started building + # things yet, stop regardless of whether they used -i or -k + # or anything else, but don't say "Stop." on the message. + global exit_status + sys.stderr.write("scons: *** %s\n" % e) + exit_status = 2 + sys.exit(exit_status) global sconscript_time sconscript_time = time.time() - start_time except PrintHelp, text: diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 645c7df..10f074f 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -74,6 +74,13 @@ class Task: so only do thread safe stuff here. Do thread unsafe stuff in prepare(), executed() or failed().""" try: + # We recorded an exception while getting this Task ready + # for execution. Raise it now. + raise self.node.exc_type, self.node.exc_value + except AttributeError: + # The normal case: no exception to raise. + pass + try: self.targets[0].build() except KeyboardInterrupt: raise @@ -198,7 +205,18 @@ class Taskmaster: # keep track of which nodes are in the execution stack: node.set_state(SCons.Node.stack) - children = node.children() + try: + children = node.children() + except: + # We had a problem just trying to figure out the + # children (like a child couldn't be linked in to a + # BuildDir). Arrange to raise the exception when the + # Task is "executed." + node.exc_type = sys.exc_type + node.exc_value = sys.exc_value + self.candidates.pop() + self.ready = node + break # detect dependency cycles: def in_stack(node): return node.get_state() == SCons.Node.stack @@ -236,10 +254,11 @@ class Taskmaster: node.set_state(SCons.Node.pending) self.candidates.pop() continue - else: - self.candidates.pop() - self.ready = node - break + + # The default when we've gotten through all of the checks above. + self.candidates.pop() + self.ready = node + break def next_task(self): """Return the next task to be executed.""" @@ -259,7 +278,15 @@ class Taskmaster: self.executing.extend(node.side_effects) task = self.tasker(self, tlist, node in self.targets, node) - task.make_ready() + try: + task.make_ready() + except: + # We had a problem just trying to get this task ready (like + # a child couldn't be linked in to a BuildDir when deciding + # whether this node is current). Arrange to raise the + # exception when the Task is "executed." + node.exc_type = sys.exc_type + node.exc_value = sys.exc_value self.ready = None return task diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 577d783..cc0b437 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -121,6 +121,9 @@ class Node: class OtherError(Exception): pass +class MyException(Exception): + pass + class TaskmasterTestCase(unittest.TestCase): @@ -344,6 +347,32 @@ class TaskmasterTestCase(unittest.TestCase): assert not tm.next_task() t.executed() + def test_make_ready_exception(self): + """Test handling exceptions from Task.make_ready() + """ + class MyTask(SCons.Taskmaster.Task): + def make_ready(self): + raise MyException, "from make_ready()" + + n1 = Node("n1") + tm = SCons.Taskmaster.Taskmaster(targets = [n1], tasker = MyTask) + t = tm.next_task() + assert n1.exc_type == MyException, n1.exc_type + assert str(n1.exc_value) == "from make_ready()", n1.exc_value + + + def test_children_errors(self): + """Test errors when fetching the children of a node. + """ + class MyNode(Node): + def children(self): + raise SCons.Errors.StopError, "stop!" + n1 = MyNode("n1") + tm = SCons.Taskmaster.Taskmaster([n1]) + t = tm.next_task() + assert n1.exc_type == SCons.Errors.StopError, "Did not record StopError on node" + assert str(n1.exc_value) == "stop!", "Unexpected exc_value `%s'" % n1.exc_value + def test_cycle_detection(self): """Test detecting dependency cycles @@ -479,6 +508,9 @@ class TaskmasterTestCase(unittest.TestCase): else: raise TestFailed, "did not catch expected BuildError" + # On a generic (non-BuildError) exception from a Builder, + # the target should throw a BuildError exception with the + # args set to the exception value, instance, and traceback. def raise_OtherError(): raise OtherError n4 = Node("n4") @@ -488,9 +520,6 @@ class TaskmasterTestCase(unittest.TestCase): try: t.execute() except SCons.Errors.BuildError, e: - # On a generic (non-BuildError) exception from a Builder, - # the target should throw a BuildError exception with the - # args set to the exception value, instance, and traceback. assert e.node == n4, e.node assert e.errstr == "Exception", e.errstr assert len(e.args) == 3, `e.args` @@ -500,6 +529,26 @@ class TaskmasterTestCase(unittest.TestCase): else: raise TestFailed, "did not catch expected BuildError" + # If the Node has had an exception recorded (during + # preparation), then execute() should raise that exception, + # not build the Node. + class MyException(Exception): + pass + + built_text = None + n5 = Node("n5") + n5.exc_type = MyException + n5.exc_value = "exception value" + tm = SCons.Taskmaster.Taskmaster([n5]) + t = tm.next_task() + exc_caught = None + try: + t.execute() + except MyException, v: + assert str(v) == "exception value", v + exc_caught = 1 + assert exc_caught, "did not catch expected MyException" + assert built_text is None, built_text |
