Skip to content

Commit

Permalink
Emitting output of commands running solo in real time
Browse files Browse the repository at this point in the history
(as opposed to holding commands output until these finish)
  • Loading branch information
maximuska committed Jul 28, 2013
1 parent f64e068 commit 7955938
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 2 deletions.
64 changes: 62 additions & 2 deletions src/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand All @@ -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);
}

Expand All @@ -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);
Expand Down Expand Up @@ -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("");
}
Expand Down Expand Up @@ -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<Edge*> GetActiveEdges();
virtual void Abort();

Expand All @@ -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<Subprocess*, Edge*>::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);
Expand Down Expand Up @@ -895,7 +943,19 @@ void Builder::ReportProgress(void)
vector<Edge*> 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,
Expand Down
12 changes: 12 additions & 0 deletions src/build.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }

Expand Down Expand Up @@ -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<Edge*> GetActiveEdges() { return vector<Edge*>(); }
virtual void Abort() {}
};
Expand Down Expand Up @@ -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();

Expand All @@ -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_;

Expand All @@ -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_;

Expand Down
8 changes: 8 additions & 0 deletions src/line_printer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
4 changes: 4 additions & 0 deletions src/line_printer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down

0 comments on commit 7955938

Please sign in to comment.