diff options
author | Peter Collingbourne <peter@pcc.me.uk> | 2011-09-19 01:56:15 (GMT) |
---|---|---|
committer | Peter Collingbourne <peter@pcc.me.uk> | 2011-10-24 00:16:38 (GMT) |
commit | 0efbbbf67a94452919084765e87106e7748274cb (patch) | |
tree | 21c8c5a13b70d7f287b2f569a35f6a03a7f1c19d /src/build_test.cc | |
parent | 386cbf6c7e49c88b87f54619b06670ea9f66c8a5 (diff) | |
download | Ninja-0efbbbf67a94452919084765e87106e7748274cb.zip Ninja-0efbbbf67a94452919084765e87106e7748274cb.tar.gz Ninja-0efbbbf67a94452919084765e87106e7748274cb.tar.bz2 |
Implement restat rules
A restat rule is a rule which is capable of pruning the build tree
depending on the timestamps of its outputs before and after a build.
After a restat rule is rebuilt, Ninja will re-stat each output file
to obtain its current timestamp. If the timestamp is unchanged from
when Ninja initially stat'ed the file before starting the build,
Ninja will mark that output file as clean, and recursively for each
reverse dependency of the output file, recompute its dirty status.
Ninja then stores the most recent timestamp of any input file in the
build log entry associated with the output file. This timestamp
will be treated by future invocations of Ninja as the output file's
modification time instead of the output file's actual modification
time for the purpose of deciding whether it is dirty (but not whether
its reverse dependencies are dirty).
Diffstat (limited to 'src/build_test.cc')
-rw-r--r-- | src/build_test.cc | 62 |
1 files changed, 61 insertions, 1 deletions
diff --git a/src/build_test.cc b/src/build_test.cc index 4544e2c..9cfa6e1 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -14,6 +14,7 @@ #include "build.h" +#include "build_log.h" #include "graph.h" #include "test.h" @@ -238,7 +239,8 @@ bool BuildTest::StartCommand(Edge* edge) { out != edge->outputs_.end(); ++out) { fs_.Create((*out)->file_->path_, now_, ""); } - } else if (edge->rule_->name_ == "fail") { + } else if (edge->rule_->name_ == "true" || + edge->rule_->name_ == "fail") { // Don't do anything. } else { printf("unknown command\n"); @@ -665,3 +667,61 @@ TEST_F(BuildTest, SwallowFailuresLimit) { ASSERT_EQ(3u, commands_ran_.size()); ASSERT_EQ("cannot make progress due to previous errors", err); } + +struct BuildWithLogTest : public BuildTest { + BuildWithLogTest() { + state_.build_log_ = builder_.log_ = &build_log_; + } + + BuildLog build_log_; +}; + +TEST_F(BuildWithLogTest, RestatTest) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule true\n" +" command = true\n" +" restat = 1\n" +"rule cc\n" +" command = cc\n" +" restat = 1\n" +"build out1: cc in\n" +"build out2: true out1\n" +"build out3: cat out2\n")); + + fs_.Create("out1", now_, ""); + fs_.Create("out2", now_, ""); + fs_.Create("out3", now_, ""); + + now_++; + + fs_.Create("in", now_, ""); + + // "cc" touches out1, so we should build out2. But because "true" does not + // touch out2, we should cancel the build of out3. + string err; + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(2u, commands_ran_.size()); + + // If we run again, it should be a no-op, because the build log has recorded + // that we've already built out2 with an input timestamp of 2 (from out1). + commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); + + now_++; + + fs_.Create("in", now_, ""); + + // The build log entry should not, however, prevent us from rebuilding out2 + // if out1 changes. + commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(2u, commands_ran_.size()); +} |