66 void 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_;
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);
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",
254 int GuessParallelism() {
262 return processors + 2;
268 bool 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()) {
304 void NinjaMain::ParsePreviousElapsedTimes() {
317 Node* 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() +
"'?";
370 bool 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);
386 int 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)
403 int NinjaMain::ToolQuery(
const Options* options,
int argc,
char* argv[]) {
405 Error(
"expected a target to query");
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());
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)
467 int NinjaMain::ToolBrowse(
const Options* options,
int argc,
char* argv[]) {
468 RunBrowsePython(&state_, ninja_command_, options->input_file, argc, argv);
473 int NinjaMain::ToolBrowse(
const Options*,
int,
char**) {
474 Fatal(
"browse tool not supported on this platform");
480 int NinjaMain::ToolMSVC(
const Options* options,
int argc,
char* argv[]) {
489 int 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);
507 int 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());
519 int 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());
542 int 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());
555 int 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());
572 for (vector<Node*>::iterator it = nodes.begin(), end = nodes.end();
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());
595 int NinjaMain::ToolMissingDeps(
const Options* options,
int argc,
char** argv) {
598 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
599 Error(
"%s", err.c_str());
606 for (vector<Node*>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
607 scanner.ProcessNode(*it);
609 scanner.PrintStats();
610 if (scanner.HadMissingDeps())
615 int 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());
655 int 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();
695 if (description != NULL) {
696 printf(
": %s", description->
Unparse().c_str());
706 int 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");
716 enum PrintCommandMode { PCM_Single, PCM_All };
717 void 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);
733 int 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);
775 int 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());
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)
854 int 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) {
913 for (
const std::string& input : inputs) {
914 printf(
"%s%s%s", node->
path().c_str(), delimiter, input.c_str());
915 fputc(terminator, stdout);
922 int 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);
971 int NinjaMain::ToolCleanDead(
const Options* options,
int argc,
char* argv[]) {
972 Cleaner cleaner(&state_, config_, &disk_interface_);
973 return cleaner.CleanDead(build_log_.
entries());
976 enum EvaluateCommandMode {
980 std::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);
1014 void 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\": \"");
1027 int 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"
1069 PrintOneCompdbObject(directory, edge, eval_mode);
1072 for (
int i = 0; i != argc; ++i) {
1077 PrintOneCompdbObject(directory, edge, eval_mode);
1088 int NinjaMain::ToolRecompact(
const Options* options,
int argc,
char* argv[]) {
1089 if (!EnsureBuildDirExists())
1092 if (!OpenBuildLog(
true) ||
1099 int 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;
1157 struct 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]);
1210 void 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);
1227 int 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: {
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);
1269 int 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)
1297 const 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());
1374 bool 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());
1418 bool 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());
1451 bool 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());
1490 bool 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());
1527 void 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);
1537 bool 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));
1549 std::unique_ptr<Jobserver::Client> NinjaMain::SetupJobserverClient(
1552 std::unique_ptr<Jobserver::Client> result;
1558 const char* makeflags = getenv(
"MAKEFLAGS");
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());
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) {
1654 void TerminateHandler() {
1655 CreateWin32MiniDump(NULL);
1656 Fatal(
"terminate handler called");
1661 int ExceptionFilter(
unsigned int code,
struct _EXCEPTION_POINTERS *ep) {
1662 Error(
"exception: 0x%X", code);
1664 CreateWin32MiniDump(ep);
1665 return EXCEPTION_EXECUTE_HANDLER;
1670 class DeferGuessParallelism {
1676 : needGuess(true), config(config) {}
1684 ~DeferGuessParallelism() { Refresh(); }
1689 int ReadFlags(
int* argc,
char*** argv,
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();
1787 NORETURN 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);
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)
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)
int main(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.
Store a log of every command ran for every build.
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.
const Entries & entries() const
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.
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.
Builder wraps the build process: starting commands, updating status.
Collects the transitive set of edges that lead into a given set of starting nodes.
std::vector< Edge * > in_edges
we use a vector to preserve order from requisites to their dependents.
void CollectFrom(const Node *node)
As build commands run they can output extra dependency information (e.g.
Deps * GetDeps(Node *node)
const std::vector< Node * > & nodes() const
Used for tests.
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.
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.
DyndepLoader loads dynamically discovered dependencies, as referenced via the "dyndep" attribute in b...
An edge in the dependency graph; links between Nodes using Rules.
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.
A tokenized string that contains variable references.
std::string Unparse() const
Runs the process of creating GraphViz .dot file output.
A Jobserver::Config models how to access or implement a GNU jobserver implementation.
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_
The singleton that stores metrics and prints the report.
void Report()
Print a summary report to stdout.
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
const std::vector< Edge * > & validation_out_edges() const
const std::string & path() 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
Implementation of DiskInterface that actually hits the disk.
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.
An invocable build command and associated metadata (description, etc.).
const EvalString * GetBinding(const std::string &key) const
const std::string & name() const
Global state (file status) for a single run.
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
StringPiece represents a slice of a string whose memory is managed externally.
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)
const char * SpellcheckString(const char *text,...)
Like SpellcheckStringV, but takes a NULL-terminated list.
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 * kNinjaVersion
The version number of the current Ninja release.
unsigned long long uint64_t
signed long long int64_t
A 64-bit integer type.