Write input files to json project

This information is useful for IDEs to monitor the gn project files in
order to know when to re-run gn to update the .json file.

Change-Id: I68eb3b13cfb6a468d2d4adfc88db3599f18e6d46
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/7080
Commit-Queue: Brett Wilson <brettw@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/json_project_writer.cc b/src/gn/json_project_writer.cc
index 6509839..7c04ea1 100644
--- a/src/gn/json_project_writer.cc
+++ b/src/gn/json_project_writer.cc
@@ -15,6 +15,7 @@
 #include "gn/desc_builder.h"
 #include "gn/exec_process.h"
 #include "gn/filesystem_utils.h"
+#include "gn/scheduler.h"
 #include "gn/settings.h"
 
 // Structure of JSON output file
@@ -208,6 +209,28 @@
       "default_toolchain",
       base::Value(default_toolchain_label.GetUserVisibleName(false)));
 
+  std::vector<base::FilePath> input_files;
+  g_scheduler->input_file_manager()->GetAllPhysicalInputFileNames(&input_files);
+
+  // Other files read by the build.
+  std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
+
+  // Sort the input files to order them deterministically.
+  // Additionally, remove duplicate filepaths that seem to creep in.
+  std::set<base::FilePath> fileset(input_files.begin(), input_files.end());
+  fileset.insert(other_files.begin(), other_files.end());
+
+  base::ListValue inputs;
+  const auto &build_path = build_settings->root_path();
+  for (const auto& other_file : fileset) {
+    std::string file;
+    if (MakeAbsolutePathRelativeIfPossible(FilePathToUTF8(build_path),
+                                           FilePathToUTF8(other_file), &file)) {
+      inputs.Append(std::make_unique<base::Value>(std::move(file)));
+    }
+  }
+  settings->SetKey("gen_input_files", std::move(inputs));
+
   auto output = std::make_unique<base::DictionaryValue>();
   output->SetWithoutPathExpansion("targets", std::move(targets));
   output->SetWithoutPathExpansion("build_settings", std::move(settings));
diff --git a/src/gn/json_project_writer.h b/src/gn/json_project_writer.h
index ee2e7b9..9d396d3 100644
--- a/src/gn/json_project_writer.h
+++ b/src/gn/json_project_writer.h
@@ -23,9 +23,9 @@
                                Err* err);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ActionWithResponseFile);
-  FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ForEachWithResponseFile);
-  FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, RustTarget);
+  FRIEND_TEST_ALL_PREFIXES(JSONWriter, ActionWithResponseFile);
+  FRIEND_TEST_ALL_PREFIXES(JSONWriter, ForEachWithResponseFile);
+  FRIEND_TEST_ALL_PREFIXES(JSONWriter, RustTarget);
 
   static std::string RenderJSON(const BuildSettings* build_settings,
                                 std::vector<const Target*>& all_targets);
diff --git a/src/gn/json_project_writer_unittest.cc b/src/gn/json_project_writer_unittest.cc
index 709d7c1..8a29425 100644
--- a/src/gn/json_project_writer_unittest.cc
+++ b/src/gn/json_project_writer_unittest.cc
@@ -7,10 +7,13 @@
 #include "gn/substitution_list.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
+#include "gn/test_with_scheduler.h"
 #include "util/build_config.h"
 #include "util/test/test.h"
 
-TEST(JSONProjectWriter, ActionWithResponseFile) {
+using JSONWriter = TestWithScheduler;
+
+TEST_F(JSONWriter, ActionWithResponseFile) {
   Err err;
   TestWithScope setup;
 
@@ -37,6 +40,15 @@
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
   std::vector<const Target*> targets;
   targets.push_back(&target);
+#if defined(OS_WIN)
+  base::FilePath root_path = base::FilePath(FILE_PATH_LITERAL("c:/path/to/src"));
+#else
+  base::FilePath root_path = base::FilePath(FILE_PATH_LITERAL("/path/to/src"));
+#endif
+  setup.build_settings()->SetRootPath(root_path);
+  g_scheduler->AddGenDependency(root_path.Append(FILE_PATH_LITERAL(".gn")));
+  g_scheduler->AddGenDependency(root_path.Append(FILE_PATH_LITERAL("BUILD.gn")));
+  g_scheduler->AddGenDependency(root_path.Append(FILE_PATH_LITERAL("build/BUILD.gn")));
   std::string out =
       JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
 #if defined(OS_WIN)
@@ -47,7 +59,12 @@
       "   \"build_settings\": {\n"
       "      \"build_dir\": \"//out/Debug/\",\n"
       "      \"default_toolchain\": \"//toolchain:default\",\n"
-      "      \"root_path\": \"\"\n"
+      "      \"gen_input_files\": [ \"//.gn\", \"//BUILD.gn\", \"//build/BUILD.gn\" ],\n"
+#if defined(OS_WIN)
+      "      \"root_path\": \"c:/path/to/src\"\n"
+#else
+      "      \"root_path\": \"/path/to/src\"\n"
+#endif
       "   },\n"
       "   \"targets\": {\n"
       "      \"//foo:bar()\": {\n"
@@ -72,7 +89,7 @@
   EXPECT_EQ(expected_json, out);
 }
 
-TEST(JSONProjectWriter, RustTarget) {
+TEST_F(JSONWriter, RustTarget) {
   Err err;
   TestWithScope setup;
 
@@ -99,6 +116,7 @@
       "   \"build_settings\": {\n"
       "      \"build_dir\": \"//out/Debug/\",\n"
       "      \"default_toolchain\": \"//toolchain:default\",\n"
+      "      \"gen_input_files\": [  ],\n"
       "      \"root_path\": \"\"\n"
       "   },\n"
       "   \"targets\": {\n"
@@ -127,7 +145,7 @@
   EXPECT_EQ(expected_json, out);
 }
 
-TEST(JSONProjectWriter, ForEachWithResponseFile) {
+TEST_F(JSONWriter, ForEachWithResponseFile) {
   Err err;
   TestWithScope setup;
 
@@ -153,6 +171,14 @@
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
   std::vector<const Target*> targets;
   targets.push_back(&target);
+#if defined(OS_WIN)
+  base::FilePath root_path = base::FilePath(FILE_PATH_LITERAL("c:/path/to/src"));
+#else
+  base::FilePath root_path = base::FilePath(FILE_PATH_LITERAL("/path/to/src"));
+#endif
+  setup.build_settings()->SetRootPath(root_path);
+  g_scheduler->AddGenDependency(root_path.Append(FILE_PATH_LITERAL(".gn")));
+  g_scheduler->AddGenDependency(root_path.Append(FILE_PATH_LITERAL("BUILD.gn")));
   std::string out =
       JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
 #if defined(OS_WIN)
@@ -163,7 +189,12 @@
       "   \"build_settings\": {\n"
       "      \"build_dir\": \"//out/Debug/\",\n"
       "      \"default_toolchain\": \"//toolchain:default\",\n"
-      "      \"root_path\": \"\"\n"
+      "      \"gen_input_files\": [ \"//.gn\", \"//BUILD.gn\" ],\n"
+#if defined(OS_WIN)
+      "      \"root_path\": \"c:/path/to/src\"\n"
+#else
+      "      \"root_path\": \"/path/to/src\"\n"
+#endif
       "   },\n"
       "   \"targets\": {\n"
       "      \"//foo:bar()\": {\n"