[rust] Write {{sources}} ninja variable for Rust targets

Rust compiles a crate using all source files in a single command.
However, rustc only requires the source_root, and then traverses
the series of `mod` statements and `include_str!` macros to find
all the source files.

On Fuchsia, it is desired to enforce that all Rust source files
are listed explicitly in BUILD.gn files.

In order to make it possible to enforce that the list matches
the depfile rustc emits, the set of source files needs to be
accessible in the `tool` target for `rust_bin`, `rust_library`, etc.

This change writes the source files to a new ninja variable
`sources`. `inputs` are also included in this variable, since a
Rust crate can read non-rust files at build-time
(eg. `include_str!("foo.json")`).

Change-Id: I076232739c54b78508db13549a3db4894b16ea11
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/9360
Reviewed-by: Tyler Mandry <tmandry@google.com>
Reviewed-by: Petr Hosek <phosek@google.com>
Commit-Queue: Petr Hosek <phosek@google.com>
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index 1584c44..74371e0 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -139,7 +139,7 @@
   // -Ldependency. Also assemble a list of extra (i.e. implicit) deps
   // for ninja dependency tracking.
   UniqueVector<OutputFile> implicit_deps;
-  AppendSourcesToImplicitDeps(&implicit_deps);
+  AppendSourcesAndInputsToImplicitDeps(&implicit_deps);
   implicit_deps.Append(extra_obj_files.begin(), extra_obj_files.end());
 
   std::vector<OutputFile> rustdeps;
@@ -197,6 +197,7 @@
             std::back_inserter(extern_deps));
   WriteExterns(extern_deps);
   WriteRustdeps(transitive_rustlibs, rustdeps, nonrustdeps);
+  WriteSourcesAndInputs();
 }
 
 void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
@@ -214,7 +215,7 @@
   WriteSharedVars(subst);
 }
 
