#include "status.h"
#include <stdarg.h>
#include <stdlib.h>
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include "debug_flags.h"
using namespace std;
StatusPrinter::StatusPrinter(const BuildConfig& config)
: config_(config),
started_edges_(0), finished_edges_(0), total_edges_(0), running_edges_(0),
time_millis_(0), progress_status_format_(NULL),
current_rate_(config.parallelism) {
if (config_.verbosity != BuildConfig::NORMAL)
printer_.set_smart_terminal(false);
progress_status_format_ = getenv("NINJA_STATUS");
if (!progress_status_format_)
progress_status_format_ = "[%f/%t] ";
}
void StatusPrinter::PlanHasTotalEdges(int total) {
total_edges_ = total;
}
void StatusPrinter::BuildEdgeStarted(const Edge* edge,
int64_t start_time_millis) {
++started_edges_;
++running_edges_;
time_millis_ = start_time_millis;
if (edge->use_console() || printer_.is_smart_terminal())
PrintStatus(edge, start_time_millis);
if (edge->use_console())
printer_.SetConsoleLocked(true);
}
void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t end_time_millis,
bool success, const string& output) {
time_millis_ = end_time_millis;
++finished_edges_;
if (edge->use_console())
printer_.SetConsoleLocked(false);
if (config_.verbosity == BuildConfig::QUIET)
return;
if (!edge->use_console())
PrintStatus(edge, end_time_millis);
--running_edges_;
if (!success) {
string outputs;
for (vector<Node*>::const_iterator o = edge->outputs_.begin();
o != edge->outputs_.end(); ++o)
outputs += (*o)->path() + " ";
if (printer_.supports_color()) {
printer_.PrintOnNewLine("\x1B[31m" "FAILED: " "\x1B[0m" + outputs + "\n");
} else {
printer_.PrintOnNewLine("FAILED: " + outputs + "\n");
}
printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n");
}
if (!output.empty()) {
string final_output;
if (!printer_.supports_color())
final_output = StripAnsiEscapeCodes(output);
else
final_output = output;
#ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY); #endif
printer_.PrintOnNewLine(final_output);
#ifdef _WIN32
_setmode(_fileno(stdout), _O_TEXT); #endif
}
}
void StatusPrinter::BuildLoadDyndeps() {
if (g_explaining)
printer_.PrintOnNewLine("");
}
void StatusPrinter::BuildStarted() {
started_edges_ = 0;
finished_edges_ = 0;
running_edges_ = 0;
}
void StatusPrinter::BuildFinished() {
printer_.SetConsoleLocked(false);
printer_.PrintOnNewLine("");
}
string StatusPrinter::FormatProgressStatus(const char* progress_status_format,
int64_t time_millis) const {
string out;
char buf[32];
for (const char* s = progress_status_format; *s != '\0'; ++s) {
if (*s == '%') {
++s;
switch (*s) {
case '%':
out.push_back('%');
break;
case 's':
snprintf(buf, sizeof(buf), "%d", started_edges_);
out += buf;
break;
case 't':
snprintf(buf, sizeof(buf), "%d", total_edges_);
out += buf;
break;
case 'r': {
snprintf(buf, sizeof(buf), "%d", running_edges_);
out += buf;
break;
}
case 'u':
snprintf(buf, sizeof(buf), "%d", total_edges_ - started_edges_);
out += buf;
break;
case 'f':
snprintf(buf, sizeof(buf), "%d", finished_edges_);
out += buf;
break;
case 'o':
SnprintfRate(finished_edges_ / (time_millis_ / 1e3), buf, "%.1f");
out += buf;
break;
case 'c':
current_rate_.UpdateRate(finished_edges_, time_millis_);
SnprintfRate(current_rate_.rate(), buf, "%.1f");
out += buf;
break;
case 'p': {
int percent = (100 * finished_edges_) / total_edges_;
snprintf(buf, sizeof(buf), "%3i%%", percent);
out += buf;
break;
}
case 'e': {
snprintf(buf, sizeof(buf), "%.3f", time_millis_ / 1e3);
out += buf;
break;
}
default:
Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s);
return "";
}
} else {
out.push_back(*s);
}
}
return out;
}
void StatusPrinter::PrintStatus(const Edge* edge, int64_t time_millis) {
if (config_.verbosity == BuildConfig::QUIET
|| config_.verbosity == BuildConfig::NO_STATUS_UPDATE)
return;
bool force_full_command = config_.verbosity == BuildConfig::VERBOSE;
string to_print = edge->GetBinding("description");
if (to_print.empty() || force_full_command)
to_print = edge->GetBinding("command");
to_print = FormatProgressStatus(progress_status_format_, time_millis)
+ to_print;
printer_.Print(to_print,
force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE);
}
void StatusPrinter::Warning(const char* msg, ...) {
va_list ap;
va_start(ap, msg);
::Warning(msg, ap);
va_end(ap);
}
void StatusPrinter::Error(const char* msg, ...) {
va_list ap;
va_start(ap, msg);
::Error(msg, ap);
va_end(ap);
}
void StatusPrinter::Info(const char* msg, ...) {
va_list ap;
va_start(ap, msg);
::Info(msg, ap);
va_end(ap);
}