summaryrefslogtreecommitdiffstats
path: root/src/build.cc
diff options
context:
space:
mode:
authorNico Weber <nicolasweber@gmx.de>2015-03-19 15:42:46 (GMT)
committerNico Weber <nicolasweber@gmx.de>2015-03-19 16:45:58 (GMT)
commitb334523f1da03adfcd23b6e7e7a66c8fcbf87840 (patch)
treeaa073a311c1e3380e26592b85e437322757f2921 /src/build.cc
parentbc38ef76ebfabe503b1b56a1e32827a851037766 (diff)
downloadNinja-b334523f1da03adfcd23b6e7e7a66c8fcbf87840.zip
Ninja-b334523f1da03adfcd23b6e7e7a66c8fcbf87840.tar.gz
Ninja-b334523f1da03adfcd23b6e7e7a66c8fcbf87840.tar.bz2
Make failing stat() calls abort the build.
Fixes #830, fixes #904. In practice, this either happens with 64-bit inodes and a 32-bit userspace when building without -D_FILE_OFFSET_BITS=64 in CFLAGS, or when a filename is longer than the system file length limit. Since DiskInterface::Stat() returns -1 on error, and Node used -1 on "stat state unknown", not aborting the build lead to ninja stat()ing the same file over and over again, until it finally ran out of stack. That's now fixed. * Change RecomputeOutputsDirty() to return success instead of dirty state (like RecomputeDirty()) and return the dirty state in a bool outparam * Node::Stat()s old return value wasn't used anywhere, change the function to return success instead and add an |err| outparam * Node::StatIfNecessary()'s old return value was used only in one place. Change that place to explicitly check status_known() and make StatIfNecessary() return success and add an |err| outparam * Plan::CleanNode() can now fail, make it return bool and add an |err| outparam
Diffstat (limited to 'src/build.cc')
-rw-r--r--src/build.cc21
1 files changed, 17 insertions, 4 deletions
diff --git a/src/build.cc b/src/build.cc
index 5d66f4b..1e10c7c 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -406,7 +406,7 @@ void Plan::NodeFinished(Node* node) {
}
}
-void Plan::CleanNode(DependencyScan* scan, Node* node) {
+bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) {
node->set_dirty(false);
for (vector<Edge*>::const_iterator oe = node->out_edges().begin();
@@ -436,10 +436,16 @@ void Plan::CleanNode(DependencyScan* scan, Node* node) {
// Now, this edge is dirty if any of the outputs are dirty.
// If the edge isn't dirty, clean the outputs and mark the edge as not
// wanted.
- if (!scan->RecomputeOutputsDirty(*oe, most_recent_input)) {
+ bool outputs_dirty = false;
+ if (!scan->RecomputeOutputsDirty(*oe, most_recent_input,
+ &outputs_dirty, err)) {
+ return false;
+ }
+ if (!outputs_dirty) {
for (vector<Node*>::iterator o = (*oe)->outputs_.begin();
o != (*oe)->outputs_.end(); ++o) {
- CleanNode(scan, *o);
+ if (!CleanNode(scan, *o, err))
+ return false;
}
want_e->second = false;
@@ -449,6 +455,7 @@ void Plan::CleanNode(DependencyScan* scan, Node* node) {
}
}
}
+ return true;
}
void Plan::Dump() {
@@ -758,7 +765,8 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
// The rule command did not change the output. Propagate the clean
// state through the build graph.
// Note that this also applies to nonexistent outputs (mtime == 0).
- plan_.CleanNode(&scan_, *o);
+ if (!plan_.CleanNode(&scan_, *o, err))
+ return false;
node_cleaned = true;
}
}
@@ -805,6 +813,11 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
assert(edge->outputs_.size() == 1 && "should have been rejected by parser");
Node* out = edge->outputs_[0];
TimeStamp deps_mtime = disk_interface_->Stat(out->path());
+ if (deps_mtime == -1) {
+ // TODO: Let DiskInterface::Stat() take err instead of it calling Error().
+ *err = "stat failed";
+ return false;
+ }
if (!scan_.deps_log()->RecordDeps(out, deps_mtime, deps_nodes)) {
*err = string("Error writing to deps log: ") + strerror(errno);
return false;