From 54ae1a37ea6396628b75bdc0021f1ef4848deb84 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Fri, 13 May 2011 10:47:26 -0700 Subject: 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.) --- manual.asciidoc | 22 ++++++++++++++++------ src/build.cc | 3 +-- src/build_test.cc | 10 +++++++--- src/graph.cc | 13 ++++++++++--- src/graph_test.cc | 22 ++++++++++++++++++++-- 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/manual.asciidoc b/manual.asciidoc index eefbe12..7aa000e 100644 --- a/manual.asciidoc +++ b/manual.asciidoc @@ -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 <>.) + 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 <>). 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 <>. 3. _Order-only dependencies_, expressed with the syntax +|| _dep1_ _dep2_+ on the end of a build line. When these are missing, the diff --git a/src/build.cc b/src/build.cc index 1deb08a..3447e0d 100644 --- a/src/build.cc +++ b/src/build.cc @@ -194,8 +194,7 @@ bool Plan::AddSubTarget(Node* node, vector* stack, string* err) { bool awaiting_inputs = false; for (vector::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; diff --git a/src/build_test.cc b/src/build_test.cc index 38d2550..a7eca14 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -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_); diff --git a/src/graph.cc b/src/graph.cc index d953361..cb51618 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -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; diff --git a/src/graph_test.cc b/src/graph_test.cc index 9a5035c..e221089 100644 --- a/src/graph_test.cc +++ b/src/graph_test.cc @@ -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) { -- cgit v0.12