diff options
-rw-r--r-- | src/build.cc | 23 | ||||
-rw-r--r-- | src/build_test.cc | 40 |
2 files changed, 51 insertions, 12 deletions
diff --git a/src/build.cc b/src/build.cc index 2fbfdec..143aeb2 100644 --- a/src/build.cc +++ b/src/build.cc @@ -422,23 +422,22 @@ void Plan::CleanNode(DependencyScan* scan, Node* node) { } string command = (*ei)->EvaluateCommand(true); - // Now, recompute the dirty state of each output. - bool all_outputs_clean = true; + // Now, this edge is dirty if any of the outputs are dirty. + bool dirty = false; for (vector<Node*>::iterator ni = (*ei)->outputs_.begin(); - ni != (*ei)->outputs_.end(); ++ni) { - if (!(*ni)->dirty()) - continue; + !dirty && ni != (*ei)->outputs_.end(); ++ni) { + dirty = scan->RecomputeOutputDirty(*ei, most_recent_input, 0, + command, *ni); + } - if (scan->RecomputeOutputDirty(*ei, most_recent_input, 0, - command, *ni)) { - all_outputs_clean = false; - } else { + // If the edge isn't dirty, clean the outputs and mark the edge as not + // wanted. + if (!dirty) { + for (vector<Node*>::iterator ni = (*ei)->outputs_.begin(); + ni != (*ei)->outputs_.end(); ++ni) { CleanNode(scan, *ni); } - } - // If we cleaned all outputs, mark the edge as not wanted. - if (all_outputs_clean) { want_i->second = false; --wanted_edges_; if (!(*ei)->is_phony()) diff --git a/src/build_test.cc b/src/build_test.cc index 313a386..d1ac0ef 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -1094,6 +1094,46 @@ TEST_F(BuildWithLogTest, RestatMissingFile) { ASSERT_EQ(1u, command_runner_.commands_ran_.size()); } +TEST_F(BuildWithLogTest, RestatSingleDependentOutputDirty) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "rule true\n" + " command = true\n" + " restat = 1\n" + "rule touch\n" + " command = touch\n" + "build out1: true in\n" + "build out2 out3: touch out1\n" + "build out4: touch out2\n" + )); + + // Create the necessary files + fs_.Create("in", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out4", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + + fs_.Tick(); + fs_.Create("in", ""); + fs_.RemoveFile("out3"); + + // Since "in" is missing, out1 will be built. Since "out3" is missing, + // out2 and out3 will be built even though "in" is not touched when built. + // Then, since out2 is rebuilt, out4 should be rebuilt -- the restat on the + // "true" rule should not lead to the "touch" edge writing out2 and out3 being + // cleard. + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out4", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); +} + // Test scenario, in which an input file is removed, but output isn't changed // https://github.com/martine/ninja/issues/295 TEST_F(BuildWithLogTest, RestatMissingInput) { |