diff options
author | Evan Martin <martine@danga.com> | 2012-02-14 00:55:16 (GMT) |
---|---|---|
committer | Evan Martin <martine@danga.com> | 2012-02-14 00:55:16 (GMT) |
commit | 0eebd5fe1dec021a63110ecab8fa411f9980745f (patch) | |
tree | 470e397b33e8538d82ab2ce842cfd708450d872c /src/build.cc | |
parent | 51066421eef67847b244154119ca77a893bd6be8 (diff) | |
parent | 44f58aeca923d6e3001636f8c0d24a6d513d8ea2 (diff) | |
download | Ninja-0eebd5fe1dec021a63110ecab8fa411f9980745f.zip Ninja-0eebd5fe1dec021a63110ecab8fa411f9980745f.tar.gz Ninja-0eebd5fe1dec021a63110ecab8fa411f9980745f.tar.bz2 |
Merge pull request #176 from pcc/exit-cleanup
Implement cleanup-on-interrupt
Diffstat (limited to 'src/build.cc')
-rw-r--r-- | src/build.cc | 129 |
1 files changed, 92 insertions, 37 deletions
diff --git a/src/build.cc b/src/build.cc index cfe2c65..326c345 100644 --- a/src/build.cc +++ b/src/build.cc @@ -39,6 +39,7 @@ struct BuildStatus { void BuildEdgeStarted(Edge* edge); void BuildEdgeFinished(Edge* edge, bool success, const string& output, int* start_time, int* end_time); + void BuildFinished(); private: void PrintStatus(Edge* edge); @@ -52,6 +53,8 @@ struct BuildStatus { int started_edges_, finished_edges_, total_edges_; + bool have_blank_line_; + /// Map of running edge to time the edge started running. typedef map<Edge*, int> RunningEdgeMap; RunningEdgeMap running_edges_; @@ -64,7 +67,8 @@ BuildStatus::BuildStatus(const BuildConfig& config) : config_(config), start_time_millis_(GetTimeMillis()), last_update_millis_(start_time_millis_), - started_edges_(0), finished_edges_(0), total_edges_(0) { + started_edges_(0), finished_edges_(0), total_edges_(0), + have_blank_line_(true) { #ifndef _WIN32 const char* term = getenv("TERM"); smart_terminal_ = isatty(1) && term && string(term) != "dumb"; @@ -115,10 +119,7 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, PrintStatus(edge); if (success && output.empty()) { - if (smart_terminal_) { - if (finished_edges_ == total_edges_) - printf("\n"); - } else { + if (!smart_terminal_) { if (total_time > 5*1000) { printf("%.1f%% %d/%d\n", finished_edges_ * 100 / (float)total_edges_, finished_edges_, total_edges_); @@ -153,9 +154,16 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, if (!final_output.empty()) printf("%s", final_output.c_str()); + + have_blank_line_ = true; } } +void BuildStatus::BuildFinished() { + if (smart_terminal_ && !have_blank_line_) + printf("\n"); +} + void BuildStatus::PrintStatus(Edge* edge) { if (config_.verbosity == BuildConfig::QUIET) return; @@ -195,6 +203,7 @@ void BuildStatus::PrintStatus(Edge* edge) { if (smart_terminal_ && !force_full_command) { printf("\x1B[K"); // Clear to end of line. fflush(stdout); + have_blank_line_ = false; } else { printf("\n"); } @@ -387,35 +396,52 @@ struct RealCommandRunner : public CommandRunner { virtual ~RealCommandRunner() {} virtual bool CanRunMore(); virtual bool StartCommand(Edge* edge); - virtual Edge* WaitForCommand(bool* success, string* output); + virtual Edge* WaitForCommand(ExitStatus* status, string* output); + virtual vector<Edge*> GetActiveEdges(); + virtual void Abort(); const BuildConfig& config_; SubprocessSet subprocs_; map<Subprocess*, Edge*> subproc_to_edge_; }; +vector<Edge*> RealCommandRunner::GetActiveEdges() { + vector<Edge*> edges; + for (map<Subprocess*, Edge*>::iterator i = subproc_to_edge_.begin(); + i != subproc_to_edge_.end(); ++i) + edges.push_back(i->second); + return edges; +} + +void RealCommandRunner::Abort() { + subprocs_.Clear(); +} + bool RealCommandRunner::CanRunMore() { return ((int)subprocs_.running_.size()) < config_.parallelism; } bool RealCommandRunner::StartCommand(Edge* edge) { string command = edge->EvaluateCommand(); - Subprocess* subproc = new Subprocess; - subproc_to_edge_.insert(make_pair(subproc, edge)); - if (!subproc->Start(&subprocs_, command)) + Subprocess* subproc = subprocs_.Add(command); + if (!subproc) return false; - - subprocs_.Add(subproc); + subproc_to_edge_.insert(make_pair(subproc, edge)); + return true; } -Edge* RealCommandRunner::WaitForCommand(bool* success, string* output) { +Edge* RealCommandRunner::WaitForCommand(ExitStatus* status, string* output) { Subprocess* subproc; while ((subproc = subprocs_.NextFinished()) == NULL) { - subprocs_.DoWork(); + bool interrupted = subprocs_.DoWork(); + if (interrupted) { + *status = ExitInterrupted; + return 0; + } } - *success = subproc->Finish(); + *status = subproc->Finish(); *output = subproc->GetOutput(); map<Subprocess*, Edge*>::iterator i = subproc_to_edge_.find(subproc); @@ -436,10 +462,12 @@ struct DryRunCommandRunner : public CommandRunner { finished_.push(edge); return true; } - virtual Edge* WaitForCommand(bool* success, string* output) { - if (finished_.empty()) + virtual Edge* WaitForCommand(ExitStatus* status, string* /* output */) { + if (finished_.empty()) { + *status = ExitFailure; return NULL; - *success = true; + } + *status = ExitSuccess; Edge* edge = finished_.front(); finished_.pop(); return edge; @@ -452,13 +480,29 @@ Builder::Builder(State* state, const BuildConfig& config) : state_(state), config_(config) { disk_interface_ = new RealDiskInterface; if (config.dry_run) - command_runner_ = new DryRunCommandRunner; + command_runner_.reset(new DryRunCommandRunner); else - command_runner_ = new RealCommandRunner(config); + command_runner_.reset(new RealCommandRunner(config)); status_ = new BuildStatus(config); log_ = state->build_log_; } +Builder::~Builder() { + if (command_runner_.get()) { + vector<Edge*> active_edges = command_runner_->GetActiveEdges(); + command_runner_->Abort(); + + for (vector<Edge*>::iterator i = active_edges.begin(); + i != active_edges.end(); ++i) { + for (vector<Node*>::iterator ni = (*i)->outputs_.begin(); + ni != (*i)->outputs_.end(); ++ni) + disk_interface_->RemoveFile((*ni)->path()); + if (!(*i)->rule_->depfile_.empty()) + disk_interface_->RemoveFile((*i)->EvaluateDepFile()); + } + } +} + Node* Builder::AddTarget(const string& name, string* err) { Node* node = state_->LookupNode(name); if (!node) { @@ -494,7 +538,7 @@ bool Builder::Build(string* err) { status_->PlanHasTotalEdges(plan_.command_edge_count()); int pending_commands = 0; - int failures_allowed = config_.swallow_failures; + int failures_allowed = config_.failures_allowed; // This main loop runs the entire build process. // It is structured like this: @@ -505,10 +549,12 @@ bool Builder::Build(string* err) { // an error. while (plan_.more_to_do()) { // See if we can start any more commands. - if (command_runner_->CanRunMore()) { + if (failures_allowed && command_runner_->CanRunMore()) { if (Edge* edge = plan_.FindWork()) { - if (!StartEdge(edge, err)) + if (!StartEdge(edge, err)) { + status_->BuildFinished(); return false; + } if (edge->is_phony()) FinishEdge(edge, true, ""); @@ -522,43 +568,52 @@ bool Builder::Build(string* err) { // See if we can reap any finished commands. if (pending_commands) { - bool success; + ExitStatus status; string output; - Edge* edge; - if ((edge = command_runner_->WaitForCommand(&success, &output))) { + Edge* edge = command_runner_->WaitForCommand(&status, &output); + if (edge && status != ExitInterrupted) { + bool success = (status == ExitSuccess); --pending_commands; FinishEdge(edge, success, output); if (!success) { - if (failures_allowed-- == 0) { - if (config_.swallow_failures != 0) - *err = "subcommands failed"; - else - *err = "subcommand failed"; - return false; - } + if (failures_allowed) + failures_allowed--; } // We made some progress; start the main loop over. continue; } + + if (status == ExitInterrupted) { + status_->BuildFinished(); + *err = "interrupted by user"; + return false; + } } // If we get here, we can neither enqueue new commands nor are any running. if (pending_commands) { + status_->BuildFinished(); *err = "stuck: pending commands but none to wait for? [this is a bug]"; return false; } // If we get here, we cannot make any more progress. - if (failures_allowed < config_.swallow_failures) { + status_->BuildFinished(); + if (failures_allowed == 0) { + if (config_.failures_allowed > 1) + *err = "subcommands failed"; + else + *err = "subcommand failed"; + } else if (failures_allowed < config_.failures_allowed) *err = "cannot make progress due to previous errors"; - return false; - } else { + else *err = "stuck [this is a bug]"; - return false; - } + + return false; } + status_->BuildFinished(); return true; } |