60 const char* input_file;
63 const char* working_dir;
72 NinjaMain(
const char* ninja_command,
const BuildConfig& config) :
73 ninja_command_(ninja_command), config_(config) {}
76 const char* ninja_command_;
94 typedef int (NinjaMain::*ToolFunc)(int,
char**);
98 Node* CollectTarget(
const char* cpath,
string* err);
101 bool CollectTargetsFromArgs(
int argc,
char* argv[],
102 vector<Node*>* targets,
string* err);
105 int ToolGraph(
int argc,
char* argv[]);
106 int ToolQuery(
int argc,
char* argv[]);
107 int ToolBrowse(
int argc,
char* argv[]);
108 int ToolMSVC(
int argc,
char* argv[]);
109 int ToolTargets(
int argc,
char* argv[]);
110 int ToolCommands(
int argc,
char* argv[]);
111 int ToolClean(
int argc,
char* argv[]);
112 int ToolCompilationDatabase(
int argc,
char* argv[]);
113 int ToolUrtle(
int argc,
char** argv);
125 bool EnsureBuildDirExists();
130 bool RebuildManifest(
const char* input_file,
string* err);
134 int RunBuild(
int argc,
char** argv);
161 NinjaMain::ToolFunc func;
167 "usage: ninja [options] [targets...]\n"
169 "if targets are unspecified, builds the 'default' target (see manual).\n"
172 " --version print ninja version (\"%s\")\n"
174 " -C DIR change to DIR before doing anything else\n"
175 " -f FILE specify input build file [default=build.ninja]\n"
177 " -j N run N jobs in parallel [default=%d, derived from CPUs available]\n"
178 " -l N do not start new jobs if the load average is greater than N\n"
180 " (not yet implemented on Windows)\n"
182 " -k N keep going until N jobs fail [default=1]\n"
183 " -n dry run (don't run commands but act like they succeeded)\n"
184 " -v show all command lines while building\n"
186 " -d MODE enable debugging (use -d list to list modes)\n"
187 " -t TOOL run a subtool (use -t list to list subtools)\n"
188 " terminates toplevel options; further flags are passed to the tool\n",
193 int GuessParallelism() {
201 return processors + 2;
208 virtual bool ReadFile(
const string& path,
string* content,
string* err) {
215 bool NinjaMain::RebuildManifest(
const char* input_file,
string* err) {
216 string path = input_file;
219 Node* node = state_.LookupNode(path);
223 Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_);
224 if (!builder.AddTarget(node, err))
227 if (builder.AlreadyUpToDate())
229 if (!builder.Build(err))
234 return node->
dirty();
237 Node* NinjaMain::CollectTarget(
const char* cpath,
string* err) {
243 bool first_dependent =
false;
244 if (!path.empty() && path[path.size() - 1] ==
'^') {
245 path.resize(path.size() - 1);
246 first_dependent =
true;
249 Node* node = state_.LookupNode(path);
251 if (first_dependent) {
253 *err =
"'" + path +
"' has no out edge";
259 Fatal(
"edge has no outputs");
265 *err =
"unknown target '" + path +
"'";
267 if (path ==
"clean") {
268 *err +=
", did you mean 'ninja -t clean'?";
269 }
else if (path ==
"help") {
270 *err +=
", did you mean 'ninja -h'?";
272 Node* suggestion = state_.SpellcheckNode(path);
274 *err +=
", did you mean '" + suggestion->
path() +
"'?";
281 bool NinjaMain::CollectTargetsFromArgs(
int argc,
char* argv[],
282 vector<Node*>* targets,
string* err) {
284 *targets = state_.DefaultNodes(err);
288 for (
int i = 0; i < argc; ++i) {
289 Node* node = CollectTarget(argv[i], err);
292 targets->push_back(node);
297 int NinjaMain::ToolGraph(
int argc,
char* argv[]) {
300 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
301 Error(
"%s", err.c_str());
307 for (vector<Node*>::const_iterator n = nodes.begin(); n != nodes.end(); ++n)
314 int NinjaMain::ToolQuery(
int argc,
char* argv[]) {
316 Error(
"expected a target to query");
320 for (
int i = 0; i < argc; ++i) {
322 Node* node = CollectTarget(argv[i], &err);
324 Error(
"%s", err.c_str());
328 printf(
"%s:\n", node->
path().c_str());
330 printf(
" input: %s\n", edge->
rule_->
name().c_str());
331 for (
int in = 0; in < (int)edge->
inputs_.size(); in++) {
332 const char* label =
"";
337 printf(
" %s%s\n", label, edge->
inputs_[in]->path().c_str());
340 printf(
" outputs:\n");
341 for (vector<Edge*>::const_iterator edge = node->
out_edges().begin();
342 edge != node->
out_edges().end(); ++edge) {
343 for (vector<Node*>::iterator out = (*edge)->outputs_.begin();
344 out != (*edge)->outputs_.end(); ++out) {
345 printf(
" %s\n", (*out)->path().c_str());
352 #if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
353 int NinjaMain::ToolBrowse(
int argc,
char* argv[]) {
355 Error(
"expected a target to browse");
364 #if defined(_MSC_VER)
365 int NinjaMain::ToolMSVC(
int argc,
char* argv[]) {
374 int ToolTargetsList(
const vector<Node*>& nodes,
int depth,
int indent) {
375 for (vector<Node*>::const_iterator n = nodes.begin();
378 for (
int i = 0; i < indent; ++i)
380 const char* target = (*n)->path().c_str();
381 if ((*n)->in_edge()) {
382 printf(
"%s: %s\n", target, (*n)->in_edge()->rule_->name().c_str());
383 if (depth > 1 || depth <= 0)
384 ToolTargetsList((*n)->in_edge()->inputs_, depth - 1, indent + 1);
386 printf(
"%s\n", target);
392 int ToolTargetsSourceList(
State* state) {
393 for (vector<Edge*>::iterator e = state->
edges_.begin();
394 e != state->
edges_.end(); ++e) {
395 for (vector<Node*>::iterator inps = (*e)->inputs_.begin();
396 inps != (*e)->inputs_.end(); ++inps) {
397 if (!(*inps)->in_edge())
398 printf(
"%s\n", (*inps)->path().c_str());
404 int ToolTargetsList(
State* state,
const string& rule_name) {
408 for (vector<Edge*>::iterator e = state->
edges_.begin();
409 e != state->
edges_.end(); ++e) {
410 if ((*e)->rule_->name() == rule_name) {
411 for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
412 out_node != (*e)->outputs_.end(); ++out_node) {
413 rules.insert((*out_node)->path());
419 for (set<string>::const_iterator i = rules.begin();
420 i != rules.end(); ++i) {
421 printf(
"%s\n", (*i).c_str());
427 int ToolTargetsList(
State* state) {
428 for (vector<Edge*>::iterator e = state->
edges_.begin();
429 e != state->
edges_.end(); ++e) {
430 for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
431 out_node != (*e)->outputs_.end(); ++out_node) {
433 (*out_node)->path().c_str(),
434 (*e)->rule_->name().c_str());
440 int NinjaMain::ToolTargets(
int argc,
char* argv[]) {
443 string mode = argv[0];
444 if (mode ==
"rule") {
449 return ToolTargetsSourceList(&state_);
451 return ToolTargetsList(&state_, rule);
452 }
else if (mode ==
"depth") {
454 depth = atoi(argv[1]);
455 }
else if (mode ==
"all") {
456 return ToolTargetsList(&state_);
458 const char* suggestion =
461 Error(
"unknown target tool mode '%s', did you mean '%s'?",
462 mode.c_str(), suggestion);
464 Error(
"unknown target tool mode '%s'", mode.c_str());
471 vector<Node*> root_nodes = state_.RootNodes(&err);
473 return ToolTargetsList(root_nodes, depth, 0);
475 Error(
"%s", err.c_str());
480 void PrintCommands(
Edge* edge, set<Edge*>* seen) {
483 if (!seen->insert(edge).second)
486 for (vector<Node*>::iterator in = edge->
inputs_.begin();
487 in != edge->
inputs_.end(); ++in)
488 PrintCommands((*in)->in_edge(), seen);
494 int NinjaMain::ToolCommands(
int argc,
char* argv[]) {
497 if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
498 Error(
"%s", err.c_str());
503 for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in)
504 PrintCommands((*in)->in_edge(), &seen);
509 int NinjaMain::ToolClean(
int argc,
char* argv[]) {
515 bool generator =
false;
516 bool clean_rules =
false;
520 while ((opt =
getopt(argc, argv, const_cast<char*>(
"hgr"))) != -1) {
530 printf(
"usage: ninja -t clean [options] [targets]\n"
533 " -g also clean files marked as ninja generator output\n"
534 " -r interpret targets as a list of rules to clean instead\n"
542 if (clean_rules && argc == 0) {
543 Error(
"expected a rule to clean");
547 Cleaner cleaner(&state_, config_);
550 return cleaner.CleanRules(argc, argv);
552 return cleaner.CleanTargets(argc, argv);
554 return cleaner.CleanAll(generator);
558 void EncodeJSONString(
const char *str) {
560 if (*str ==
'"' || *str ==
'\\')
567 int NinjaMain::ToolCompilationDatabase(
int argc,
char* argv[]) {
572 cwd.resize(cwd.size() + 1024);
574 }
while (!getcwd(&cwd[0], cwd.size()) && errno == ERANGE);
575 if (errno != 0 && errno != ERANGE) {
576 Error(
"cannot determine working directory: %s", strerror(errno));
581 for (vector<Edge*>::iterator e = state_.edges_.begin();
582 e != state_.edges_.end(); ++e) {
583 for (
int i = 0; i != argc; ++i) {
584 if ((*e)->rule_->name() == argv[i]) {
588 printf(
"\n {\n \"directory\": \"");
589 EncodeJSONString(&cwd[0]);
590 printf(
"\",\n \"command\": \"");
591 EncodeJSONString((*e)->EvaluateCommand().c_str());
592 printf(
"\",\n \"file\": \"");
593 EncodeJSONString((*e)->inputs_[0]->path().c_str());
605 int NinjaMain::ToolUrtle(
int argc,
char** argv) {
608 " 13 ,3;2!2;\n8 ,;<11!;\n5 `'<10!(2`'2!\n11 ,6;, `\\. `\\9 .,c13$ec,.\n6 "
609 ",2;11!>; `. ,;!2> .e8$2\".2 \"?7$e.\n <:<8!'` 2.3,.2` ,3!' ;,(?7\";2!2'<"
610 "; `?6$PF ,;,\n2 `'4!8;<!3'`2 3! ;,`'2`2'3!;4!`2.`!;2 3,2 .<!2'`).\n5 3`5"
611 "'2`9 `!2 `4!><3;5! J2$b,`!>;2!:2!`,d?b`!>\n26 `'-;,(<9!> $F3 )3.:!.2 d\""
612 "2 ) !>\n30 7`2'<3!- \"=-='5 .2 `2-=\",!>\n25 .ze9$er2 .,cd16$bc.'\n22 .e"
613 "14$,26$.\n21 z45$c .\n20 J50$c\n20 14$P\"`?34$b\n20 14$ dbc `2\"?22$?7$c"
614 "\n20 ?18$c.6 4\"8?4\" c8$P\n9 .2,.8 \"20$c.3 ._14 J9$\n .2,2c9$bec,.2 `?"
615 "21$c.3`4%,3%,3 c8$P\"\n22$c2 2\"?21$bc2,.2` .2,c7$P2\",cb\n23$b bc,.2\"2"
616 "?14$2F2\"5?2\",J5$P\" ,zd3$\n24$ ?$3?%3 `2\"2?12$bcucd3$P3\"2 2=7$\n23$P"
617 "\" ,3;<5!>2;,. `4\"6?2\"2 ,9;, `\"?2$\n";
619 for (
const char* p = urtle; *p; p++) {
620 if (
'0' <= *p && *p <=
'9') {
621 count = count*10 + *p -
'0';
623 for (
int i = 0; i < std::max(count, 1); ++i)
633 const Tool* ChooseTool(
const string& tool_name) {
634 static const Tool kTools[] = {
635 #if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
636 {
"browse",
"browse dependency graph in a web browser",
637 Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse },
639 #if defined(_MSC_VER)
640 {
"msvc",
"build helper for MSVC cl.exe (EXPERIMENTAL)",
641 Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC },
643 {
"clean",
"clean built files",
644 Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean },
645 {
"commands",
"list all commands required to rebuild given targets",
646 Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands },
647 {
"graph",
"output graphviz dot file for targets",
648 Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph },
649 {
"query",
"show inputs/outputs for a path",
650 Tool::RUN_AFTER_LOGS, &NinjaMain::ToolQuery },
651 {
"targets",
"list targets by their rule or depth in the DAG",
652 Tool::RUN_AFTER_LOAD, &NinjaMain::ToolTargets },
653 {
"compdb",
"dump JSON compilation database to stdout",
654 Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase },
656 Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolUrtle },
657 { NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL }
660 if (tool_name ==
"list") {
661 printf(
"ninja subtools:\n");
662 for (
const Tool* tool = &kTools[0]; tool->name; ++tool) {
664 printf(
"%10s %s\n", tool->name, tool->desc);
669 for (
const Tool* tool = &kTools[0]; tool->name; ++tool) {
670 if (tool->name == tool_name)
674 vector<const char*> words;
675 for (
const Tool* tool = &kTools[0]; tool->name; ++tool)
676 words.push_back(tool->name);
679 Fatal(
"unknown tool '%s', did you mean '%s'?",
680 tool_name.c_str(), suggestion);
682 Fatal(
"unknown tool '%s'", tool_name.c_str());
689 bool DebugEnable(
const string& name) {
690 if (name ==
"list") {
691 printf(
"debugging modes:\n"
692 " stats print operation counts/timing info\n"
693 " explain explain what caused a command to execute\n"
694 "multiple modes can be enabled via -d FOO -d BAR\n");
696 }
else if (name ==
"stats") {
699 }
else if (name ==
"explain") {
703 const char* suggestion =
706 Error(
"unknown debug setting '%s', did you mean '%s'?",
707 name.c_str(), suggestion);
709 Error(
"unknown debug setting '%s'", name.c_str());
715 bool NinjaMain::OpenBuildLog() {
716 string log_path =
".ninja_log";
717 if (!build_dir_.empty())
718 log_path = build_dir_ +
"/" + log_path;
721 if (!build_log_.Load(log_path, &err)) {
722 Error(
"loading build log %s: %s", log_path.c_str(), err.c_str());
731 if (!config_.dry_run) {
732 if (!build_log_.OpenForWrite(log_path, &err)) {
733 Error(
"opening build log: %s", err.c_str());
743 bool NinjaMain::OpenDepsLog() {
744 string path =
".ninja_deps";
745 if (!build_dir_.empty())
746 path = build_dir_ +
"/" + path;
749 if (!deps_log_.Load(path, &state_, &err)) {
750 Error(
"loading deps log %s: %s", path.c_str(), err.c_str());
759 if (!config_.dry_run) {
760 if (!deps_log_.OpenForWrite(path, &err)) {
761 Error(
"opening deps log: %s", err.c_str());
769 void NinjaMain::DumpMetrics() {
773 int count = (int)state_.paths_.size();
774 int buckets = (int)state_.paths_.bucket_count();
775 printf(
"path->node hash load %.2f (%d entries / %d buckets)\n",
776 count / (
double) buckets, count, buckets);
779 bool NinjaMain::EnsureBuildDirExists() {
780 build_dir_ = state_.bindings_.LookupVariable(
"builddir");
781 if (!build_dir_.empty() && !config_.dry_run) {
782 if (!disk_interface_.MakeDirs(build_dir_ +
"/.") && errno != EEXIST) {
783 Error(
"creating build directory %s: %s",
784 build_dir_.c_str(), strerror(errno));
791 int NinjaMain::RunBuild(
int argc,
char** argv) {
793 vector<Node*> targets;
794 if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) {
795 Error(
"%s", err.c_str());
799 Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_);
800 for (
size_t i = 0; i < targets.size(); ++i) {
801 if (!builder.AddTarget(targets[i], &err)) {
803 Error(
"%s", err.c_str());
812 if (builder.AlreadyUpToDate()) {
813 printf(
"ninja: no work to do.\n");
817 if (!builder.Build(&err)) {
818 printf(
"ninja: build stopped: %s.\n", err.c_str());
819 if (err.find(
"interrupted by user") != string::npos) {
835 void TerminateHandler() {
837 Fatal(
"terminate handler called");
842 int ExceptionFilter(
unsigned int code,
struct _EXCEPTION_POINTERS *ep) {
843 Error(
"exception: 0x%X", code);
846 return EXCEPTION_EXECUTE_HANDLER;
853 int ReadFlags(
int* argc,
char*** argv,
857 enum { OPT_VERSION = 1 };
858 const option kLongOptions[] = {
865 while (!options->tool &&
866 (opt =
getopt_long(*argc, *argv,
"d:f:j:k:l:nt:vC:h", kLongOptions,
870 if (!DebugEnable(optarg))
874 options->input_file =
optarg;
878 int value = strtol(optarg, &end, 10);
879 if (*end != 0 || value <= 0)
880 Fatal(
"invalid -j parameter");
886 int value = strtol(optarg, &end, 10);
888 Fatal(
"-k parameter not numeric; did you mean -k 0?");
898 double value = strtod(optarg, &end);
900 Fatal(
"-l parameter not numeric: did you mean -l 0.0?");
908 options->tool = ChooseTool(optarg);
916 options->working_dir =
optarg;
933 int real_main(
int argc,
char** argv) {
935 Options options = {};
936 options.input_file =
"build.ninja";
938 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
940 int exit_code = ReadFlags(&argc, &argv, &options, &config);
944 if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) {
947 NinjaMain ninja(argv[0], config);
948 return (ninja.*options.tool->func)(argc, argv);
951 if (options.working_dir) {
958 printf(
"ninja: Entering directory `%s'\n", options.working_dir);
959 if (chdir(options.working_dir) < 0) {
960 Fatal(
"chdir to '%s' - %s", options.working_dir, strerror(errno));
966 for (
int cycle = 0; cycle < 2; ++cycle) {
967 NinjaMain ninja(argv[0], config);
969 RealFileReader file_reader;
972 if (!parser.Load(options.input_file, &err)) {
973 Error(
"%s", err.c_str());
977 if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD)
978 return (ninja.*options.tool->func)(argc, argv);
980 if (!ninja.EnsureBuildDirExists())
983 if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog())
986 if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS)
987 return (ninja.*options.tool->func)(argc, argv);
992 if (ninja.RebuildManifest(options.input_file, &err)) {
995 }
else if (!err.empty()) {
996 Error(
"rebuilding '%s': %s", options.input_file, err.c_str());
1001 int result = ninja.RunBuild(argc, argv);
1003 ninja.DumpMetrics();
1013 #if !defined(NINJA_BOOTSTRAP) && defined(_MSC_VER)
1016 set_terminate(TerminateHandler);
1020 return real_main(argc, argv);
1022 __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
1028 return real_main(argc, argv);