summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/manual.asciidoc6
-rw-r--r--src/build_test.cc47
-rw-r--r--src/graph.cc25
3 files changed, 59 insertions, 19 deletions
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index ff197b1..7fd2715 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -502,9 +502,9 @@ Note that dependencies as loaded through depfiles have slightly different
semantics, as described in the <<ref_rule,rule reference>>.
3. _Order-only dependencies_, expressed with the syntax +|| _dep1_
- _dep2_+ on the end of a build line. When these are missing, the
- output is not rebuilt until they are built, but once they are
- available further changes to the files do not affect the output.
+ _dep2_+ on the end of a build line. When these are out of date, the
+ output is not rebuilt until they are built, but changes in order-only
+ dependencies alone do not cause the output to be rebuilt.
+
Order-only dependencies can be useful for bootstrapping dependencies
that are only discovered during build time: for example, to generate a
diff --git a/src/build_test.cc b/src/build_test.cc
index a4bf256..4544e2c 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -532,6 +532,53 @@ TEST_F(BuildTest, OrderOnlyDeps) {
ASSERT_EQ(1u, commands_ran_.size());
}
+TEST_F(BuildTest, RebuildOrderOnlyDeps) {
+ string err;
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule cc\n command = cc $in\n"
+"rule true\n command = true\n"
+"build oo.h: cc oo.h.in\n"
+"build foo.o: cc foo.c || oo.h\n"));
+
+ fs_.Create("foo.c", now_, "");
+ fs_.Create("oo.h.in", now_, "");
+
+ // foo.o and order-only dep dirty, build both.
+ EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
+ EXPECT_TRUE(builder_.Build(&err));
+ ASSERT_EQ("", err);
+ ASSERT_EQ(2u, commands_ran_.size());
+
+ // all clean, no rebuild.
+ commands_ran_.clear();
+ state_.Reset();
+ EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
+ EXPECT_EQ("", err);
+ EXPECT_TRUE(builder_.AlreadyUpToDate());
+
+ // order-only dep missing, build it only.
+ fs_.RemoveFile("oo.h");
+ commands_ran_.clear();
+ state_.Reset();
+ EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
+ EXPECT_TRUE(builder_.Build(&err));
+ ASSERT_EQ("", err);
+ ASSERT_EQ(1u, commands_ran_.size());
+ ASSERT_EQ("cc oo.h.in", commands_ran_[0]);
+
+ now_++;
+
+ // order-only dep dirty, build it only.
+ fs_.Create("oo.h.in", now_, "");
+ commands_ran_.clear();
+ state_.Reset();
+ EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
+ EXPECT_TRUE(builder_.Build(&err));
+ ASSERT_EQ("", err);
+ ASSERT_EQ(1u, commands_ran_.size());
+ ASSERT_EQ("cc oo.h.in", commands_ran_[0]);
+}
+
TEST_F(BuildTest, Phony) {
string err;
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
diff --git a/src/graph.cc b/src/graph.cc
index 65fed2e..9df6ecb 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -51,27 +51,20 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
}
}
- if (is_order_only(i - inputs_.begin())) {
- // Order-only deps only make us dirty if they're missing.
- if (!(*i)->file_->exists()) {
- dirty = true;
- outputs_ready_ = false;
- }
- continue;
- }
-
// If an input is not ready, neither are our outputs.
if (Edge* edge = (*i)->in_edge_)
if (!edge->outputs_ready_)
outputs_ready_ = false;
- // If a regular input is dirty (or missing), we're dirty.
- // Otherwise consider mtime.
- if ((*i)->dirty_) {
- dirty = true;
- } else {
- if ((*i)->file_->mtime_ > most_recent_input)
- most_recent_input = (*i)->file_->mtime_;
+ if (!is_order_only(i - inputs_.begin())) {
+ // If a regular input is dirty (or missing), we're dirty.
+ // Otherwise consider mtime.
+ if ((*i)->dirty_) {
+ dirty = true;
+ } else {
+ if ((*i)->file_->mtime_ > most_recent_input)
+ most_recent_input = (*i)->file_->mtime_;
+ }
}
}