-void NinjaRustBinaryTargetWriter::AppendSourcesToImplicitDeps(
+void NinjaRustBinaryTargetWriter::AppendSourcesAndInputsToImplicitDeps(
     UniqueVector<OutputFile>* deps) const {
   // Only the crate_root file needs to be given to rustc as input.
   // Any other 'sources' are just implicit deps.
@@ -224,6 +225,22 @@
   for (const auto& source : target_->sources()) {
     deps->push_back(OutputFile(settings_->build_settings(), source));
   }
+  for (const auto& data : target_->config_values().inputs()) {
+    deps->push_back(OutputFile(settings_->build_settings(), data));
+  }
+}
+
+void NinjaRustBinaryTargetWriter::WriteSourcesAndInputs() {
+  out_ << "  sources =";
+  for (const auto& source : target_->sources()) {
+    out_ << " ";
+    path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), source));
+  }
+  for (const auto& data : target_->config_values().inputs()) {
+    out_ << " ";
+    path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), data));
+  }
+  out_ << std::endl;
 }
 
 void NinjaRustBinaryTargetWriter::WriteExterns(
diff --git a/src/gn/ninja_rust_binary_target_writer.h b/src/gn/ninja_rust_binary_target_writer.h
index 4d6e146..bd060ab 100644
--- a/src/gn/ninja_rust_binary_target_writer.h
+++ b/src/gn/ninja_rust_binary_target_writer.h
@@ -28,8 +28,11 @@
   void WriteRustdeps(const std::vector<OutputFile>& transitive_rustdeps,
                      const std::vector<OutputFile>& rustdeps,
                      const std::vector<OutputFile>& nonrustdeps);
+  // Unlike C/C++, Rust compiles all sources of a crate in one command.
+  // Write a ninja variable `sources` that contains all sources and input files.
+  void WriteSourcesAndInputs();
   void WriteEdition();
-  void AppendSourcesToImplicitDeps(UniqueVector<OutputFile>* deps) const;
+  void AppendSourcesAndInputsToImplicitDeps(UniqueVector<OutputFile>* deps) const;
 
   const RustTool* tool_;
 
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index be19f3b..34d87f0 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -64,7 +64,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/input3.rs "
         "../../foo/main.rs\n"
         "  externs =\n"
-        "  rustdeps =\n";
+        "  rustdeps =\n"
+        "  sources = ../../foo/input3.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -105,7 +106,8 @@
         "build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
         "../../bar/mylib.rs ../../bar/lib.rs\n"
         "  externs =\n"
-        "  rustdeps =\n";
+        "  rustdeps =\n"
+        "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -155,7 +157,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
         "../../foo/main.rs obj/foo/libdirect.rlib\n"
         "  externs = --extern direct=obj/foo/libdirect.rlib\n"
-        "  rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n";
+        "  rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -197,7 +200,8 @@
         "build obj/bar/libmymacro.so: rust_macro ../../bar/lib.rs | "
         "../../bar/mylib.rs ../../bar/lib.rs\n"
         "  externs =\n"
-        "  rustdeps =\n";
+        "  rustdeps =\n"
+        "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -242,7 +246,8 @@
         "../../bar/mylib.rs ../../bar/lib.rs obj/bar/libmymacro.so || "
         "obj/baz/group.stamp\n"
         "  externs = --extern mymacro=obj/bar/libmymacro.so\n"
-        "  rustdeps = -Ldependency=obj/bar\n";
+        "  rustdeps = -Ldependency=obj/bar\n"
+        "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -279,7 +284,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | "
         "../../foo/source.rs ../../foo/main.rs obj/bar/libmylib.rlib\n"
         "  externs = --extern mylib=obj/bar/libmylib.rlib\n"
-        "  rustdeps = -Ldependency=obj/bar\n";
+        "  rustdeps = -Ldependency=obj/bar\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -334,7 +340,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
         "../../foo/main.rs obj/foo/libdirect.rlib\n"
         "  externs = --extern direct_renamed=obj/foo/libdirect.rlib\n"
-        "  rustdeps = -Ldependency=obj/foo\n";
+        "  rustdeps = -Ldependency=obj/foo\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -433,7 +440,8 @@
         "  externs = --extern mylib=obj/bar/libmylib.rlib\n"
         "  rustdeps = -Ldependency=obj/bar -Lnative=obj/baz -Lnative=obj/foo "
         "-Lnative=. -Clink-arg=obj/baz/sourceset.csourceset.o -lstatic "
-        "-lshared -lshared_with_toc\n";
+        "-lshared -lshared_with_toc\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -469,7 +477,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
         "../../foo/main.rs obj/foo/libstatic.a\n"
         "  externs =\n"
-        "  rustdeps = -Lnative=obj/foo -lstatic\n";
+        "  rustdeps = -Lnative=obj/foo -lstatic\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -512,7 +521,8 @@
         "build ./foo_bar.exe: rust_bin ../../foo/main.rs | ../../foo/input3.rs "
         "../../foo/main.rs\n"
         "  externs =\n"
-        "  rustdeps =\n";
+        "  rustdeps =\n"
+        "  sources = ../../foo/input3.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -556,7 +566,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/input.rs "
         "../../foo/main.rs\n"
         "  externs =\n"
-        "  rustdeps = -Lnative=../../baz -lquux\n";
+        "  rustdeps = -Lnative=../../baz -lquux\n"
+        "  sources = ../../foo/input.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -614,7 +625,8 @@
         "build obj/bar/libmymacro.so: rust_macro ../../bar/lib.rs | "
         "../../bar/mylib.rs ../../bar/lib.rs obj/baz/libmymacrodep.rlib\n"
         "  externs = --extern mymacrodep=obj/baz/libmymacrodep.rlib\n"
-        "  rustdeps = -Ldependency=obj/baz\n";
+        "  rustdeps = -Ldependency=obj/baz\n"
+        "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -651,7 +663,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
         "../../foo/main.rs obj/bar/libmymacro.so\n"
         "  externs = --extern mymacro=obj/bar/libmymacro.so\n"
-        "  rustdeps = -Ldependency=obj/bar\n";
+        "  rustdeps = -Ldependency=obj/bar\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -692,7 +705,8 @@
         "build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
         "../../bar/mylib.rs ../../bar/lib.rs\n"
         "  externs =\n"
-        "  rustdeps =\n";
+        "  rustdeps =\n"
+        "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -736,7 +750,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
         "../../foo/main.rs obj/bar/libmylib.rlib || obj/baz/group.stamp\n"
         "  externs = --extern mylib=obj/bar/libmylib.rlib\n"
-        "  rustdeps = -Ldependency=obj/bar\n";
+        "  rustdeps = -Ldependency=obj/bar\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
@@ -782,7 +797,55 @@
         "../../foo/main.rs ../../foo/lib1.rlib\n"
         "  externs = --extern lib1=../../foo/lib1.rlib --extern "
         "lib2=lib2.rlib\n"
-        "  rustdeps =\n";
+        "  rustdeps =\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs\n";
+    std::string out_str = out.str();
+    EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+  }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, Inputs) {
+  Err err;
+  TestWithScope setup;
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::EXECUTABLE);
+  target.visibility().SetPublic();
+  SourceFile main("//foo/main.rs");
+  target.sources().push_back(SourceFile("//foo/source.rs"));
+  target.sources().push_back(main);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(main);
+  target.rust_values().crate_name() = "foo_bar";
+  target.config_values().inputs().push_back(SourceFile("//foo/config.json"));
+  target.config_values().inputs().push_back(SourceFile("//foo/template.h"));
+  target.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  {
+    std::ostringstream out;
+    NinjaRustBinaryTargetWriter writer(&target, out);
+    writer.Run();
+
+    const char expected[] =
+        "build obj/foo/bar.inputs.stamp: stamp ../../foo/config.json ../../foo/template.h\n"
+        "crate_name = foo_bar\n"
+        "crate_type = bin\n"
+        "output_extension = \n"
+        "output_dir = \n"
+        "rustflags =\n"
+        "rustenv =\n"
+        "root_out_dir = .\n"
+        "target_out_dir = obj/foo\n"
+        "target_output_name = bar\n"
+        "\n"
+        "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+        "../../foo/main.rs ../../foo/config.json ../../foo/template.h "
+        "|| obj/foo/bar.inputs.stamp\n"
+        "  externs =\n"
+        "  rustdeps =\n"
+        "  sources = ../../foo/source.rs ../../foo/main.rs "
+        "../../foo/config.json ../../foo/template.h\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
diff --git a/src/gn/rust_substitution_type.cc b/src/gn/rust_substitution_type.cc
index 8fd6a40..c417019 100644
--- a/src/gn/rust_substitution_type.cc
+++ b/src/gn/rust_substitution_type.cc
@@ -14,6 +14,7 @@
     &kRustSubstitutionCrateName,       &kRustSubstitutionCrateType,
     &kRustSubstitutionRustDeps,        &kRustSubstitutionRustFlags,
     &kRustSubstitutionRustEnv,         &kRustSubstitutionExterns,
+    &kRustSubstitutionSources,
 };
 
 // Valid for Rust tools.
