66void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep);
76 const char* input_file;
79 const char* working_dir;
85 bool phony_cycle_should_err;
91 NinjaMain(
const char* ninja_command,
const BuildConfig& config) :
92 ninja_command_(ninja_command), config_(config),
96 const char* ninja_command_;
99 const BuildConfig& config_;
105 RealDiskInterface disk_interface_;
114 typedef int (NinjaMain::*ToolFunc)(
const Options*, int,
char**);
118 Node* CollectTarget(
const char* cpath,
string* err);
121 bool CollectTargetsFromArgs(
int argc,
char* argv[],
122 vector<Node*>* targets,
string* err);
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,
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[]);
148 bool OpenBuildLog(
bool recompact_only =
false);
152 bool OpenDepsLog(
bool recompact_only =
false);
156 bool EnsureBuildDirExists();
161 bool RebuildManifest(
const char* input_file,
string* err, Status* status);
165 void ParsePreviousElapsedTimes();
169 std::unique_ptr<Jobserver::Client> SetupJobserverClient(Status* status);
173 ExitStatus RunBuild(
int argc,
char** argv, Status* status);
178 virtual bool IsPathDead(StringPiece s)
const {
179 Node* n = state_.LookupNode(s);
194 Error(
"%s", err.c_str());
223 NinjaMain::ToolFunc func;
229"usage: ninja [options] [targets...]\n"
231"if targets are unspecified, builds the 'default' target (see manual).\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"
238" -C DIR change to DIR before doing anything else\n"
239" -f FILE specify input build file [default=build.ninja]\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"
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",
254int GuessParallelism() {
262 return processors + 2;
268bool NinjaMain::RebuildManifest(
const char* input_file,
string* err,
270 string path = input_file;
281 Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_,
282 status, start_time_millis_);
283 if (!builder.AddTarget(node, err))
286 if (builder.AlreadyUpToDate())
294 if (!node->
dirty()) {
304void NinjaMain::ParsePreviousElapsedTimes() {
305 for (Edge* edge : state_.
edges_) {
317Node* NinjaMain::CollectTarget(
const char* cpath,
string* err) {
327 bool first_dependent =
false;
328 if (!path.empty() && path[path.size() - 1] ==
'^') {
329 path.resize(path.size() - 1);
330 first_dependent =
true;
335 if (first_dependent) {
339 *err =
"'" + path +
"' has no out edge";
347 Fatal(
"edge has no outputs");
356 if (path ==
"clean") {
357 *err +=
", did you mean 'ninja -t clean'?";
358 }
else if (path ==
"help") {
359 *err +=
", did you mean 'ninja -h'?";
363 *err +=
", did you mean '" + suggestion->
path() +
"'?";
370bool NinjaMain::CollectTargetsFromArgs(
int argc,
char* argv[],
371 vector<Node*>* targets,
string* err) {
377 for (
int i = 0; i < argc; ++i) {
378 Node* node = CollectTarget(argv[i], err);
381 targets->push_back(node);
386int NinjaMain::ToolGraph(
const Options* options,
int argc,
char* argv[]) {
389 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
390 Error(
"%s", err.c_str());
394 GraphViz graph(&state_, &disk_interface_);
396 for (vector<Node*>::const_iterator n = nodes.begin(); n != nodes.end(); ++n)
403int NinjaMain::ToolQuery(
const Options* options,
int argc,
char* argv[]) {
405 Error(
"expected a target to query");
409 DyndepLoader dyndep_loader(&state_, &disk_interface_);
411 for (
int i = 0; i < argc; ++i) {
413 Node* node = CollectTarget(argv[i], &err);
415 Error(
"%s", err.c_str());
419 printf(
"%s:\n", node->
path().c_str());
420 if (Edge* edge = node->
in_edge()) {
422 if (!dyndep_loader.LoadDyndeps(edge->
dyndep_, &err)) {
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 =
"";
433 printf(
" %s%s\n", label, edge->
inputs_[in]->path().c_str());
436 printf(
" validations:\n");
437 for (std::vector<Node*>::iterator validation = edge->
validations_.begin();
439 printf(
" %s\n", (*validation)->path().c_str());
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());
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());
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);
473int NinjaMain::ToolBrowse(
const Options*,
int,
char**) {
474 Fatal(
"browse tool not supported on this platform");
480int NinjaMain::ToolMSVC(
const Options* options,
int argc,
char* argv[]) {
489int ToolTargetsList(
const vector<Node*>& nodes,
int depth,
int indent) {
490 for (vector<Node*>::const_iterator n = nodes.begin();
493 for (
int i = 0; i < indent; ++i)
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);
501 printf(
"%s\n", target);
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());
519int ToolTargetsList(State* state,
const string& rule_name) {
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());
534 for (set<string>::const_iterator i = rules.begin();
535 i != rules.end(); ++i) {
536 printf(
"%s\n", (*i).c_str());
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) {
548 (*out_node)->path().c_str(),
549 (*e)->rule_->name().c_str());
555int NinjaMain::ToolDeps(
const Options* options,
int argc,
char** argv) {
558 for (vector<Node*>::const_iterator ni = deps_log_.
nodes().begin();
559 ni != deps_log_.
nodes().end(); ++ni) {
561 nodes.push_back(*ni);
565 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
566 Error(
"%s", err.c_str());
571 RealDiskInterface disk_interface;
572 for (vector<Node*>::iterator it = nodes.begin(), end = nodes.end();
574 DepsLog::Deps* deps = deps_log_.
GetDeps(*it);
576 printf(
"%s: deps not found\n", (*it)->path().c_str());
583 Error(
"%s", err.c_str());
584 printf(
"%s: #deps %d, deps mtime %" PRId64 " (%s)\n",
586 (!mtime || mtime > deps->
mtime ?
"STALE":
"VALID"));
588 printf(
" %s\n", deps->
nodes[i]->
path().c_str());
595int NinjaMain::ToolMissingDeps(
const Options* options,
int argc,
char** argv) {
598 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
599 Error(
"%s", err.c_str());
602 RealDiskInterface disk_interface;
603 MissingDependencyPrinter printer;
604 MissingDependencyScanner scanner(&printer, &deps_log_, &state_,
606 for (vector<Node*>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
607 scanner.ProcessNode(*it);
609 scanner.PrintStats();
610 if (scanner.HadMissingDeps())
615int NinjaMain::ToolTargets(
const Options* options,
int argc,
char* argv[]) {
618 string mode = argv[0];
619 if (mode ==
"rule") {
624 return ToolTargetsSourceList(&state_);
626 return ToolTargetsList(&state_, rule);
627 }
else if (mode ==
"depth") {
629 depth = atoi(argv[1]);
630 }
else if (mode ==
"all") {
631 return ToolTargetsList(&state_);
633 const char* suggestion =
636 Error(
"unknown target tool mode '%s', did you mean '%s'?",
637 mode.c_str(), suggestion);
639 Error(
"unknown target tool mode '%s'", mode.c_str());
646 vector<Node*> root_nodes = state_.
RootNodes(&err);
648 return ToolTargetsList(root_nodes, depth, 0);
650 Error(
"%s", err.c_str());
655int NinjaMain::ToolRules(
const Options* options,
int argc,
char* argv[]) {
663 bool print_description =
false;
667 while ((opt =
getopt(argc, argv,
const_cast<char*
>(
"hd"))) != -1) {
670 print_description =
true;
674 printf(
"usage: ninja -t rules [options]\n"
677 " -d also print the description of the rule\n"
678 " -h print this message\n"
688 typedef map<string, std::unique_ptr<const Rule>> Rules;
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());
706int NinjaMain::ToolWinCodePage(
const Options* options,
int argc,
char* argv[]) {
708 printf(
"usage: ninja -t wincodepage\n");
711 printf(
"Build file encoding: %s\n", GetACP() == CP_UTF8?
"UTF-8" :
"ANSI");
716enum PrintCommandMode { PCM_Single, PCM_All };
717void PrintCommands(Edge* edge,
EdgeSet* seen, PrintCommandMode mode) {
720 if (!seen->insert(edge).second)
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);
733int NinjaMain::ToolCommands(
const Options* options,
int argc,
char* argv[]) {
739 PrintCommandMode mode = PCM_All;
743 while ((opt =
getopt(argc, argv,
const_cast<char*
>(
"hs"))) != -1) {
750 printf(
"usage: ninja -t commands [options] [targets]\n"
753" -s only print the final command to build [target], not the whole chain\n"
763 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
764 Error(
"%s", err.c_str());
769 for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in)
770 PrintCommands((*in)->in_edge(), &seen, mode);
775int NinjaMain::ToolInputs(
const Options* options,
int argc,
char* argv[]) {
782 bool shell_escape =
true;
783 bool dependency_order =
false;
792 { NULL, 0, NULL, 0 } };
793 while ((opt =
getopt_long(argc, argv,
"h0Ed", kLongOptions, NULL)) != -1) {
796 dependency_order =
true;
799 shell_escape =
false;
808"Usage '-t inputs [options] [targets]\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"
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"
826 std::vector<Node*> nodes;
828 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
829 Error(
"%s", err.c_str());
833 InputsCollector collector;
834 for (
const Node* node : nodes)
838 if (!dependency_order)
839 std::sort(inputs.begin(), inputs.end());
842 for (
const std::string& input : inputs) {
843 fwrite(input.c_str(), input.size(), 1, stdout);
848 for (
const std::string& input : inputs)
854int NinjaMain::ToolMultiInputs(
const Options* options,
int argc,
char* argv[]) {
862 char terminator =
'\n';
863 const char* delimiter =
"\t";
868 { NULL, 0, NULL, 0 } };
869 while ((opt =
getopt_long(argc, argv,
"d:h0", kLongOptions, NULL)) != -1) {
881"Usage '-t multi-inputs [options] [targets]\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"
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"
900 std::vector<Node*> nodes;
902 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
903 Error(
"%s", err.c_str());
907 for (
const Node* node : nodes) {
908 InputsCollector collector;
913 for (
const std::string& input : inputs) {
914 printf(
"%s%s%s", node->
path().c_str(), delimiter, input.c_str());
915 fputc(terminator, stdout);
922int NinjaMain::ToolClean(
const Options* options,
int argc,
char* argv[]) {
928 bool generator =
false;
929 bool clean_rules =
false;
933 while ((opt =
getopt(argc, argv,
const_cast<char*
>(
"hgr"))) != -1) {
943 printf(
"usage: ninja -t clean [options] [targets]\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"
955 if (clean_rules && argc == 0) {
956 Error(
"expected a rule to clean");
960 Cleaner cleaner(&state_, config_, &disk_interface_);
963 return cleaner.CleanRules(argc, argv);
965 return cleaner.CleanTargets(argc, argv);
967 return cleaner.CleanAll(generator);
971int NinjaMain::ToolCleanDead(
const Options* options,
int argc,
char* argv[]) {
972 Cleaner cleaner(&state_, config_, &disk_interface_);
973 return cleaner.CleanDead(build_log_.
entries());
976enum EvaluateCommandMode {
980std::string EvaluateCommandWithRspfile(
const Edge* edge,
981 const EvaluateCommandMode mode) {
983 if (mode == ECM_NORMAL)
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))
997 string rspfile_content = edge->
GetBinding(
"rspfile_content");
998 size_t newline_index = 0;
999 while ((newline_index = rspfile_content.find(
'\n', newline_index)) !=
1001 rspfile_content.replace(newline_index, 1, 1,
' ');
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);
1009 command.replace(index - 14, rspfile.length() + 14, rspfile_content);
1014void PrintOneCompdbObject(std::string
const& directory,
const Edge*
const edge,
1015 const EvaluateCommandMode eval_mode) {
1016 printf(
"\n {\n \"directory\": \"");
1018 printf(
"\",\n \"command\": \"");
1020 printf(
"\",\n \"file\": \"");
1022 printf(
"\",\n \"output\": \"");
1027int NinjaMain::ToolCompilationDatabase(
const Options* options,
int argc,
1034 EvaluateCommandMode eval_mode = ECM_NORMAL;
1038 while ((opt =
getopt(argc, argv,
const_cast<char*
>(
"hx"))) != -1) {
1041 eval_mode = ECM_EXPAND_RSPFILE;
1047 "usage: ninja -t compdb [options] [rules]\n"
1050 " -x expand @rspfile style response file invocations\n"
1062 for (
const Edge* edge : state_.
edges_) {
1069 PrintOneCompdbObject(directory, edge, eval_mode);
1072 for (
int i = 0; i != argc; ++i) {
1077 PrintOneCompdbObject(directory, edge, eval_mode);
1088int NinjaMain::ToolRecompact(
const Options* options,
int argc,
char* argv[]) {
1089 if (!EnsureBuildDirExists())
1092 if (!OpenBuildLog(
true) ||
1099int NinjaMain::ToolRestat(
const Options* options,
int argc,
char* argv[]) {
1107 while ((opt =
getopt(argc, argv,
const_cast<char*
>(
"h"))) != -1) {
1111 printf(
"usage: ninja -t restat [outputs]\n");
1118 if (!EnsureBuildDirExists())
1121 string log_path =
".ninja_log";
1122 if (!build_dir_.empty())
1123 log_path = build_dir_ +
"/" + log_path;
1128 Error(
"loading build log %s: %s", log_path.c_str(), err.c_str());
1129 return EXIT_FAILURE;
1133 return EXIT_SUCCESS;
1141 bool success = build_log_.
Restat(log_path, disk_interface_, argc, argv, &err);
1143 Error(
"failed recompaction: %s", err.c_str());
1144 return EXIT_FAILURE;
1149 Error(
"opening build log: %s", err.c_str());
1150 return EXIT_FAILURE;
1154 return EXIT_SUCCESS;
1157struct CompdbTargets {
1158 enum class Action { kDisplayHelpAndExit, kEmitCommands };
1161 EvaluateCommandMode eval_mode = ECM_NORMAL;
1163 std::vector<std::string> targets;
1165 static CompdbTargets CreateFromArgs(
int argc,
char* argv[]) {
1180 while ((opt =
getopt(argc, argv,
const_cast<char*
>(
"hx"))) != -1) {
1183 ret.eval_mode = ECM_EXPAND_RSPFILE;
1187 ret.action = CompdbTargets::Action::kDisplayHelpAndExit;
1193 int const targets_begin =
optind;
1194 int const targets_end = argc;
1196 if (targets_begin == targets_end) {
1197 Error(
"compdb-targets expects the name of at least one target");
1198 ret.action = CompdbTargets::Action::kDisplayHelpAndExit;
1200 ret.action = CompdbTargets::Action::kEmitCommands;
1201 for (
int i = targets_begin; i < targets_end; ++i) {
1202 ret.targets.push_back(argv[i]);
1210void PrintCompdb(std::string
const& directory, std::vector<Edge*>
const& edges,
1211 const EvaluateCommandMode eval_mode) {
1215 for (
const Edge* edge : edges) {
1220 PrintOneCompdbObject(directory, edge, eval_mode);
1227int NinjaMain::ToolCompilationDatabaseForTargets(
const Options* options,
1228 int argc,
char* argv[]) {
1229 auto compdb = CompdbTargets::CreateFromArgs(argc, argv);
1231 switch (compdb.action) {
1232 case CompdbTargets::Action::kDisplayHelpAndExit: {
1234 "usage: ninja -t compdb [-hx] target [targets]\n"
1237 " -h display this help message\n"
1238 " -x expand @rspfile style response file invocations\n");
1242 case CompdbTargets::Action::kEmitCommands: {
1243 CommandCollector collector;
1245 for (
const std::string& target_arg : compdb.targets) {
1247 Node* node = CollectTarget(target_arg.c_str(), &err);
1249 Fatal(
"%s", err.c_str());
1254 "'%s' is not a target "
1255 "(i.e. it is not an output of any `build` statement)",
1256 node->
path().c_str());
1262 PrintCompdb(directory, collector.
in_edges, compdb.eval_mode);
1269int NinjaMain::ToolUrtle(
const Options* options,
int argc,
char** argv) {
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";
1283 for (
const char* p = urtle; *p; p++) {
1284 if (
'0' <= *p && *p <=
'9') {
1285 count = count*10 + *p -
'0';
1287 for (
int i = 0; i < max(count, 1); ++i)
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 },
1302 {
"msvc",
"build helper for MSVC cl.exe (DEPRECATED)",
1303 Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC },
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 },
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 },
1337 Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolUrtle },
1339 {
"wincodepage",
"print the Windows code page used by ninja",
1340 Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolWinCodePage },
1342 { NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL }
1345 if (tool_name ==
"list") {
1346 printf(
"ninja subtools:\n");
1347 for (
const Tool* tool = &kTools[0]; tool->name; ++tool) {
1349 printf(
"%11s %s\n", tool->name, tool->desc);
1354 for (
const Tool* tool = &kTools[0]; tool->name; ++tool) {
1355 if (tool->name == tool_name)
1359 vector<const char*> words;
1360 for (
const Tool* tool = &kTools[0]; tool->name; ++tool)
1361 words.push_back(tool->name);
1364 Fatal(
"unknown tool '%s', did you mean '%s'?",
1365 tool_name.c_str(), suggestion);
1367 Fatal(
"unknown tool '%s'", tool_name.c_str());
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"
1382" nostatcache don't batch stat() calls per directory and cache them\n"
1384"multiple modes can be enabled via -d FOO -d BAR\n");
1386 }
else if (name ==
"stats") {
1389 }
else if (name ==
"explain") {
1392 }
else if (name ==
"keepdepfile") {
1395 }
else if (name ==
"keeprsp") {
1398 }
else if (name ==
"nostatcache") {
1402 const char* suggestion =
1404 "stats",
"explain",
"keepdepfile",
"keeprsp",
1405 "nostatcache", NULL);
1407 Error(
"unknown debug setting '%s', did you mean '%s'?",
1408 name.c_str(), suggestion);
1410 Error(
"unknown debug setting '%s'", name.c_str());
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"
1424 }
else if (name ==
"phonycycle=err") {
1425 options->phony_cycle_should_err =
true;
1427 }
else if (name ==
"phonycycle=warn") {
1428 options->phony_cycle_should_err =
false;
1430 }
else if (name ==
"dupbuild=err" ||
1431 name ==
"dupbuild=warn") {
1432 Warning(
"deprecated warning 'dupbuild'");
1434 }
else if (name ==
"depfilemulti=err" ||
1435 name ==
"depfilemulti=warn") {
1436 Warning(
"deprecated warning 'depfilemulti'");
1440 "phonycycle=warn",
nullptr);
1442 Error(
"unknown warning flag '%s', did you mean '%s'?",
1443 name.c_str(), suggestion);
1445 Error(
"unknown warning flag '%s'", name.c_str());
1451bool NinjaMain::OpenBuildLog(
bool recompact_only) {
1452 string log_path =
".ninja_log";
1453 if (!build_dir_.empty())
1454 log_path = build_dir_ +
"/" + log_path;
1459 Error(
"loading build log %s: %s", log_path.c_str(), err.c_str());
1468 if (recompact_only) {
1472 bool success = build_log_.
Recompact(log_path, *
this, &err);
1474 Error(
"failed recompaction: %s", err.c_str());
1480 Error(
"opening build log: %s", err.c_str());
1490bool NinjaMain::OpenDepsLog(
bool recompact_only) {
1491 string path =
".ninja_deps";
1492 if (!build_dir_.empty())
1493 path = build_dir_ +
"/" + path;
1498 Error(
"loading deps log %s: %s", path.c_str(), err.c_str());
1507 if (recompact_only) {
1511 bool success = deps_log_.
Recompact(path, &err);
1513 Error(
"failed recompaction: %s", err.c_str());
1519 Error(
"opening deps log: %s", err.c_str());
1527void NinjaMain::DumpMetrics() {
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);
1537bool NinjaMain::EnsureBuildDirExists() {
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));
1549std::unique_ptr<Jobserver::Client> NinjaMain::SetupJobserverClient(
1552 std::unique_ptr<Jobserver::Client> result;
1558 const char* makeflags = getenv(
"MAKEFLAGS");
1565 Jobserver::Config jobserver_config;
1570 status->
Warning(
"Ignoring jobserver: %s [%s]", err.c_str(), makeflags);
1574 if (!jobserver_config.
HasMode()) {
1580 status->
Info(
"Jobserver mode detected: %s", makeflags);
1584 if (!result.get()) {
1587 status->
Error(
"Could not initialize jobserver: %s", err.c_str());
1592ExitStatus NinjaMain::RunBuild(
int argc,
char** argv, Status* status) {
1594 std::vector<Node*> targets;
1595 if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) {
1596 status->
Error(
"%s", err.c_str());
1604 std::unique_ptr<Jobserver::Client> jobserver_client =
1605 SetupJobserverClient(status);
1607 Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_,
1608 status, start_time_millis_);
1610 if (jobserver_client.get()) {
1611 builder.SetJobserverClient(std::move(jobserver_client));
1614 for (
size_t i = 0; i < targets.size(); ++i) {
1615 if (!builder.AddTarget(targets[i], &err)) {
1617 status->
Error(
"%s", err.c_str());
1629 if (builder.AlreadyUpToDate()) {
1631 status->
Info(
"no work to do.");
1636 ExitStatus exit_status = builder.Build(&err);
1638 status->
Info(
"build stopped: %s.", err.c_str());
1639 if (err.find(
"interrupted by user") != string::npos) {
1654void TerminateHandler() {
1655 CreateWin32MiniDump(NULL);
1656 Fatal(
"terminate handler called");
1661int ExceptionFilter(
unsigned int code,
struct _EXCEPTION_POINTERS *ep) {
1662 Error(
"exception: 0x%X", code);
1664 CreateWin32MiniDump(ep);
1665 return EXCEPTION_EXECUTE_HANDLER;
1670class DeferGuessParallelism {
1673 BuildConfig* config;
1675 DeferGuessParallelism(BuildConfig* config)
1676 : needGuess(true), config(config) {}
1684 ~DeferGuessParallelism() { Refresh(); }
1689int ReadFlags(
int* argc,
char*** argv,
1690 Options* options, BuildConfig* config) {
1691 DeferGuessParallelism deferGuessParallelism(config);
1693 enum { OPT_VERSION = 1, OPT_QUIET = 2 };
1694 const option kLongOptions[] = {
1699 { NULL, 0, NULL, 0 }
1703 while (!options->tool &&
1704 (opt =
getopt_long(*argc, *argv,
"d:f:j:k:l:nt:vw:C:h", kLongOptions,
1708 if (!DebugEnable(
optarg))
1712 options->input_file =
optarg;
1716 long value = strtol(
optarg, &end, 10);
1717 if (*end != 0 || value < 0)
1718 Fatal(
"invalid -j parameter");
1723 static_cast<int>((value > 0 && value < INT_MAX) ? value : INT_MAX);
1725 deferGuessParallelism.needGuess =
false;
1730 long value = strtol(
optarg, &end, 10);
1732 Fatal(
"-k parameter not numeric; did you mean -k 0?");
1738 static_cast<int>((value > 0 && value < INT_MAX) ? value : INT_MAX);
1743 double value = strtod(
optarg, &end);
1745 Fatal(
"-l parameter not numeric: did you mean -l 0.0?");
1754 options->tool = ChooseTool(
optarg);
1765 if (!WarningEnable(
optarg, options))
1769 options->working_dir =
optarg;
1776 deferGuessParallelism.Refresh();
1787NORETURN void real_main(
int argc,
char** argv) {
1791 Options options = {};
1792 options.input_file =
"build.ninja";
1794 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
1795 const char* ninja_command = argv[0];
1797 int exit_code = ReadFlags(&argc, &argv, &options, &config);
1803 if (options.working_dir) {
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));
1816 if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) {
1819 NinjaMain ninja(ninja_command, config);
1820 exit((ninja.*options.tool->func)(&options, argc, argv));
1824 const int kCycleLimit = 100;
1825 for (
int cycle = 1; cycle <= kCycleLimit; ++cycle) {
1826 NinjaMain ninja(ninja_command, config);
1828 ManifestParserOptions parser_opts;
1829 if (options.phony_cycle_should_err) {
1832 ManifestParser parser(&ninja.state_, &ninja.disk_interface_, parser_opts);
1834 if (!parser.Load(options.input_file, &err)) {
1835 status->
Error(
"%s", err.c_str());
1839 if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD)
1840 exit((ninja.*options.tool->func)(&options, argc, argv));
1842 if (!ninja.EnsureBuildDirExists())
1845 if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog())
1848 if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS)
1849 exit((ninja.*options.tool->func)(&options, argc, argv));
1852 if (ninja.RebuildManifest(options.input_file, &err, status)) {
1859 }
else if (!err.empty()) {
1860 status->
Error(
"rebuilding '%s': %s", options.input_file, err.c_str());
1864 ninja.ParsePreviousElapsedTimes();
1866 ExitStatus result = ninja.RunBuild(argc, argv, status);
1868 ninja.DumpMetrics();
1872 status->
Error(
"manifest '%s' still dirty after %d tries, perhaps system time is not set",
1873 options.input_file, kCycleLimit);
1880#if defined(_MSC_VER)
1883 std::set_terminate(TerminateHandler);
1887 real_main(argc, argv);
1889 __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
1895 real_main(argc, argv);
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.
static std::unique_ptr< Client > Create(const Config &, std::string *error)
Create a new Client instance from a given configuration.
bool g_experimental_statcache
#define required_argument
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
std::set< Edge *, EdgeCmp > EdgeSet
void PrintJSONString(const std::string &in)
int64_t GetTimeMillis()
Get the current time as relative to some epoch.
int MSVCHelperMain(int argc, char **argv)
const std::map< std::string, std::unique_ptr< const Rule > > & GetRules() const
virtual std::string LookupVariable(const std::string &var)
Options (e.g. verbosity, parallelism) passed to a build.
double max_load_average
The maximum load average we must not exceed.
bool disable_jobserver_client
Can answer questions about the manifest for the BuildLog.
LogEntry * LookupByOutput(const std::string &path)
Lookup a previously-run command by its output path.
LoadStatus Load(const std::string &path, std::string *err)
Load the on-disk log.
bool Recompact(const std::string &path, const BuildLogUser &user, std::string *err)
Rewrite the known log entries, throwing away old data.
bool Restat(StringPiece path, const DiskInterface &disk_interface, int output_count, char **outputs, std::string *err)
Restat all outputs in the log.
const Entries & entries() const
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.
std::vector< Edge * > in_edges
we use a vector to preserve order from requisites to their dependents.
void CollectFrom(const Node *node)
Deps * GetDeps(Node *node)
Node * GetFirstReverseDepsNode(Node *node)
bool Recompact(const std::string &path, std::string *err)
Rewrite the known log entries, throwing away old data.
bool OpenForWrite(const std::string &path, std::string *err)
static bool IsDepsEntryLiveFor(const Node *node)
Returns if the deps entry for a node is still reachable from the manifest.
const std::vector< Node * > & nodes() const
Used for tests.
LoadStatus Load(const std::string &path, State *state, std::string *err)
bool MakeDirs(const std::string &path)
Create all the parent directories for path; like mkdir -p basename path.
int64_t prev_elapsed_time_millis
std::string GetBinding(const std::string &key) const
Returns the shell-escaped value of |key|.
std::vector< Node * > outputs_
bool is_order_only(size_t index)
bool is_implicit(size_t index)
std::string EvaluateCommand(bool incl_rsp_file=false) const
Expand all variables in a command and return it as a string.
void Dump(const char *prefix="") const
std::vector< Node * > validations_
std::vector< Node * > inputs_
std::string GetUnescapedRspfile() const
Like GetBinding("rspfile"), but without shell escaping.
std::string Unparse() const
bool HasMode()
Return true if this instance matches an active implementation mode.
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...
PhonyCycleAction phony_cycle_action_
void Report()
Print a summary report to stdout.
const std::string & path() const
const std::vector< Edge * > & validation_out_edges() const
bool dyndep_pending() const
std::string PathDecanonicalized() const
Get |path()| but use slash_bits to convert back to original slash styles.
const std::vector< Edge * > & out_edges() const
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
const std::string & name() const
std::vector< Node * > RootNodes(std::string *error) const
std::vector< Edge * > edges_
All the edges of the graph.
std::vector< Node * > DefaultNodes(std::string *error) const
Node * SpellcheckNode(const std::string &path)
Node * LookupNode(StringPiece path) const
Abstract interface to object that tracks the status of a build: completion fraction,...
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.
void Error(const char *msg, va_list ap)
std::string GetWorkingDirectory()
a wrapper for getcwd()
void Warning(const char *msg, va_list ap)
void CanonicalizePath(string *path, uint64_t *slash_bits)
const char * SpellcheckStringV(const string &text, const vector< const char * > &words)
void Fatal(const char *msg,...)
Log a fatal message and exit.
const char * SpellcheckString(const char *text,...)
Like SpellcheckStringV, but takes a NULL-terminated list.
const char * kNinjaVersion
The version number of the current Ninja release.
unsigned long long uint64_t
signed long long int64_t
A 64-bit integer type.