diff options
author | Evan Martin <martine@danga.com> | 2013-05-23 21:06:27 (GMT) |
---|---|---|
committer | Evan Martin <martine@danga.com> | 2013-05-24 16:54:35 (GMT) |
commit | 0575b6f5ca8118afafeb15fb615bd4c1c55b9d35 (patch) | |
tree | eab55f893a127cb77ae837763befba8124413eb1 | |
parent | 7b59fde1feb193d63df8b6b31ea6894635f18906 (diff) | |
download | Ninja-0575b6f5ca8118afafeb15fb615bd4c1c55b9d35.zip Ninja-0575b6f5ca8118afafeb15fb615bd4c1c55b9d35.tar.gz Ninja-0575b6f5ca8118afafeb15fb615bd4c1c55b9d35.tar.bz2 |
factor out flag parsing from enormous NinjaMain()
-rw-r--r-- | src/ninja.cc | 108 |
1 files changed, 64 insertions, 44 deletions
diff --git a/src/ninja.cc b/src/ninja.cc index 58c1c80..6c4a1f9 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -47,6 +47,20 @@ int MSVCHelperMain(int argc, char** argv); namespace { +struct Tool; + +/// Command-line options. +struct Options { + /// Build file to load. + const char* input_file; + + /// Directory to change into before running. + const char* working_dir; + + /// Tool to run rather than building. + const Tool* tool; +}; + /// Global information passed into subtools. struct Globals { Globals() : state(new State()) {} @@ -558,8 +572,8 @@ int ToolUrtle(Globals* globals, int argc, char** argv) { } /// Find the function to execute for \a tool_name and return it via \a func. -/// If there is no tool to run (e.g.: unknown tool), returns an exit code. -int ChooseTool(const string& tool_name, const Tool** tool_out) { +/// Returns a Tool, or NULL if Ninja should exit. +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", @@ -592,14 +606,12 @@ int ChooseTool(const string& tool_name, const Tool** tool_out) { if (tool->desc) printf("%10s %s\n", tool->name, tool->desc); } - return 0; + return NULL; } for (const Tool* tool = &kTools[0]; tool->name; ++tool) { - if (tool->name == tool_name) { - *tool_out = tool; - return 0; - } + if (tool->name == tool_name) + return tool; } vector<const char*> words; @@ -607,17 +619,17 @@ int ChooseTool(const string& tool_name, const Tool** tool_out) { words.push_back(tool->name); const char* suggestion = SpellcheckStringV(tool_name, words); if (suggestion) { - Error("unknown tool '%s', did you mean '%s'?", + Fatal("unknown tool '%s', did you mean '%s'?", tool_name.c_str(), suggestion); } else { - Error("unknown tool '%s'", tool_name.c_str()); + Fatal("unknown tool '%s'", tool_name.c_str()); } - return 1; + return NULL; // Not reached. } /// Enable a debugging mode. Returns false if Ninja should exit instead /// of continuing. -bool DebugEnable(const string& name, Globals* globals) { +bool DebugEnable(const string& name) { if (name == "list") { printf("debugging modes:\n" " stats print operation counts/timing info\n" @@ -779,16 +791,13 @@ int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) { #endif // _MSC_VER -int NinjaMain(int argc, char** argv) { - BuildConfig config; - Globals globals; - globals.ninja_command = argv[0]; - globals.config = &config; - const char* input_file = "build.ninja"; - const char* working_dir = NULL; - string tool_name; - - setvbuf(stdout, NULL, _IOLBF, BUFSIZ); +/// 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(); @@ -800,16 +809,16 @@ int NinjaMain(int argc, char** argv) { }; int opt; - while (tool_name.empty() && - (opt = getopt_long(argc, argv, "d:f:j:k:l:nt:vC:h", kLongOptions, + while (!options->tool && + (opt = getopt_long(*argc, *argv, "d:f:j:k:l:nt:vC:h", kLongOptions, NULL)) != -1) { switch (opt) { case 'd': - if (!DebugEnable(optarg, &globals)) + if (!DebugEnable(optarg)) return 1; break; case 'f': - input_file = optarg; + options->input_file = optarg; break; case 'j': { char* end; @@ -843,13 +852,15 @@ int NinjaMain(int argc, char** argv) { config.dry_run = true; break; case 't': - tool_name = optarg; + options->tool = ChooseTool(optarg); + if (!options->tool) + return 0; break; case 'v': config.verbosity = BuildConfig::VERBOSE; break; case 'C': - working_dir = optarg; + options->working_dir = optarg; break; case OPT_VERSION: printf("%s\n", kNinjaVersion); @@ -860,31 +871,40 @@ int NinjaMain(int argc, char** argv) { return 1; } } - argv += optind; - argc -= optind; + *argv += optind; + *argc -= optind; - // If specified, select a tool as early as possible, so commands like - // -t list can run before we attempt to load build.ninja etc. - const Tool* tool = NULL; - if (!tool_name.empty()) { - int exit_code = ChooseTool(tool_name, &tool); - if (!tool) - return exit_code; - } + return -1; +} + +int NinjaMain(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); + if (exit_code >= 0) + return exit_code; if (tool && tool->when == Tool::RUN_AFTER_FLAGS) return tool->func(&globals, argc, argv); - if (working_dir) { + if (options.working_dir) { // The formatting of this string, complete with funny quotes, is // so Emacs can properly identify that the cwd has changed for // 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) - printf("ninja: Entering directory `%s'\n", working_dir); - if (chdir(working_dir) < 0) { - Fatal("chdir to '%s' - %s", working_dir, strerror(errno)); + 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)); } } @@ -894,7 +914,7 @@ reload: RealFileReader file_reader; ManifestParser parser(globals.state, &file_reader); string err; - if (!parser.Load(input_file, &err)) { + if (!parser.Load(options.input_file, &err)) { Error("%s", err.c_str()); return 1; } @@ -927,12 +947,12 @@ reload: // target that is never up to date. Builder manifest_builder(globals.state, config, &build_log, &deps_log, &disk_interface); - if (RebuildManifest(&manifest_builder, input_file, &err)) { + if (RebuildManifest(&manifest_builder, options.input_file, &err)) { rebuilt_manifest = true; globals.ResetState(); goto reload; } else if (!err.empty()) { - Error("rebuilding '%s': %s", input_file, err.c_str()); + Error("rebuilding '%s': %s", options.input_file, err.c_str()); return 1; } } |