diff options
author | Jan Niklas Hasse <jhasse@bixense.com> | 2019-01-30 18:57:52 (GMT) |
---|---|---|
committer | Jan Niklas Hasse <jhasse@bixense.com> | 2019-01-30 18:57:52 (GMT) |
commit | 6d5a4b9eb973e9d82d63f8f9a421fad97d20e6d0 (patch) | |
tree | b427025388b026f169a135343c8a275379314127 /src/ninja.cc | |
parent | 253e94c1fa511704baeb61cf69995bbf09ba435e (diff) | |
parent | 0c158431f30a14d771e5c82c1e69eff7c69a08ce (diff) | |
download | Ninja-6d5a4b9eb973e9d82d63f8f9a421fad97d20e6d0.zip Ninja-6d5a4b9eb973e9d82d63f8f9a421fad97d20e6d0.tar.gz Ninja-6d5a4b9eb973e9d82d63f8f9a421fad97d20e6d0.tar.bz2 |
Merge branch 'master' into release
Diffstat (limited to 'src/ninja.cc')
-rw-r--r-- | src/ninja.cc | 149 |
1 files changed, 118 insertions, 31 deletions
diff --git a/src/ninja.cc b/src/ninja.cc index ed004ac..b608426 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -73,6 +73,10 @@ struct Options { /// Whether phony cycles should warn or print an error. bool phony_cycle_should_err; + + /// Whether a depfile with multiple targets on separate lines should + /// warn or print an error. + bool depfile_distinct_target_lines_should_err; }; /// The Ninja main() loads up a series of data structures; various tools need @@ -154,7 +158,7 @@ struct NinjaMain : public BuildLogUser { // Just checking n isn't enough: If an old output is both in the build log // and in the deps log, it will have a Node object in state_. (It will also // have an in edge if one of its inputs is another output that's in the deps - // log, but having a deps edge product an output thats input to another deps + // log, but having a deps edge product an output that's input to another deps // edge is rare, and the first recompaction will delete all old outputs from // the deps log, and then a second recompaction will clear the build log, // which seems good enough for this corner case.) @@ -201,21 +205,21 @@ void Usage(const BuildConfig& config) { "if targets are unspecified, builds the 'default' target (see manual).\n" "\n" "options:\n" -" --version print ninja version (\"%s\")\n" +" --version print ninja version (\"%s\")\n" +" -v, --verbose show all command lines while building\n" "\n" " -C DIR change to DIR before doing anything else\n" " -f FILE specify input build file [default=build.ninja]\n" "\n" -" -j N run N jobs in parallel [default=%d, derived from CPUs available]\n" -" -k N keep going until N jobs fail [default=1]\n" +" -j N run N jobs in parallel (0 means infinity) [default=%d on this system]\n" +" -k N keep going until N jobs fail (0 means infinity) [default=1]\n" " -l N do not start new jobs if the load average is greater than N\n" " -n dry run (don't run commands but act like they succeeded)\n" -" -v show all command lines while building\n" "\n" -" -d MODE enable debugging (use -d list to list modes)\n" -" -t TOOL run a subtool (use -t list to list subtools)\n" +" -d MODE enable debugging (use '-d list' to list modes)\n" +" -t TOOL run a subtool (use '-t list' to list subtools)\n" " terminates toplevel options; further flags are passed to the tool\n" -" -w FLAG adjust warnings (use -w list to list warnings)\n", +" -w FLAG adjust warnings (use '-w list' to list warnings)\n", kNinjaVersion, config.parallelism); } @@ -387,7 +391,12 @@ int NinjaMain::ToolBrowse(const Options* options, int argc, char* argv[]) { // If we get here, the browse failed. return 1; } -#endif // _WIN32 +#else +int NinjaMain::ToolBrowse(const Options*, int, char**) { + Fatal("browse tool not supported on this platform"); + return 1; +} +#endif #if defined(_MSC_VER) int NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) { @@ -494,7 +503,7 @@ int NinjaMain::ToolDeps(const Options* options, int argc, char** argv) { TimeStamp mtime = disk_interface.Stat((*it)->path(), &err); if (mtime == -1) Error("%s", err.c_str()); // Log and ignore Stat() errors; - printf("%s: #deps %d, deps mtime %d (%s)\n", + printf("%s: #deps %d, deps mtime %" PRId64 " (%s)\n", (*it)->path().c_str(), deps->node_count, deps->mtime, (!mtime || mtime > deps->mtime ? "STALE":"VALID")); for (int i = 0; i < deps->node_count; ++i) @@ -662,7 +671,65 @@ void EncodeJSONString(const char *str) { } } -int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* argv[]) { +enum EvaluateCommandMode { + ECM_NORMAL, + ECM_EXPAND_RSPFILE +}; +string EvaluateCommandWithRspfile(Edge* edge, EvaluateCommandMode mode) { + string command = edge->EvaluateCommand(); + if (mode == ECM_NORMAL) + return command; + + string rspfile = edge->GetUnescapedRspfile(); + if (rspfile.empty()) + return command; + + size_t index = command.find(rspfile); + if (index == 0 || index == string::npos || command[index - 1] != '@') + return command; + + string rspfile_content = edge->GetBinding("rspfile_content"); + size_t newline_index = 0; + while ((newline_index = rspfile_content.find('\n', newline_index)) != + string::npos) { + rspfile_content.replace(newline_index, 1, 1, ' '); + ++newline_index; + } + command.replace(index - 1, rspfile.length() + 1, rspfile_content); + return command; +} + +int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, + char* argv[]) { + // The compdb tool uses getopt, and expects argv[0] to contain the name of + // the tool, i.e. "compdb". + argc++; + argv--; + + EvaluateCommandMode eval_mode = ECM_NORMAL; + + optind = 1; + int opt; + while ((opt = getopt(argc, argv, const_cast<char*>("hx"))) != -1) { + switch(opt) { + case 'x': + eval_mode = ECM_EXPAND_RSPFILE; + break; + + case 'h': + default: + printf( + "usage: ninja -t compdb [options] [rules]\n" + "\n" + "options:\n" + " -x expand @rspfile style response file invocations\n" + ); + return 1; + } + } + argv += optind; + argc -= optind; + bool first = true; vector<char> cwd; @@ -688,9 +755,11 @@ int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* a printf("\n {\n \"directory\": \""); EncodeJSONString(&cwd[0]); printf("\",\n \"command\": \""); - EncodeJSONString((*e)->EvaluateCommand().c_str()); + EncodeJSONString(EvaluateCommandWithRspfile(*e, eval_mode).c_str()); printf("\",\n \"file\": \""); EncodeJSONString((*e)->inputs_[0]->path().c_str()); + printf("\",\n \"output\": \""); + EncodeJSONString((*e)->outputs_[0]->path().c_str()); printf("\"\n }"); first = false; @@ -743,10 +812,8 @@ int NinjaMain::ToolUrtle(const Options* options, int argc, char** argv) { /// Returns a Tool, or NULL if Ninja should exit. const Tool* ChooseTool(const string& tool_name) { static const Tool kTools[] = { -#if defined(NINJA_HAVE_BROWSE) { "browse", "browse dependency graph in a web browser", Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse }, -#endif #if defined(_MSC_VER) { "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)", Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC }, @@ -849,7 +916,9 @@ bool WarningEnable(const string& name, Options* options) { if (name == "list") { printf("warning flags:\n" " dupbuild={err,warn} multiple build lines for one target\n" -" phonycycle={err,warn} phony build statement references itself\n"); +" phonycycle={err,warn} phony build statement references itself\n" +" depfilemulti={err,warn} depfile has multiple output paths on separate lines\n" + ); return false; } else if (name == "dupbuild=err") { options->dupe_edges_should_err = true; @@ -863,6 +932,12 @@ bool WarningEnable(const string& name, Options* options) { } else if (name == "phonycycle=warn") { options->phony_cycle_should_err = false; return true; + } else if (name == "depfilemulti=err") { + options->depfile_distinct_target_lines_should_err = true; + return true; + } else if (name == "depfilemulti=warn") { + options->depfile_distinct_target_lines_should_err = false; + return true; } else { const char* suggestion = SpellcheckString(name.c_str(), "dupbuild=err", "dupbuild=warn", @@ -1042,6 +1117,7 @@ int ReadFlags(int* argc, char*** argv, const option kLongOptions[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, OPT_VERSION }, + { "verbose", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; @@ -1060,9 +1136,12 @@ int ReadFlags(int* argc, char*** argv, case 'j': { char* end; int value = strtol(optarg, &end, 10); - if (*end != 0 || value <= 0) + if (*end != 0 || value < 0) Fatal("invalid -j parameter"); - config->parallelism = value; + + // We want to run N jobs in parallel. For N = 0, INT_MAX + // is close enough to infinite for most sane builds. + config->parallelism = value > 0 ? value : INT_MAX; break; } case 'k': { @@ -1118,17 +1197,25 @@ int ReadFlags(int* argc, char*** argv, return -1; } -int real_main(int argc, char** argv) { +NORETURN void real_main(int argc, char** argv) { + // Use exit() instead of return in this function to avoid potentially + // expensive cleanup when destructing NinjaMain. BuildConfig config; Options options = {}; options.input_file = "build.ninja"; + options.dupe_edges_should_err = true; setvbuf(stdout, NULL, _IOLBF, BUFSIZ); const char* ninja_command = argv[0]; int exit_code = ReadFlags(&argc, &argv, &options, &config); if (exit_code >= 0) - return exit_code; + exit(exit_code); + + if (options.depfile_distinct_target_lines_should_err) { + config.depfile_parser_options.depfile_distinct_target_lines_action_ = + kDepfileDistinctTargetLinesActionError; + } if (options.working_dir) { // The formatting of this string, complete with funny quotes, is @@ -1147,7 +1234,7 @@ int real_main(int argc, char** argv) { // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed // by other tools. NinjaMain ninja(ninja_command, config); - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); } // Limit number of rebuilds, to prevent infinite loops. @@ -1166,43 +1253,43 @@ int real_main(int argc, char** argv) { string err; if (!parser.Load(options.input_file, &err)) { Error("%s", err.c_str()); - return 1; + exit(1); } if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD) - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); if (!ninja.EnsureBuildDirExists()) - return 1; + exit(1); if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog()) - return 1; + exit(1); if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS) - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); // Attempt to rebuild the manifest before building anything else if (ninja.RebuildManifest(options.input_file, &err)) { // In dry_run mode the regeneration will succeed without changing the // manifest forever. Better to return immediately. if (config.dry_run) - return 0; + exit(0); // 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; + exit(1); } int result = ninja.RunBuild(argc, argv); if (g_metrics) ninja.DumpMetrics(); - return result; + exit(result); } Error("manifest '%s' still dirty after %d tries\n", options.input_file, kCycleLimit); - return 1; + exit(1); } } // anonymous namespace @@ -1215,7 +1302,7 @@ int main(int argc, char** argv) { __try { // Running inside __try ... __except suppresses any Windows error // dialogs for errors such as bad_alloc. - return real_main(argc, argv); + real_main(argc, argv); } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { // Common error situations return exitCode=1. 2 was chosen to @@ -1223,6 +1310,6 @@ int main(int argc, char** argv) { return 2; } #else - return real_main(argc, argv); + real_main(argc, argv); #endif } |