diff --git a/src/build.cc b/src/build.cc index deb8f04c8b..bc08a19135 100644 --- a/src/build.cc +++ b/src/build.cc @@ -211,8 +211,10 @@ bool Plan::EdgeFinished(Edge* edge, EdgeResult result, string* err) { edge->outputs_ready_ = true; // Check off any nodes we were waiting for with this edge. - for (vector::iterator o = edge->outputs_.begin(); - o != edge->outputs_.end(); ++o) { + // Iterate over a copy, since NodeFinished might update (implicit) outputs. + vector edgeOutputs(edge->outputs_); + for (vector::iterator o = edgeOutputs.begin(); + o != edgeOutputs.end(); ++o) { if (!NodeFinished(*o, err)) return false; } diff --git a/src/dyndep.cc b/src/dyndep.cc index 15e69841b6..0e7d26c9e4 100644 --- a/src/dyndep.cc +++ b/src/dyndep.cc @@ -43,6 +43,23 @@ bool DyndepLoader::LoadDyndeps(Node* node, DyndepFile* ddf, if (!LoadDyndepFile(node, ddf, err)) return false; + if(node->in_edge() != nullptr && node->in_edge()->dyndep_ == node) { + Edge* const edge = node->in_edge(); + DyndepFile::iterator ddi = ddf->find(edge); + if (ddi == ddf->end()) { + *err = ("'" + edge->outputs_[0]->path() + "' " + "not mentioned in its dyndep file " + "'" + node->path() + "'"); + return false; + } + + ddi->second.used_ = true; + Dyndeps const& dyndeps = ddi->second; + if (!UpdateEdge(edge, &dyndeps, err)) { + return false; + } + } + // Update each edge that specified this node as its dyndep binding. std::vector const& out_edges = node->out_edges(); for (Edge* edge : out_edges) { diff --git a/src/graph.cc b/src/graph.cc index 143eabdfb4..f576b83573 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -110,8 +110,8 @@ bool DependencyScan::RecomputeNodeDirty(Node* node, std::vector* stack, stack->push_back(node); bool dirty = false; - edge->outputs_ready_ = true; edge->deps_missing_ = false; + edge->outputs_ready_ = true; if (!edge->deps_loaded_) { // This is our first encounter with this edge. @@ -126,18 +126,63 @@ bool DependencyScan::RecomputeNodeDirty(Node* node, std::vector* stack, // Later during the build the dyndep file will become ready and be // loaded to update this edge before it can possibly be scheduled. if (edge->dyndep_ && edge->dyndep_->dyndep_pending()) { - if (!RecomputeNodeDirty(edge->dyndep_, stack, validation_nodes, err)) - return false; + if(edge->dyndep_->in_edge() == edge) { + Node* most_recent_input = NULL; + for (vector::iterator i = edge->inputs_.begin(); + i != edge->inputs_.end(); ++i) { + // Visit this input. + if (!RecomputeNodeDirty(*i, stack, validation_nodes, err)) + return false; + + // If an input is not ready, neither are our outputs. + if (Edge* in_edge = (*i)->in_edge()) { + if (!in_edge->outputs_ready_) + edge->outputs_ready_ = false; + } + + if (!edge->is_order_only(i - edge->inputs_.begin())) { + // If a regular input is dirty (or missing), we're dirty. + // Otherwise consider mtime. + if ((*i)->dirty()) { + explanations_.Record(node, "%s is dirty", (*i)->path().c_str()); + dirty = true; + } else { + if (!most_recent_input || (*i)->mtime() > most_recent_input->mtime()) { + most_recent_input = *i; + } + } + } + } + + // Load output mtimes so we can compare them to the most recent input below. + for (vector::iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) { + if (!(*o)->StatIfNecessary(disk_interface_, err)) + return false; + } - if (!edge->dyndep_->in_edge() || - edge->dyndep_->in_edge()->outputs_ready()) { - // The dyndep file is ready, so load it now. - if (!LoadDyndeps(edge->dyndep_, err)) + if (!RecomputeOutputsDirty(edge, most_recent_input, &dirty, err)) return false; + if(!dirty) { + // The dyndep file is ready, so load it now. + if (!LoadDyndeps(edge->dyndep_, err)) + return false; + } + } else { + if (!RecomputeNodeDirty(edge->dyndep_, stack, validation_nodes, err)) + return false; + + if (!edge->dyndep_->in_edge() || + edge->dyndep_->in_edge()->outputs_ready()) { + // The dyndep file is ready, so load it now. + if (!LoadDyndeps(edge->dyndep_, err)) + return false; + } } } } + // Load output mtimes so we can compare them to the most recent input below. for (vector::iterator o = edge->outputs_.begin(); o != edge->outputs_.end(); ++o) { diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index c4b2980164..0db27bf6b6 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -405,7 +405,11 @@ bool ManifestParser::ParseEdge(string* err) { vector::iterator dgi = std::find(edge->inputs_.begin(), edge->inputs_.end(), edge->dyndep_); if (dgi == edge->inputs_.end()) { - return lexer_.Error("dyndep '" + dyndep + "' is not an input", err); + //dyndep not found in inputs, lets check if we have it as output + dgi = std::find(edge->outputs_.begin(), edge->outputs_.end(), edge->dyndep_); + if (dgi == edge->outputs_.end()) { + return lexer_.Error("dyndep '" + dyndep + "' is not an input nor an output", err); + } } assert(!edge->dyndep_->generated_by_dep_loader()); } diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index c5a1fe8fd2..e164cb0bc8 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -1086,7 +1086,7 @@ TEST_F(ParserTest, DyndepNotInput) { "build result: touch\n" " dyndep = notin\n", &err)); - EXPECT_EQ("input:5: dyndep 'notin' is not an input\n", err); + EXPECT_EQ("input:5: dyndep 'notin' is not an input nor an output\n", err); } TEST_F(ParserTest, DyndepExplicitInput) {