summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2017-05-18 21:29:45 -0700
committerColin Cross <ccross@android.com>2017-05-22 11:29:06 -0700
commita127dda3ee92916ef459b3da7aa9f2920ff1a5ab (patch)
tree0b5524287de8ab1bb9ca33efb7c5329baed740e7
parent036003d20e80cbb044a3f0f0b41e2fdefcde66af (diff)
downloadninja-a127dda3ee92916ef459b3da7aa9f2920ff1a5ab.tar.gz
Add a test that fails if a rule updates it output file but fails
https://groups.google.com/forum/#!msg/ninja-build/YQuGNrECI-4/ti-lAs9SPv8J discusses a case where an rule updates its output file and then fails. The next run of ninja considers the ouptut file clean and doesn't rebuild it. Add a test for this case, which currently fails.
-rw-r--r--src/build.cc7
-rw-r--r--src/build.h3
-rw-r--r--src/build_test.cc51
3 files changed, 59 insertions, 2 deletions
diff --git a/src/build.cc b/src/build.cc
index a0c7ec8..44d0663 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -275,6 +275,13 @@ void BuildStatus::PrintStatus(Edge* edge, EdgeStatus status) {
Plan::Plan() : command_edges_(0), wanted_edges_(0) {}
+void Plan::Reset() {
+ command_edges_ = 0;
+ wanted_edges_ = 0;
+ ready_.clear();
+ want_.clear();
+}
+
bool Plan::AddTarget(Node* node, string* err) {
vector<Node*> stack;
return AddSubTarget(node, &stack, err);
diff --git a/src/build.h b/src/build.h
index 66ce607..f97d67e 100644
--- a/src/build.h
+++ b/src/build.h
@@ -71,6 +71,9 @@ struct Plan {
/// Number of edges with commands to run.
int command_edge_count() const { return command_edges_; }
+ /// Reset state. Clears want and ready sets.
+ void Reset();
+
private:
bool AddSubTarget(Node* node, vector<Node*>* stack, string* err);
bool CheckDependencyCycle(Node* node, const vector<Node*>& stack,
diff --git a/src/build_test.cc b/src/build_test.cc
index 640e1b0..d617143 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -608,7 +608,8 @@ bool FakeCommandRunner::StartCommand(Edge* edge) {
edge->rule().name() == "cat_rsp_out" ||
edge->rule().name() == "cc" ||
edge->rule().name() == "touch" ||
- edge->rule().name() == "touch-interrupt") {
+ edge->rule().name() == "touch-interrupt" ||
+ edge->rule().name() == "touch-fail-tick2") {
for (vector<Node*>::iterator out = edge->outputs_.begin();
out != edge->outputs_.end(); ++out) {
fs_->Create((*out)->path(), "");
@@ -649,7 +650,8 @@ bool FakeCommandRunner::WaitForCommand(Result* result) {
return true;
}
- if (edge->rule().name() == "fail")
+ if (edge->rule().name() == "fail" ||
+ (edge->rule().name() == "touch-fail-tick2" && fs_->now_ == 2))
result->status = ExitFailure;
else
result->status = ExitSuccess;
@@ -1258,6 +1260,51 @@ TEST_F(BuildWithLogTest, NotInLogButOnDisk) {
EXPECT_TRUE(builder_.AlreadyUpToDate());
}
+TEST_F(BuildWithLogTest, RebuildAfterFailure) {
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule touch-fail-tick2\n"
+" command = touch-fail-tick2\n"
+"build out1: touch-fail-tick2 in\n"));
+
+ string err;
+
+ fs_.Create("in", "");
+
+ // Run once successfully to get out1 in the log
+ EXPECT_TRUE(builder_.AddTarget("out1", &err));
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ("", err);
+ EXPECT_EQ(1u, command_runner_.commands_ran_.size());
+
+ command_runner_.commands_ran_.clear();
+ state_.Reset();
+ builder_.Cleanup();
+ builder_.plan_.Reset();
+
+ fs_.Tick();
+ fs_.Create("in", "");
+
+ // Run again with a failure that updates the output file timestamp
+ EXPECT_TRUE(builder_.AddTarget("out1", &err));
+ EXPECT_FALSE(builder_.Build(&err));
+ EXPECT_EQ("subcommand failed", err);
+ EXPECT_EQ(1u, command_runner_.commands_ran_.size());
+
+ command_runner_.commands_ran_.clear();
+ state_.Reset();
+ builder_.Cleanup();
+ builder_.plan_.Reset();
+
+ fs_.Tick();
+
+ // Run again, should rerun even though the output file is up to date on disk
+ EXPECT_TRUE(builder_.AddTarget("out1", &err));
+ EXPECT_FALSE(builder_.AlreadyUpToDate());
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ(1u, command_runner_.commands_ran_.size());
+ EXPECT_EQ("", err);
+}
+
TEST_F(BuildWithLogTest, RestatTest) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"rule true\n"