1// Copyright 2016 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "status.h"
16
17#include <stdarg.h>
18#include <stdlib.h>
19
20#ifdef _WIN32
21#include <fcntl.h>
22#include <io.h>
23#endif
24
25#include "debug_flags.h"
26
27using namespace std;
28
29StatusPrinter::StatusPrinter(const BuildConfig& config)
30 : config_(config),
31 started_edges_(0), finished_edges_(0), total_edges_(0), running_edges_(0),
32 time_millis_(0), progress_status_format_(NULL),
33 current_rate_(config.parallelism) {
34
35 // Don't do anything fancy in verbose mode.
36 if (config_.verbosity != BuildConfig::NORMAL)
37 printer_.set_smart_terminal(false);
38
39 progress_status_format_ = getenv("NINJA_STATUS");
40 if (!progress_status_format_)
41 progress_status_format_ = "[%f/%t] ";
42}
43
44void StatusPrinter::PlanHasTotalEdges(int total) {
45 total_edges_ = total;
46}
47
48void StatusPrinter::BuildEdgeStarted(const Edge* edge,
49 int64_t start_time_millis) {
50 ++started_edges_;
51 ++running_edges_;
52 time_millis_ = start_time_millis;
53
54 if (edge->use_console() || printer_.is_smart_terminal())
55 PrintStatus(edge, start_time_millis);
56
57 if (edge->use_console())
58 printer_.SetConsoleLocked(true);
59}
60
61void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t end_time_millis,
62 bool success, const string& output) {
63 time_millis_ = end_time_millis;
64 ++finished_edges_;
65
66 if (edge->use_console())
67 printer_.SetConsoleLocked(false);
68
69 if (config_.verbosity == BuildConfig::QUIET)
70 return;
71
72 if (!edge->use_console())
73 PrintStatus(edge, end_time_millis);
74
75 --running_edges_;
76
77 // Print the command that is spewing before printing its output.
78 if (!success) {
79 string outputs;
80 for (vector<Node*>::const_iterator o = edge->outputs_.begin();
81 o != edge->outputs_.end(); ++o)
82 outputs += (*o)->path() + " ";
83
84 if (printer_.supports_color()) {
85 printer_.PrintOnNewLine("\x1B[31m" "FAILED: " "\x1B[0m" + outputs + "\n");
86 } else {
87 printer_.PrintOnNewLine("FAILED: " + outputs + "\n");
88 }
89 printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n");
90 }
91
92 if (!output.empty()) {
93 // ninja sets stdout and stderr of subprocesses to a pipe, to be able to
94 // check if the output is empty. Some compilers, e.g. clang, check
95 // isatty(stderr) to decide if they should print colored output.
96 // To make it possible to use colored output with ninja, subprocesses should
97 // be run with a flag that forces them to always print color escape codes.
98 // To make sure these escape codes don't show up in a file if ninja's output
99 // is piped to a file, ninja strips ansi escape codes again if it's not
100 // writing to a |smart_terminal_|.
101 // (Launching subprocesses in pseudo ttys doesn't work because there are
102 // only a few hundred available on some systems, and ninja can launch
103 // thousands of parallel compile commands.)
104 string final_output;
105 if (!printer_.supports_color())
106 final_output = StripAnsiEscapeCodes(output);
107 else
108 final_output = output;
109
110#ifdef _WIN32
111 // Fix extra CR being added on Windows, writing out CR CR LF (#773)
112 _setmode(_fileno(stdout), _O_BINARY); // Begin Windows extra CR fix
113#endif
114
115 printer_.PrintOnNewLine(final_output);
116
117#ifdef _WIN32
118 _setmode(_fileno(stdout), _O_TEXT); // End Windows extra CR fix
119#endif
120 }
121}
122
123void StatusPrinter::BuildLoadDyndeps() {
124 // The DependencyScan calls EXPLAIN() to print lines explaining why
125 // it considers a portion of the graph to be out of date. Normally
126 // this is done before the build starts, but our caller is about to
127 // load a dyndep file during the build. Doing so may generate more
128 // explanation lines (via fprintf directly to stderr), but in an
129 // interactive console the cursor is currently at the end of a status
130 // line. Start a new line so that the first explanation does not
131 // append to the status line. After the explanations are done a
132 // new build status line will appear.
133 if (g_explaining)
134 printer_.PrintOnNewLine("");
135}
136
137void StatusPrinter::BuildStarted() {
138 started_edges_ = 0;
139 finished_edges_ = 0;
140 running_edges_ = 0;
141}
142
143void StatusPrinter::BuildFinished() {
144 printer_.SetConsoleLocked(false);
145 printer_.PrintOnNewLine("");
146}
147
148string StatusPrinter::FormatProgressStatus(const char* progress_status_format,
149 int64_t time_millis) const {
150 string out;
151 char buf[32];
152 for (const char* s = progress_status_format; *s != '\0'; ++s) {
153 if (*s == '%') {
154 ++s;
155 switch (*s) {
156 case '%':
157 out.push_back('%');
158 break;
159
160 // Started edges.
161 case 's':
162 snprintf(buf, sizeof(buf), "%d", started_edges_);
163 out += buf;
164 break;
165
166 // Total edges.
167 case 't':
168 snprintf(buf, sizeof(buf), "%d", total_edges_);
169 out += buf;
170 break;
171
172 // Running edges.
173 case 'r': {
174 snprintf(buf, sizeof(buf), "%d", running_edges_);
175 out += buf;
176 break;
177 }
178
179 // Unstarted edges.
180 case 'u':
181 snprintf(buf, sizeof(buf), "%d", total_edges_ - started_edges_);
182 out += buf;
183 break;
184
185 // Finished edges.
186 case 'f':
187 snprintf(buf, sizeof(buf), "%d", finished_edges_);
188 out += buf;
189 break;
190
191 // Overall finished edges per second.
192 case 'o':
193 SnprintfRate(finished_edges_ / (time_millis_ / 1e3), buf, "%.1f");
194 out += buf;
195 break;
196
197 // Current rate, average over the last '-j' jobs.
198 case 'c':
199 current_rate_.UpdateRate(finished_edges_, time_millis_);
200 SnprintfRate(current_rate_.rate(), buf, "%.1f");
201 out += buf;
202 break;
203
204 // Percentage
205 case 'p': {
206 int percent = (100 * finished_edges_) / total_edges_;
207 snprintf(buf, sizeof(buf), "%3i%%", percent);
208 out += buf;
209 break;
210 }
211
212 case 'e': {
213 snprintf(buf, sizeof(buf), "%.3f", time_millis_ / 1e3);
214 out += buf;
215 break;
216 }
217
218 default:
219 Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s);
220 return "";
221 }
222 } else {
223 out.push_back(*s);
224 }
225 }
226
227 return out;
228}
229
230void StatusPrinter::PrintStatus(const Edge* edge, int64_t time_millis) {
231 if (config_.verbosity == BuildConfig::QUIET
232 || config_.verbosity == BuildConfig::NO_STATUS_UPDATE)
233 return;
234
235 bool force_full_command = config_.verbosity == BuildConfig::VERBOSE;
236
237 string to_print = edge->GetBinding("description");
238 if (to_print.empty() || force_full_command)
239 to_print = edge->GetBinding("command");
240
241 to_print = FormatProgressStatus(progress_status_format_, time_millis)
242 + to_print;
243
244 printer_.Print(to_print,
245 force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE);
246}
247
248void StatusPrinter::Warning(const char* msg, ...) {
249 va_list ap;
250 va_start(ap, msg);
251 ::Warning(msg, ap);
252 va_end(ap);
253}
254
255void StatusPrinter::Error(const char* msg, ...) {
256 va_list ap;
257 va_start(ap, msg);
258 ::Error(msg, ap);
259 va_end(ap);
260}
261
262void StatusPrinter::Info(const char* msg, ...) {
263 va_list ap;
264 va_start(ap, msg);
265 ::Info(msg, ap);
266 va_end(ap);
267}
268