Merge pull request #231 from pcc/exit-cleanup-modified
[platform/upstream/ninja.git] / src / ninja.cc
1 // Copyright 2011 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <errno.h>
16 #include <limits.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21
22 #if defined(__APPLE__) || defined(__FreeBSD__)
23 #include <sys/sysctl.h>
24 #elif defined(linux)
25 #include <sys/sysinfo.h>
26 #endif
27
28 #ifdef _WIN32
29 #include "getopt.h"
30 #include <direct.h>
31 #include <windows.h>
32 #else
33 #include <getopt.h>
34 #endif
35
36 #include "browse.h"
37 #include "build.h"
38 #include "build_log.h"
39 #include "clean.h"
40 #include "edit_distance.h"
41 #include "graph.h"
42 #include "graphviz.h"
43 #include "metrics.h"
44 #include "parsers.h"
45 #include "state.h"
46 #include "util.h"
47
48 namespace {
49
50 /// Global information passed into subtools.
51 struct Globals {
52   Globals() : state(new State()) {}
53   ~Globals() {
54     delete state;
55   }
56
57   /// Deletes and recreates state so it is empty.
58   void ResetState() {
59     delete state;
60     state = new State();
61   }
62
63   /// Command line used to run Ninja.
64   const char* ninja_command;
65   /// Build configuration (e.g. parallelism).
66   BuildConfig config;
67   /// Loaded state (rules, nodes). This is a pointer so it can be reset.
68   State* state;
69 };
70
71 /// Print usage information.
72 void Usage(const BuildConfig& config) {
73   fprintf(stderr,
74 "usage: ninja [options] [targets...]\n"
75 "\n"
76 "if targets are unspecified, builds the 'default' target (see manual).\n"
77 "targets are paths, with additional special syntax:\n"
78 "  'target^' means 'the first output that uses target'.\n"
79 "  example: 'ninja foo.cc^' will likely build foo.o.\n"
80 "\n"
81 "options:\n"
82 "  -C DIR   change to DIR before doing anything else\n"
83 "  -f FILE  specify input build file [default=build.ninja]\n"
84 "\n"
85 "  -j N     run N jobs in parallel [default=%d]\n"
86 "  -k N     keep going until N jobs fail [default=1]\n"
87 "  -n       dry run (don't run commands but pretend they succeeded)\n"
88 "  -v       show all command lines while building\n"
89 "\n"
90 "  -d MODE  enable debugging (use -d list to list modes)\n"
91 "  -t TOOL  run a subtool\n"
92 "    use '-t list' to list subtools.\n"
93 "    terminates toplevel options; further flags are passed to the tool.\n",
94           config.parallelism);
95 }
96
97 /// Choose a default value for the -j (parallelism) flag.
98 int GuessParallelism() {
99   int processors = 0;
100
101 #if defined(linux)
102   processors = get_nprocs();
103 #elif defined(__APPLE__) || defined(__FreeBSD__)
104   size_t processors_size = sizeof(processors);
105   int name[] = {CTL_HW, HW_NCPU};
106   if (sysctl(name, sizeof(name) / sizeof(int),
107              &processors, &processors_size,
108              NULL, 0) < 0) {
109     processors = 1;
110   }
111 #elif defined(_WIN32)
112   SYSTEM_INFO info;
113   GetSystemInfo(&info);
114   processors = info.dwNumberOfProcessors;
115 #endif
116
117   switch (processors) {
118   case 0:
119   case 1:
120     return 2;
121   case 2:
122     return 3;
123   default:
124     return processors + 2;
125   }
126 }
127
128 /// An implementation of ManifestParser::FileReader that actually reads
129 /// the file.
130 struct RealFileReader : public ManifestParser::FileReader {
131   bool ReadFile(const string& path, string* content, string* err) {
132     return ::ReadFile(path, content, err) == 0;
133   }
134 };
135
136 /// Rebuild the build manifest, if necessary.
137 /// Returns true if the manifest was rebuilt.
138 bool RebuildManifest(State* state, const BuildConfig& config,
139                      const char* input_file, string* err) {
140   string path = input_file;
141   if (!CanonicalizePath(&path, err))
142     return false;
143   Node* node = state->LookupNode(path);
144   if (!node)
145     return false;
146
147   Builder manifest_builder(state, config);
148   if (!manifest_builder.AddTarget(node, err))
149     return false;
150
151   if (manifest_builder.AlreadyUpToDate())
152     return false;  // Not an error, but we didn't rebuild.
153   if (!manifest_builder.Build(err))
154     return false;
155
156   // The manifest was only rebuilt if it is now dirty (it may have been cleaned
157   // by a restat).
158   return node->dirty();
159 }
160
161 bool CollectTargetsFromArgs(State* state, int argc, char* argv[],
162                             vector<Node*>* targets, string* err) {
163   if (argc == 0) {
164     *targets = state->DefaultNodes(err);
165     if (!err->empty())
166       return false;
167   } else {
168     for (int i = 0; i < argc; ++i) {
169       string path = argv[i];
170       if (!CanonicalizePath(&path, err))
171         return false;
172
173       // Special syntax: "foo.cc^" means "the first output of foo.cc".
174       bool first_dependent = false;
175       if (!path.empty() && path[path.size() - 1] == '^') {
176         path.resize(path.size() - 1);
177         first_dependent = true;
178       }
179
180       Node* node = state->LookupNode(path);
181       if (node) {
182         if (first_dependent) {
183           if (node->out_edges().empty()) {
184             *err = "'" + path + "' has no out edge";
185             return false;
186           }
187           Edge* edge = node->out_edges()[0];
188           if (edge->outputs_.empty()) {
189             edge->Dump();
190             Fatal("edge has no outputs");
191           }
192           node = edge->outputs_[0];
193         }
194         targets->push_back(node);
195       } else {
196         *err = "unknown target '" + path + "'";
197
198         Node* suggestion = state->SpellcheckNode(path);
199         if (suggestion) {
200           *err += ", did you mean '" + suggestion->path() + "'?";
201         }
202         return false;
203       }
204     }
205   }
206   return true;
207 }
208
209 int ToolGraph(Globals* globals, int argc, char* argv[]) {
210   vector<Node*> nodes;
211   string err;
212   if (!CollectTargetsFromArgs(globals->state, argc, argv, &nodes, &err)) {
213     Error("%s", err.c_str());
214     return 1;
215   }
216
217   GraphViz graph;
218   graph.Start();
219   for (vector<Node*>::const_iterator n = nodes.begin(); n != nodes.end(); ++n)
220     graph.AddTarget(*n);
221   graph.Finish();
222
223   return 0;
224 }
225
226 int ToolQuery(Globals* globals, int argc, char* argv[]) {
227   if (argc == 0) {
228     Error("expected a target to query");
229     return 1;
230   }
231   for (int i = 0; i < argc; ++i) {
232     Node* node = globals->state->LookupNode(argv[i]);
233     if (!node) {
234       Node* suggestion = globals->state->SpellcheckNode(argv[i]);
235       if (suggestion) {
236         printf("%s unknown, did you mean %s?\n",
237                argv[i], suggestion->path().c_str());
238       } else {
239         printf("%s unknown\n", argv[i]);
240       }
241       return 1;
242     }
243
244     printf("%s:\n", argv[i]);
245     if (Edge* edge = node->in_edge()) {
246       printf("  input: %s\n", edge->rule_->name().c_str());
247       for (int in = 0; in < (int)edge->inputs_.size(); in++) {
248         const char* label = "";
249         if (edge->is_implicit(in))
250           label = "| ";
251         else if (edge->is_order_only(in))
252           label = "|| ";
253         printf("    %s%s\n", label, edge->inputs_[in]->path().c_str());
254       }
255     }
256     printf("  outputs:\n");
257     for (vector<Edge*>::const_iterator edge = node->out_edges().begin();
258          edge != node->out_edges().end(); ++edge) {
259       for (vector<Node*>::iterator out = (*edge)->outputs_.begin();
260            out != (*edge)->outputs_.end(); ++out) {
261         printf("    %s\n", (*out)->path().c_str());
262       }
263     }
264   }
265   return 0;
266 }
267
268 #if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
269 int ToolBrowse(Globals* globals, int argc, char* argv[]) {
270   if (argc < 1) {
271     Error("expected a target to browse");
272     return 1;
273   }
274   RunBrowsePython(globals->state, globals->ninja_command, argv[0]);
275   // If we get here, the browse failed.
276   return 1;
277 }
278 #endif  // _WIN32
279
280 int ToolTargetsList(const vector<Node*>& nodes, int depth, int indent) {
281   for (vector<Node*>::const_iterator n = nodes.begin();
282        n != nodes.end();
283        ++n) {
284     for (int i = 0; i < indent; ++i)
285       printf("  ");
286     const char* target = (*n)->path().c_str();
287     if ((*n)->in_edge()) {
288       printf("%s: %s\n", target, (*n)->in_edge()->rule_->name().c_str());
289       if (depth > 1 || depth <= 0)
290         ToolTargetsList((*n)->in_edge()->inputs_, depth - 1, indent + 1);
291     } else {
292       printf("%s\n", target);
293     }
294   }
295   return 0;
296 }
297
298 int ToolTargetsSourceList(State* state) {
299   for (vector<Edge*>::iterator e = state->edges_.begin();
300        e != state->edges_.end(); ++e) {
301     for (vector<Node*>::iterator inps = (*e)->inputs_.begin();
302          inps != (*e)->inputs_.end(); ++inps) {
303       if (!(*inps)->in_edge())
304         printf("%s\n", (*inps)->path().c_str());
305     }
306   }
307   return 0;
308 }
309
310 int ToolTargetsList(State* state, const string& rule_name) {
311   set<string> rules;
312
313   // Gather the outputs.
314   for (vector<Edge*>::iterator e = state->edges_.begin();
315        e != state->edges_.end(); ++e) {
316     if ((*e)->rule_->name() == rule_name) {
317       for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
318            out_node != (*e)->outputs_.end(); ++out_node) {
319         rules.insert((*out_node)->path());
320       }
321     }
322   }
323
324   // Print them.
325   for (set<string>::const_iterator i = rules.begin();
326        i != rules.end(); ++i) {
327     printf("%s\n", (*i).c_str());
328   }
329
330   return 0;
331 }
332
333 int ToolTargetsList(State* state) {
334   for (vector<Edge*>::iterator e = state->edges_.begin();
335        e != state->edges_.end(); ++e) {
336     for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
337          out_node != (*e)->outputs_.end(); ++out_node) {
338       printf("%s: %s\n",
339              (*out_node)->path().c_str(),
340              (*e)->rule_->name().c_str());
341     }
342   }
343   return 0;
344 }
345
346 int ToolTargets(Globals* globals, int argc, char* argv[]) {
347   int depth = 1;
348   if (argc >= 1) {
349     string mode = argv[0];
350     if (mode == "rule") {
351       string rule;
352       if (argc > 1)
353         rule = argv[1];
354       if (rule.empty())
355         return ToolTargetsSourceList(globals->state);
356       else
357         return ToolTargetsList(globals->state, rule);
358     } else if (mode == "depth") {
359       if (argc > 1)
360         depth = atoi(argv[1]);
361     } else if (mode == "all") {
362       return ToolTargetsList(globals->state);
363     } else {
364       const char* suggestion =
365           SpellcheckString(mode, "rule", "depth", "all", NULL);
366       if (suggestion) {
367         Error("unknown target tool mode '%s', did you mean '%s'?",
368               mode.c_str(), suggestion);
369       } else {
370         Error("unknown target tool mode '%s'", mode.c_str());
371       }
372       return 1;
373     }
374   }
375
376   string err;
377   vector<Node*> root_nodes = globals->state->RootNodes(&err);
378   if (err.empty()) {
379     return ToolTargetsList(root_nodes, depth, 0);
380   } else {
381     Error("%s", err.c_str());
382     return 1;
383   }
384 }
385
386 int ToolRules(Globals* globals, int argc, char* /* argv */[]) {
387   for (map<string, const Rule*>::iterator i = globals->state->rules_.begin();
388        i != globals->state->rules_.end(); ++i) {
389     if (i->second->description().empty()) {
390       printf("%s\n", i->first.c_str());
391     } else {
392       printf("%s: %s\n",
393              i->first.c_str(),
394              // XXX I changed it such that we don't have an easy way
395              // to get the source text anymore, so this output is
396              // unsatisfactory.  How useful is this command, anyway?
397              i->second->description().Serialize().c_str());
398     }
399   }
400   return 0;
401 }
402
403 void PrintCommands(Edge* edge, set<Edge*>* seen) {
404   if (!edge)
405     return;
406   if (!seen->insert(edge).second)
407     return;
408
409   for (vector<Node*>::iterator in = edge->inputs_.begin();
410        in != edge->inputs_.end(); ++in)
411     PrintCommands((*in)->in_edge(), seen);
412
413   if (!edge->is_phony())
414     puts(edge->EvaluateCommand().c_str());
415 }
416
417 int ToolCommands(Globals* globals, int argc, char* argv[]) {
418   vector<Node*> nodes;
419   string err;
420   if (!CollectTargetsFromArgs(globals->state, argc, argv, &nodes, &err)) {
421     Error("%s", err.c_str());
422     return 1;
423   }
424
425   set<Edge*> seen;
426   for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in)
427     PrintCommands((*in)->in_edge(), &seen);
428
429   return 0;
430 }
431
432 int ToolClean(Globals* globals, int argc, char* argv[]) {
433   // The clean tool uses getopt, and expects argv[0] to contain the name of
434   // the tool, i.e. "clean".
435   argc++;
436   argv--;
437
438   bool generator = false;
439   bool clean_rules = false;
440
441   optind = 1;
442   int opt;
443   while ((opt = getopt(argc, argv, const_cast<char*>("hgr"))) != -1) {
444     switch (opt) {
445     case 'g':
446       generator = true;
447       break;
448     case 'r':
449       clean_rules = true;
450       break;
451     case 'h':
452     default:
453       printf("usage: ninja -t clean [options] [targets]\n"
454 "\n"
455 "options:\n"
456 "  -g     also clean files marked as ninja generator output\n"
457 "  -r     interpret targets as a list of rules to clean instead\n"
458              );
459     return 1;
460     }
461   }
462   argv += optind;
463   argc -= optind;
464
465   if (clean_rules && argc == 0) {
466     Error("expected a rule to clean");
467     return 1;
468   }
469
470   Cleaner cleaner(globals->state, globals->config);
471   if (argc >= 1) {
472     if (clean_rules)
473       return cleaner.CleanRules(argc, argv);
474     else
475       return cleaner.CleanTargets(argc, argv);
476   } else {
477     return cleaner.CleanAll(generator);
478   }
479 }
480
481 void ToolUrtle() {
482   // RLE encoded.
483   const char* urtle =
484 " 13 ,3;2!2;\n8 ,;<11!;\n5 `'<10!(2`'2!\n11 ,6;, `\\. `\\9 .,c13$ec,.\n6 "
485 ",2;11!>; `. ,;!2> .e8$2\".2 \"?7$e.\n <:<8!'` 2.3,.2` ,3!' ;,(?7\";2!2'<"
486 "; `?6$PF ,;,\n2 `'4!8;<!3'`2 3! ;,`'2`2'3!;4!`2.`!;2 3,2 .<!2'`).\n5 3`5"
487 "'2`9 `!2 `4!><3;5! J2$b,`!>;2!:2!`,d?b`!>\n26 `'-;,(<9!> $F3 )3.:!.2 d\""
488 "2 ) !>\n30 7`2'<3!- \"=-='5 .2 `2-=\",!>\n25 .ze9$er2 .,cd16$bc.'\n22 .e"
489 "14$,26$.\n21 z45$c .\n20 J50$c\n20 14$P\"`?34$b\n20 14$ dbc `2\"?22$?7$c"
490 "\n20 ?18$c.6 4\"8?4\" c8$P\n9 .2,.8 \"20$c.3 ._14 J9$\n .2,2c9$bec,.2 `?"
491 "21$c.3`4%,3%,3 c8$P\"\n22$c2 2\"?21$bc2,.2` .2,c7$P2\",cb\n23$b bc,.2\"2"
492 "?14$2F2\"5?2\",J5$P\" ,zd3$\n24$ ?$3?%3 `2\"2?12$bcucd3$P3\"2 2=7$\n23$P"
493 "\" ,3;<5!>2;,. `4\"6?2\"2 ,9;, `\"?2$\n19$P2\",;23!6;17!;2 \"\n";
494   int count = 0;
495   for (const char* p = urtle; *p; p++) {
496     if ('0' <= *p && *p <= '9') {
497       count = count*10 + *p - '0';
498     } else {
499       for (int i = 0; i < std::max(count, 1); ++i)
500         printf("%c", *p);
501       count = 0;
502     }
503   }
504 }
505
506 int RunTool(const string& tool, Globals* globals, int argc, char** argv) {
507   typedef int (*ToolFunc)(Globals*, int, char**);
508   struct Tool {
509     const char* name;
510     const char* desc;
511     ToolFunc func;
512   } tools[] = {
513 #if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
514     { "browse", "browse dependency graph in a web browser",
515       ToolBrowse },
516 #endif
517     { "clean", "clean built files",
518       ToolClean },
519     { "commands", "list all commands required to rebuild given targets",
520       ToolCommands },
521     { "graph", "output graphviz dot file for targets",
522       ToolGraph },
523     { "query", "show inputs/outputs for a path",
524       ToolQuery },
525     { "rules",    "list all rules",
526       ToolRules },
527     { "targets",  "list targets by their rule or depth in the DAG",
528       ToolTargets },
529     { NULL, NULL, NULL }
530   };
531
532   if (tool == "list") {
533     printf("ninja subtools:\n");
534     for (int i = 0; tools[i].name; ++i) {
535       printf("%10s  %s\n", tools[i].name, tools[i].desc);
536     }
537     return 0;
538   } else if (tool == "urtle") {
539     ToolUrtle();
540     return 0;
541   }
542
543   for (int i = 0; tools[i].name; ++i) {
544     if (tool == tools[i].name)
545       return tools[i].func(globals, argc, argv);
546   }
547
548   vector<const char*> words;
549   for (int i = 0; tools[i].name; ++i)
550     words.push_back(tools[i].name);
551   const char* suggestion = SpellcheckStringV(tool, words);
552   if (suggestion) {
553     Error("unknown tool '%s', did you mean '%s'?", tool.c_str(), suggestion);
554   } else {
555     Error("unknown tool '%s'", tool.c_str());
556   }
557   return 1;
558 }
559
560 /// Enable a debugging mode.  Returns false if Ninja should exit instead
561 /// of continuing.
562 bool DebugEnable(const string& name, Globals* globals) {
563   if (name == "list") {
564     printf("debugging modes:\n"
565 "  stats  print operation counts/timing info\n");
566 //"multiple modes can be enabled via -d FOO -d BAR\n");
567     return false;
568   } else if (name == "stats") {
569     g_metrics = new Metrics;
570     return true;
571   } else {
572     printf("ninja: unknown debug setting '%s'\n", name.c_str());
573     return false;
574   }
575 }
576
577 int RunBuild(Globals* globals, int argc, char** argv) {
578   string err;
579   vector<Node*> targets;
580   if (!CollectTargetsFromArgs(globals->state, argc, argv, &targets, &err)) {
581     Error("%s", err.c_str());
582     return 1;
583   }
584
585   Builder builder(globals->state, globals->config);
586   for (size_t i = 0; i < targets.size(); ++i) {
587     if (!builder.AddTarget(targets[i], &err)) {
588       if (!err.empty()) {
589         Error("%s", err.c_str());
590         return 1;
591       } else {
592         // Added a target that is already up-to-date; not really
593         // an error.
594       }
595     }
596   }
597
598   if (builder.AlreadyUpToDate()) {
599     printf("ninja: no work to do.\n");
600     return 0;
601   }
602
603   if (!builder.Build(&err)) {
604     printf("ninja: build stopped: %s.\n", err.c_str());
605     return 1;
606   }
607
608   return 0;
609 }
610
611 }  // anonymous namespace
612
613 int main(int argc, char** argv) {
614   Globals globals;
615   globals.ninja_command = argv[0];
616   const char* input_file = "build.ninja";
617   const char* working_dir = NULL;
618   string tool;
619
620   setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
621
622   globals.config.parallelism = GuessParallelism();
623
624   const option kLongOptions[] = {
625     { "help", no_argument, NULL, 'h' },
626     { NULL, 0, NULL, 0 }
627   };
628
629   int opt;
630   while (tool.empty() &&
631          (opt = getopt_long(argc, argv, "d:f:hj:k:nt:vC:", kLongOptions,
632                             NULL)) != -1) {
633     switch (opt) {
634       case 'd':
635         if (!DebugEnable(optarg, &globals))
636           return 1;
637         break;
638       case 'f':
639         input_file = optarg;
640         break;
641       case 'j':
642         globals.config.parallelism = atoi(optarg);
643         break;
644       case 'k': {
645         char* end;
646         int value = strtol(optarg, &end, 10);
647         if (*end != 0)
648           Fatal("-k parameter not numeric; did you mean -k0?");
649
650         // We want to go until N jobs fail, which means we should allow
651         // N failures and then stop.  For N <= 0, INT_MAX is close enough
652         // to infinite for most sane builds.
653         globals.config.failures_allowed = value > 0 ? value : INT_MAX;
654         break;
655       }
656       case 'n':
657         globals.config.dry_run = true;
658         break;
659       case 'v':
660         globals.config.verbosity = BuildConfig::VERBOSE;
661         break;
662       case 't':
663         tool = optarg;
664         break;
665       case 'C':
666         working_dir = optarg;
667         break;
668       case 'h':
669       default:
670         Usage(globals.config);
671         return 1;
672     }
673   }
674   argv += optind;
675   argc -= optind;
676
677   if (working_dir) {
678     // The formatting of this string, complete with funny quotes, is
679     // so Emacs can properly identify that the cwd has changed for
680     // subsequent commands.
681     printf("ninja: Entering directory `%s'\n", working_dir);
682 #ifdef _WIN32
683     if (_chdir(working_dir) < 0) {
684 #else
685     if (chdir(working_dir) < 0) {
686 #endif
687       Fatal("chdir to '%s' - %s", working_dir, strerror(errno));
688     }
689   }
690
691   bool rebuilt_manifest = false;
692
693 reload:
694   RealFileReader file_reader;
695   ManifestParser parser(globals.state, &file_reader);
696   string err;
697   if (!parser.Load(input_file, &err)) {
698     Error("%s", err.c_str());
699     return 1;
700   }
701
702   if (!tool.empty())
703     return RunTool(tool, &globals, argc, argv);
704
705   BuildLog build_log;
706   build_log.SetConfig(&globals.config);
707   globals.state->build_log_ = &build_log;
708
709   const string build_dir = globals.state->bindings_.LookupVariable("builddir");
710   const char* kLogPath = ".ninja_log";
711   string log_path = kLogPath;
712   if (!build_dir.empty()) {
713     if (MakeDir(build_dir) < 0 && errno != EEXIST) {
714       Error("creating build directory %s: %s",
715             build_dir.c_str(), strerror(errno));
716       return 1;
717     }
718     log_path = build_dir + "/" + kLogPath;
719   }
720
721   if (!build_log.Load(log_path.c_str(), &err)) {
722     Error("loading build log %s: %s",
723           log_path.c_str(), err.c_str());
724     return 1;
725   }
726
727   if (!build_log.OpenForWrite(log_path.c_str(), &err)) {
728     Error("opening build log: %s", err.c_str());
729     return 1;
730   }
731
732   if (!rebuilt_manifest) { // Don't get caught in an infinite loop by a rebuild
733                            // target that is never up to date.
734     if (RebuildManifest(globals.state, globals.config, input_file, &err)) {
735       rebuilt_manifest = true;
736       globals.ResetState();
737       goto reload;
738     } else if (!err.empty()) {
739       Error("rebuilding '%s': %s", input_file, err.c_str());
740       return 1;
741     }
742   }
743
744   int result = RunBuild(&globals, argc, argv);
745   if (g_metrics) {
746     g_metrics->Report();
747
748     printf("\n");
749     int count = (int)globals.state->paths_.size();
750     int buckets =
751 #ifdef _MSC_VER
752         (int)globals.state->paths_.comp.bucket_size;
753 #else
754         (int)globals.state->paths_.bucket_count();
755 #endif
756     printf("path->node hash load %.2f (%d entries / %d buckets)\n",
757            count / (double) buckets, count, buckets);
758   }
759   return result;
760 }