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,
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);
void Report()
Print a summary report to stdout.
virtual bool ReadFile(const string &path, string *content, string *err)=0
const char * SpellcheckString(const char *text,...)
Like SpellcheckStringV, but takes a NULL-terminated list.
vector< Edge * > edges_
All the edges of the graph.
double max_load_average
The maximum load average we must not exceed.
int MSVCHelperMain(int argc, char **argv)
Information about a node in the dependency graph: the file, whether it's dirty, mtime, etc.
void Dump(const char *prefix="") const
An edge in the dependency graph; links between Nodes using Rules.
const char * kNinjaVersion
The version number of the current Ninja release.
Store a log of every command ran for every build.
string EvaluateCommand(bool incl_rsp_file=false)
Expand all variables in a command and return it as a string.
bool is_order_only(size_t index)
void RunBrowsePython(State *state, const char *ninja_command, const char *initial_target)
Run in "browse" mode, which execs a Python webserver.
bool CanonicalizePath(string *path, string *err)
Canonicalize a path like "foo/../bar.h" into just "bar.h".
void CreateWin32MiniDump(_EXCEPTION_POINTERS *pep)
Creates a windows minidump in temp folder.
As build commands run they can output extra dependency information (e.g.
Implementation of DiskInterface that actually hits the disk.
bool is_implicit(size_t index)
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)
int ReadFile(const string &path, string *contents, string *err)
Read a file to a string (in text mode: with CRLF conversion on Windows).
int main(int argc, char **argv)
const string & path() const
Builder wraps the build process: starting commands, updating status.
void Fatal(const char *msg,...)
Log a fatal message and exit.
const char * SpellcheckStringV(const string &text, const vector< const char * > &words)
Given a misspelled string and a list of correct spellings, returns the closest match or NULL if there...
The singleton that stores metrics and prints the report.
Runs the process of creating GraphViz .dot file output.
Options (e.g. verbosity, parallelism) passed to a build.
Global state (file status, loaded rules) for a single run.
const string & name() const
void Warning(const char *msg,...)
Log a warning message.
void AddTarget(Node *node)
const vector< Edge * > & out_edges() const
void Error(const char *msg,...)
Log an error message.
vector< Node * > outputs_