diff --git a/src/build.cc b/src/build.cc index 0a6dd8e7..51989778 100644 --- a/src/build.cc +++ b/src/build.cc @@ -604,7 +604,7 @@ Builder::Builder(State* state, const BuildConfig& config, BuildLog* build_log, Status* status, int64_t start_time_millis) : state_(state), config_(config), plan_(this), status_(status), start_time_millis_(start_time_millis), disk_interface_(disk_interface), - explanations_(g_explaining ? new Explanations() : nullptr), + explanations_(g_explaining ? new Explanations(status) : nullptr), scan_(state, build_log, deps_log, disk_interface, &config_.depfile_parser_options, explanations_.get()) { lock_file_path_ = ".ninja_lock"; diff --git a/src/explanations.cc b/src/explanations.cc index 2b44a918..d42ec7a1 100644 --- a/src/explanations.cc +++ b/src/explanations.cc @@ -21,6 +21,26 @@ #include #include +#include "graph.h" +#include "status_printer.h" + +namespace { +void Explain(const char* msg, va_list ap) { + fputs("ninja explain: ", stderr); + vfprintf(stderr, msg, ap); + fputs("\n", stderr); +} + +void Explain(const char* msg, ...) { + va_list ap; + va_start(ap, msg); + Explain(msg, ap); + va_end(ap); +} +} // anonymous namespace + +Explanations::Explanations(Status* status) : status_(status) {} + void Explanations::Record(const void* item, const char* fmt, ...) { va_list args; va_start(args, fmt); @@ -34,6 +54,24 @@ void Explanations::RecordArgs(const void* item, const char* fmt, va_list args) { map_[item].emplace_back(buffer); } +void Explanations::ExplainEdge(const Edge* edge) { + // Collect all explanations for the current edge's outputs. + std::vector explanations; + for (Node* output : edge->outputs_) { + this->LookupAndAppend(output, &explanations); + } + if (!explanations.empty()) { + if (status_) { + // Start a new line so that the first explanation + // does not append to the status line. + status_->NewLine(); + } + for (const auto& exp : explanations) { + Explain("%s", exp.c_str()); + } + } +} + void Explanations::LookupAndAppend(const void* item, std::vector* out) { auto it = map_.find(item); diff --git a/src/explanations.h b/src/explanations.h index 2160e282..82a698f0 100644 --- a/src/explanations.h +++ b/src/explanations.h @@ -20,23 +20,32 @@ #include #include +struct Edge; +struct Status; + /// A class used to record a list of explanation strings associated /// with a given 'item' pointer. This is used to implement the /// `-d explain` feature. struct Explanations { public: + Explanations(Status* status = nullptr); + /// Record an explanation for |item| if this instance is enabled. void Record(const void* item, const char* fmt, ...); /// Same as Record(), but uses a va_list to pass formatting arguments. void RecordArgs(const void* item, const char* fmt, va_list args); + /// Print recorded explanations for an edge. + void ExplainEdge(const Edge* edge); + /// Lookup the explanations recorded for |item|, and append them /// to |*out|, if any. void LookupAndAppend(const void* item, std::vector* out); private: std::unordered_map> map_; + Status* status_; }; /// Convenience wrapper for an Explanations pointer, which can be null diff --git a/src/status.h b/src/status.h index a8739934..2984ac20 100644 --- a/src/status.h +++ b/src/status.h @@ -40,6 +40,9 @@ struct Status { /// (which is the default). virtual void SetExplanations(Explanations*) = 0; + /// Start a new line so that new output does not append to the status line. + virtual void NewLine() = 0; + virtual void Info(const char* msg, ...) = 0; virtual void Warning(const char* msg, ...) = 0; virtual void Error(const char* msg, ...) = 0; diff --git a/src/status_printer.cc b/src/status_printer.cc index e69cd15a..407a5818 100644 --- a/src/status_printer.cc +++ b/src/status_printer.cc @@ -407,19 +407,7 @@ string StatusPrinter::FormatProgressStatus(const char* progress_status_format, void StatusPrinter::PrintStatus(const Edge* edge, int64_t time_millis) { if (explanations_) { - // Collect all explanations for the current edge's outputs. - std::vector explanations; - for (Node* output : edge->outputs_) { - explanations_->LookupAndAppend(output, &explanations); - } - if (!explanations.empty()) { - // Start a new line so that the first explanation does not append to the - // status line. - printer_.PrintOnNewLine(""); - for (const auto& exp : explanations) { - fprintf(stderr, "ninja explain: %s\n", exp.c_str()); - } - } + explanations_->ExplainEdge(edge); } if (config_.verbosity == BuildConfig::QUIET @@ -441,6 +429,10 @@ void StatusPrinter::PrintStatus(const Edge* edge, int64_t time_millis) { force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE); } +void StatusPrinter::NewLine() { + printer_.PrintOnNewLine(""); +} + void StatusPrinter::Warning(const char* msg, ...) { va_list ap; va_start(ap, msg); diff --git a/src/status_printer.h b/src/status_printer.h index 213e9ce8..af5bdca0 100644 --- a/src/status_printer.h +++ b/src/status_printer.h @@ -37,6 +37,7 @@ struct StatusPrinter : Status { void BuildStarted() override; void BuildFinished() override; + void NewLine() override; void Info(const char* msg, ...) override; void Warning(const char* msg, ...) override; void Error(const char* msg, ...) override;