Skip to content

Commit 9ae7926

Browse files
authored
Merge pull request #1943 from jdrouhard/output_time_before_command
Provide resiliency against inputs changing during the build
2 parents 55f5451 + a2b5e6d commit 9ae7926

File tree

8 files changed

+396
-80
lines changed

8 files changed

+396
-80
lines changed

src/build.cc

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,10 @@ Builder::Builder(State* state, const BuildConfig& config,
518518
start_time_millis_(start_time_millis), disk_interface_(disk_interface),
519519
scan_(state, build_log, deps_log, disk_interface,
520520
&config_.depfile_parser_options) {
521+
lock_file_path_ = ".ninja_lock";
522+
string build_dir = state_->bindings_.LookupVariable("builddir");
523+
if (!build_dir.empty())
524+
lock_file_path_ = build_dir + "/" + lock_file_path_;
521525
}
522526

523527
Builder::~Builder() {
@@ -552,6 +556,10 @@ void Builder::Cleanup() {
552556
disk_interface_->RemoveFile(depfile);
553557
}
554558
}
559+
560+
string err;
561+
if (disk_interface_->Stat(lock_file_path_, &err) > 0)
562+
disk_interface_->RemoveFile(lock_file_path_);
555563
}
556564

557565
Node* Builder::AddTarget(const string& name, string* err) {
@@ -704,14 +712,25 @@ bool Builder::StartEdge(Edge* edge, string* err) {
704712

705713
status_->BuildEdgeStarted(edge, start_time_millis);
706714

707-
// Create directories necessary for outputs.
715+
TimeStamp build_start = -1;
716+
717+
// Create directories necessary for outputs and remember the current
718+
// filesystem mtime to record later
708719
// XXX: this will block; do we care?
709720
for (vector<Node*>::iterator o = edge->outputs_.begin();
710721
o != edge->outputs_.end(); ++o) {
711722
if (!disk_interface_->MakeDirs((*o)->path()))
712723
return false;
724+
if (build_start == -1) {
725+
disk_interface_->WriteFile(lock_file_path_, "");
726+
build_start = disk_interface_->Stat(lock_file_path_, err);
727+
if (build_start == -1)
728+
build_start = 0;
729+
}
713730
}
714731

732+
edge->command_start_time_ = build_start;
733+
715734
// Create response file, if needed
716735
// XXX: this may also block; do we care?
717736
string rspfile = edge->GetUnescapedRspfile();
@@ -770,55 +789,42 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
770789
}
771790

772791
// Restat the edge outputs
773-
TimeStamp output_mtime = 0;
774-
bool restat = edge->GetBindingBool("restat");
792+
TimeStamp record_mtime = 0;
775793
if (!config_.dry_run) {
794+
const bool restat = edge->GetBindingBool("restat");
795+
const bool generator = edge->GetBindingBool("generator");
776796
bool node_cleaned = false;
777-
778-
for (vector<Node*>::iterator o = edge->outputs_.begin();
779-
o != edge->outputs_.end(); ++o) {
780-
TimeStamp new_mtime = disk_interface_->Stat((*o)->path(), err);
781-
if (new_mtime == -1)
782-
return false;
783-
if (new_mtime > output_mtime)
784-
output_mtime = new_mtime;
785-
if ((*o)->mtime() == new_mtime && restat) {
786-
// The rule command did not change the output. Propagate the clean
787-
// state through the build graph.
788-
// Note that this also applies to nonexistent outputs (mtime == 0).
789-
if (!plan_.CleanNode(&scan_, *o, err))
797+
record_mtime = edge->command_start_time_;
798+
799+
// restat and generator rules must restat the outputs after the build
800+
// has finished. if record_mtime == 0, then there was an error while
801+
// attempting to touch/stat the temp file when the edge started and
802+
// we should fall back to recording the outputs' current mtime in the
803+
// log.
804+
if (record_mtime == 0 || restat || generator) {
805+
for (vector<Node*>::iterator o = edge->outputs_.begin();
806+
o != edge->outputs_.end(); ++o) {
807+
TimeStamp new_mtime = disk_interface_->Stat((*o)->path(), err);
808+
if (new_mtime == -1)
790809
return false;
791-
node_cleaned = true;
810+
if (new_mtime > record_mtime)
811+
record_mtime = new_mtime;
812+
if ((*o)->mtime() == new_mtime && restat) {
813+
// The rule command did not change the output. Propagate the clean
814+
// state through the build graph.
815+
// Note that this also applies to nonexistent outputs (mtime == 0).
816+
if (!plan_.CleanNode(&scan_, *o, err))
817+
return false;
818+
node_cleaned = true;
819+
}
792820
}
793821
}
794-
795822
if (node_cleaned) {
796-
TimeStamp restat_mtime = 0;
797-
// If any output was cleaned, find the most recent mtime of any
798-
// (existing) non-order-only input or the depfile.
799-
for (vector<Node*>::iterator i = edge->inputs_.begin();
800-
i != edge->inputs_.end() - edge->order_only_deps_; ++i) {
801-
TimeStamp input_mtime = disk_interface_->Stat((*i)->path(), err);
802-
if (input_mtime == -1)
803-
return false;
804-
if (input_mtime > restat_mtime)
805-
restat_mtime = input_mtime;
806-
}
807-
808-
string depfile = edge->GetUnescapedDepfile();
809-
if (restat_mtime != 0 && deps_type.empty() && !depfile.empty()) {
810-
TimeStamp depfile_mtime = disk_interface_->Stat(depfile, err);
811-
if (depfile_mtime == -1)
812-
return false;
813-
if (depfile_mtime > restat_mtime)
814-
restat_mtime = depfile_mtime;
815-
}
823+
record_mtime = edge->command_start_time_;
816824

817825
// The total number of edges in the plan may have changed as a result
818826
// of a restat.
819827
status_->PlanHasTotalEdges(plan_.command_edge_count());
820-
821-
output_mtime = restat_mtime;
822828
}
823829
}
824830

@@ -832,7 +838,7 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
832838

833839
if (scan_.build_log()) {
834840
if (!scan_.build_log()->RecordCommand(edge, start_time_millis,
835-
end_time_millis, output_mtime)) {
841+
end_time_millis, record_mtime)) {
836842
*err = string("Error writing to build log: ") + strerror(errno);
837843
return false;
838844
}

src/build.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ struct Builder {
234234
/// Time the build started.
235235
int64_t start_time_millis_;
236236

237+
std::string lock_file_path_;
237238
DiskInterface* disk_interface_;
238239
DependencyScan scan_;
239240

src/build_log.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ BuildLog::LogEntry::LogEntry(const string& output)
116116
: output(output) {}
117117

118118
BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash,
119-
int start_time, int end_time, TimeStamp restat_mtime)
119+
int start_time, int end_time, TimeStamp mtime)
120120
: output(output), command_hash(command_hash),
121-
start_time(start_time), end_time(end_time), mtime(restat_mtime)
121+
start_time(start_time), end_time(end_time), mtime(mtime)
122122
{}
123123

124124
BuildLog::BuildLog()
@@ -303,7 +303,7 @@ LoadStatus BuildLog::Load(const string& path, string* err) {
303303
*end = 0;
304304

305305
int start_time = 0, end_time = 0;
306-
TimeStamp restat_mtime = 0;
306+
TimeStamp mtime = 0;
307307

308308
start_time = atoi(start);
309309
start = end + 1;
@@ -319,7 +319,7 @@ LoadStatus BuildLog::Load(const string& path, string* err) {
319319
if (!end)
320320
continue;
321321
*end = 0;
322-
restat_mtime = strtoll(start, NULL, 10);
322+
mtime = strtoll(start, NULL, 10);
323323
start = end + 1;
324324

325325
end = (char*)memchr(start, kFieldSeparator, line_end - start);
@@ -343,7 +343,7 @@ LoadStatus BuildLog::Load(const string& path, string* err) {
343343

344344
entry->start_time = start_time;
345345
entry->end_time = end_time;
346-
entry->mtime = restat_mtime;
346+
entry->mtime = mtime;
347347
if (log_version >= 5) {
348348
char c = *end; *end = '\0';
349349
entry->command_hash = (uint64_t)strtoull(start, NULL, 16);

src/build_log.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ struct BuildLog {
7373

7474
explicit LogEntry(const std::string& output);
7575
LogEntry(const std::string& output, uint64_t command_hash,
76-
int start_time, int end_time, TimeStamp restat_mtime);
76+
int start_time, int end_time, TimeStamp mtime);
7777
};
7878

7979
/// Lookup a previously-run command by its output path.

0 commit comments

Comments
 (0)