summaryrefslogtreecommitdiffstats
path: root/src/build.cc
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2012-02-14 00:55:16 (GMT)
committerEvan Martin <martine@danga.com>2012-02-14 00:55:16 (GMT)
commit0eebd5fe1dec021a63110ecab8fa411f9980745f (patch)
tree470e397b33e8538d82ab2ce842cfd708450d872c /src/build.cc
parent51066421eef67847b244154119ca77a893bd6be8 (diff)
parent44f58aeca923d6e3001636f8c0d24a6d513d8ea2 (diff)
downloadNinja-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.cc129
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;
}