summaryrefslogtreecommitdiffstats
path: root/src/ninja.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninja.cc')
-rw-r--r--src/ninja.cc149
1 files changed, 128 insertions, 21 deletions
diff --git a/src/ninja.cc b/src/ninja.cc
index 3e5c971..39672c3 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -17,6 +17,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+
+#include <algorithm>
#include <cstdlib>
#ifdef _WIN32
@@ -52,7 +54,7 @@
using namespace std;
-#ifdef _MSC_VER
+#ifdef _WIN32
// Defined in msvc_helper_main-win32.cc.
int MSVCHelperMain(int argc, char** argv);
@@ -127,6 +129,7 @@ struct NinjaMain : public BuildLogUser {
int ToolMSVC(const Options* options, int argc, char* argv[]);
int ToolTargets(const Options* options, int argc, char* argv[]);
int ToolCommands(const Options* options, int argc, char* argv[]);
+ int ToolInputs(const Options* options, int argc, char* argv[]);
int ToolClean(const Options* options, int argc, char* argv[]);
int ToolCleanDead(const Options* options, int argc, char* argv[]);
int ToolCompilationDatabase(const Options* options, int argc, char* argv[]);
@@ -404,6 +407,13 @@ int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) {
label = "|| ";
printf(" %s%s\n", label, edge->inputs_[in]->path().c_str());
}
+ if (!edge->validations_.empty()) {
+ printf(" validations:\n");
+ for (std::vector<Node*>::iterator validation = edge->validations_.begin();
+ validation != edge->validations_.end(); ++validation) {
+ printf(" %s\n", (*validation)->path().c_str());
+ }
+ }
}
printf(" outputs:\n");
for (vector<Edge*>::const_iterator edge = node->out_edges().begin();
@@ -413,6 +423,17 @@ int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) {
printf(" %s\n", (*out)->path().c_str());
}
}
+ const std::vector<Edge*> validation_edges = node->validation_out_edges();
+ if (!validation_edges.empty()) {
+ printf(" validation for:\n");
+ for (std::vector<Edge*>::const_iterator edge = validation_edges.begin();
+ edge != validation_edges.end(); ++edge) {
+ for (vector<Node*>::iterator out = (*edge)->outputs_.begin();
+ out != (*edge)->outputs_.end(); ++out) {
+ printf(" %s\n", (*out)->path().c_str());
+ }
+ }
+ }
}
return 0;
}
@@ -430,7 +451,7 @@ int NinjaMain::ToolBrowse(const Options*, int, char**) {
}
#endif
-#if defined(_MSC_VER)
+#if defined(_WIN32)
int NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) {
// Reset getopt: push one argument onto the front of argv, reset optind.
argc++;
@@ -511,7 +532,7 @@ int NinjaMain::ToolDeps(const Options* options, int argc, char** argv) {
if (argc == 0) {
for (vector<Node*>::const_iterator ni = deps_log_.nodes().begin();
ni != deps_log_.nodes().end(); ++ni) {
- if (deps_log_.IsDepsEntryLiveFor(*ni))
+ if (DepsLog::IsDepsEntryLiveFor(*ni))
nodes.push_back(*ni);
}
} else {
@@ -651,6 +672,7 @@ int NinjaMain::ToolRules(const Options* options, int argc, char* argv[]) {
}
}
printf("\n");
+ fflush(stdout);
}
return 0;
}
@@ -684,7 +706,7 @@ void PrintCommands(Edge* edge, EdgeSet* seen, PrintCommandMode mode) {
}
int NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) {
- // The clean tool uses getopt, and expects argv[0] to contain the name of
+ // The commands tool uses getopt, and expects argv[0] to contain the name of
// the tool, i.e. "commands".
++argc;
--argv;
@@ -725,6 +747,72 @@ int NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) {
return 0;
}
+void CollectInputs(Edge* edge, std::set<Edge*>* seen,
+ std::vector<std::string>* result) {
+ if (!edge)
+ return;
+ if (!seen->insert(edge).second)
+ return;
+
+ for (vector<Node*>::iterator in = edge->inputs_.begin();
+ in != edge->inputs_.end(); ++in)
+ CollectInputs((*in)->in_edge(), seen, result);
+
+ if (!edge->is_phony()) {
+ edge->CollectInputs(true, result);
+ }
+}
+
+int NinjaMain::ToolInputs(const Options* options, int argc, char* argv[]) {
+ // The inputs tool uses getopt, and expects argv[0] to contain the name of
+ // the tool, i.e. "inputs".
+ argc++;
+ argv--;
+ optind = 1;
+ int opt;
+ const option kLongOptions[] = { { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 } };
+ while ((opt = getopt_long(argc, argv, "h", kLongOptions, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ default:
+ // clang-format off
+ printf(
+"Usage '-t inputs [options] [targets]\n"
+"\n"
+"List all inputs used for a set of targets. Note that this includes\n"
+"explicit, implicit and order-only inputs, but not validation ones.\n\n"
+"Options:\n"
+" -h, --help Print this message.\n");
+ // clang-format on
+ return 1;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ vector<Node*> nodes;
+ string err;
+ if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
+ Error("%s", err.c_str());
+ return 1;
+ }
+
+ std::set<Edge*> seen;
+ std::vector<std::string> result;
+ for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in)
+ CollectInputs((*in)->in_edge(), &seen, &result);
+
+ // Make output deterministic by sorting then removing duplicates.
+ std::sort(result.begin(), result.end());
+ result.erase(std::unique(result.begin(), result.end()), result.end());
+
+ for (size_t n = 0; n < result.size(); ++n)
+ puts(result[n].c_str());
+
+ return 0;
+}
+
int NinjaMain::ToolClean(const Options* options, int argc, char* argv[]) {
// The clean tool uses getopt, and expects argv[0] to contain the name of
// the tool, i.e. "clean".
@@ -794,7 +882,10 @@ std::string EvaluateCommandWithRspfile(const Edge* edge,
return command;
size_t index = command.find(rspfile);
- if (index == 0 || index == string::npos || command[index - 1] != '@')
+ if (index == 0 || index == string::npos ||
+ (command[index - 1] != '@' &&
+ command.find("--option-file=") != index - 14 &&
+ command.find("-f ") != index - 3))
return command;
string rspfile_content = edge->GetBinding("rspfile_content");
@@ -804,7 +895,13 @@ std::string EvaluateCommandWithRspfile(const Edge* edge,
rspfile_content.replace(newline_index, 1, 1, ' ');
++newline_index;
}
- command.replace(index - 1, rspfile.length() + 1, rspfile_content);
+ if (command[index - 1] == '@') {
+ command.replace(index - 1, rspfile.length() + 1, rspfile_content);
+ } else if (command.find("-f ") == index - 3) {
+ command.replace(index - 3, rspfile.length() + 3, rspfile_content);
+ } else { // --option-file syntax
+ command.replace(index - 14, rspfile.length() + 14, rspfile_content);
+ }
return command;
}
@@ -995,14 +1092,16 @@ const Tool* ChooseTool(const string& tool_name) {
static const Tool kTools[] = {
{ "browse", "browse dependency graph in a web browser",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse },
-#if defined(_MSC_VER)
- { "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)",
+#ifdef _WIN32
+ { "msvc", "build helper for MSVC cl.exe (DEPRECATED)",
Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC },
#endif
{ "clean", "clean built files",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean },
{ "commands", "list all commands required to rebuild given targets",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands },
+ { "inputs", "list all inputs required to rebuild given targets",
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolInputs},
{ "deps", "show dependencies stored in the deps log",
Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps },
{ "missingdeps", "check deps log dependencies on generated files",
@@ -1305,11 +1404,28 @@ int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
#endif // _MSC_VER
+class DeferGuessParallelism {
+ public:
+ bool needGuess;
+ BuildConfig* config;
+
+ DeferGuessParallelism(BuildConfig* config)
+ : needGuess(true), config(config) {}
+
+ void Refresh() {
+ if (needGuess) {
+ needGuess = false;
+ config->parallelism = GuessParallelism();
+ }
+ }
+ ~DeferGuessParallelism() { Refresh(); }
+};
+
/// 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();
+ DeferGuessParallelism deferGuessParallelism(config);
enum { OPT_VERSION = 1, OPT_QUIET = 2 };
const option kLongOptions[] = {
@@ -1341,6 +1457,7 @@ int ReadFlags(int* argc, char*** argv,
// 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;
+ deferGuessParallelism.needGuess = false;
break;
}
case 'k': {
@@ -1389,6 +1506,7 @@ int ReadFlags(int* argc, char*** argv,
return 0;
case 'h':
default:
+ deferGuessParallelism.Refresh();
Usage(*config);
return 1;
}
@@ -1436,17 +1554,6 @@ NORETURN void real_main(int argc, char** argv) {
exit((ninja.*options.tool->func)(&options, argc, argv));
}
-#ifdef WIN32
- // It'd be nice to use line buffering but MSDN says: "For some systems,
- // [_IOLBF] provides line buffering. However, for Win32, the behavior is the
- // same as _IOFBF - Full Buffering."
- // Buffering used to be disabled in the LinePrinter constructor but that
- // now disables it too early and breaks -t deps performance (see issue #2018)
- // so we disable it here instead, but only when not running a tool.
- if (!options.tool)
- setvbuf(stdout, NULL, _IONBF, 0);
-#endif
-
// Limit number of rebuilds, to prevent infinite loops.
const int kCycleLimit = 100;
for (int cycle = 1; cycle <= kCycleLimit; ++cycle) {
@@ -1497,7 +1604,7 @@ NORETURN void real_main(int argc, char** argv) {
exit(result);
}
- status->Error("manifest '%s' still dirty after %d tries",
+ status->Error("manifest '%s' still dirty after %d tries, perhaps system time is not set",
options.input_file, kCycleLimit);
exit(1);
}