Skip to content

Commit f8ce671

Browse files
dendyDaniel Levin
authored and
Daniel Levin
committed
Add option to split stdout and stderr
When enabled ninja prints rule output into appropriate stream: either stdout or stderr. Previously both outputs were always concatenated into single buffer to be printed to stdout when rule is finished. When enabled behavior is next: Subprocess sequentially appends rule output into internal buffer looking at the source channel of incoming output. When channel changes already collected buffer is printed and cleared.
1 parent a6da182 commit f8ce671

13 files changed

+242
-109
lines changed

src/build.cc

+7-3
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ size_t RealCommandRunner::CanRunMore() const {
643643

644644
bool RealCommandRunner::StartCommand(Edge* edge) {
645645
string command = edge->EvaluateCommand();
646-
Subprocess* subproc = subprocs_.Add(command, edge->use_console());
646+
Subprocess* subproc = subprocs_.Add(command, edge->use_console(), edge->use_stderr());
647647
if (!subproc)
648648
return false;
649649
subproc_to_edge_.insert(make_pair(subproc, edge));
@@ -659,8 +659,10 @@ bool RealCommandRunner::WaitForCommand(Result* result) {
659659
return false;
660660
}
661661

662+
result->use_stderr = subproc->UseStdErr();
662663
result->status = subproc->Finish();
663664
result->output = subproc->GetOutput();
665+
result->error = subproc->GetError();
664666

665667
map<const Subprocess*, Edge*>::iterator e = subproc_to_edge_.find(subproc);
666668
result->edge = e->second;
@@ -944,7 +946,9 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
944946
result->success()) {
945947
if (!result->output.empty())
946948
result->output.append("\n");
947-
result->output.append(extract_err);
949+
if (!result->error.empty())
950+
result->error.append("\n");
951+
(result->use_stderr ? result->error : result->output).append(extract_err);
948952
result->status = ExitFailure;
949953
}
950954
}
@@ -956,7 +960,7 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
956960
running_edges_.erase(it);
957961

958962
status_->BuildEdgeFinished(edge, start_time_millis, end_time_millis,
959-
result->success(), result->output);
963+
result->success(), result->output, result->error);
960964

