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