• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
StatusPrinter(const BuildConfig & config)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 
PlanHasTotalEdges(int total)44 void StatusPrinter::PlanHasTotalEdges(int total) {
45   total_edges_ = total;
46 }
47 
BuildEdgeStarted(const Edge * edge,int64_t start_time_millis)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 
BuildEdgeFinished(Edge * edge,int64_t end_time_millis,bool success,const string & output)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 
BuildLoadDyndeps()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 
BuildStarted()137 void StatusPrinter::BuildStarted() {
138   started_edges_ = 0;
139   finished_edges_ = 0;
140   running_edges_ = 0;
141 }
142 
BuildFinished()143 void StatusPrinter::BuildFinished() {
144   printer_.SetConsoleLocked(false);
145   printer_.PrintOnNewLine("");
146 }
147 
FormatProgressStatus(const char * progress_status_format,int64_t time_millis) const148 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 
PrintStatus(const Edge * edge,int64_t time_millis)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 
Warning(const char * msg,...)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 
Error(const char * msg,...)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 
Info(const char * msg,...)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