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