summaryrefslogtreecommitdiff
path: root/src/graph.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/graph.cc')
-rw-r--r--src/graph.cc136
1 files changed, 115 insertions, 21 deletions
diff --git a/src/graph.cc b/src/graph.cc
index ea11360..43ba45a 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -15,6 +15,7 @@
#include "graph.h"
#include <algorithm>
+#include <deque>
#include <assert.h>
#include <stdio.h>
@@ -31,16 +32,57 @@
using namespace std;
bool Node::Stat(DiskInterface* disk_interface, string* err) {
- return (mtime_ = disk_interface->Stat(path_, err)) != -1;
+ METRIC_RECORD("node stat");
+ mtime_ = disk_interface->Stat(path_, err);
+ if (mtime_ == -1) {
+ return false;
+ }
+ exists_ = (mtime_ != 0) ? ExistenceStatusExists : ExistenceStatusMissing;
+ return true;
}
-bool DependencyScan::RecomputeDirty(Node* node, string* err) {
- vector<Node*> stack;
- return RecomputeDirty(node, &stack, err);
+void Node::UpdatePhonyMtime(TimeStamp mtime) {
+ if (!exists()) {
+ mtime_ = std::max(mtime_, mtime);
+ }
}
-bool DependencyScan::RecomputeDirty(Node* node, vector<Node*>* stack,
+bool DependencyScan::RecomputeDirty(Node* initial_node,
+ std::vector<Node*>* validation_nodes,
string* err) {
+ std::vector<Node*> stack;
+ std::vector<Node*> new_validation_nodes;
+
+ std::deque<Node*> nodes(1, initial_node);
+
+ // RecomputeNodeDirty might return new validation nodes that need to be
+ // checked for dirty state, keep a queue of nodes to visit.
+ while (!nodes.empty()) {
+ Node* node = nodes.front();
+ nodes.pop_front();
+
+ stack.clear();
+ new_validation_nodes.clear();
+
+ if (!RecomputeNodeDirty(node, &stack, &new_validation_nodes, err))
+ return false;
+ nodes.insert(nodes.end(), new_validation_nodes.begin(),
+ new_validation_nodes.end());
+ if (!new_validation_nodes.empty()) {
+ assert(validation_nodes &&
+ "validations require RecomputeDirty to be called with validation_nodes");
+ validation_nodes->insert(validation_nodes->end(),
+ new_validation_nodes.begin(),
+ new_validation_nodes.end());
+ }
+ }
+
+ return true;
+}
+
+bool DependencyScan::RecomputeNodeDirty(Node* node, std::vector<Node*>* stack,
+ std::vector<Node*>* validation_nodes,
+ string* err) {
Edge* edge = node->in_edge();
if (!edge) {
// If we already visited this leaf node then we are done.
@@ -84,7 +126,7 @@ bool DependencyScan::RecomputeDirty(Node* node, vector<Node*>* 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 (!RecomputeDirty(edge->dyndep_, stack, err))
+ if (!RecomputeNodeDirty(edge->dyndep_, stack, validation_nodes, err))
return false;
if (!edge->dyndep_->in_edge() ||
@@ -115,12 +157,20 @@ bool DependencyScan::RecomputeDirty(Node* node, vector<Node*>* stack,
}
}
+ // Store any validation nodes from the edge for adding to the initial
+ // nodes. Don't recurse into them, that would trigger the dependency
+ // cycle detector if the validation node depends on this node.
+ // RecomputeDirty will add the validation nodes to the initial nodes
+ // and recurse into them.
+ validation_nodes->insert(validation_nodes->end(),
+ edge->validations_.begin(), edge->validations_.end());
+
// Visit all inputs; we're dirty if any of the inputs are dirty.
Node* most_recent_input = NULL;
for (vector<Node*>::iterator i = edge->inputs_.begin();
i != edge->inputs_.end(); ++i) {
// Visit this input.
- if (!RecomputeDirty(*i, stack, err))
+ if (!RecomputeNodeDirty(*i, stack, validation_nodes, err))
return false;
// If an input is not ready, neither are our outputs.
@@ -237,6 +287,14 @@ bool DependencyScan::RecomputeOutputDirty(const Edge* edge,
output->path().c_str());
return true;
}
+
+ // Update the mtime with the newest input. Dependents can thus call mtime()
+ // on the fake node and get the latest mtime of the dependencies
+ if (most_recent_input) {
+ output->UpdatePhonyMtime(most_recent_input->mtime());
+ }
+
+ // Phony edges are clean, nothing to do
return false;
}
@@ -398,6 +456,28 @@ std::string EdgeEnv::MakePathList(const Node* const* const span,
return result;
}
+void Edge::CollectInputs(bool shell_escape,
+ std::vector<std::string>* out) const {
+ for (std::vector<Node*>::const_iterator it = inputs_.begin();
+ it != inputs_.end(); ++it) {
+ std::string path = (*it)->PathDecanonicalized();
+ if (shell_escape) {
+ std::string unescaped;
+ unescaped.swap(path);
+#ifdef _WIN32
+ GetWin32EscapedString(unescaped, &path);
+#else
+ GetShellEscapedString(unescaped, &path);
+#endif
+ }
+#if __cplusplus >= 201103L
+ out->push_back(std::move(path));
+#else
+ out->push_back(path);
+#endif
+ }
+}
+
std::string Edge::EvaluateCommand(const bool incl_rsp_file) const {
string command = GetBinding("command");
if (incl_rsp_file) {
@@ -443,6 +523,13 @@ void Edge::Dump(const char* prefix) const {
i != outputs_.end() && *i != NULL; ++i) {
printf("%s ", (*i)->path().c_str());
}
+ if (!validations_.empty()) {
+ printf(" validations ");
+ for (std::vector<Node*>::const_iterator i = validations_.begin();
+ i != validations_.end() && *i != NULL; ++i) {
+ printf("%s ", (*i)->path().c_str());
+ }
+ }
if (pool_) {
if (!pool_->name().empty()) {
printf("(in pool '%s')", pool_->name().c_str());
@@ -487,7 +574,7 @@ string Node::PathDecanonicalized(const string& path, uint64_t slash_bits) {
void Node::Dump(const char* prefix) const {
printf("%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ",
prefix, path().c_str(), this,
- mtime(), mtime() ? "" : " (:missing)",
+ mtime(), exists() ? "" : " (:missing)",
dirty() ? " dirty" : " clean");
if (in_edge()) {
in_edge()->Dump("in-edge: ");
@@ -499,6 +586,13 @@ void Node::Dump(const char* prefix) const {
e != out_edges().end() && *e != NULL; ++e) {
(*e)->Dump(" +- ");
}
+ if (!validation_out_edges().empty()) {
+ printf(" validation out edges:\n");
+ for (std::vector<Edge*>::const_iterator e = validation_out_edges().begin();
+ e != validation_out_edges().end() && *e != NULL; ++e) {
+ (*e)->Dump(" +- ");
+ }
+ }
}
bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) {
@@ -515,7 +609,7 @@ bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) {
}
struct matches {
- matches(std::vector<StringPiece>::iterator i) : i_(i) {}
+ explicit matches(std::vector<StringPiece>::iterator i) : i_(i) {}
bool operator()(const Node* node) const {
StringPiece opath = StringPiece(node->path());
@@ -562,11 +656,8 @@ bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path,
uint64_t unused;
std::vector<StringPiece>::iterator primary_out = depfile.outs_.begin();
- if (!CanonicalizePath(const_cast<char*>(primary_out->str_),
- &primary_out->len_, &unused, err)) {
- *err = path + ": " + *err;
- return false;
- }
+ CanonicalizePath(const_cast<char*>(primary_out->str_), &primary_out->len_,
+ &unused);
// Check that this depfile matches the edge's output, if not return false to
// mark the edge as dirty.
@@ -588,18 +679,20 @@ bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path,
}
}
+ return ProcessDepfileDeps(edge, &depfile.ins_, err);
+}
+
+bool ImplicitDepLoader::ProcessDepfileDeps(
+ Edge* edge, std::vector<StringPiece>* depfile_ins, std::string* err) {
// Preallocate space in edge->inputs_ to be filled in below.
vector<Node*>::iterator implicit_dep =
- PreallocateSpace(edge, depfile.ins_.size());
+ PreallocateSpace(edge, depfile_ins->size());
// Add all its in-edges.
- for (vector<StringPiece>::iterator i = depfile.ins_.begin();
- i != depfile.ins_.end(); ++i, ++implicit_dep) {
+ for (std::vector<StringPiece>::iterator i = depfile_ins->begin();
+ i != depfile_ins->end(); ++i, ++implicit_dep) {
uint64_t slash_bits;
- if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits,
- err))
- return false;
-
+ CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits);
Node* node = state_->GetNode(*i, slash_bits);
*implicit_dep = node;
node->AddOutEdge(edge);
@@ -649,6 +742,7 @@ void ImplicitDepLoader::CreatePhonyInEdge(Node* node) {
return;
Edge* phony_edge = state_->AddEdge(&State::kPhonyRule);
+ phony_edge->generated_by_dep_loader_ = true;
node->set_in_edge(phony_edge);
phony_edge->outputs_.push_back(node);