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 | |
27 | using namespace std; |
28 | |
29 | StatusPrinter::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 | |
44 | void StatusPrinter::PlanHasTotalEdges(int total) { |
45 | total_edges_ = total; |
46 | } |
47 | |
48 | void 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 | |
61 | void 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 | |
123 | void 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 | |
137 | void StatusPrinter::BuildStarted() { |
138 | started_edges_ = 0; |
139 | finished_edges_ = 0; |
140 | running_edges_ = 0; |
141 | } |
142 | |
143 | void StatusPrinter::BuildFinished() { |
144 | printer_.SetConsoleLocked(false); |
145 | printer_.PrintOnNewLine("" ); |
146 | } |
147 | |
148 | string 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 | |
230 | void 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 | |
248 | void StatusPrinter::Warning(const char* msg, ...) { |
249 | va_list ap; |
250 | va_start(ap, msg); |
251 | ::Warning(msg, ap); |
252 | va_end(ap); |
253 | } |
254 | |
255 | void StatusPrinter::Error(const char* msg, ...) { |
256 | va_list ap; |
257 | va_start(ap, msg); |
258 | ::Error(msg, ap); |
259 | va_end(ap); |
260 | } |
261 | |
262 | void StatusPrinter::Info(const char* msg, ...) { |
263 | va_list ap; |
264 | va_start(ap, msg); |
265 | ::Info(msg, ap); |
266 | va_end(ap); |
267 | } |
268 | |