Merge "Add a debug tool to show all (recursive) inputs to a given target."
diff --git a/src/graph.h b/src/graph.h
index 4559027..5c8ea8a 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -202,6 +202,10 @@
 
   void Dump(const char* prefix="") const;
 
+  // Used in the inputs debug tool.
+  bool InputsChecked() const { return inputs_checked_; }
+  void MarkInputsChecked() { inputs_checked_ = true; }
+
 private:
   const HashedStr path_;
 
@@ -243,6 +247,10 @@
   std::atomic<EdgeList*> out_edges_ { nullptr };
 
   std::vector<Edge*> dep_scan_out_edges_;
+
+  /// Stores if this node's inputs have been already computed. Used in the
+  /// inputs debug tool.
+  bool inputs_checked_ = false;
 };
 
 struct EdgeEval {
diff --git a/src/ninja.cc b/src/ninja.cc
index eae093c..c84342d 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -127,6 +127,7 @@
   // The various subcommands, run via "-t XXX".
   int ToolGraph(const Options* options, int argc, char* argv[]);
   int ToolPath(const Options* options, int argc, char* argv[]);
+  int ToolInputs(const Options* options, int argc, char* argv[]);
   int ToolQuery(const Options* options, int argc, char* argv[]);
   int ToolDeps(const Options* options, int argc, char* argv[]);
   int ToolBrowse(const Options* options, int argc, char* argv[]);
@@ -412,6 +413,104 @@
   return 1;
 }
 
+void ToolInputsProcessNodeDeps(Node* node, DepsLog* deps_log, bool leaf_only,
+                               bool include_deps);
+
+void ToolInputsProcessNode(Node* input, DepsLog* deps_log, bool leaf_only,
+                           bool include_deps) {
+  if (input->InputsChecked()) return;
+  input->MarkInputsChecked();
+
+  // Recursively process input edges, possibly printing this node here.
+  Edge* input_edge = input->in_edge();
+  if (input_edge == nullptr || !leaf_only) {
+    printf("%s\n", input->path().c_str());
+  }
+  if (input_edge) {
+    for (Node* input : input_edge->inputs_) {
+      ToolInputsProcessNode(input, deps_log, leaf_only, include_deps);
+    }
+    if (include_deps && input_edge->outputs_.size() > 0) {
+      // Check deps on the input edge's first output because deps log entries
+      // are only stored on the first output of an edge.
+      ToolInputsProcessNodeDeps(input_edge->outputs_[0], deps_log, leaf_only,
+                                include_deps);
+    }
+  }
+}
+
+void ToolInputsProcessNodeDeps(Node* node, DepsLog* deps_log, bool leaf_only,
+                               bool include_deps) {
+  // Print all of this node's deps from the deps log. This often includes files
+  // that are not known by the node's input edge.
+  if (deps_log->IsDepsEntryLiveFor(node)) {
+    if (DepsLog::Deps* deps = deps_log->GetDeps(node); deps != nullptr) {
+      for (int i = 0; i < deps->node_count; ++i) {
+        if (Node* dep = deps->nodes[i]; dep != nullptr) {
+          ToolInputsProcessNode(dep, deps_log, leaf_only, include_deps);
+        }
+      }
+    }
+  }
+}
+
+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;
+
+  bool leaf_only = true;
+  bool include_deps = false;
+
+  optind = 1;
+  int opt;
+  while ((opt = getopt(argc, argv, "idh")) != -1) {
+    switch (opt) {
+      case 'i':
+        leaf_only = false;
+        break;
+      case 'd':
+        include_deps = true;
+        break;
+      case 'h':
+      default:
+        printf(
+            "usage: ninja -t inputs [options] target [target...]\n\n"
+            "options:\n"
+            "  -i    Include intermediate inputs.\n"
+            "  -d    Include deps from the deps log file (.ninja_deps).\n");
+        return 1;
+    }
+  }
+  argv += optind;
+  argc -= optind;
+
+  vector<Node*> nodes;
+  string err;
+  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
+    Error("%s", err.c_str());
+    return 1;
+  }
+
+  for (Node* node : nodes) {
+    node->MarkInputsChecked();
+    // Call ToolInputsProcessNode on this node's inputs, and not on itself,
+    // so that this node is not included in the output.
+    if (Edge* edge = node->in_edge(); edge != nullptr) {
+      for (Node* input : edge->inputs_) {
+        ToolInputsProcessNode(input, &deps_log_, leaf_only, include_deps);
+      }
+      if (include_deps && edge->outputs_.size() > 0) {
+        ToolInputsProcessNodeDeps(edge->outputs_[0], &deps_log_, leaf_only,
+                                  include_deps);
+      }
+    }
+  }
+  return 0;
+}
+
+
 int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) {
   if (argc == 0) {
     Error("expected a target to query");
@@ -892,6 +991,8 @@
       Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps },
     { "graph", "output graphviz dot file for targets",
       Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph },
+    { "inputs", "show all (recursive) inputs for a target",
+      Tool::RUN_AFTER_LOGS, &NinjaMain::ToolInputs },
     { "path", "find dependency path between two targets",
       Tool::RUN_AFTER_LOGS, &NinjaMain::ToolPath },
     { "query", "show inputs/outputs for a path",