1 | // Copyright 2011 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 <errno.h> |
16 | #include <limits.h> |
17 | #include <stdio.h> |
18 | #include <stdlib.h> |
19 | #include <string.h> |
20 | |
21 | #include <algorithm> |
22 | #include <cstdlib> |
23 | |
24 | #ifdef _WIN32 |
25 | #include "getopt.h" |
26 | #include <direct.h> |
27 | #include <windows.h> |
28 | #elif defined(_AIX) |
29 | #include "getopt.h" |
30 | #include <unistd.h> |
31 | #else |
32 | #include <getopt.h> |
33 | #include <unistd.h> |
34 | #endif |
35 | |
36 | #include "browse.h" |
37 | #include "build.h" |
38 | #include "build_log.h" |
39 | #include "deps_log.h" |
40 | #include "clean.h" |
41 | #include "debug_flags.h" |
42 | #include "depfile_parser.h" |
43 | #include "disk_interface.h" |
44 | #include "graph.h" |
45 | #include "graphviz.h" |
46 | #include "json.h" |
47 | #include "manifest_parser.h" |
48 | #include "metrics.h" |
49 | #include "missing_deps.h" |
50 | #include "state.h" |
51 | #include "status.h" |
52 | #include "util.h" |
53 | #include "version.h" |
54 | |
55 | using namespace std; |
56 | |
57 | #ifdef _WIN32 |
58 | // Defined in msvc_helper_main-win32.cc. |
59 | int MSVCHelperMain(int argc, char** argv); |
60 | |
61 | // Defined in minidump-win32.cc. |
62 | void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep); |
63 | #endif |
64 | |
65 | namespace { |
66 | |
67 | struct Tool; |
68 | |
69 | /// Command-line options. |
70 | struct Options { |
71 | /// Build file to load. |
72 | const char* input_file; |
73 | |
74 | /// Directory to change into before running. |
75 | const char* working_dir; |
76 | |
77 | /// Tool to run rather than building. |
78 | const Tool* tool; |
79 | |
80 | /// Whether duplicate rules for one target should warn or print an error. |
81 | bool dupe_edges_should_err; |
82 | |
83 | /// Whether phony cycles should warn or print an error. |
84 | bool phony_cycle_should_err; |
85 | }; |
86 | |
87 | /// The Ninja main() loads up a series of data structures; various tools need |
88 | /// to poke into these, so store them as fields on an object. |
89 | struct NinjaMain : public BuildLogUser { |
90 | NinjaMain(const char* ninja_command, const BuildConfig& config) : |
91 | ninja_command_(ninja_command), config_(config), |
92 | start_time_millis_(GetTimeMillis()) {} |
93 | |
94 | /// Command line used to run Ninja. |
95 | const char* ninja_command_; |
96 | |
97 | /// Build configuration set from flags (e.g. parallelism). |
98 | const BuildConfig& config_; |
99 | |
100 | /// Loaded state (rules, nodes). |
101 | State state_; |
102 | |
103 | /// Functions for accessing the disk. |
104 | RealDiskInterface disk_interface_; |
105 | |
106 | /// The build directory, used for storing the build log etc. |
107 | string build_dir_; |
108 | |
109 | BuildLog build_log_; |
110 | DepsLog deps_log_; |
111 | |
112 | /// The type of functions that are the entry points to tools (subcommands). |
113 | typedef int (NinjaMain::*ToolFunc)(const Options*, int, char**); |
114 | |
115 | /// Get the Node for a given command-line path, handling features like |
116 | /// spell correction. |
117 | Node* CollectTarget(const char* cpath, string* err); |
118 | |
119 | /// CollectTarget for all command-line arguments, filling in \a targets. |
120 | bool CollectTargetsFromArgs(int argc, char* argv[], |
121 | vector<Node*>* targets, string* err); |
122 | |
123 | // The various subcommands, run via "-t XXX". |
124 | int ToolGraph(const Options* options, int argc, char* argv[]); |
125 | int ToolQuery(const Options* options, int argc, char* argv[]); |
126 | int ToolDeps(const Options* options, int argc, char* argv[]); |
127 | int ToolMissingDeps(const Options* options, int argc, char* argv[]); |
128 | int ToolBrowse(const Options* options, int argc, char* argv[]); |
129 | int ToolMSVC(const Options* options, int argc, char* argv[]); |
130 | int ToolTargets(const Options* options, int argc, char* argv[]); |
131 | int ToolCommands(const Options* options, int argc, char* argv[]); |
132 | int ToolInputs(const Options* options, int argc, char* argv[]); |
133 | int ToolClean(const Options* options, int argc, char* argv[]); |
134 | int ToolCleanDead(const Options* options, int argc, char* argv[]); |
135 | int ToolCompilationDatabase(const Options* options, int argc, char* argv[]); |
136 | int ToolRecompact(const Options* options, int argc, char* argv[]); |
137 | int ToolRestat(const Options* options, int argc, char* argv[]); |
138 | int ToolUrtle(const Options* options, int argc, char** argv); |
139 | int ToolRules(const Options* options, int argc, char* argv[]); |
140 | int ToolWinCodePage(const Options* options, int argc, char* argv[]); |
141 | |
142 | /// Open the build log. |
143 | /// @return false on error. |
144 | bool OpenBuildLog(bool recompact_only = false); |
145 | |
146 | /// Open the deps log: load it, then open for writing. |
147 | /// @return false on error. |
148 | bool OpenDepsLog(bool recompact_only = false); |
149 | |
150 | /// Ensure the build directory exists, creating it if necessary. |
151 | /// @return false on error. |
152 | bool EnsureBuildDirExists(); |
153 | |
154 | /// Rebuild the manifest, if necessary. |
155 | /// Fills in \a err on error. |
156 | /// @return true if the manifest was rebuilt. |
157 | bool RebuildManifest(const char* input_file, string* err, Status* status); |
158 | |
159 | /// Build the targets listed on the command line. |
160 | /// @return an exit code. |
161 | int RunBuild(int argc, char** argv, Status* status); |
162 | |
163 | /// Dump the output requested by '-d stats'. |
164 | void DumpMetrics(); |
165 | |
166 | virtual bool IsPathDead(StringPiece s) const { |
167 | Node* n = state_.LookupNode(s); |
168 | if (n && n->in_edge()) |
169 | return false; |
170 | // Just checking n isn't enough: If an old output is both in the build log |
171 | // and in the deps log, it will have a Node object in state_. (It will also |
172 | // have an in edge if one of its inputs is another output that's in the deps |
173 | // log, but having a deps edge product an output that's input to another deps |
174 | // edge is rare, and the first recompaction will delete all old outputs from |
175 | // the deps log, and then a second recompaction will clear the build log, |
176 | // which seems good enough for this corner case.) |
177 | // Do keep entries around for files which still exist on disk, for |
178 | // generators that want to use this information. |
179 | string err; |
180 | TimeStamp mtime = disk_interface_.Stat(s.AsString(), &err); |
181 | if (mtime == -1) |
182 | Error("%s" , err.c_str()); // Log and ignore Stat() errors. |
183 | return mtime == 0; |
184 | } |
185 | |
186 | int64_t start_time_millis_; |
187 | }; |
188 | |
189 | /// Subtools, accessible via "-t foo". |
190 | struct Tool { |
191 | /// Short name of the tool. |
192 | const char* name; |
193 | |
194 | /// Description (shown in "-t list"). |
195 | const char* desc; |
196 | |
197 | /// When to run the tool. |
198 | enum { |
199 | /// Run after parsing the command-line flags and potentially changing |
200 | /// the current working directory (as early as possible). |
201 | RUN_AFTER_FLAGS, |
202 | |
203 | /// Run after loading build.ninja. |
204 | RUN_AFTER_LOAD, |
205 | |
206 | /// Run after loading the build/deps logs. |
207 | RUN_AFTER_LOGS, |
208 | } when; |
209 | |
210 | /// Implementation of the tool. |
211 | NinjaMain::ToolFunc func; |
212 | }; |
213 | |
214 | /// Print usage information. |
215 | void Usage(const BuildConfig& config) { |
216 | fprintf(stderr, |
217 | "usage: ninja [options] [targets...]\n" |
218 | "\n" |
219 | "if targets are unspecified, builds the 'default' target (see manual).\n" |
220 | "\n" |
221 | "options:\n" |
222 | " --version print ninja version (\"%s\")\n" |
223 | " -v, --verbose show all command lines while building\n" |
224 | " --quiet don't show progress status, just command output\n" |
225 | "\n" |
226 | " -C DIR change to DIR before doing anything else\n" |
227 | " -f FILE specify input build file [default=build.ninja]\n" |
228 | "\n" |
229 | " -j N run N jobs in parallel (0 means infinity) [default=%d on this system]\n" |
230 | " -k N keep going until N jobs fail (0 means infinity) [default=1]\n" |
231 | " -l N do not start new jobs if the load average is greater than N\n" |
232 | " -n dry run (don't run commands but act like they succeeded)\n" |
233 | "\n" |
234 | " -d MODE enable debugging (use '-d list' to list modes)\n" |
235 | " -t TOOL run a subtool (use '-t list' to list subtools)\n" |
236 | " terminates toplevel options; further flags are passed to the tool\n" |
237 | " -w FLAG adjust warnings (use '-w list' to list warnings)\n" , |
238 | kNinjaVersion, config.parallelism); |
239 | } |
240 | |
241 | /// Choose a default value for the -j (parallelism) flag. |
242 | int GuessParallelism() { |
243 | switch (int processors = GetProcessorCount()) { |
244 | case 0: |
245 | case 1: |
246 | return 2; |
247 | case 2: |
248 | return 3; |
249 | default: |
250 | return processors + 2; |
251 | } |
252 | } |
253 | |
254 | /// Rebuild the build manifest, if necessary. |
255 | /// Returns true if the manifest was rebuilt. |
256 | bool NinjaMain::RebuildManifest(const char* input_file, string* err, |
257 | Status* status) { |
258 | string path = input_file; |
259 | if (path.empty()) { |
260 | *err = "empty path" ; |
261 | return false; |
262 | } |
263 | uint64_t slash_bits; // Unused because this path is only used for lookup. |
264 | CanonicalizePath(&path, &slash_bits); |
265 | Node* node = state_.LookupNode(path); |
266 | if (!node) |
267 | return false; |
268 | |
269 | Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_, |
270 | status, start_time_millis_); |
271 | if (!builder.AddTarget(node, err)) |
272 | return false; |
273 | |
274 | if (builder.AlreadyUpToDate()) |
275 | return false; // Not an error, but we didn't rebuild. |
276 | |
277 | if (!builder.Build(err)) |
278 | return false; |
279 | |
280 | // The manifest was only rebuilt if it is now dirty (it may have been cleaned |
281 | // by a restat). |
282 | if (!node->dirty()) { |
283 | // Reset the state to prevent problems like |
284 | // https://github.com/ninja-build/ninja/issues/874 |
285 | state_.Reset(); |
286 | return false; |
287 | } |
288 | |
289 | return true; |
290 | } |
291 | |
292 | Node* NinjaMain::CollectTarget(const char* cpath, string* err) { |
293 | string path = cpath; |
294 | if (path.empty()) { |
295 | *err = "empty path" ; |
296 | return NULL; |
297 | } |
298 | uint64_t slash_bits; |
299 | CanonicalizePath(&path, &slash_bits); |
300 | |
301 | // Special syntax: "foo.cc^" means "the first output of foo.cc". |
302 | bool first_dependent = false; |
303 | if (!path.empty() && path[path.size() - 1] == '^') { |
304 | path.resize(path.size() - 1); |
305 | first_dependent = true; |
306 | } |
307 | |
308 | Node* node = state_.LookupNode(path); |
309 | if (node) { |
310 | if (first_dependent) { |
311 | if (node->out_edges().empty()) { |
312 | Node* rev_deps = deps_log_.GetFirstReverseDepsNode(node); |
313 | if (!rev_deps) { |
314 | *err = "'" + path + "' has no out edge" ; |
315 | return NULL; |
316 | } |
317 | node = rev_deps; |
318 | } else { |
319 | Edge* edge = node->out_edges()[0]; |
320 | if (edge->outputs_.empty()) { |
321 | edge->Dump(); |
322 | Fatal("edge has no outputs" ); |
323 | } |
324 | node = edge->outputs_[0]; |
325 | } |
326 | } |
327 | return node; |
328 | } else { |
329 | *err = |
330 | "unknown target '" + Node::PathDecanonicalized(path, slash_bits) + "'" ; |
331 | if (path == "clean" ) { |
332 | *err += ", did you mean 'ninja -t clean'?" ; |
333 | } else if (path == "help" ) { |
334 | *err += ", did you mean 'ninja -h'?" ; |
335 | } else { |
336 | Node* suggestion = state_.SpellcheckNode(path); |
337 | if (suggestion) { |
338 | *err += ", did you mean '" + suggestion->path() + "'?" ; |
339 | } |
340 | } |
341 | return NULL; |
342 | } |
343 | } |
344 | |
345 | bool NinjaMain::CollectTargetsFromArgs(int argc, char* argv[], |
346 | vector<Node*>* targets, string* err) { |
347 | if (argc == 0) { |
348 | *targets = state_.DefaultNodes(err); |
349 | return err->empty(); |
350 | } |
351 | |
352 | for (int i = 0; i < argc; ++i) { |
353 | Node* node = CollectTarget(argv[i], err); |
354 | if (node == NULL) |
355 | return false; |
356 | targets->push_back(node); |
357 | } |
358 | return true; |
359 | } |
360 | |
361 | int NinjaMain::ToolGraph(const Options* options, int argc, char* argv[]) { |
362 | vector<Node*> nodes; |
363 | string err; |
364 | if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { |
365 | Error("%s" , err.c_str()); |
366 | return 1; |
367 | } |
368 | |
369 | GraphViz graph(&state_, &disk_interface_); |
370 | graph.Start(); |
371 | for (vector<Node*>::const_iterator n = nodes.begin(); n != nodes.end(); ++n) |
372 | graph.AddTarget(*n); |
373 | graph.Finish(); |
374 | |
375 | return 0; |
376 | } |
377 | |
378 | int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) { |
379 | if (argc == 0) { |
380 | Error("expected a target to query" ); |
381 | return 1; |
382 | } |
383 | |
384 | DyndepLoader dyndep_loader(&state_, &disk_interface_); |
385 | |
386 | for (int i = 0; i < argc; ++i) { |
387 | string err; |
388 | Node* node = CollectTarget(argv[i], &err); |
389 | if (!node) { |
390 | Error("%s" , err.c_str()); |
391 | return 1; |
392 | } |
393 | |
394 | printf("%s:\n" , node->path().c_str()); |
395 | if (Edge* edge = node->in_edge()) { |
396 | if (edge->dyndep_ && edge->dyndep_->dyndep_pending()) { |
397 | if (!dyndep_loader.LoadDyndeps(edge->dyndep_, &err)) { |
398 | Warning("%s\n" , err.c_str()); |
399 | } |
400 | } |
401 | printf(" input: %s\n" , edge->rule_->name().c_str()); |
402 | for (int in = 0; in < (int)edge->inputs_.size(); in++) { |
403 | const char* label = "" ; |
404 | if (edge->is_implicit(in)) |
405 | label = "| " ; |
406 | else if (edge->is_order_only(in)) |
407 | label = "|| " ; |
408 | printf(" %s%s\n" , label, edge->inputs_[in]->path().c_str()); |
409 | } |
410 | if (!edge->validations_.empty()) { |
411 | printf(" validations:\n" ); |
412 | for (std::vector<Node*>::iterator validation = edge->validations_.begin(); |
413 | validation != edge->validations_.end(); ++validation) { |
414 | printf(" %s\n" , (*validation)->path().c_str()); |
415 | } |
416 | } |
417 | } |
418 | printf(" outputs:\n" ); |
419 | for (vector<Edge*>::const_iterator edge = node->out_edges().begin(); |
420 | edge != node->out_edges().end(); ++edge) { |
421 | for (vector<Node*>::iterator out = (*edge)->outputs_.begin(); |
422 | out != (*edge)->outputs_.end(); ++out) { |
423 | printf(" %s\n" , (*out)->path().c_str()); |
424 | } |
425 | } |
426 | const std::vector<Edge*> validation_edges = node->validation_out_edges(); |
427 | if (!validation_edges.empty()) { |
428 | printf(" validation for:\n" ); |
429 | for (std::vector<Edge*>::const_iterator edge = validation_edges.begin(); |
430 | edge != validation_edges.end(); ++edge) { |
431 | for (vector<Node*>::iterator out = (*edge)->outputs_.begin(); |
432 | out != (*edge)->outputs_.end(); ++out) { |
433 | printf(" %s\n" , (*out)->path().c_str()); |
434 | } |
435 | } |
436 | } |
437 | } |
438 | return 0; |
439 | } |
440 | |
441 | #if defined(NINJA_HAVE_BROWSE) |
442 | int NinjaMain::ToolBrowse(const Options* options, int argc, char* argv[]) { |
443 | RunBrowsePython(&state_, ninja_command_, options->input_file, argc, argv); |
444 | // If we get here, the browse failed. |
445 | return 1; |
446 | } |
447 | #else |
448 | int NinjaMain::ToolBrowse(const Options*, int, char**) { |
449 | Fatal("browse tool not supported on this platform" ); |
450 | return 1; |
451 | } |
452 | #endif |
453 | |
454 | #if defined(_WIN32) |
455 | int NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) { |
456 | // Reset getopt: push one argument onto the front of argv, reset optind. |
457 | argc++; |
458 | argv--; |
459 | optind = 0; |
460 | return MSVCHelperMain(argc, argv); |
461 | } |
462 | #endif |
463 | |
464 | int ToolTargetsList(const vector<Node*>& nodes, int depth, int indent) { |
465 | for (vector<Node*>::const_iterator n = nodes.begin(); |
466 | n != nodes.end(); |
467 | ++n) { |
468 | for (int i = 0; i < indent; ++i) |
469 | printf(" " ); |
470 | const char* target = (*n)->path().c_str(); |
471 | if ((*n)->in_edge()) { |
472 | printf("%s: %s\n" , target, (*n)->in_edge()->rule_->name().c_str()); |
473 | if (depth > 1 || depth <= 0) |
474 | ToolTargetsList((*n)->in_edge()->inputs_, depth - 1, indent + 1); |
475 | } else { |
476 | printf("%s\n" , target); |
477 | } |
478 | } |
479 | return 0; |
480 | } |
481 | |
482 | int ToolTargetsSourceList(State* state) { |
483 | for (vector<Edge*>::iterator e = state->edges_.begin(); |
484 | e != state->edges_.end(); ++e) { |
485 | for (vector<Node*>::iterator inps = (*e)->inputs_.begin(); |
486 | inps != (*e)->inputs_.end(); ++inps) { |
487 | if (!(*inps)->in_edge()) |
488 | printf("%s\n" , (*inps)->path().c_str()); |
489 | } |
490 | } |
491 | return 0; |
492 | } |
493 | |
494 | int ToolTargetsList(State* state, const string& rule_name) { |
495 | set<string> rules; |
496 | |
497 | // Gather the outputs. |
498 | for (vector<Edge*>::iterator e = state->edges_.begin(); |
499 | e != state->edges_.end(); ++e) { |
500 | if ((*e)->rule_->name() == rule_name) { |
501 | for (vector<Node*>::iterator out_node = (*e)->outputs_.begin(); |
502 | out_node != (*e)->outputs_.end(); ++out_node) { |
503 | rules.insert((*out_node)->path()); |
504 | } |
505 | } |
506 | } |
507 | |
508 | // Print them. |
509 | for (set<string>::const_iterator i = rules.begin(); |
510 | i != rules.end(); ++i) { |
511 | printf("%s\n" , (*i).c_str()); |
512 | } |
513 | |
514 | return 0; |
515 | } |
516 | |
517 | int ToolTargetsList(State* state) { |
518 | for (vector<Edge*>::iterator e = state->edges_.begin(); |
519 | e != state->edges_.end(); ++e) { |
520 | for (vector<Node*>::iterator out_node = (*e)->outputs_.begin(); |
521 | out_node != (*e)->outputs_.end(); ++out_node) { |
522 | printf("%s: %s\n" , |
523 | (*out_node)->path().c_str(), |
524 | (*e)->rule_->name().c_str()); |
525 | } |
526 | } |
527 | return 0; |
528 | } |
529 | |
530 | int NinjaMain::ToolDeps(const Options* options, int argc, char** argv) { |
531 | vector<Node*> nodes; |
532 | if (argc == 0) { |
533 | for (vector<Node*>::const_iterator ni = deps_log_.nodes().begin(); |
534 | ni != deps_log_.nodes().end(); ++ni) { |
535 | if (DepsLog::IsDepsEntryLiveFor(*ni)) |
536 | nodes.push_back(*ni); |
537 | } |
538 | } else { |
539 | string err; |
540 | if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { |
541 | Error("%s" , err.c_str()); |
542 | return 1; |
543 | } |
544 | } |
545 | |
546 | RealDiskInterface disk_interface; |
547 | for (vector<Node*>::iterator it = nodes.begin(), end = nodes.end(); |
548 | it != end; ++it) { |
549 | DepsLog::Deps* deps = deps_log_.GetDeps(*it); |
550 | if (!deps) { |
551 | printf("%s: deps not found\n" , (*it)->path().c_str()); |
552 | continue; |
553 | } |
554 | |
555 | string err; |
556 | TimeStamp mtime = disk_interface.Stat((*it)->path(), &err); |
557 | if (mtime == -1) |
558 | Error("%s" , err.c_str()); // Log and ignore Stat() errors; |
559 | printf("%s: #deps %d, deps mtime %" PRId64 " (%s)\n" , |
560 | (*it)->path().c_str(), deps->node_count, deps->mtime, |
561 | (!mtime || mtime > deps->mtime ? "STALE" :"VALID" )); |
562 | for (int i = 0; i < deps->node_count; ++i) |
563 | printf(" %s\n" , deps->nodes[i]->path().c_str()); |
564 | printf("\n" ); |
565 | } |
566 | |
567 | return 0; |
568 | } |
569 | |
570 | int NinjaMain::ToolMissingDeps(const Options* options, int argc, char** argv) { |
571 | vector<Node*> nodes; |
572 | string err; |
573 | if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { |
574 | Error("%s" , err.c_str()); |
575 | return 1; |
576 | } |
577 | RealDiskInterface disk_interface; |
578 | MissingDependencyPrinter printer; |
579 | MissingDependencyScanner scanner(&printer, &deps_log_, &state_, |
580 | &disk_interface); |
581 | for (vector<Node*>::iterator it = nodes.begin(); it != nodes.end(); ++it) { |
582 | scanner.ProcessNode(*it); |
583 | } |
584 | scanner.PrintStats(); |
585 | if (scanner.HadMissingDeps()) |
586 | return 3; |
587 | return 0; |
588 | } |
589 | |
590 | int NinjaMain::ToolTargets(const Options* options, int argc, char* argv[]) { |
591 | int depth = 1; |
592 | if (argc >= 1) { |
593 | string mode = argv[0]; |
594 | if (mode == "rule" ) { |
595 | string rule; |
596 | if (argc > 1) |
597 | rule = argv[1]; |
598 | if (rule.empty()) |
599 | return ToolTargetsSourceList(&state_); |
600 | else |
601 | return ToolTargetsList(&state_, rule); |
602 | } else if (mode == "depth" ) { |
603 | if (argc > 1) |
604 | depth = atoi(argv[1]); |
605 | } else if (mode == "all" ) { |
606 | return ToolTargetsList(&state_); |
607 | } else { |
608 | const char* suggestion = |
609 | SpellcheckString(mode.c_str(), "rule" , "depth" , "all" , NULL); |
610 | if (suggestion) { |
611 | Error("unknown target tool mode '%s', did you mean '%s'?" , |
612 | mode.c_str(), suggestion); |
613 | } else { |
614 | Error("unknown target tool mode '%s'" , mode.c_str()); |
615 | } |
616 | return 1; |
617 | } |
618 | } |
619 | |
620 | string err; |
621 | vector<Node*> root_nodes = state_.RootNodes(&err); |
622 | if (err.empty()) { |
623 | return ToolTargetsList(root_nodes, depth, 0); |
624 | } else { |
625 | Error("%s" , err.c_str()); |
626 | return 1; |
627 | } |
628 | } |
629 | |
630 | int NinjaMain::ToolRules(const Options* options, int argc, char* argv[]) { |
631 | // Parse options. |
632 | |
633 | // The rules tool uses getopt, and expects argv[0] to contain the name of |
634 | // the tool, i.e. "rules". |
635 | argc++; |
636 | argv--; |
637 | |
638 | bool print_description = false; |
639 | |
640 | optind = 1; |
641 | int opt; |
642 | while ((opt = getopt(argc, argv, const_cast<char*>("hd" ))) != -1) { |
643 | switch (opt) { |
644 | case 'd': |
645 | print_description = true; |
646 | break; |
647 | case 'h': |
648 | default: |
649 | printf("usage: ninja -t rules [options]\n" |
650 | "\n" |
651 | "options:\n" |
652 | " -d also print the description of the rule\n" |
653 | " -h print this message\n" |
654 | ); |
655 | return 1; |
656 | } |
657 | } |
658 | argv += optind; |
659 | argc -= optind; |
660 | |
661 | // Print rules |
662 | |
663 | typedef map<string, const Rule*> Rules; |
664 | const Rules& rules = state_.bindings_.GetRules(); |
665 | for (Rules::const_iterator i = rules.begin(); i != rules.end(); ++i) { |
666 | printf("%s" , i->first.c_str()); |
667 | if (print_description) { |
668 | const Rule* rule = i->second; |
669 | const EvalString* description = rule->GetBinding("description" ); |
670 | if (description != NULL) { |
671 | printf(": %s" , description->Unparse().c_str()); |
672 | } |
673 | } |
674 | printf("\n" ); |
675 | } |
676 | return 0; |
677 | } |
678 | |
679 | #ifdef _WIN32 |
680 | int NinjaMain::ToolWinCodePage(const Options* options, int argc, char* argv[]) { |
681 | if (argc != 0) { |
682 | printf("usage: ninja -t wincodepage\n" ); |
683 | return 1; |
684 | } |
685 | printf("Build file encoding: %s\n" , GetACP() == CP_UTF8? "UTF-8" : "ANSI" ); |
686 | return 0; |
687 | } |
688 | #endif |
689 | |
690 | enum PrintCommandMode { PCM_Single, PCM_All }; |
691 | void PrintCommands(Edge* edge, EdgeSet* seen, PrintCommandMode mode) { |
692 | if (!edge) |
693 | return; |
694 | if (!seen->insert(edge).second) |
695 | return; |
696 | |
697 | if (mode == PCM_All) { |
698 | for (vector<Node*>::iterator in = edge->inputs_.begin(); |
699 | in != edge->inputs_.end(); ++in) |
700 | PrintCommands((*in)->in_edge(), seen, mode); |
701 | } |
702 | |
703 | if (!edge->is_phony()) |
704 | puts(edge->EvaluateCommand().c_str()); |
705 | } |
706 | |
707 | int NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) { |
708 | // The commands tool uses getopt, and expects argv[0] to contain the name of |
709 | // the tool, i.e. "commands". |
710 | ++argc; |
711 | --argv; |
712 | |
713 | PrintCommandMode mode = PCM_All; |
714 | |
715 | optind = 1; |
716 | int opt; |
717 | while ((opt = getopt(argc, argv, const_cast<char*>("hs" ))) != -1) { |
718 | switch (opt) { |
719 | case 's': |
720 | mode = PCM_Single; |
721 | break; |
722 | case 'h': |
723 | default: |
724 | printf("usage: ninja -t commands [options] [targets]\n" |
725 | "\n" |
726 | "options:\n" |
727 | " -s only print the final command to build [target], not the whole chain\n" |
728 | ); |
729 | return 1; |
730 | } |
731 | } |
732 | argv += optind; |
733 | argc -= optind; |
734 | |
735 | vector<Node*> nodes; |
736 | string err; |
737 | if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { |
738 | Error("%s" , err.c_str()); |
739 | return 1; |
740 | } |
741 | |
742 | EdgeSet seen; |
743 | for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in) |
744 | PrintCommands((*in)->in_edge(), &seen, mode); |
745 | |
746 | return 0; |
747 | } |
748 | |
749 | void CollectInputs(Edge* edge, std::set<Edge*>* seen, |
750 | std::vector<std::string>* result) { |
751 | if (!edge) |
752 | return; |
753 | if (!seen->insert(edge).second) |
754 | return; |
755 | |
756 | for (vector<Node*>::iterator in = edge->inputs_.begin(); |
757 | in != edge->inputs_.end(); ++in) |
758 | CollectInputs((*in)->in_edge(), seen, result); |
759 | |
760 | if (!edge->is_phony()) { |
761 | edge->CollectInputs(true, result); |
762 | } |
763 | } |
764 | |
765 | int NinjaMain::ToolInputs(const Options* options, int argc, char* argv[]) { |
766 | // The inputs tool uses getopt, and expects argv[0] to contain the name of |
767 | // the tool, i.e. "inputs". |
768 | argc++; |
769 | argv--; |
770 | optind = 1; |
771 | int opt; |
772 | const option kLongOptions[] = { { "help" , no_argument, NULL, 'h' }, |
773 | { NULL, 0, NULL, 0 } }; |
774 | while ((opt = getopt_long(argc, argv, "h" , kLongOptions, NULL)) != -1) { |
775 | switch (opt) { |
776 | case 'h': |
777 | default: |
778 | // clang-format off |
779 | printf( |
780 | "Usage '-t inputs [options] [targets]\n" |
781 | "\n" |
782 | "List all inputs used for a set of targets. Note that this includes\n" |
783 | "explicit, implicit and order-only inputs, but not validation ones.\n\n" |
784 | "Options:\n" |
785 | " -h, --help Print this message.\n" ); |
786 | // clang-format on |
787 | return 1; |
788 | } |
789 | } |
790 | argv += optind; |
791 | argc -= optind; |
792 | |
793 | vector<Node*> nodes; |
794 | string err; |
795 | if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { |
796 | Error("%s" , err.c_str()); |
797 | return 1; |
798 | } |
799 | |
800 | std::set<Edge*> seen; |
801 | std::vector<std::string> result; |
802 | for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in) |
803 | CollectInputs((*in)->in_edge(), &seen, &result); |
804 | |
805 | // Make output deterministic by sorting then removing duplicates. |
806 | std::sort(result.begin(), result.end()); |
807 | result.erase(std::unique(result.begin(), result.end()), result.end()); |
808 | |
809 | for (size_t n = 0; n < result.size(); ++n) |
810 | puts(result[n].c_str()); |
811 | |
812 | return 0; |
813 | } |
814 | |
815 | int NinjaMain::ToolClean(const Options* options, int argc, char* argv[]) { |
816 | // The clean tool uses getopt, and expects argv[0] to contain the name of |
817 | // the tool, i.e. "clean". |
818 | argc++; |
819 | argv--; |
820 | |
821 | bool generator = false; |
822 | bool clean_rules = false; |
823 | |
824 | optind = 1; |
825 | int opt; |
826 | while ((opt = getopt(argc, argv, const_cast<char*>("hgr" ))) != -1) { |
827 | switch (opt) { |
828 | case 'g': |
829 | generator = true; |
830 | break; |
831 | case 'r': |
832 | clean_rules = true; |
833 | break; |
834 | case 'h': |
835 | default: |
836 | printf("usage: ninja -t clean [options] [targets]\n" |
837 | "\n" |
838 | "options:\n" |
839 | " -g also clean files marked as ninja generator output\n" |
840 | " -r interpret targets as a list of rules to clean instead\n" |
841 | ); |
842 | return 1; |
843 | } |
844 | } |
845 | argv += optind; |
846 | argc -= optind; |
847 | |
848 | if (clean_rules && argc == 0) { |
849 | Error("expected a rule to clean" ); |
850 | return 1; |
851 | } |
852 | |
853 | Cleaner cleaner(&state_, config_, &disk_interface_); |
854 | if (argc >= 1) { |
855 | if (clean_rules) |
856 | return cleaner.CleanRules(argc, argv); |
857 | else |
858 | return cleaner.CleanTargets(argc, argv); |
859 | } else { |
860 | return cleaner.CleanAll(generator); |
861 | } |
862 | } |
863 | |
864 | int NinjaMain::ToolCleanDead(const Options* options, int argc, char* argv[]) { |
865 | Cleaner cleaner(&state_, config_, &disk_interface_); |
866 | return cleaner.CleanDead(build_log_.entries()); |
867 | } |
868 | |
869 | enum EvaluateCommandMode { |
870 | ECM_NORMAL, |
871 | ECM_EXPAND_RSPFILE |
872 | }; |
873 | std::string EvaluateCommandWithRspfile(const Edge* edge, |
874 | const EvaluateCommandMode mode) { |
875 | string command = edge->EvaluateCommand(); |
876 | if (mode == ECM_NORMAL) |
877 | return command; |
878 | |
879 | string rspfile = edge->GetUnescapedRspfile(); |
880 | if (rspfile.empty()) |
881 | return command; |
882 | |
883 | size_t index = command.find(rspfile); |
884 | if (index == 0 || index == string::npos || command[index - 1] != '@') |
885 | return command; |
886 | |
887 | string rspfile_content = edge->GetBinding("rspfile_content" ); |
888 | size_t newline_index = 0; |
889 | while ((newline_index = rspfile_content.find('\n', newline_index)) != |
890 | string::npos) { |
891 | rspfile_content.replace(newline_index, 1, 1, ' '); |
892 | ++newline_index; |
893 | } |
894 | command.replace(index - 1, rspfile.length() + 1, rspfile_content); |
895 | return command; |
896 | } |
897 | |
898 | void printCompdb(const char* const directory, const Edge* const edge, |
899 | const EvaluateCommandMode eval_mode) { |
900 | printf("\n {\n \"directory\": \"" ); |
901 | PrintJSONString(directory); |
902 | printf("\",\n \"command\": \"" ); |
903 | PrintJSONString(EvaluateCommandWithRspfile(edge, eval_mode)); |
904 | printf("\",\n \"file\": \"" ); |
905 | PrintJSONString(edge->inputs_[0]->path()); |
906 | printf("\",\n \"output\": \"" ); |
907 | PrintJSONString(edge->outputs_[0]->path()); |
908 | printf("\"\n }" ); |
909 | } |
910 | |
911 | int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, |
912 | char* argv[]) { |
913 | // The compdb tool uses getopt, and expects argv[0] to contain the name of |
914 | // the tool, i.e. "compdb". |
915 | argc++; |
916 | argv--; |
917 | |
918 | EvaluateCommandMode eval_mode = ECM_NORMAL; |
919 | |
920 | optind = 1; |
921 | int opt; |
922 | while ((opt = getopt(argc, argv, const_cast<char*>("hx" ))) != -1) { |
923 | switch(opt) { |
924 | case 'x': |
925 | eval_mode = ECM_EXPAND_RSPFILE; |
926 | break; |
927 | |
928 | case 'h': |
929 | default: |
930 | printf( |
931 | "usage: ninja -t compdb [options] [rules]\n" |
932 | "\n" |
933 | "options:\n" |
934 | " -x expand @rspfile style response file invocations\n" |
935 | ); |
936 | return 1; |
937 | } |
938 | } |
939 | argv += optind; |
940 | argc -= optind; |
941 | |
942 | bool first = true; |
943 | vector<char> cwd; |
944 | char* success = NULL; |
945 | |
946 | do { |
947 | cwd.resize(cwd.size() + 1024); |
948 | errno = 0; |
949 | success = getcwd(&cwd[0], cwd.size()); |
950 | } while (!success && errno == ERANGE); |
951 | if (!success) { |
952 | Error("cannot determine working directory: %s" , strerror(errno)); |
953 | return 1; |
954 | } |
955 | |
956 | putchar('['); |
957 | for (vector<Edge*>::iterator e = state_.edges_.begin(); |
958 | e != state_.edges_.end(); ++e) { |
959 | if ((*e)->inputs_.empty()) |
960 | continue; |
961 | if (argc == 0) { |
962 | if (!first) { |
963 | putchar(','); |
964 | } |
965 | printCompdb(&cwd[0], *e, eval_mode); |
966 | first = false; |
967 | } else { |
968 | for (int i = 0; i != argc; ++i) { |
969 | if ((*e)->rule_->name() == argv[i]) { |
970 | if (!first) { |
971 | putchar(','); |
972 | } |
973 | printCompdb(&cwd[0], *e, eval_mode); |
974 | first = false; |
975 | } |
976 | } |
977 | } |
978 | } |
979 | |
980 | puts("\n]" ); |
981 | return 0; |
982 | } |
983 | |
984 | int NinjaMain::ToolRecompact(const Options* options, int argc, char* argv[]) { |
985 | if (!EnsureBuildDirExists()) |
986 | return 1; |
987 | |
988 | if (!OpenBuildLog(/*recompact_only=*/true) || |
989 | !OpenDepsLog(/*recompact_only=*/true)) |
990 | return 1; |
991 | |
992 | return 0; |
993 | } |
994 | |
995 | int NinjaMain::ToolRestat(const Options* options, int argc, char* argv[]) { |
996 | // The restat tool uses getopt, and expects argv[0] to contain the name of the |
997 | // tool, i.e. "restat" |
998 | argc++; |
999 | argv--; |
1000 | |
1001 | optind = 1; |
1002 | int opt; |
1003 | while ((opt = getopt(argc, argv, const_cast<char*>("h" ))) != -1) { |
1004 | switch (opt) { |
1005 | case 'h': |
1006 | default: |
1007 | printf("usage: ninja -t restat [outputs]\n" ); |
1008 | return 1; |
1009 | } |
1010 | } |
1011 | argv += optind; |
1012 | argc -= optind; |
1013 | |
1014 | if (!EnsureBuildDirExists()) |
1015 | return 1; |
1016 | |
1017 | string log_path = ".ninja_log" ; |
1018 | if (!build_dir_.empty()) |
1019 | log_path = build_dir_ + "/" + log_path; |
1020 | |
1021 | string err; |
1022 | const LoadStatus status = build_log_.Load(log_path, &err); |
1023 | if (status == LOAD_ERROR) { |
1024 | Error("loading build log %s: %s" , log_path.c_str(), err.c_str()); |
1025 | return EXIT_FAILURE; |
1026 | } |
1027 | if (status == LOAD_NOT_FOUND) { |
1028 | // Nothing to restat, ignore this |
1029 | return EXIT_SUCCESS; |
1030 | } |
1031 | if (!err.empty()) { |
1032 | // Hack: Load() can return a warning via err by returning LOAD_SUCCESS. |
1033 | Warning("%s" , err.c_str()); |
1034 | err.clear(); |
1035 | } |
1036 | |
1037 | bool success = build_log_.Restat(log_path, disk_interface_, argc, argv, &err); |
1038 | if (!success) { |
1039 | Error("failed recompaction: %s" , err.c_str()); |
1040 | return EXIT_FAILURE; |
1041 | } |
1042 | |
1043 | if (!config_.dry_run) { |
1044 | if (!build_log_.OpenForWrite(log_path, *this, &err)) { |
1045 | Error("opening build log: %s" , err.c_str()); |
1046 | return EXIT_FAILURE; |
1047 | } |
1048 | } |
1049 | |
1050 | return EXIT_SUCCESS; |
1051 | } |
1052 | |
1053 | int NinjaMain::ToolUrtle(const Options* options, int argc, char** argv) { |
1054 | // RLE encoded. |
1055 | const char* urtle = |
1056 | " 13 ,3;2!2;\n8 ,;<11!;\n5 `'<10!(2`'2!\n11 ,6;, `\\. `\\9 .,c13$ec,.\n6 " |
1057 | ",2;11!>; `. ,;!2> .e8$2\".2 \"?7$e.\n <:<8!'` 2.3,.2` ,3!' ;,(?7\";2!2'<" |
1058 | "; `?6$PF ,;,\n2 `'4!8;<!3'`2 3! ;,`'2`2'3!;4!`2.`!;2 3,2 .<!2'`).\n5 3`5" |
1059 | "'2`9 `!2 `4!><3;5! J2$b,`!>;2!:2!`,d?b`!>\n26 `'-;,(<9!> $F3 )3.:!.2 d\"" |
1060 | "2 ) !>\n30 7`2'<3!- \"=-='5 .2 `2-=\",!>\n25 .ze9$er2 .,cd16$bc.'\n22 .e" |
1061 | "14$,26$.\n21 z45$c .\n20 J50$c\n20 14$P\"`?34$b\n20 14$ dbc `2\"?22$?7$c" |
1062 | "\n20 ?18$c.6 4\"8?4\" c8$P\n9 .2,.8 \"20$c.3 ._14 J9$\n .2,2c9$bec,.2 `?" |
1063 | "21$c.3`4%,3%,3 c8$P\"\n22$c2 2\"?21$bc2,.2` .2,c7$P2\",cb\n23$b bc,.2\"2" |
1064 | "?14$2F2\"5?2\",J5$P\" ,zd3$\n24$ ?$3?%3 `2\"2?12$bcucd3$P3\"2 2=7$\n23$P" |
1065 | "\" ,3;<5!>2;,. `4\"6?2\"2 ,9;, `\"?2$\n" ; |
1066 | int count = 0; |
1067 | for (const char* p = urtle; *p; p++) { |
1068 | if ('0' <= *p && *p <= '9') { |
1069 | count = count*10 + *p - '0'; |
1070 | } else { |
1071 | for (int i = 0; i < max(count, 1); ++i) |
1072 | printf("%c" , *p); |
1073 | count = 0; |
1074 | } |
1075 | } |
1076 | return 0; |
1077 | } |
1078 | |
1079 | /// Find the function to execute for \a tool_name and return it via \a func. |
1080 | /// Returns a Tool, or NULL if Ninja should exit. |
1081 | const Tool* ChooseTool(const string& tool_name) { |
1082 | static const Tool kTools[] = { |
1083 | { "browse" , "browse dependency graph in a web browser" , |
1084 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse }, |
1085 | #ifdef _WIN32 |
1086 | { "msvc" , "build helper for MSVC cl.exe (DEPRECATED)" , |
1087 | Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC }, |
1088 | #endif |
1089 | { "clean" , "clean built files" , |
1090 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean }, |
1091 | { "commands" , "list all commands required to rebuild given targets" , |
1092 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands }, |
1093 | { "inputs" , "list all inputs required to rebuild given targets" , |
1094 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolInputs}, |
1095 | { "deps" , "show dependencies stored in the deps log" , |
1096 | Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps }, |
1097 | { "missingdeps" , "check deps log dependencies on generated files" , |
1098 | Tool::RUN_AFTER_LOGS, &NinjaMain::ToolMissingDeps }, |
1099 | { "graph" , "output graphviz dot file for targets" , |
1100 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph }, |
1101 | { "query" , "show inputs/outputs for a path" , |
1102 | Tool::RUN_AFTER_LOGS, &NinjaMain::ToolQuery }, |
1103 | { "targets" , "list targets by their rule or depth in the DAG" , |
1104 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolTargets }, |
1105 | { "compdb" , "dump JSON compilation database to stdout" , |
1106 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase }, |
1107 | { "recompact" , "recompacts ninja-internal data structures" , |
1108 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRecompact }, |
1109 | { "restat" , "restats all outputs in the build log" , |
1110 | Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolRestat }, |
1111 | { "rules" , "list all rules" , |
1112 | Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRules }, |
1113 | { "cleandead" , "clean built files that are no longer produced by the manifest" , |
1114 | Tool::RUN_AFTER_LOGS, &NinjaMain::ToolCleanDead }, |
1115 | { "urtle" , NULL, |
1116 | Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolUrtle }, |
1117 | #ifdef _WIN32 |
1118 | { "wincodepage" , "print the Windows code page used by ninja" , |
1119 | Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolWinCodePage }, |
1120 | #endif |
1121 | { NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL } |
1122 | }; |
1123 | |
1124 | if (tool_name == "list" ) { |
1125 | printf("ninja subtools:\n" ); |
1126 | for (const Tool* tool = &kTools[0]; tool->name; ++tool) { |
1127 | if (tool->desc) |
1128 | printf("%11s %s\n" , tool->name, tool->desc); |
1129 | } |
1130 | return NULL; |
1131 | } |
1132 | |
1133 | for (const Tool* tool = &kTools[0]; tool->name; ++tool) { |
1134 | if (tool->name == tool_name) |
1135 | return tool; |
1136 | } |
1137 | |
1138 | vector<const char*> words; |
1139 | for (const Tool* tool = &kTools[0]; tool->name; ++tool) |
1140 | words.push_back(tool->name); |
1141 | const char* suggestion = SpellcheckStringV(tool_name, words); |
1142 | if (suggestion) { |
1143 | Fatal("unknown tool '%s', did you mean '%s'?" , |
1144 | tool_name.c_str(), suggestion); |
1145 | } else { |
1146 | Fatal("unknown tool '%s'" , tool_name.c_str()); |
1147 | } |
1148 | return NULL; // Not reached. |
1149 | } |
1150 | |
1151 | /// Enable a debugging mode. Returns false if Ninja should exit instead |
1152 | /// of continuing. |
1153 | bool DebugEnable(const string& name) { |
1154 | if (name == "list" ) { |
1155 | printf("debugging modes:\n" |
1156 | " stats print operation counts/timing info\n" |
1157 | " explain explain what caused a command to execute\n" |
1158 | " keepdepfile don't delete depfiles after they're read by ninja\n" |
1159 | " keeprsp don't delete @response files on success\n" |
1160 | #ifdef _WIN32 |
1161 | " nostatcache don't batch stat() calls per directory and cache them\n" |
1162 | #endif |
1163 | "multiple modes can be enabled via -d FOO -d BAR\n" ); |
1164 | return false; |
1165 | } else if (name == "stats" ) { |
1166 | g_metrics = new Metrics; |
1167 | return true; |
1168 | } else if (name == "explain" ) { |
1169 | g_explaining = true; |
1170 | return true; |
1171 | } else if (name == "keepdepfile" ) { |
1172 | g_keep_depfile = true; |
1173 | return true; |
1174 | } else if (name == "keeprsp" ) { |
1175 | g_keep_rsp = true; |
1176 | return true; |
1177 | } else if (name == "nostatcache" ) { |
1178 | g_experimental_statcache = false; |
1179 | return true; |
1180 | } else { |
1181 | const char* suggestion = |
1182 | SpellcheckString(name.c_str(), |
1183 | "stats" , "explain" , "keepdepfile" , "keeprsp" , |
1184 | "nostatcache" , NULL); |
1185 | if (suggestion) { |
1186 | Error("unknown debug setting '%s', did you mean '%s'?" , |
1187 | name.c_str(), suggestion); |
1188 | } else { |
1189 | Error("unknown debug setting '%s'" , name.c_str()); |
1190 | } |
1191 | return false; |
1192 | } |
1193 | } |
1194 | |
1195 | /// Set a warning flag. Returns false if Ninja should exit instead of |
1196 | /// continuing. |
1197 | bool WarningEnable(const string& name, Options* options) { |
1198 | if (name == "list" ) { |
1199 | printf("warning flags:\n" |
1200 | " phonycycle={err,warn} phony build statement references itself\n" |
1201 | ); |
1202 | return false; |
1203 | } else if (name == "dupbuild=err" ) { |
1204 | options->dupe_edges_should_err = true; |
1205 | return true; |
1206 | } else if (name == "dupbuild=warn" ) { |
1207 | options->dupe_edges_should_err = false; |
1208 | return true; |
1209 | } else if (name == "phonycycle=err" ) { |
1210 | options->phony_cycle_should_err = true; |
1211 | return true; |
1212 | } else if (name == "phonycycle=warn" ) { |
1213 | options->phony_cycle_should_err = false; |
1214 | return true; |
1215 | } else if (name == "depfilemulti=err" || |
1216 | name == "depfilemulti=warn" ) { |
1217 | Warning("deprecated warning 'depfilemulti'" ); |
1218 | return true; |
1219 | } else { |
1220 | const char* suggestion = |
1221 | SpellcheckString(name.c_str(), "dupbuild=err" , "dupbuild=warn" , |
1222 | "phonycycle=err" , "phonycycle=warn" , NULL); |
1223 | if (suggestion) { |
1224 | Error("unknown warning flag '%s', did you mean '%s'?" , |
1225 | name.c_str(), suggestion); |
1226 | } else { |
1227 | Error("unknown warning flag '%s'" , name.c_str()); |
1228 | } |
1229 | return false; |
1230 | } |
1231 | } |
1232 | |
1233 | bool NinjaMain::OpenBuildLog(bool recompact_only) { |
1234 | string log_path = ".ninja_log" ; |
1235 | if (!build_dir_.empty()) |
1236 | log_path = build_dir_ + "/" + log_path; |
1237 | |
1238 | string err; |
1239 | const LoadStatus status = build_log_.Load(log_path, &err); |
1240 | if (status == LOAD_ERROR) { |
1241 | Error("loading build log %s: %s" , log_path.c_str(), err.c_str()); |
1242 | return false; |
1243 | } |
1244 | if (!err.empty()) { |
1245 | // Hack: Load() can return a warning via err by returning LOAD_SUCCESS. |
1246 | Warning("%s" , err.c_str()); |
1247 | err.clear(); |
1248 | } |
1249 | |
1250 | if (recompact_only) { |
1251 | if (status == LOAD_NOT_FOUND) { |
1252 | return true; |
1253 | } |
1254 | bool success = build_log_.Recompact(log_path, *this, &err); |
1255 | if (!success) |
1256 | Error("failed recompaction: %s" , err.c_str()); |
1257 | return success; |
1258 | } |
1259 | |
1260 | if (!config_.dry_run) { |
1261 | if (!build_log_.OpenForWrite(log_path, *this, &err)) { |
1262 | Error("opening build log: %s" , err.c_str()); |
1263 | return false; |
1264 | } |
1265 | } |
1266 | |
1267 | return true; |
1268 | } |
1269 | |
1270 | /// Open the deps log: load it, then open for writing. |
1271 | /// @return false on error. |
1272 | bool NinjaMain::OpenDepsLog(bool recompact_only) { |
1273 | string path = ".ninja_deps" ; |
1274 | if (!build_dir_.empty()) |
1275 | path = build_dir_ + "/" + path; |
1276 | |
1277 | string err; |
1278 | const LoadStatus status = deps_log_.Load(path, &state_, &err); |
1279 | if (status == LOAD_ERROR) { |
1280 | Error("loading deps log %s: %s" , path.c_str(), err.c_str()); |
1281 | return false; |
1282 | } |
1283 | if (!err.empty()) { |
1284 | // Hack: Load() can return a warning via err by returning LOAD_SUCCESS. |
1285 | Warning("%s" , err.c_str()); |
1286 | err.clear(); |
1287 | } |
1288 | |
1289 | if (recompact_only) { |
1290 | if (status == LOAD_NOT_FOUND) { |
1291 | return true; |
1292 | } |
1293 | bool success = deps_log_.Recompact(path, &err); |
1294 | if (!success) |
1295 | Error("failed recompaction: %s" , err.c_str()); |
1296 | return success; |
1297 | } |
1298 | |
1299 | if (!config_.dry_run) { |
1300 | if (!deps_log_.OpenForWrite(path, &err)) { |
1301 | Error("opening deps log: %s" , err.c_str()); |
1302 | return false; |
1303 | } |
1304 | } |
1305 | |
1306 | return true; |
1307 | } |
1308 | |
1309 | void NinjaMain::DumpMetrics() { |
1310 | g_metrics->Report(); |
1311 | |
1312 | printf("\n" ); |
1313 | int count = (int)state_.paths_.size(); |
1314 | int buckets = (int)state_.paths_.bucket_count(); |
1315 | printf("path->node hash load %.2f (%d entries / %d buckets)\n" , |
1316 | count / (double) buckets, count, buckets); |
1317 | } |
1318 | |
1319 | bool NinjaMain::EnsureBuildDirExists() { |
1320 | build_dir_ = state_.bindings_.LookupVariable("builddir" ); |
1321 | if (!build_dir_.empty() && !config_.dry_run) { |
1322 | if (!disk_interface_.MakeDirs(build_dir_ + "/." ) && errno != EEXIST) { |
1323 | Error("creating build directory %s: %s" , |
1324 | build_dir_.c_str(), strerror(errno)); |
1325 | return false; |
1326 | } |
1327 | } |
1328 | return true; |
1329 | } |
1330 | |
1331 | int NinjaMain::RunBuild(int argc, char** argv, Status* status) { |
1332 | string err; |
1333 | vector<Node*> targets; |
1334 | if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) { |
1335 | status->Error("%s" , err.c_str()); |
1336 | return 1; |
1337 | } |
1338 | |
1339 | disk_interface_.AllowStatCache(g_experimental_statcache); |
1340 | |
1341 | Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_, |
1342 | status, start_time_millis_); |
1343 | for (size_t i = 0; i < targets.size(); ++i) { |
1344 | if (!builder.AddTarget(targets[i], &err)) { |
1345 | if (!err.empty()) { |
1346 | status->Error("%s" , err.c_str()); |
1347 | return 1; |
1348 | } else { |
1349 | // Added a target that is already up-to-date; not really |
1350 | // an error. |
1351 | } |
1352 | } |
1353 | } |
1354 | |
1355 | // Make sure restat rules do not see stale timestamps. |
1356 | disk_interface_.AllowStatCache(false); |
1357 | |
1358 | if (builder.AlreadyUpToDate()) { |
1359 | status->Info("no work to do." ); |
1360 | return 0; |
1361 | } |
1362 | |
1363 | if (!builder.Build(&err)) { |
1364 | status->Info("build stopped: %s." , err.c_str()); |
1365 | if (err.find("interrupted by user" ) != string::npos) { |
1366 | return 2; |
1367 | } |
1368 | return 1; |
1369 | } |
1370 | |
1371 | return 0; |
1372 | } |
1373 | |
1374 | #ifdef _MSC_VER |
1375 | |
1376 | /// This handler processes fatal crashes that you can't catch |
1377 | /// Test example: C++ exception in a stack-unwind-block |
1378 | /// Real-world example: ninja launched a compiler to process a tricky |
1379 | /// C++ input file. The compiler got itself into a state where it |
1380 | /// generated 3 GB of output and caused ninja to crash. |
1381 | void TerminateHandler() { |
1382 | CreateWin32MiniDump(NULL); |
1383 | Fatal("terminate handler called" ); |
1384 | } |
1385 | |
1386 | /// On Windows, we want to prevent error dialogs in case of exceptions. |
1387 | /// This function handles the exception, and writes a minidump. |
1388 | int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) { |
1389 | Error("exception: 0x%X" , code); // e.g. EXCEPTION_ACCESS_VIOLATION |
1390 | fflush(stderr); |
1391 | CreateWin32MiniDump(ep); |
1392 | return EXCEPTION_EXECUTE_HANDLER; |
1393 | } |
1394 | |
1395 | #endif // _MSC_VER |
1396 | |
1397 | class DeferGuessParallelism { |
1398 | public: |
1399 | bool needGuess; |
1400 | BuildConfig* config; |
1401 | |
1402 | DeferGuessParallelism(BuildConfig* config) |
1403 | : needGuess(true), config(config) {} |
1404 | |
1405 | void Refresh() { |
1406 | if (needGuess) { |
1407 | needGuess = false; |
1408 | config->parallelism = GuessParallelism(); |
1409 | } |
1410 | } |
1411 | ~DeferGuessParallelism() { Refresh(); } |
1412 | }; |
1413 | |
1414 | /// Parse argv for command-line options. |
1415 | /// Returns an exit code, or -1 if Ninja should continue. |
1416 | int ReadFlags(int* argc, char*** argv, |
1417 | Options* options, BuildConfig* config) { |
1418 | DeferGuessParallelism deferGuessParallelism(config); |
1419 | |
1420 | enum { OPT_VERSION = 1, OPT_QUIET = 2 }; |
1421 | const option kLongOptions[] = { |
1422 | { "help" , no_argument, NULL, 'h' }, |
1423 | { "version" , no_argument, NULL, OPT_VERSION }, |
1424 | { "verbose" , no_argument, NULL, 'v' }, |
1425 | { "quiet" , no_argument, NULL, OPT_QUIET }, |
1426 | { NULL, 0, NULL, 0 } |
1427 | }; |
1428 | |
1429 | int opt; |
1430 | while (!options->tool && |
1431 | (opt = getopt_long(*argc, *argv, "d:f:j:k:l:nt:vw:C:h" , kLongOptions, |
1432 | NULL)) != -1) { |
1433 | switch (opt) { |
1434 | case 'd': |
1435 | if (!DebugEnable(optarg)) |
1436 | return 1; |
1437 | break; |
1438 | case 'f': |
1439 | options->input_file = optarg; |
1440 | break; |
1441 | case 'j': { |
1442 | char* end; |
1443 | int value = strtol(optarg, &end, 10); |
1444 | if (*end != 0 || value < 0) |
1445 | Fatal("invalid -j parameter" ); |
1446 | |
1447 | // We want to run N jobs in parallel. For N = 0, INT_MAX |
1448 | // is close enough to infinite for most sane builds. |
1449 | config->parallelism = value > 0 ? value : INT_MAX; |
1450 | deferGuessParallelism.needGuess = false; |
1451 | break; |
1452 | } |
1453 | case 'k': { |
1454 | char* end; |
1455 | int value = strtol(optarg, &end, 10); |
1456 | if (*end != 0) |
1457 | Fatal("-k parameter not numeric; did you mean -k 0?" ); |
1458 | |
1459 | // We want to go until N jobs fail, which means we should allow |
1460 | // N failures and then stop. For N <= 0, INT_MAX is close enough |
1461 | // to infinite for most sane builds. |
1462 | config->failures_allowed = value > 0 ? value : INT_MAX; |
1463 | break; |
1464 | } |
1465 | case 'l': { |
1466 | char* end; |
1467 | double value = strtod(optarg, &end); |
1468 | if (end == optarg) |
1469 | Fatal("-l parameter not numeric: did you mean -l 0.0?" ); |
1470 | config->max_load_average = value; |
1471 | break; |
1472 | } |
1473 | case 'n': |
1474 | config->dry_run = true; |
1475 | break; |
1476 | case 't': |
1477 | options->tool = ChooseTool(optarg); |
1478 | if (!options->tool) |
1479 | return 0; |
1480 | break; |
1481 | case 'v': |
1482 | config->verbosity = BuildConfig::VERBOSE; |
1483 | break; |
1484 | case OPT_QUIET: |
1485 | config->verbosity = BuildConfig::NO_STATUS_UPDATE; |
1486 | break; |
1487 | case 'w': |
1488 | if (!WarningEnable(optarg, options)) |
1489 | return 1; |
1490 | break; |
1491 | case 'C': |
1492 | options->working_dir = optarg; |
1493 | break; |
1494 | case OPT_VERSION: |
1495 | printf("%s\n" , kNinjaVersion); |
1496 | return 0; |
1497 | case 'h': |
1498 | default: |
1499 | deferGuessParallelism.Refresh(); |
1500 | Usage(*config); |
1501 | return 1; |
1502 | } |
1503 | } |
1504 | *argv += optind; |
1505 | *argc -= optind; |
1506 | |
1507 | return -1; |
1508 | } |
1509 | |
1510 | NORETURN void real_main(int argc, char** argv) { |
1511 | // Use exit() instead of return in this function to avoid potentially |
1512 | // expensive cleanup when destructing NinjaMain. |
1513 | BuildConfig config; |
1514 | Options options = {}; |
1515 | options.input_file = "build.ninja" ; |
1516 | options.dupe_edges_should_err = true; |
1517 | |
1518 | setvbuf(stdout, NULL, _IOLBF, BUFSIZ); |
1519 | const char* ninja_command = argv[0]; |
1520 | |
1521 | int exit_code = ReadFlags(&argc, &argv, &options, &config); |
1522 | if (exit_code >= 0) |
1523 | exit(exit_code); |
1524 | |
1525 | Status* status = new StatusPrinter(config); |
1526 | |
1527 | if (options.working_dir) { |
1528 | // The formatting of this string, complete with funny quotes, is |
1529 | // so Emacs can properly identify that the cwd has changed for |
1530 | // subsequent commands. |
1531 | // Don't print this if a tool is being used, so that tool output |
1532 | // can be piped into a file without this string showing up. |
1533 | if (!options.tool && config.verbosity != BuildConfig::NO_STATUS_UPDATE) |
1534 | status->Info("Entering directory `%s'" , options.working_dir); |
1535 | if (chdir(options.working_dir) < 0) { |
1536 | Fatal("chdir to '%s' - %s" , options.working_dir, strerror(errno)); |
1537 | } |
1538 | } |
1539 | |
1540 | if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) { |
1541 | // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed |
1542 | // by other tools. |
1543 | NinjaMain ninja(ninja_command, config); |
1544 | exit((ninja.*options.tool->func)(&options, argc, argv)); |
1545 | } |
1546 | |
1547 | // Limit number of rebuilds, to prevent infinite loops. |
1548 | const int kCycleLimit = 100; |
1549 | for (int cycle = 1; cycle <= kCycleLimit; ++cycle) { |
1550 | NinjaMain ninja(ninja_command, config); |
1551 | |
1552 | ManifestParserOptions parser_opts; |
1553 | if (options.dupe_edges_should_err) { |
1554 | parser_opts.dupe_edge_action_ = kDupeEdgeActionError; |
1555 | } |
1556 | if (options.phony_cycle_should_err) { |
1557 | parser_opts.phony_cycle_action_ = kPhonyCycleActionError; |
1558 | } |
1559 | ManifestParser parser(&ninja.state_, &ninja.disk_interface_, parser_opts); |
1560 | string err; |
1561 | if (!parser.Load(options.input_file, &err)) { |
1562 | status->Error("%s" , err.c_str()); |
1563 | exit(1); |
1564 | } |
1565 | |
1566 | if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD) |
1567 | exit((ninja.*options.tool->func)(&options, argc, argv)); |
1568 | |
1569 | if (!ninja.EnsureBuildDirExists()) |
1570 | exit(1); |
1571 | |
1572 | if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog()) |
1573 | exit(1); |
1574 | |
1575 | if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS) |
1576 | exit((ninja.*options.tool->func)(&options, argc, argv)); |
1577 | |
1578 | // Attempt to rebuild the manifest before building anything else |
1579 | if (ninja.RebuildManifest(options.input_file, &err, status)) { |
1580 | // In dry_run mode the regeneration will succeed without changing the |
1581 | // manifest forever. Better to return immediately. |
1582 | if (config.dry_run) |
1583 | exit(0); |
1584 | // Start the build over with the new manifest. |
1585 | continue; |
1586 | } else if (!err.empty()) { |
1587 | status->Error("rebuilding '%s': %s" , options.input_file, err.c_str()); |
1588 | exit(1); |
1589 | } |
1590 | |
1591 | int result = ninja.RunBuild(argc, argv, status); |
1592 | if (g_metrics) |
1593 | ninja.DumpMetrics(); |
1594 | exit(result); |
1595 | } |
1596 | |
1597 | status->Error("manifest '%s' still dirty after %d tries, perhaps system time is not set" , |
1598 | options.input_file, kCycleLimit); |
1599 | exit(1); |
1600 | } |
1601 | |
1602 | } // anonymous namespace |
1603 | |
1604 | int main(int argc, char** argv) { |
1605 | #if defined(_MSC_VER) |
1606 | // Set a handler to catch crashes not caught by the __try..__except |
1607 | // block (e.g. an exception in a stack-unwind-block). |
1608 | std::set_terminate(TerminateHandler); |
1609 | __try { |
1610 | // Running inside __try ... __except suppresses any Windows error |
1611 | // dialogs for errors such as bad_alloc. |
1612 | real_main(argc, argv); |
1613 | } |
1614 | __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { |
1615 | // Common error situations return exitCode=1. 2 was chosen to |
1616 | // indicate a more serious problem. |
1617 | return 2; |
1618 | } |
1619 | #else |
1620 | real_main(argc, argv); |
1621 | #endif |
1622 | } |
1623 | |