961965
// The rest of this function only applies to successful commands.
962966
if (!result->success()) {

src/build.h

+2
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ struct CommandRunner {
153153
struct Result {
154154
Result() : edge(NULL) {}
155155
Edge* edge;
156+
bool use_stderr;
156157
ExitStatus status;
157158
std::string output;
159+
std::string error;
158160
bool success() const { return status == ExitSuccess; }
159161
};
160162
/// Wait for a command to complete, or return false if interrupted.

src/eval_env.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ bool Rule::IsReservedBinding(const string& var) {
7575
var == "restat" ||
7676
var == "rspfile" ||
7777
var == "rspfile_content" ||
78-
var == "msvc_deps_prefix";
78+
var == "msvc_deps_prefix" ||
79+
var == "stderr";
7980
}
8081

8182
const map<string, const Rule*>& BindingEnv::GetRules() const {

src/graph.cc

+11
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,17 @@ bool Edge::use_console() const {
588588
return pool() == &State::kConsolePool;
589589
}
590590

591+
static bool s_use_stderr()
592+
{
593+
const char* const use_string = getenv("NINJA_USE_STDERR");
594+
return !use_string || strcmp(use_string, "0") != 0;
595+
}
596+
597+
bool Edge::use_stderr() const {
598+
static const bool use = s_use_stderr();
599+
return use;
600+
}
601+
591602
bool Edge::maybe_phonycycle_diagnostic() const {
592603
// CMake 2.8.12.x and 3.0.x produced self-referencing phony rules
593604
// of the form "build a: phony ... a ...". Restrict our

src/graph.h

+1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ struct Edge {
264264

265265
bool is_phony() const;
266266
bool use_console() const;
267+
bool use_stderr() const;
267268
bool maybe_phonycycle_diagnostic() const;
268269

269270
// Historical info: how long did this edge take last time,

src/line_printer.cc

+10-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
using namespace std;
3434

35-
LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
35+
LinePrinter::LinePrinter(bool out) : out_(out), have_blank_line_(true), console_locked_(false) {
3636
const char* term = getenv("TERM");
3737
#ifndef _WIN32
3838
smart_terminal_ = isatty(1) && term && string(term) != "dumb";
@@ -128,7 +128,14 @@ void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
128128
} else {
129129
// Avoid printf and C strings, since the actual output might contain null
130130
// bytes like UTF-16 does (yuck).
131-
fwrite(data, 1, size, stdout);
131+
fwrite(data, 1, size, file());
132+
}
133+
}
134+
135+
void LinePrinter::CompleteLine() {
136+
if (!have_blank_line_) {
137+
PrintOrBuffer("\n", 1);
138+
have_blank_line_ = false;
132139
}
133140
}
134141

@@ -138,9 +145,7 @@ void LinePrinter::PrintOnNewLine(const string& to_print) {
138145
output_buffer_.append(1, '\n');
139146
line_buffer_.clear();
140147
}
141-
if (!have_blank_line_) {
142-
PrintOrBuffer("\n", 1);
143-
}
148+
CompleteLine();
144149
if (!to_print.empty()) {
145150
PrintOrBuffer(&to_print[0], to_print.size());
146151
}

src/line_printer.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
#define NINJA_LINE_PRINTER_H_
1717

1818
#include <stddef.h>
19+
#include <stdio.h>
1920
#include <string>
2021

2122
/// Prints lines of text, possibly overprinting previously printed lines
2223
/// if the terminal supports it.
2324
struct LinePrinter {
24-
LinePrinter();
25+
LinePrinter(bool out);
2526

2627
bool is_smart_terminal() const { return smart_terminal_; }
2728
void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
@@ -36,6 +37,9 @@ struct LinePrinter {
3637
/// one line.
3738
void Print(std::string to_print, LineType type);
3839

40+
/// Prints line break if needed.
41+
void CompleteLine();
42+
3943
/// Prints a string on a new line, not overprinting previous output.
4044
void PrintOnNewLine(const std::string& to_print);
4145

@@ -44,6 +48,10 @@ struct LinePrinter {
4448
void SetConsoleLocked(bool locked);
4549

4650
private:
51+
FILE* file() const { return out_ ? stdout : stderr; }
52+
53+
const bool out_;
54+
4755
/// Whether we can do fancy terminal control codes.
4856
bool smart_terminal_;
4957

src/status.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct Status {
3030
int64_t start_time_millis) = 0;
3131
virtual void BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
3232
int64_t end_time_millis, bool success,
33-
const std::string& output) = 0;
33+
const std::string& output, const std::string& error) = 0;
3434
virtual void BuildStarted() = 0;
3535
virtual void BuildFinished() = 0;
3636

src/status_printer.cc

+30-8
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Status* Status::factory(const BuildConfig& config) {
4242

4343
StatusPrinter::StatusPrinter(const BuildConfig& config)
4444
: config_(config), started_edges_(0), finished_edges_(0), total_edges_(0),
45-
running_edges_(0), progress_status_format_(NULL),
45+
running_edges_(0), printer_(true), err_printer_(false), progress_status_format_(NULL),
4646
current_rate_(config.parallelism) {
4747
// Don't do anything fancy in verbose mode.
4848
if (config_.verbosity != BuildConfig::NORMAL)
@@ -90,8 +90,10 @@ void StatusPrinter::BuildEdgeStarted(const Edge* edge,
9090
if (edge->use_console() || printer_.is_smart_terminal())
9191
PrintStatus(edge, start_time_millis);
9292

93-
if (edge->use_console())
93+
if (edge->use_console()) {
9494
printer_.SetConsoleLocked(true);
95+
err_printer_.SetConsoleLocked(true);
96+
}
9597
}
9698

9799
void StatusPrinter::RecalculateProgressPrediction() {
@@ -175,7 +177,7 @@ void StatusPrinter::RecalculateProgressPrediction() {
175177

176178
void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
177179
int64_t end_time_millis, bool success,
178-
const string& output) {
180+
const string& output, const std::string& error) {
179181
time_millis_ = end_time_millis;
180182
++finished_edges_;
181183

@@ -190,8 +192,10 @@ void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
190192
} else
191193
--eta_unpredictable_edges_remaining_;
192194

193-
if (edge->use_console())
195+
if (edge->use_console()) {
194196
printer_.SetConsoleLocked(false);
197+
err_printer_.SetConsoleLocked(false);
198+
}
195199

196200
if (config_.verbosity == BuildConfig::QUIET)
197201
return;
@@ -208,12 +212,14 @@ void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
208212
o != edge->outputs_.end(); ++o)
209213
outputs += (*o)->path() + " ";
210214

211-
if (printer_.supports_color()) {
212-
printer_.PrintOnNewLine("\x1B[31m" "FAILED: " "\x1B[0m" + outputs + "\n");
215+
printer_.CompleteLine();
216+
217+
if (err_printer_.supports_color()) {
218+
err_printer_.PrintOnNewLine("\x1B[31m" "FAILED: " "\x1B[0m" + outputs + "\n");
213219
} else {
214-
printer_.PrintOnNewLine("FAILED: " + outputs + "\n");
220+
err_printer_.PrintOnNewLine("FAILED: " + outputs + "\n");
215221
}
216-
printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n");
222+
err_printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n");
217223
}
218224

219225
if (!output.empty()) {
@@ -247,6 +253,22 @@ void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
247253
_setmode(_fileno(stdout), _O_TEXT); // End Windows extra CR fix
248254
#endif
249255
}
256+
257+
if (!error.empty()) {
258+
string::size_type begin = 0;
259+
while (true) {
260+
const std::string::size_type crpos = error.find('\xd', begin);
261+
if (crpos == string::npos) {
262+
fwrite(error.c_str() + begin, error.size() - begin, 1, stderr);
263+
break;
264+
}
265+
const std::string::size_type size = crpos - begin;
266+
if (size != 0)
267+
fwrite(error.c_str() + begin, crpos - begin, 1, stderr);
268+
begin = crpos + 1;
269+
}
270+
fflush(stderr);
271+
}
250272
}
251273

252274
void StatusPrinter::BuildStarted() {

src/status_printer.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ struct StatusPrinter : Status {
3232
virtual void BuildEdgeStarted(const Edge* edge, int64_t start_time_millis);
3333
virtual void BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
3434
int64_t end_time_millis, bool success,
35-
const std::string& output);
35+
const std::string& output, const std::string& error);
3636
virtual void BuildStarted();
3737
virtual void BuildFinished();
3838

@@ -88,6 +88,7 @@ struct StatusPrinter : Status {
8888

8989
/// Prints progress output.
9090
LinePrinter printer_;
91+
LinePrinter err_printer_;
9192

9293
/// An optional Explanations pointer, used to implement `-d explain`.
9394
Explanations* explanations_ = nullptr;

0 commit comments

Comments
 (0)