@@ -25,6 +26,7 @@
 const Substitution kRustSubstitutionRustDeps = {"{{rustdeps}}", "rustdeps"};
 const Substitution kRustSubstitutionRustEnv = {"{{rustenv}}", "rustenv"};
 const Substitution kRustSubstitutionRustFlags = {"{{rustflags}}", "rustflags"};
+const Substitution kRustSubstitutionSources = {"{{sources}}", "sources"};
 
 bool IsValidRustSubstitution(const Substitution* type) {
   return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
@@ -35,5 +37,6 @@
          type == &kRustSubstitutionExterns ||
          type == &kRustSubstitutionRustDeps ||
          type == &kRustSubstitutionRustEnv ||
-         type == &kRustSubstitutionRustFlags;
+         type == &kRustSubstitutionRustFlags ||
+         type == &kRustSubstitutionSources;
 }
diff --git a/src/gn/rust_substitution_type.h b/src/gn/rust_substitution_type.h
index 8e9b5c8..2eeb7dc 100644
--- a/src/gn/rust_substitution_type.h
+++ b/src/gn/rust_substitution_type.h
@@ -20,6 +20,7 @@
 extern const Substitution kRustSubstitutionRustDeps;
 extern const Substitution kRustSubstitutionRustEnv;
 extern const Substitution kRustSubstitutionRustFlags;
+extern const Substitution kRustSubstitutionSources;
 
 bool IsValidRustSubstitution(const Substitution* type);