Ninja
ninja.cc
Go to the documentation of this file.
00001 // Copyright 2011 Google Inc. All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //     http://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include <errno.h>
00016 #include <stdio.h>
00017 #include <string.h>
00018 #include <sys/stat.h>
00019 #include <sys/types.h>
00020 
00021 #if defined(__APPLE__) || defined(__FreeBSD__)
00022 #include <sys/sysctl.h>
00023 #elif defined(linux)
00024 #include <sys/sysinfo.h>
00025 #endif
00026 
00027 #ifdef WIN32
00028 #include "getopt.h"
00029 #include <direct.h>
00030 #include <windows.h>
00031 #else
00032 #include <getopt.h>
00033 #endif
00034 
00035 #include "browse.h"
00036 #include "build.h"
00037 #include "build_log.h"
00038 #include "clean.h"
00039 #include "graph.h"
00040 #include "graphviz.h"
00041 #include "parsers.h"
00042 #include "state.h"
00043 #include "util.h"
00044 
00045 namespace {
00046 
00047 /// Print usage information.
00048 void Usage(const BuildConfig& config) {
00049   fprintf(stderr,
00050 "usage: ninja [options] [targets...]\n"
00051 "\n"
00052 "if targets are unspecified, builds the 'default' target (see manual).\n"
00053 "targets are paths, with additional special syntax:\n"
00054 "  target^ means 'the first output that uses target'.\n"
00055 "  example: 'ninja foo.cc^' will likely build foo.o.\n"
00056 "\n"
00057 "options:\n"
00058 "  -f FILE  specify input build file [default=build.ninja]\n"
00059 "  -j N     run N jobs in parallel [default=%d]\n"
00060 "  -k N     keep going until N jobs fail [default=1]\n"
00061 "  -n       dry run (don't run commands but pretend they succeeded)\n"
00062 "  -v       show all command lines\n"
00063 "  -C DIR   change to DIR before doing anything else\n"
00064 "\n"
00065 "  -t TOOL  run a subtool.\n"
00066 "           terminates toplevel options; further flags are passed to the tool.\n"
00067 "           tools are:\n"
00068 "             browse  browse dependency graph in a web browser\n"
00069 "             graph   output graphviz dot file for targets\n"
00070 "             query   show inputs/outputs for a path\n"
00071 "             targets list targets by their rule or depth in the DAG\n"
00072 "             rules   list all rules\n"
00073 "             clean   clean built files\n",
00074           config.parallelism);
00075 }
00076 
00077 /// Choose a default value for the -j (parallelism) flag.
00078 int GuessParallelism() {
00079   int processors = 0;
00080 
00081 #if defined(linux)
00082   processors = get_nprocs();
00083 #elif defined(__APPLE__) || defined(__FreeBSD__)
00084   size_t processors_size = sizeof(processors);
00085   int name[] = {CTL_HW, HW_NCPU};
00086   if (sysctl(name, sizeof(name) / sizeof(int),
00087              &processors, &processors_size,
00088              NULL, 0) < 0) {
00089     processors = 1;
00090   }
00091 #elif defined(WIN32)
00092   SYSTEM_INFO info;
00093   GetSystemInfo(&info);
00094   processors = info.dwNumberOfProcessors;
00095 #endif
00096 
00097   switch (processors) {
00098   case 0:
00099   case 1:
00100     return 2;
00101   case 2:
00102     return 3;
00103   default:
00104     return processors + 2;
00105   }
00106 }
00107 
00108 /// An implementation of ManifestParser::FileReader that actually reads
00109 /// the file.
00110 struct RealFileReader : public ManifestParser::FileReader {
00111   bool ReadFile(const string& path, string* content, string* err) {
00112     return ::ReadFile(path, content, err) == 0;
00113   }
00114 };
00115 
00116 /// Rebuild the build manifest, if necessary.
00117 /// Returns true if the manifest was rebuilt.
00118 bool RebuildManifest(State* state, const BuildConfig& config,
00119                      const char* input_file, string* err) {
00120   string path = input_file;
00121   if (!CanonicalizePath(&path, err))
00122     return false;
00123   Node* node = state->LookupNode(path);
00124   if (!node)
00125     return false;
00126 
00127   Builder manifest_builder(state, config);
00128   if (!manifest_builder.AddTarget(node, err))
00129     return false;
00130 
00131   if (manifest_builder.AlreadyUpToDate())
00132     return false;  // Not an error, but we didn't rebuild.
00133   return manifest_builder.Build(err);
00134 }
00135 
00136 bool CollectTargetsFromArgs(State* state, int argc, char* argv[],
00137                             vector<Node*>* targets, string* err) {
00138   if (argc == 0) {
00139     *targets = state->DefaultNodes(err);
00140     if (!err->empty())
00141       return false;
00142   } else {
00143     for (int i = 0; i < argc; ++i) {
00144       string path = argv[i];
00145       if (!CanonicalizePath(&path, err))
00146         return false;
00147 
00148       // Special syntax: "foo.cc^" means "the first output of foo.cc".
00149       bool first_dependent = false;
00150       if (!path.empty() && path[path.size() - 1] == '^') {
00151         path.resize(path.size() - 1);
00152         first_dependent = true;
00153       }
00154 
00155       Node* node = state->LookupNode(path);
00156       if (node) {
00157         if (first_dependent) {
00158           if (node->out_edges_.empty()) {
00159             *err = "'" + path + "' has no out edge";
00160             return false;
00161           }
00162           Edge* edge = node->out_edges_[0];
00163           if (edge->outputs_.empty()) {
00164             edge->Dump();
00165             Fatal("edge has no outputs");
00166           }
00167           node = edge->outputs_[0];
00168         }
00169         targets->push_back(node);
00170       } else {
00171         *err = "unknown target '" + path + "'";
00172         return false;
00173       }
00174     }
00175   }
00176   return true;
00177 }
00178 
00179 int CmdGraph(State* state, int argc, char* argv[]) {
00180   vector<Node*> nodes;
00181   string err;
00182   if (!CollectTargetsFromArgs(state, argc, argv, &nodes, &err)) {
00183     Error("%s", err.c_str());
00184     return 1;
00185   }
00186 
00187   GraphViz graph;
00188   graph.Start();
00189   for (vector<Node*>::const_iterator n = nodes.begin(); n != nodes.end(); ++n)
00190     graph.AddTarget(*n);
00191   graph.Finish();
00192 
00193   return 0;
00194 }
00195 
00196 int CmdQuery(State* state, int argc, char* argv[]) {
00197   if (argc == 0) {
00198     Error("expected a target to query");
00199     return 1;
00200   }
00201   for (int i = 0; i < argc; ++i) {
00202     Node* node = state->GetNode(argv[i]);
00203     if (node) {
00204       printf("%s:\n", argv[i]);
00205       if (node->in_edge_) {
00206         printf("  input: %s\n", node->in_edge_->rule_->name_.c_str());
00207         for (vector<Node*>::iterator in = node->in_edge_->inputs_.begin();
00208              in != node->in_edge_->inputs_.end(); ++in) {
00209           printf("    %s\n", (*in)->file_->path_.c_str());
00210         }
00211       }
00212       for (vector<Edge*>::iterator edge = node->out_edges_.begin();
00213            edge != node->out_edges_.end(); ++edge) {
00214         printf("  output: %s\n", (*edge)->rule_->name_.c_str());
00215         for (vector<Node*>::iterator out = (*edge)->outputs_.begin();
00216              out != (*edge)->outputs_.end(); ++out) {
00217           printf("    %s\n", (*out)->file_->path_.c_str());
00218         }
00219       }
00220     } else {
00221       printf("%s unknown\n", argv[i]);
00222       return 1;
00223     }
00224   }
00225   return 0;
00226 }
00227 
00228 int CmdBrowse(State* state, const char* ninja_command,
00229               int argc, char* argv[]) {
00230 #ifndef WIN32
00231   if (argc < 1) {
00232     Error("expected a target to browse");
00233     return 1;
00234   }
00235   RunBrowsePython(state, ninja_command, argv[0]);
00236 #else
00237   Error("browse mode not yet supported on Windows");
00238 #endif
00239   // If we get here, the browse failed.
00240   return 1;
00241 }
00242 
00243 int CmdTargetsList(const vector<Node*>& nodes, int depth, int indent) {
00244   for (vector<Node*>::const_iterator n = nodes.begin();
00245        n != nodes.end();
00246        ++n) {
00247     for (int i = 0; i < indent; ++i)
00248       printf("  ");
00249     const char* target = (*n)->file_->path_.c_str();
00250     if ((*n)->in_edge_) {
00251       printf("%s: %s\n", target, (*n)->in_edge_->rule_->name_.c_str());
00252       if (depth > 1 || depth <= 0)
00253         CmdTargetsList((*n)->in_edge_->inputs_, depth - 1, indent + 1);
00254     } else {
00255       printf("%s\n", target);
00256     }
00257   }
00258   return 0;
00259 }
00260 
00261 int CmdTargetsList(const vector<Node*>& nodes, int depth) {
00262   return CmdTargetsList(nodes, depth, 0);
00263 }
00264 
00265 int CmdTargetsSourceList(State* state) {
00266   for (vector<Edge*>::iterator e = state->edges_.begin();
00267        e != state->edges_.end();
00268        ++e)
00269     for (vector<Node*>::iterator inps = (*e)->inputs_.begin();
00270          inps != (*e)->inputs_.end();
00271          ++inps)
00272       if (!(*inps)->in_edge_)
00273         printf("%s\n", (*inps)->file_->path_.c_str());
00274   return 0;
00275 }
00276 
00277 int CmdTargetsList(State* state, const string& rule_name) {
00278   set<string> rules;
00279 
00280   // Gather the outputs.
00281   for (vector<Edge*>::iterator e = state->edges_.begin();
00282        e != state->edges_.end(); ++e) {
00283     if ((*e)->rule_->name_ == rule_name) {
00284       for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
00285            out_node != (*e)->outputs_.end(); ++out_node) {
00286         rules.insert((*out_node)->file_->path_);
00287       }
00288     }
00289   }
00290 
00291   // Print them.
00292   for (set<string>::const_iterator i = rules.begin();
00293        i != rules.end(); ++i) {
00294     printf("%s\n", (*i).c_str());
00295   }
00296 
00297   return 0;
00298 }
00299 
00300 int CmdTargetsList(State* state) {
00301   for (vector<Edge*>::iterator e = state->edges_.begin();
00302        e != state->edges_.end(); ++e) {
00303     for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
00304          out_node != (*e)->outputs_.end(); ++out_node) {
00305       printf("%s: %s\n",
00306              (*out_node)->file_->path_.c_str(),
00307              (*e)->rule_->name_.c_str());
00308     }
00309   }
00310   return 0;
00311 }
00312 
00313 int CmdTargets(State* state, int argc, char* argv[]) {
00314   int depth = 1;
00315   if (argc >= 1) {
00316     string mode = argv[0];
00317     if (mode == "rule") {
00318       string rule;
00319       if (argc > 1)
00320         rule = argv[1];
00321       if (rule.empty())
00322         return CmdTargetsSourceList(state);
00323       else
00324         return CmdTargetsList(state, rule);
00325     } else if (mode == "depth") {
00326       if (argc > 1)
00327         depth = atoi(argv[1]);
00328     } else if (mode == "all") {
00329       return CmdTargetsList(state);
00330     } else {
00331       Error("unknown target tool mode '%s'", mode.c_str());
00332       return 1;
00333     }
00334   }
00335 
00336   string err;
00337   vector<Node*> root_nodes = state->RootNodes(&err);
00338   if (err.empty()) {
00339     return CmdTargetsList(root_nodes, depth);
00340   } else {
00341     Error("%s", err.c_str());
00342     return 1;
00343   }
00344 }
00345 
00346 int CmdRules(State* state, int argc, char* argv[]) {
00347   for (map<string, const Rule*>::iterator i = state->rules_.begin();
00348        i != state->rules_.end(); ++i) {
00349     if (i->second->description_.unparsed_.empty()) {
00350       printf("%s\n", i->first.c_str());
00351     } else {
00352       printf("%s: %s\n",
00353              i->first.c_str(),
00354              i->second->description_.unparsed_.c_str());
00355     }
00356   }
00357   return 0;
00358 }
00359 
00360 int CmdClean(State* state, int argc, char* argv[], const BuildConfig& config) {
00361   bool generator = false;
00362   bool clean_rules = false;
00363 
00364   optind = 1;
00365   int opt;
00366   while ((opt = getopt(argc, argv, "gr")) != -1) {
00367     switch (opt) {
00368       case 'g':
00369         generator = true;
00370         break;
00371       case 'r':
00372         clean_rules = true;
00373         break;
00374       default:
00375         Usage(config);
00376         return 1;
00377     }
00378   }
00379   argv += optind;
00380   argc -= optind;
00381 
00382   if (clean_rules && argc == 0) {
00383     Error("expected a rule to clean");
00384     return 1;
00385   }
00386 
00387   Cleaner cleaner(state, config);
00388   if (argc >= 1) {
00389     if (clean_rules)
00390       return cleaner.CleanRules(argc, argv);
00391     else
00392       return cleaner.CleanTargets(argc, argv);
00393   } else {
00394     return cleaner.CleanAll(generator);
00395   }
00396 }
00397 
00398 }  // anonymous namespace
00399 
00400 int main(int argc, char** argv) {
00401   const char* ninja_command = argv[0];
00402   BuildConfig config;
00403   const char* input_file = "build.ninja";
00404   const char* working_dir = 0;
00405   string tool;
00406 
00407   setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
00408 
00409   config.parallelism = GuessParallelism();
00410 
00411   const option kLongOptions[] = {
00412     { "help", no_argument, NULL, 'h' },
00413     { }
00414   };
00415 
00416   int opt;
00417   while (tool.empty() &&
00418          (opt = getopt_long(argc, argv, "f:hj:k:nt:vC:", kLongOptions,
00419                             NULL)) != -1) {
00420     switch (opt) {
00421       case 'f':
00422         input_file = optarg;
00423         break;
00424       case 'j':
00425         config.parallelism = atoi(optarg);
00426         break;
00427       case 'k': {
00428         char* end;
00429         int value = strtol(optarg, &end, 10);
00430         if (*end != 0)
00431           Fatal("-k parameter not numeric; did you mean -k0?");
00432 
00433         // We want to go until N jobs fail, which means we should ignore
00434         // the first N-1 that fail and then stop.
00435         config.swallow_failures = value - 1;
00436         break;
00437       }
00438       case 'n':
00439         config.dry_run = true;
00440         break;
00441       case 'v':
00442         config.verbosity = BuildConfig::VERBOSE;
00443         break;
00444       case 't':
00445         tool = optarg;
00446         break;
00447       case 'C':
00448         working_dir = optarg;
00449         break;
00450       case 'h':
00451       default:
00452         Usage(config);
00453         return 1;
00454     }
00455   }
00456   argv += optind;
00457   argc -= optind;
00458 
00459   if (working_dir) {
00460 #ifdef _WIN32
00461     if (_chdir(working_dir) < 0) {
00462 #else
00463     if (chdir(working_dir) < 0) {
00464 #endif
00465       Fatal("chdir to '%s' - %s", working_dir, strerror(errno));
00466     }
00467   }
00468 
00469   bool rebuilt_manifest = false;
00470 
00471 reload:
00472   State state;
00473   RealFileReader file_reader;
00474   ManifestParser parser(&state, &file_reader);
00475   string err;
00476   if (!parser.Load(input_file, &err)) {
00477     Error("loading '%s': %s", input_file, err.c_str());
00478     return 1;
00479   }
00480 
00481   if (!tool.empty()) {
00482     if (tool == "graph")
00483       return CmdGraph(&state, argc, argv);
00484     if (tool == "query")
00485       return CmdQuery(&state, argc, argv);
00486     if (tool == "browse")
00487       return CmdBrowse(&state, ninja_command, argc, argv);
00488     if (tool == "targets")
00489       return CmdTargets(&state, argc, argv);
00490     if (tool == "rules")
00491       return CmdRules(&state, argc, argv);
00492     // The clean tool uses getopt, and expects argv[0] to contain the name of
00493     // the tool, i.e. "clean".
00494     if (tool == "clean")
00495       return CmdClean(&state, argc+1, argv-1, config);
00496     Error("unknown tool '%s'", tool.c_str());
00497   }
00498 
00499   BuildLog build_log;
00500   build_log.SetConfig(&config);
00501   state.build_log_ = &build_log;
00502 
00503   const string build_dir = state.bindings_.LookupVariable("builddir");
00504   const char* kLogPath = ".ninja_log";
00505   string log_path = kLogPath;
00506   if (!build_dir.empty()) {
00507     if (MakeDir(build_dir) < 0 && errno != EEXIST) {
00508       Error("creating build directory %s: %s",
00509             build_dir.c_str(), strerror(errno));
00510       return 1;
00511     }
00512     log_path = build_dir + "/" + kLogPath;
00513   }
00514 
00515   if (!build_log.Load(log_path.c_str(), &err)) {
00516     Error("loading build log %s: %s",
00517           log_path.c_str(), err.c_str());
00518     return 1;
00519   }
00520 
00521   if (!build_log.OpenForWrite(log_path.c_str(), &err)) {
00522     Error("opening build log: %s", err.c_str());
00523     return 1;
00524   }
00525 
00526   if (!rebuilt_manifest) { // Don't get caught in an infinite loop by a rebuild
00527                            // target that is never up to date.
00528     if (RebuildManifest(&state, config, input_file, &err)) {
00529       rebuilt_manifest = true;
00530       goto reload;
00531     } else if (!err.empty()) {
00532       Error("rebuilding '%s': %s", input_file, err.c_str());
00533       return 1;
00534     }
00535   }
00536 
00537   vector<Node*> targets;
00538   if (!CollectTargetsFromArgs(&state, argc, argv, &targets, &err)) {
00539     Error("%s", err.c_str());
00540     return 1;
00541   }
00542 
00543   Builder builder(&state, config);
00544   for (size_t i = 0; i < targets.size(); ++i) {
00545     if (!builder.AddTarget(targets[i], &err)) {
00546       if (!err.empty()) {
00547         Error("%s", err.c_str());
00548         return 1;
00549       } else {
00550         // Added a target that is already up-to-date; not really
00551         // an error.
00552       }
00553     }
00554   }
00555 
00556   if (builder.AlreadyUpToDate()) {
00557     printf("ninja: no work to do.\n");
00558     return 0;
00559   }
00560 
00561   if (!builder.Build(&err)) {
00562     printf("ninja: build stopped: %s.\n", err.c_str());
00563     return 1;
00564   }
00565 
00566   return 0;
00567 }