From e391fe7ef6808bec4afdcd2fa000063f2f1fc536 Mon Sep 17 00:00:00 2001 From: Marc Delorme Date: Thu, 15 Apr 2021 21:15:28 +0900 Subject: [PATCH] Add tools: 'outputs' and 'dynouts' 'outputs' lists all the outputs generated by the graph, including dynamic outputs. 'dynouts' lists all dynamic outputs stored in the deps log. Co-authored-by: Hampus Adolfsson --- doc/manual.asciidoc | 6 +++ src/ninja.cc | 89 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 681ac30e80..72a0a4965b 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -292,6 +292,9 @@ _Available since Ninja 1.2._ `deps`:: show all dependencies stored in the `.ninja_deps` file. When given a target, show just the target's dependencies. _Available since Ninja 1.4._ +`dynouts`:: show all dynamic outputs stored in the `.ninja_deps` file. When +given a target, show just the target's outputs. + `missingdeps`:: given a list of targets, look for targets that depend on a generated file, but do not have a properly (possibly transitive) dependency on the generator. Such targets may cause build flakiness on clean builds. @@ -307,6 +310,9 @@ each of them with a missing include error or equivalent pointing to the generated file. _Available since Ninja 1.11._ +`outputs`:: list all outputs of the build graph. This includes any dynamic +outputs stored in the deps log. + `recompact`:: recompact the `.ninja_deps` file. _Available since Ninja 1.4._ `restat`:: updates all recorded file modification timestamps in the `.ninja_log` diff --git a/src/ninja.cc b/src/ninja.cc index 1b26cb059f..b9b286ff60 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -125,6 +125,8 @@ struct NinjaMain : public BuildLogUser { int ToolBrowse(const Options* options, int argc, char* argv[]); int ToolMSVC(const Options* options, int argc, char* argv[]); int ToolTargets(const Options* options, int argc, char* argv[]); + int ToolDynOuts(const Options* options, int argc, char* argv[]); + int ToolOutputs(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[]); @@ -570,10 +572,11 @@ 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; + int deps_count = deps->node_count - deps->outputs_count; printf("%s: #deps %d, deps mtime %" PRId64 " (%s)\n", - (*it)->path().c_str(), deps->node_count, deps->mtime, + (*it)->path().c_str(), deps_count, deps->mtime, (!mtime || mtime > deps->mtime ? "STALE":"VALID")); - for (int i = 0; i < deps->node_count; ++i) + for (int i = 0; i < deps_count; ++i) printf(" %s\n", deps->nodes[i]->path().c_str()); printf("\n"); } @@ -601,6 +604,84 @@ int NinjaMain::ToolMissingDeps(const Options* options, int argc, char** argv) { return 0; } +int NinjaMain::ToolDynOuts(const Options*, int argc, char** argv) { + vector nodes; + if (argc == 0) { + for (vector::const_iterator ni = deps_log_.nodes().begin(); + ni != deps_log_.nodes().end(); ++ni) { + if (deps_log_.IsDepsEntryLiveFor(*ni)) + nodes.push_back(*ni); + } + } else { + string err; + if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { + Error("%s", err.c_str()); + return 1; + } + } + + RealDiskInterface disk_interface; + for (vector::iterator it = nodes.begin(), end = nodes.end(); + it != end; ++it) { + DepsLog::Deps* deps = deps_log_.GetDeps(*it); + if (!deps) { + printf("%s: deps not found\n", (*it)->path().c_str()); + continue; + } + + string err; + TimeStamp mtime = disk_interface.Stat((*it)->path(), &err); + if (mtime == -1) + Error("%s", err.c_str()); // Log and ignore Stat() errors; + int deps_count = deps->node_count - deps->outputs_count; + printf("%s: #dynouts %d, deps mtime %" PRId64 " (%s)\n", + (*it)->path().c_str(), deps->outputs_count, deps->mtime, + (!mtime || mtime > deps->mtime ? "STALE":"VALID")); + for (int i = deps_count; i < deps->node_count; ++i) + printf(" %s\n", deps->nodes[i]->path().c_str()); + printf("\n"); + } + + return 0; +} + +int NinjaMain::ToolOutputs(const Options*, int, char*[]) { + // Load dyndep files that exist, in order to load dynamic outputs + DyndepLoader dyndep_loader(&state_, &disk_interface_); + for (vector::iterator e = state_.edges_.begin(); + e != state_.edges_.end(); ++e) { + if (Node* dyndep = (*e)->dyndep_) { + std::string err; + dyndep_loader.LoadDyndeps(dyndep, &err); + } + } + + std::string err; + // Load dynamic outputs which may exist in the deps log + DepfileParserOptions depfileOptions; + ImplicitDepLoader implicit_dep_loader(&state_, &deps_log_, &disk_interface_, + &depfileOptions); + for (vector::iterator e = state_.edges_.begin(); + e != state_.edges_.end(); ++e) { + string dynout = (*e)->GetUnescapedDynout(); + + if (!dynout.empty()) { + implicit_dep_loader.LoadImplicitOutputs(*e, &err); + } + } + + for (vector::iterator e = state_.edges_.begin(); + e != state_.edges_.end(); ++e) { + for (vector::iterator out_node = (*e)->outputs_.begin(); + out_node != (*e)->outputs_.end(); ++out_node) { + printf("%s: %s\n", (*out_node)->path().c_str(), + (*e)->rule_->name().c_str()); + } + } + + return 0; +} + int NinjaMain::ToolTargets(const Options* options, int argc, char* argv[]) { int depth = 1; if (argc >= 1) { @@ -1118,6 +1199,8 @@ const Tool* ChooseTool(const string& tool_name) { Tool::RUN_AFTER_LOAD, &NinjaMain::ToolInputs}, { "deps", "show dependencies stored in the deps log", Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps }, + { "dynouts", "shows dynamic outputs stored in the deps log", + Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDynOuts }, { "missingdeps", "check deps log dependencies on generated files", Tool::RUN_AFTER_LOGS, &NinjaMain::ToolMissingDeps }, { "graph", "output graphviz dot file for targets", @@ -1126,6 +1209,8 @@ const Tool* ChooseTool(const string& tool_name) { Tool::RUN_AFTER_LOGS, &NinjaMain::ToolQuery }, { "targets", "list targets by their rule or depth in the DAG", Tool::RUN_AFTER_LOAD, &NinjaMain::ToolTargets }, + { "outputs", "list all outputs of the build graph, including dynamic outputs stored in the deps log", + Tool::RUN_AFTER_LOGS, &NinjaMain::ToolOutputs }, { "compdb", "dump JSON compilation database to stdout", Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase }, { "recompact", "recompacts ninja-internal data structures",