SEMANTIC CHANGE: implicit inputs are now required to exist

Edges found through depfiles are special: they get an extra
empty "phony" rule.  (This is identical to the way you hack this
with Makefiles.)
This commit is contained in:
Evan Martin 2011-05-13 10:47:26 -07:00
parent 400ca686fe
commit 54ae1a37ea
5 changed files with 54 additions and 16 deletions

View File

@ -353,9 +353,11 @@ A file is a series of declarations. A declaration can be one of:
2. A build edge, which looks like +build _output1_ _output2_:
_rulename_ _input1_ _input2_+. +
Implicit dependencies may be tacked on the end with +|
_dependency1_ _dependency2_+.
_dependency1_ _dependency2_+. +
Order-only dependencies may be tacked on the end with +||
_dependency1_ _dependency2_+.
_dependency1_ _dependency2_+. (See <<ref_dependencies,the reference on
dependency types>>.)
3. Variable declarations, which look like +_variable_ = _value_+.
@ -386,7 +388,7 @@ keys.
interpretation by Ninja.
`depfile`:: path to an optional `Makefile` that contains extra
_implicit dependencies_ (see the <<ref_dependencies,the reference on
_implicit dependencies_ (see <<ref_dependencies,the reference on
dependency types>>). This is explicitly to support `gcc` and its `-M`
family of flags, which output the list of headers a given `.c` file
depends on.
@ -398,6 +400,11 @@ rule cc
depfile = $out.d
command = gcc -MMD -MF $out.d [other gcc flags here]
----
+
When loading a `depfile`, Ninja implicitly adds edges such that it is
not an error if the listed dependency is missing. This allows you to
delete a depfile-discovered header file and rebuild, without the build
aborting due to a missing input.
`description`:: a short description of the command, used to pretty-print
@ -425,14 +432,17 @@ source file of a compile command.
2. _Implicit dependencies_, either as picked up from
a `depfile` attribute on a rule or from the syntax +| _dep1_
_dep2_+ on the end of a build line. Changes in these files cause
the output to be rebuilt; if they are missing, they are just
skipped.
_dep2_+ on the end of a build line. The semantics are identical to
explicit dependencies, the only difference is that implicit dependencies
don't show up in the `$in` variable.
+
This is for expressing dependencies that don't show up on the
command line of the command; for example, for a rule that runs a
script, the script itself should be an implicit dependency, as
changes to the script should cause the output to rebuild.
+
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

View File

@ -194,8 +194,7 @@ bool Plan::AddSubTarget(Node* node, vector<Node*>* stack, string* err) {
bool awaiting_inputs = false;
for (vector<Node*>::iterator i = edge->inputs_.begin();
i != edge->inputs_.end(); ++i) {
if (!edge->is_implicit(i - edge->inputs_.begin()) &&
AddSubTarget(*i, stack, err)) {
if (AddSubTarget(*i, stack, err)) {
awaiting_inputs = true;
} else if (!err->empty()) {
return false;

View File

@ -428,6 +428,8 @@ TEST_F(BuildTest, DepFileOK) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"rule cc\n command = cc $in\n depfile = $out.d\n"
"build foo.o: cc foo.c\n"));
Edge* edge = state_.edges_.back();
fs_.Create("foo.c", now_, "");
GetNode("bar.h")->dirty_ = true; // Mark bar.h as missing.
fs_.Create("foo.o.d", now_, "foo.o: blah.h bar.h\n");
@ -436,9 +438,10 @@ TEST_F(BuildTest, DepFileOK) {
ASSERT_EQ(1u, fs_.files_read_.size());
EXPECT_EQ("foo.o.d", fs_.files_read_[0]);
// Expect three new edges: one generating foo.o, and two more from
// loading the depfile.
ASSERT_EQ(orig_edges + 3, (int)state_.edges_.size());
// Expect our edge to now have three inputs: foo.c and two headers.
ASSERT_EQ(orig_edges + 1, (int)state_.edges_.size());
Edge* edge = state_.edges_.back();
ASSERT_EQ(3u, edge->inputs_.size());
// Expect the command line we generate to only use the original input.
@ -461,13 +464,14 @@ TEST_F(BuildTest, OrderOnlyDeps) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"rule cc\n command = cc $in\n depfile = $out.d\n"
"build foo.o: cc foo.c || otherfile\n"));
Edge* edge = state_.edges_.back();
fs_.Create("foo.c", now_, "");
fs_.Create("otherfile", now_, "");
fs_.Create("foo.o.d", now_, "foo.o: blah.h bar.h\n");
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
ASSERT_EQ("", err);
Edge* edge = state_.edges_.back();
// One explicit, two implicit, one order only.
ASSERT_EQ(4u, edge->inputs_.size());
EXPECT_EQ(2, edge->implicit_deps_);

View File

@ -43,9 +43,7 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
return false;
} else {
// This input has no in-edge; it is dirty if it is missing.
// But it's ok for implicit deps to be missing.
if (!is_implicit(i - inputs_.begin()))
(*i)->dirty_ = !(*i)->file_->exists();
(*i)->dirty_ = !(*i)->file_->exists();
}
}
@ -165,6 +163,15 @@ bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface,
inputs_.insert(inputs_.end() - order_only_deps_, node);
node->out_edges_.push_back(this);
++implicit_deps_;
// If we don't have a edge that generates this input already,
// create one; this makes us not abort if the input is missing,
// but instead will rebuild in that circumstance.
if (!node->in_edge_) {
Edge* phony_edge = state->AddEdge(&State::kPhonyRule);
node->in_edge_ = phony_edge;
phony_edge->outputs_.push_back(node);
}
}
return true;

View File

@ -31,8 +31,26 @@ TEST_F(GraphTest, MissingImplicit) {
EXPECT_TRUE(edge->RecomputeDirty(&state_, &fs_, &err));
ASSERT_EQ("", err);
// A missing implicit dep does not make the output dirty.
EXPECT_FALSE(GetNode("out")->dirty_);
// A missing implicit dep *should* make the output dirty.
// (In fact, a build will fail.)
// This is a change from prior semantics of ninja.
EXPECT_TRUE(GetNode("out")->dirty_);
}
TEST_F(GraphTest, ModifiedImplicit) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build out: cat in | implicit\n"));
fs_.Create("in", 1, "");
fs_.Create("out", 1, "");
fs_.Create("implicit", 2, "");
Edge* edge = GetNode("out")->in_edge_;
string err;
EXPECT_TRUE(edge->RecomputeDirty(&state_, &fs_, &err));
ASSERT_EQ("", err);
// A modified implicit dep should make the output dirty.
EXPECT_TRUE(GetNode("out")->dirty_);
}
TEST_F(GraphTest, FunkyMakefilePath) {