summaryrefslogtreecommitdiffstats
path: root/src/build.cc
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2011-09-19 01:56:15 (GMT)
committerPeter Collingbourne <peter@pcc.me.uk>2011-10-24 00:16:38 (GMT)
commit0efbbbf67a94452919084765e87106e7748274cb (patch)
tree21c8c5a13b70d7f287b2f569a35f6a03a7f1c19d /src/build.cc
parent386cbf6c7e49c88b87f54619b06670ea9f66c8a5 (diff)
downloadNinja-0efbbbf67a94452919084765e87106e7748274cb.zip
Ninja-0efbbbf67a94452919084765e87106e7748274cb.tar.gz
Ninja-0efbbbf67a94452919084765e87106e7748274cb.tar.bz2
Implement restat rules
A restat rule is a rule which is capable of pruning the build tree depending on the timestamps of its outputs before and after a build. After a restat rule is rebuilt, Ninja will re-stat each output file to obtain its current timestamp. If the timestamp is unchanged from when Ninja initially stat'ed the file before starting the build, Ninja will mark that output file as clean, and recursively for each reverse dependency of the output file, recompute its dirty status. Ninja then stores the most recent timestamp of any input file in the build log entry associated with the output file. This timestamp will be treated by future invocations of Ninja as the output file's modification time instead of the output file's actual modification time for the purpose of deciding whether it is dirty (but not whether its reverse dependencies are dirty).
Diffstat (limited to 'src/build.cc')
-rw-r--r--src/build.cc97
1 files changed, 95 insertions, 2 deletions
diff --git a/src/build.cc b/src/build.cc
index c9ffe7e..9465532 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -301,6 +301,60 @@ void Plan::NodeFinished(Node* node) {
}
}
+void Plan::CleanNode(BuildLog* build_log, Node* node) {
+ node->dirty_ = false;
+
+ for (vector<Edge*>::iterator ei = node->out_edges_.begin();
+ ei != node->out_edges_.end(); ++ei) {
+ // Don't process edges that we don't actually want.
+ map<Edge*, bool>::iterator want_i = want_.find(*ei);
+ if (want_i == want_.end() || !want_i->second)
+ continue;
+
+ // If all non-order-only inputs for this edge are now clean,
+ // we might have changed the dirty state of the outputs.
+ vector<Node*>::iterator begin = (*ei)->inputs_.begin(),
+ end = (*ei)->inputs_.end() - (*ei)->order_only_deps_;
+ if (find_if(begin, end, mem_fun(&Node::dirty)) == end) {
+ // Recompute most_recent_input and command.
+ time_t most_recent_input = 1;
+ for (vector<Node*>::iterator ni = begin; ni != end; ++ni)
+ if ((*ni)->file_->mtime_ > most_recent_input)
+ most_recent_input = (*ni)->file_->mtime_;
+ string command = (*ei)->EvaluateCommand();
+
+ // Now, recompute the dirty state of each output.
+ bool all_outputs_clean = true;
+ for (vector<Node*>::iterator ni = (*ei)->outputs_.begin();
+ ni != (*ei)->outputs_.end(); ++ni) {
+ if (!(*ni)->dirty_)
+ continue;
+
+ // RecomputeOutputDirty will not modify dirty_ if the output is clean.
+ (*ni)->dirty_ = false;
+
+ // Since we know that all non-order-only inputs are clean, we can pass
+ // "false" as the "dirty" argument here.
+ (*ei)->RecomputeOutputDirty(build_log, most_recent_input, false,
+ command, *ni);
+ if ((*ni)->dirty_) {
+ all_outputs_clean = false;
+ } else {
+ CleanNode(build_log, *ni);
+ }
+ }
+
+ // If we cleaned all outputs, mark the node as not wanted.
+ if (all_outputs_clean) {
+ want_i->second = false;
+ --wanted_edges_;
+ if (!(*ei)->is_phony())
+ --command_edges_;
+ }
+ }
+ }
+}
+
void Plan::Dump() {
printf("pending: %d\n", (int)want_.size());
for (map<Edge*, bool>::iterator i = want_.begin(); i != want_.end(); ++i) {
@@ -516,8 +570,47 @@ bool Builder::StartEdge(Edge* edge, string* err) {
}
void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
- if (success)
+ time_t restat_mtime = 0;
+
+ if (success) {
+ if (edge->rule_->restat_) {
+ bool node_cleaned = false;
+
+ for (vector<Node*>::iterator i = edge->outputs_.begin();
+ i != edge->outputs_.end(); ++i) {
+ if ((*i)->file_->exists()) {
+ time_t new_mtime = disk_interface_->Stat((*i)->file_->path_);
+ if ((*i)->file_->mtime_ == new_mtime) {
+ // The rule command did not change the output. Propagate the clean
+ // state through the build graph.
+ plan_.CleanNode(log_, *i);
+ node_cleaned = true;
+ }
+ }
+ }
+
+ if (node_cleaned) {
+ // If any output was cleaned, find the most recent mtime of any
+ // (existing) non-order-only input.
+ for (vector<Node*>::iterator i = edge->inputs_.begin();
+ i != edge->inputs_.end() - edge->order_only_deps_; ++i) {
+ time_t input_mtime = disk_interface_->Stat((*i)->file_->path_);
+ if (input_mtime == 0) {
+ restat_mtime = 0;
+ break;
+ }
+ if (input_mtime > restat_mtime)
+ restat_mtime = input_mtime;
+ }
+
+ // The total number of edges in the plan may have changed as a result
+ // of a restat.
+ status_->PlanHasTotalEdges(plan_.command_edge_count());
+ }
+ }
+
plan_.EdgeFinished(edge);
+ }
if (edge->is_phony())
return;
@@ -525,5 +618,5 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
int start_time, end_time;
status_->BuildEdgeFinished(edge, success, output, &start_time, &end_time);
if (success && log_)
- log_->RecordCommand(edge, start_time, end_time);
+ log_->RecordCommand(edge, start_time, end_time, restat_mtime);
}