summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.cc23
-rw-r--r--src/build_test.cc40
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) {