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