summaryrefslogtreecommitdiffstats
path: root/src/ninja.cc
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2013-05-24 23:32:49 (GMT)
committerEvan Martin <martine@danga.com>2013-05-24 23:33:50 (GMT)
commitcc47bfb1ce599f4b3f20679d96e258bb595c2dc3 (patch)
treed3475129e3251a10d7c9d1c83cabde3794cb628f /src/ninja.cc
parent32e707e5e9bf6c29ea80b70ec2d00091a5f8bb60 (diff)
downloadNinja-cc47bfb1ce599f4b3f20679d96e258bb595c2dc3.zip
Ninja-cc47bfb1ce599f4b3f20679d96e258bb595c2dc3.tar.gz
Ninja-cc47bfb1ce599f4b3f20679d96e258bb595c2dc3.tar.bz2
refactor ninja main() into a struct with methods
This removes the ugly Globals struct, and the ResetState() hack. This also eliminates the only goto!
Diffstat (limited to 'src/ninja.cc')
-rw-r--r--src/ninja.cc362
1 files changed, 203 insertions, 159 deletions
diff --git a/src/ninja.cc b/src/ninja.cc
index 4d0c16a..cd34b1a 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -66,29 +66,76 @@ struct Options {
const Tool* tool;
};
-/// Global information passed into subtools.
-struct Globals {
- Globals() : state(new State()) {}
- ~Globals() {
- delete state;
- }
-
- /// Deletes and recreates state so it is empty.
- void ResetState() {
- delete state;
- state = new State();
- }
+/// The Ninja main() loads up a series of data structures; various tools need
+/// to poke into these, so store them as fields on an object.
+struct NinjaMain {
+ NinjaMain(const char* ninja_command, const BuildConfig& config) :
+ ninja_command_(ninja_command), config_(config) {}
/// Command line used to run Ninja.
- const char* ninja_command;
+ const char* ninja_command_;
+
/// Build configuration set from flags (e.g. parallelism).
- BuildConfig* config;
- /// Loaded state (rules, nodes). This is a pointer so it can be reset.
- State* state;
-};
+ const BuildConfig& config_;
+
+ /// Loaded state (rules, nodes).
+ State state_;
+
+ /// Functions for accesssing the disk.
+ RealDiskInterface disk_interface_;
+
+ /// The build directory, used for storing the build log etc.
+ string build_dir_;
+
+ BuildLog build_log_;
+ DepsLog deps_log_;
+
+ /// The type of functions that are the entry points to tools (subcommands).
+ typedef int (NinjaMain::*ToolFunc)(int, char**);
+
+ /// Get the Node for a given command-line path, handling features like
+ /// spell correction.
+ Node* CollectTarget(const char* cpath, string* err);
+
+ /// CollectTarget for all command-line arguments, filling in \a targets.
+ bool CollectTargetsFromArgs(int argc, char* argv[],
+ vector<Node*>* targets, string* err);
+
+ // The various subcommands, run via "-t XXX".
+ int ToolGraph(int argc, char* argv[]);
+ int ToolQuery(int argc, char* argv[]);
+ int ToolBrowse(int argc, char* argv[]);
+ int ToolMSVC(int argc, char* argv[]);
+ int ToolTargets(int argc, char* argv[]);
+ int ToolCommands(int argc, char* argv[]);
+ int ToolClean(int argc, char* argv[]);
+ int ToolCompilationDatabase(int argc, char* argv[]);
+ int ToolUrtle(int argc, char** argv);
-/// The type of functions that are the entry points to tools (subcommands).
-typedef int (*ToolFunc)(Globals*, int, char**);
+ /// Open the build log.
+ /// @return false on error.
+ bool OpenBuildLog();
+
+ /// Open the deps log: load it, then open for writing.
+ /// @return false on error.
+ bool OpenDepsLog();
+
+ /// Ensure the build directory exists, creating it if necessary.
+ /// @return false on error.
+ bool EnsureBuildDirExists();
+
+ /// Rebuild the manifest, if necessary.
+ /// Fills in \a err on error.
+ /// @return true if the manifest was rebuilt.
+ bool RebuildManifest(const char* input_file, string* err);
+
+ /// Build the targets listed on the command line.
+ /// @return an exit code.
+ int RunBuild(int argc, char** argv);
+
+ /// Dump the output requested by '-d stats'.
+ void DumpMetrics();
+};
/// Subtools, accessible via "-t foo".
struct Tool {
@@ -105,10 +152,13 @@ struct Tool {
/// Run after loading build.ninja.
RUN_AFTER_LOAD,
+
+ /// Run after loading the build/deps logs.
+ RUN_AFTER_LOGS,
} when;
/// Implementation of the tool.
- ToolFunc func;
+ NinjaMain::ToolFunc func;
};
/// Print usage information.
@@ -162,20 +212,21 @@ struct RealFileReader : public ManifestParser::FileReader {
/// Rebuild the build manifest, if necessary.
/// Returns true if the manifest was rebuilt.
-bool RebuildManifest(Builder* builder, const char* input_file, string* err) {
+bool NinjaMain::RebuildManifest(const char* input_file, string* err) {
string path = input_file;
if (!CanonicalizePath(&path, err))
return false;
- Node* node = builder->state_->LookupNode(path);
+ Node* node = state_.LookupNode(path);
if (!node)
return false;
- if (!builder->AddTarget(node, err))
+ Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_);
+ if (!builder.AddTarget(node, err))
return false;
- if (builder->AlreadyUpToDate())
+ if (builder.AlreadyUpToDate())
return false; // Not an error, but we didn't rebuild.
- if (!builder->Build(err))
+ if (!builder.Build(err))
return false;
// The manifest was only rebuilt if it is now dirty (it may have been cleaned
@@ -183,7 +234,7 @@ bool RebuildManifest(Builder* builder, const char* input_file, string* err) {
return node->dirty();
}
-Node* CollectTarget(State* state, const char* cpath, string* err) {
+Node* NinjaMain::CollectTarget(const char* cpath, string* err) {
string path = cpath;
if (!CanonicalizePath(&path, err))
return NULL;
@@ -195,7 +246,7 @@ Node* CollectTarget(State* state, const char* cpath, string* err) {
first_dependent = true;
}
- Node* node = state->LookupNode(path);
+ Node* node = state_.LookupNode(path);
if (node) {
if (first_dependent) {
if (node->out_edges().empty()) {
@@ -218,7 +269,7 @@ Node* CollectTarget(State* state, const char* cpath, string* err) {
} else if (path == "help") {
*err += ", did you mean 'ninja -h'?";
} else {
- Node* suggestion = state->SpellcheckNode(path);
+ Node* suggestion = state_.SpellcheckNode(path);
if (suggestion) {
*err += ", did you mean '" + suggestion->path() + "'?";
}
@@ -227,15 +278,15 @@ Node* CollectTarget(State* state, const char* cpath, string* err) {
}
}
-bool CollectTargetsFromArgs(State* state, int argc, char* argv[],
- vector<Node*>* targets, string* err) {
+bool NinjaMain::CollectTargetsFromArgs(int argc, char* argv[],
+ vector<Node*>* targets, string* err) {
if (argc == 0) {
- *targets = state->DefaultNodes(err);
+ *targets = state_.DefaultNodes(err);
return err->empty();
}
for (int i = 0; i < argc; ++i) {
- Node* node = CollectTarget(state, argv[i], err);
+ Node* node = CollectTarget(argv[i], err);
if (node == NULL)
return false;
targets->push_back(node);
@@ -243,10 +294,10 @@ bool CollectTargetsFromArgs(State* state, int argc, char* argv[],
return true;
}
-int ToolGraph(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolGraph(int argc, char* argv[]) {
vector<Node*> nodes;
string err;
- if (!CollectTargetsFromArgs(globals->state, argc, argv, &nodes, &err)) {
+ if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
Error("%s", err.c_str());
return 1;
}
@@ -260,14 +311,15 @@ int ToolGraph(Globals* globals, int argc, char* argv[]) {
return 0;
}
-int ToolQuery(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolQuery(int argc, char* argv[]) {
if (argc == 0) {
Error("expected a target to query");
return 1;
}
+
for (int i = 0; i < argc; ++i) {
string err;
- Node* node = CollectTarget(globals->state, argv[i], &err);
+ Node* node = CollectTarget(argv[i], &err);
if (!node) {
Error("%s", err.c_str());
return 1;
@@ -298,19 +350,19 @@ int ToolQuery(Globals* globals, int argc, char* argv[]) {
}
#if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
-int ToolBrowse(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolBrowse(int argc, char* argv[]) {
if (argc < 1) {
Error("expected a target to browse");
return 1;
}
- RunBrowsePython(globals->state, globals->ninja_command, argv[0]);
+ RunBrowsePython(&state_, ninja_command_, argv[0]);
// If we get here, the browse failed.
return 1;
}
#endif // _WIN32
#if defined(_WIN32)
-int ToolMSVC(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolMSVC(int argc, char* argv[]) {
// Reset getopt: push one argument onto the front of argv, reset optind.
argc++;
argv--;
@@ -385,7 +437,7 @@ int ToolTargetsList(State* state) {
return 0;
}
-int ToolTargets(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolTargets(int argc, char* argv[]) {
int depth = 1;
if (argc >= 1) {
string mode = argv[0];
@@ -394,14 +446,14 @@ int ToolTargets(Globals* globals, int argc, char* argv[]) {
if (argc > 1)
rule = argv[1];
if (rule.empty())
- return ToolTargetsSourceList(globals->state);
+ return ToolTargetsSourceList(&state_);
else
- return ToolTargetsList(globals->state, rule);
+ return ToolTargetsList(&state_, rule);
} else if (mode == "depth") {
if (argc > 1)
depth = atoi(argv[1]);
} else if (mode == "all") {
- return ToolTargetsList(globals->state);
+ return ToolTargetsList(&state_);
} else {
const char* suggestion =
SpellcheckString(mode.c_str(), "rule", "depth", "all", NULL);
@@ -416,7 +468,7 @@ int ToolTargets(Globals* globals, int argc, char* argv[]) {
}
string err;
- vector<Node*> root_nodes = globals->state->RootNodes(&err);
+ vector<Node*> root_nodes = state_.RootNodes(&err);
if (err.empty()) {
return ToolTargetsList(root_nodes, depth, 0);
} else {
@@ -439,10 +491,10 @@ void PrintCommands(Edge* edge, set<Edge*>* seen) {
puts(edge->EvaluateCommand().c_str());
}
-int ToolCommands(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolCommands(int argc, char* argv[]) {
vector<Node*> nodes;
string err;
- if (!CollectTargetsFromArgs(globals->state, argc, argv, &nodes, &err)) {
+ if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
Error("%s", err.c_str());
return 1;
}
@@ -454,7 +506,7 @@ int ToolCommands(Globals* globals, int argc, char* argv[]) {
return 0;
}
-int ToolClean(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolClean(int argc, char* argv[]) {
// The clean tool uses getopt, and expects argv[0] to contain the name of
// the tool, i.e. "clean".
argc++;
@@ -492,7 +544,7 @@ int ToolClean(Globals* globals, int argc, char* argv[]) {
return 1;
}
- Cleaner cleaner(globals->state, *globals->config);
+ Cleaner cleaner(&state_, config_);
if (argc >= 1) {
if (clean_rules)
return cleaner.CleanRules(argc, argv);
@@ -512,7 +564,7 @@ void EncodeJSONString(const char *str) {
}
}
-int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolCompilationDatabase(int argc, char* argv[]) {
bool first = true;
vector<char> cwd;
@@ -526,8 +578,8 @@ int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
}
putchar('[');
- for (vector<Edge*>::iterator e = globals->state->edges_.begin();
- e != globals->state->edges_.end(); ++e) {
+ for (vector<Edge*>::iterator e = state_.edges_.begin();
+ e != state_.edges_.end(); ++e) {
for (int i = 0; i != argc; ++i) {
if ((*e)->rule_->name() == argv[i]) {
if (!first)
@@ -550,7 +602,7 @@ int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
return 0;
}
-int ToolUrtle(Globals* globals, int argc, char** argv) {
+int NinjaMain::ToolUrtle(int argc, char** argv) {
// RLE encoded.
const char* urtle =
" 13 ,3;2!2;\n8 ,;<11!;\n5 `'<10!(2`'2!\n11 ,6;, `\\. `\\9 .,c13$ec,.\n6 "
@@ -582,26 +634,26 @@ const Tool* ChooseTool(const string& tool_name) {
static const Tool kTools[] = {
#if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
{ "browse", "browse dependency graph in a web browser",
- Tool::RUN_AFTER_LOAD, ToolBrowse },
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse },
#endif
#if defined(_WIN32)
{ "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)",
- Tool::RUN_AFTER_FLAGS, ToolMSVC },
+ Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC },
#endif
{ "clean", "clean built files",
- Tool::RUN_AFTER_LOAD, ToolClean },
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean },
{ "commands", "list all commands required to rebuild given targets",
- Tool::RUN_AFTER_LOAD, ToolCommands },
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands },
{ "graph", "output graphviz dot file for targets",
- Tool::RUN_AFTER_LOAD, ToolGraph },
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph },
{ "query", "show inputs/outputs for a path",
- Tool::RUN_AFTER_LOAD, ToolQuery },
+ Tool::RUN_AFTER_LOGS, &NinjaMain::ToolQuery },
{ "targets", "list targets by their rule or depth in the DAG",
- Tool::RUN_AFTER_LOAD, ToolTargets },
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolTargets },
{ "compdb", "dump JSON compilation database to stdout",
- Tool::RUN_AFTER_LOAD, ToolCompilationDatabase },
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase },
{ "urtle", NULL,
- Tool::RUN_AFTER_FLAGS, ToolUrtle },
+ Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolUrtle },
{ NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL }
};
@@ -660,16 +712,13 @@ bool DebugEnable(const string& name) {
}
}
-/// Open the build log.
-/// @return false on error.
-bool OpenBuildLog(BuildLog* build_log, const string& build_dir,
- Globals* globals, DiskInterface* disk_interface) {
+bool NinjaMain::OpenBuildLog() {
string log_path = ".ninja_log";
- if (!build_dir.empty())
- log_path = build_dir + "/" + log_path;
+ if (!build_dir_.empty())
+ log_path = build_dir_ + "/" + log_path;
string err;
- if (!build_log->Load(log_path, &err)) {
+ if (!build_log_.Load(log_path, &err)) {
Error("loading build log %s: %s", log_path.c_str(), err.c_str());
return false;
}
@@ -679,8 +728,8 @@ bool OpenBuildLog(BuildLog* build_log, const string& build_dir,
err.clear();
}
- if (!globals->config->dry_run) {
- if (!build_log->OpenForWrite(log_path, &err)) {
+ if (!config_.dry_run) {
+ if (!build_log_.OpenForWrite(log_path, &err)) {
Error("opening build log: %s", err.c_str());
return false;
}
@@ -691,14 +740,13 @@ bool OpenBuildLog(BuildLog* build_log, const string& build_dir,
/// Open the deps log: load it, then open for writing.
/// @return false on error.
-bool OpenDepsLog(DepsLog* deps_log, const string& build_dir,
- Globals* globals, DiskInterface* disk_interface) {
+bool NinjaMain::OpenDepsLog() {
string path = ".ninja_deps";
- if (!build_dir.empty())
- path = build_dir + "/" + path;
+ if (!build_dir_.empty())
+ path = build_dir_ + "/" + path;
string err;
- if (!deps_log->Load(path, globals->state, &err)) {
+ if (!deps_log_.Load(path, &state_, &err)) {
Error("loading deps log %s: %s", path.c_str(), err.c_str());
return false;
}
@@ -708,8 +756,8 @@ bool OpenDepsLog(DepsLog* deps_log, const string& build_dir,
err.clear();
}
- if (!globals->config->dry_run) {
- if (!deps_log->OpenForWrite(path, &err)) {
+ if (!config_.dry_run) {
+ if (!deps_log_.OpenForWrite(path, &err)) {
Error("opening deps log: %s", err.c_str());
return false;
}
@@ -718,28 +766,39 @@ bool OpenDepsLog(DepsLog* deps_log, const string& build_dir,
return true;
}
-
-/// Dump the output requested by '-d stats'.
-void DumpMetrics(Globals* globals) {
+void NinjaMain::DumpMetrics() {
g_metrics->Report();
printf("\n");
- int count = (int)globals->state->paths_.size();
- int buckets = (int)globals->state->paths_.bucket_count();
+ int count = (int)state_.paths_.size();
+ int buckets = (int)state_.paths_.bucket_count();
printf("path->node hash load %.2f (%d entries / %d buckets)\n",
count / (double) buckets, count, buckets);
}
-int RunBuild(Builder* builder, int argc, char** argv) {
+bool NinjaMain::EnsureBuildDirExists() {
+ build_dir_ = state_.bindings_.LookupVariable("builddir");
+ if (!build_dir_.empty() && !config_.dry_run) {
+ if (!disk_interface_.MakeDirs(build_dir_ + "/.") && errno != EEXIST) {
+ Error("creating build directory %s: %s",
+ build_dir_.c_str(), strerror(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+int NinjaMain::RunBuild(int argc, char** argv) {
string err;
vector<Node*> targets;
- if (!CollectTargetsFromArgs(builder->state_, argc, argv, &targets, &err)) {
+ if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) {
Error("%s", err.c_str());
return 1;
}
+ Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_);
for (size_t i = 0; i < targets.size(); ++i) {
- if (!builder->AddTarget(targets[i], &err)) {
+ if (!builder.AddTarget(targets[i], &err)) {
if (!err.empty()) {
Error("%s", err.c_str());
return 1;
@@ -750,15 +809,15 @@ int RunBuild(Builder* builder, int argc, char** argv) {
}
}
- if (builder->AlreadyUpToDate()) {
+ if (builder.AlreadyUpToDate()) {
printf("ninja: no work to do.\n");
return 0;
}
- if (!builder->Build(&err)) {
+ if (!builder.Build(&err)) {
printf("ninja: build stopped: %s.\n", err.c_str());
if (err.find("interrupted by user") != string::npos) {
- return 2;
+ return 2;
}
return 1;
}
@@ -789,15 +848,11 @@ int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
#endif // _MSC_VER
-/// Parse argc/argv for command-line options.
-/// Returns an exit code or -1 if Ninja should continue.
-int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
- Options* options) {
- // To reduce the churn of this code, use a reference here; this will
- // be refactored out in a subsequent change.
- BuildConfig& config = *config_ptr;
-
- config.parallelism = GuessParallelism();
+/// Parse argv for command-line options.
+/// Returns an exit code, or -1 if Ninja should continue.
+int ReadFlags(int* argc, char*** argv,
+ Options* options, BuildConfig* config) {
+ config->parallelism = GuessParallelism();
enum { OPT_VERSION = 1 };
const option kLongOptions[] = {
@@ -823,7 +878,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
int value = strtol(optarg, &end, 10);
if (*end != 0 || value <= 0)
Fatal("invalid -j parameter");
- config.parallelism = value;
+ config->parallelism = value;
break;
}
case 'k': {
@@ -835,7 +890,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
// We want to go until N jobs fail, which means we should allow
// N failures and then stop. For N <= 0, INT_MAX is close enough
// to infinite for most sane builds.
- config.failures_allowed = value > 0 ? value : INT_MAX;
+ config->failures_allowed = value > 0 ? value : INT_MAX;
break;
}
case 'l': {
@@ -843,11 +898,11 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
double value = strtod(optarg, &end);
if (end == optarg)
Fatal("-l parameter not numeric: did you mean -l 0.0?");
- config.max_load_average = value;
+ config->max_load_average = value;
break;
}
case 'n':
- config.dry_run = true;
+ config->dry_run = true;
break;
case 't':
options->tool = ChooseTool(optarg);
@@ -855,7 +910,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
return 0;
break;
case 'v':
- config.verbosity = BuildConfig::VERBOSE;
+ config->verbosity = BuildConfig::VERBOSE;
break;
case 'C':
options->working_dir = optarg;
@@ -865,7 +920,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
return 0;
case 'h':
default:
- Usage(config);
+ Usage(*config);
return 1;
}
}
@@ -875,23 +930,23 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
return -1;
}
-int NinjaMain(int argc, char** argv) {
+int real_main(int argc, char** argv) {
BuildConfig config;
- Globals globals;
- globals.ninja_command = argv[0];
- globals.config = &config;
Options options = {};
options.input_file = "build.ninja";
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
- Tool* tool = NULL;
- int exit_code = ReadFlags(&config, &argc, &argv, &options);
+ int exit_code = ReadFlags(&argc, &argv, &options, &config);
if (exit_code >= 0)
return exit_code;
- if (tool && tool->when == Tool::RUN_AFTER_FLAGS)
- return tool->func(&globals, argc, argv);
+ if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) {
+ // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed
+ // by other tools.
+ NinjaMain ninja(argv[0], config);
+ return (ninja.*options.tool->func)(argc, argv);
+ }
if (options.working_dir) {
// The formatting of this string, complete with funny quotes, is
@@ -899,68 +954,57 @@ int NinjaMain(int argc, char** argv) {
// subsequent commands.
// Don't print this if a tool is being used, so that tool output
// can be piped into a file without this string showing up.
- if (!tool)
+ if (!options.tool)
printf("ninja: Entering directory `%s'\n", options.working_dir);
if (chdir(options.working_dir) < 0) {
Fatal("chdir to '%s' - %s", options.working_dir, strerror(errno));
}
}
- bool rebuilt_manifest = false;
-
-reload:
- RealFileReader file_reader;
- ManifestParser parser(globals.state, &file_reader);
- string err;
- if (!parser.Load(options.input_file, &err)) {
- Error("%s", err.c_str());
- return 1;
- }
-
- if (tool && tool->when == Tool::RUN_AFTER_LOAD)
- return tool->func(&globals, argc, argv);
+ // The build can take up to 2 passes: one to rebuild the manifest, then
+ // another to build the desired target.
+ for (int cycle = 0; cycle < 2; ++cycle) {
+ NinjaMain ninja(argv[0], config);
- RealDiskInterface disk_interface;
-
- // Create the build dir if it doesn't exist.
- const string build_dir = globals.state->bindings_.LookupVariable("builddir");
- if (!build_dir.empty() && !config.dry_run) {
- if (!disk_interface.MakeDirs(build_dir + "/.") &&
- errno != EEXIST) {
- Error("creating build directory %s: %s",
- build_dir.c_str(), strerror(errno));
+ RealFileReader file_reader;
+ ManifestParser parser(&ninja.state_, &file_reader);
+ string err;
+ if (!parser.Load(options.input_file, &err)) {
+ Error("%s", err.c_str());
return 1;
}
- }
- BuildLog build_log;
- if (!OpenBuildLog(&build_log, build_dir, &globals, &disk_interface))
- return 1;
+ if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD)
+ return (ninja.*options.tool->func)(argc, argv);
- DepsLog deps_log;
- if (!OpenDepsLog(&deps_log, build_dir, &globals, &disk_interface))
- return 1;
+ if (!ninja.EnsureBuildDirExists())
+ return 1;
- if (!rebuilt_manifest) { // Don't get caught in an infinite loop by a rebuild
- // target that is never up to date.
- Builder manifest_builder(globals.state, config, &build_log, &deps_log,
- &disk_interface);
- if (RebuildManifest(&manifest_builder, options.input_file, &err)) {
- rebuilt_manifest = true;
- globals.ResetState();
- goto reload;
- } else if (!err.empty()) {
- Error("rebuilding '%s': %s", options.input_file, err.c_str());
+ if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog())
return 1;
+
+ if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS)
+ return (ninja.*options.tool->func)(argc, argv);
+
+ // The first time through, attempt to rebuild the manifest before
+ // building anything else.
+ if (cycle == 0) {
+ if (ninja.RebuildManifest(options.input_file, &err)) {
+ // Start the build over with the new manifest.
+ continue;
+ } else if (!err.empty()) {
+ Error("rebuilding '%s': %s", options.input_file, err.c_str());
+ return 1;
+ }
}
+
+ int result = ninja.RunBuild(argc, argv);
+ if (g_metrics)
+ ninja.DumpMetrics();
+ return result;
}
- Builder builder(globals.state, config, &build_log, &deps_log,
- &disk_interface);
- int result = RunBuild(&builder, argc, argv);
- if (g_metrics)
- DumpMetrics(&globals);
- return result;
+ return 1; // Shouldn't be reached.
}
} // anonymous namespace
@@ -973,7 +1017,7 @@ int main(int argc, char** argv) {
__try {
// Running inside __try ... __except suppresses any Windows error
// dialogs for errors such as bad_alloc.
- return NinjaMain(argc, argv);
+ return real_main(argc, argv);
}
__except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
// Common error situations return exitCode=1. 2 was chosen to
@@ -981,6 +1025,6 @@ int main(int argc, char** argv) {
return 2;
}
#else
- return NinjaMain(argc, argv);
+ return real_main(argc, argv);
#endif
}