• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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