From 7955938ea58c329ce9f16c54fcf66f7f1d7fefdc Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Mon, 22 Jul 2013 23:39:38 +0300 Subject: [PATCH] Emitting output of commands running solo in real time (as opposed to holding commands output until these finish) --- src/build.cc | 64 +++++++++++++++++++++++++++++++++++++++++++-- src/build.h | 12 +++++++++ src/line_printer.cc | 8 ++++++ src/line_printer.h | 4 +++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/build.cc b/src/build.cc index a95406ef0f..5978a7a69a 100644 --- a/src/build.cc +++ b/src/build.cc @@ -76,6 +76,7 @@ BuildStatus::BuildStatus(const BuildConfig& config) start_time_millis_(GetTimeMillis()), started_edges_(0), finished_edges_(0), restarted_edges_(0), total_edges_(0), next_progress_update_at_(0), + solo_bytes_printed_(0), progress_status_format_(NULL), overall_rate_(), current_rate_(config.parallelism) { @@ -97,8 +98,10 @@ void BuildStatus::BuildEdgeStarted(Edge* edge) { running_edges_.insert(make_pair(edge, start_time)); ++started_edges_; - RestartStillRunningDelay(); + // Edge running solo is supposed to keep running alone. + assert(solo_bytes_printed_ == 0); + RestartStillRunningDelay(); PrintStatus(edge); } @@ -124,6 +127,11 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, return; RestartStillRunningDelay(); + if (solo_bytes_printed_ > 0) { + // Special treatment to edges status + BuildEdgeFinishedSolo(edge, success, output); + return; + } if (printer_.is_smart_terminal()) PrintStatus(edge); @@ -176,6 +184,35 @@ void BuildStatus::RestartStillRunningDelay() next_progress_update_at_ = GetTimeMillis() + kStillRunningDelayMsec; } +void BuildStatus::BuildEdgeRunningSolo(Edge* edge, const string& output) +{ + if (config_.verbosity == BuildConfig::QUIET || !printer_.is_smart_terminal()) + return; + + if (output.size() > solo_bytes_printed_) { + if (solo_bytes_printed_ == 0) { + // Print edge description before starting to print its output + PrintStatus(edge, "\n"); + } + printer_.PrintRaw(output.substr(solo_bytes_printed_)); + solo_bytes_printed_ = output.size(); + } +} + +void BuildStatus::BuildEdgeFinishedSolo(Edge* edge, + bool success, + const string& output) +{ + // Print reminders of the output, if any + BuildEdgeRunningSolo(edge, output); + + // Print the command that has failed + if (!success) + printer_.PrintOnNewLine("^^FAILED: " + edge->EvaluateCommand() + "\n"); + + solo_bytes_printed_ = 0; +} + void BuildStatus::BuildFinished() { printer_.PrintOnNewLine(""); } @@ -499,6 +536,7 @@ struct RealCommandRunner : public CommandRunner { virtual bool CanRunMore(); virtual bool StartCommand(Edge* edge); virtual bool WaitForCommand(Result* result); + virtual void PeekCommandOutput(Edge* edge, string* out); virtual vector GetActiveEdges(); virtual void Abort(); @@ -525,6 +563,16 @@ bool RealCommandRunner::CanRunMore() { || GetLoadAverage() < config_.max_load_average); } +void RealCommandRunner::PeekCommandOutput(Edge* edge, string* out) +{ + // Note: search linearly, but as long this is only invoked when number of + // edges == 1, nothing better is necessary. + for (map::iterator i = subproc_to_edge_.begin(); + i != subproc_to_edge_.end(); ++i) + if (i->second == edge) + *out = i->first->GetOutput(); +} + bool RealCommandRunner::StartCommand(Edge* edge) { string command = edge->EvaluateCommand(); Subprocess* subproc = subprocs_.Add(command); @@ -895,7 +943,19 @@ void Builder::ReportProgress(void) vector active_edges = command_runner_->GetActiveEdges(); if (active_edges.size() < 1) return; - status_->BuildEdgeStillRunning(active_edges[0]); + + Edge* edge = active_edges[0]; + if (active_edges.size() == 1 && !plan_.HasWork()) { + // If a command is running 'solo' AND produces some output, start printing + // its output in the real time w/o waiting for its completion. + string output; + command_runner_->PeekCommandOutput(edge, &output); + if (output.size()) { + status_->BuildEdgeRunningSolo(edge, output); + return; + } + } + status_->BuildEdgeStillRunning(edge); } bool Builder::ExtractDeps(CommandRunner::Result* result, diff --git a/src/build.h b/src/build.h index 67bd23fb0c..47720f967e 100644 --- a/src/build.h +++ b/src/build.h @@ -53,6 +53,9 @@ struct Plan { // Returns NULL if there's no work to do. Edge* FindWork(); + // Returns true if there are edges waiting to run now. + bool HasWork() const { return !ready_.empty(); } + /// Returns true if there's more work to be done. bool more_to_do() const { return wanted_edges_; } @@ -119,6 +122,9 @@ struct CommandRunner { /// Wait for a command to complete, or return false if interrupted. virtual bool WaitForCommand(Result* result) = 0; + /// Return command output collected so far + virtual void PeekCommandOutput(Edge* edge, string* out) {} + virtual vector GetActiveEdges() { return vector(); } virtual void Abort() {} }; @@ -206,6 +212,7 @@ struct BuildStatus { void BuildEdgeFinished(Edge* edge, bool success, const string& output, int* start_time, int* end_time); void BuildEdgeStillRunning(Edge* edge); + void BuildEdgeRunningSolo(Edge* edge, const string& output); void BuildEdgeShouldRestart(); void BuildFinished(); @@ -218,6 +225,7 @@ struct BuildStatus { private: void PrintStatus(Edge* edge, const char* trailer = ""); void RestartStillRunningDelay(); + void BuildEdgeFinishedSolo(Edge* edge, bool success, const string& output); const BuildConfig& config_; @@ -237,6 +245,10 @@ struct BuildStatus { /// displayed. Reset when regular edge start/end notifications are printed. int64_t next_progress_update_at_; + /// Counts how much was printed so far for an edge running solo. + /// Non-zero means that only one edge can be running. + size_t solo_bytes_printed_; + /// The custom progress status format to use. const char* progress_status_format_; diff --git a/src/line_printer.cc b/src/line_printer.cc index a75eb0505f..e2354098c4 100644 --- a/src/line_printer.cc +++ b/src/line_printer.cc @@ -107,3 +107,11 @@ void LinePrinter::PrintOnNewLine(const string& to_print) { printf("%s", to_print.c_str()); have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n'; } + +void LinePrinter::PrintRaw(const string& to_print) { + if (to_print.empty()) + return; + fwrite(to_print.c_str(), to_print.size(), 1, stdout); + fflush(stdout); + have_blank_line_ = (*to_print.rbegin() == '\n'); +} diff --git a/src/line_printer.h b/src/line_printer.h index aea2817272..8527f53b34 100644 --- a/src/line_printer.h +++ b/src/line_printer.h @@ -37,6 +37,10 @@ struct LinePrinter { /// Prints a string on a new line, not overprinting previous output. void PrintOnNewLine(const string& to_print); + /// Prints the string at current cursor position as-is, not overprinting + /// previous output and flushes the stdout. + void PrintRaw(const string& to_print); + private: /// Whether we can do fancy terminal control codes. bool smart_terminal_;