Process transitive dependecies for externs

When generating Rust externs, we need to handle both direct and
transitive dependencies i.e. dependencies coming from public deps.

Bug: crbug.com/gn/105
Change-Id: I6bc89358f8c42f1cf1fc0e3527516c13b5a9f842
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/5820
Commit-Queue: Petr Hosek <phosek@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/ninja_rust_binary_target_writer.cc b/tools/gn/ninja_rust_binary_target_writer.cc
index e1e762c..1e4efbb 100644
--- a/tools/gn/ninja_rust_binary_target_writer.cc
+++ b/tools/gn/ninja_rust_binary_target_writer.cc
@@ -162,7 +162,12 @@
         target_, tool_, tool_->outputs(), &tool_outputs);
     WriteCompilerBuildLine(target_->rust_values().crate_root(), deps.vector(),
                            order_only_deps, tool_->name(), tool_outputs);
-    WriteExterns();
+
+    std::vector<const Target*> extern_deps(linkable_deps.vector());
+    std::copy(non_linkable_deps.begin(), non_linkable_deps.end(),
+              std::back_inserter(extern_deps));
+    WriteExterns(extern_deps);
+
     WriteRustdeps(rustdeps, nonrustdeps);
     WriteEdition();
   }
@@ -185,12 +190,13 @@
   WriteSharedVars(subst);
 }
 
-void NinjaRustBinaryTargetWriter::WriteExterns() {
+void NinjaRustBinaryTargetWriter::WriteExterns(
+    const std::vector<const Target*>& deps) {
   std::vector<const Target*> externs;
-  for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
-    if (pair.ptr->output_type() == Target::RUST_LIBRARY ||
-        pair.ptr->rust_values().crate_type() == RustValues::CRATE_PROC_MACRO) {
-      externs.push_back(pair.ptr);
+  for (const Target* target : deps) {
+    if (target->output_type() == Target::RUST_LIBRARY ||
+        target->rust_values().crate_type() == RustValues::CRATE_PROC_MACRO) {
+      externs.push_back(target);
     }
   }
   if (externs.empty())
diff --git a/tools/gn/ninja_rust_binary_target_writer.h b/tools/gn/ninja_rust_binary_target_writer.h
index 4302abf..31d636b 100644
--- a/tools/gn/ninja_rust_binary_target_writer.h
+++ b/tools/gn/ninja_rust_binary_target_writer.h
@@ -24,7 +24,7 @@
   void WriteCompilerVars();
   void WriteSources(const OutputFile& input_dep,
                     const std::vector<OutputFile>& order_only_deps);
-  void WriteExterns();
+  void WriteExterns(const std::vector<const Target*>& deps);
   void WriteRustdeps(const std::vector<OutputFile>& rustdeps,
                      const std::vector<OutputFile>& nonrustdeps);
   void WriteEdition();
diff --git a/tools/gn/ninja_rust_binary_target_writer_unittest.cc b/tools/gn/ninja_rust_binary_target_writer_unittest.cc
index e247f3d..5749c84 100644
--- a/tools/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/tools/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -185,7 +185,8 @@
         "\n"
         "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
         "../../foo/main.rs obj/foo/libdirect.rlib obj/bar/libmylib.rlib\n"
-        "  externs = --extern direct=obj/foo/libdirect.rlib\n"
+        "  externs = --extern direct=obj/foo/libdirect.rlib --extern "
+        "mylib=obj/bar/libmylib.rlib\n"
         "  rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
         "  edition = 2018\n";
     std::string out_str = out.str();
@@ -450,3 +451,91 @@
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
 }
+
+TEST_F(NinjaRustBinaryTargetWriterTest, GroupDeps) {
+  Err err;
+  TestWithScope setup;
+
+  Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
+  rlib.set_output_type(Target::RUST_LIBRARY);
+  rlib.visibility().SetPublic();
+  SourceFile barlib("//bar/lib.rs");
+  rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
+  rlib.sources().push_back(barlib);
+  rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+  rlib.rust_values().set_crate_root(barlib);
+  rlib.rust_values().crate_name() = "mylib";
+  rlib.rust_values().edition() = "2018";
+  rlib.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(rlib.OnResolved(&err));
+
+  {
+    std::ostringstream out;
+    NinjaRustBinaryTargetWriter writer(&rlib, out);
+    writer.Run();
+
+    const char expected[] =
+        "crate_name = mylib\n"
+        "crate_type = rlib\n"
+        "output_dir = \n"
+        "rustc_output_extension = .rlib\n"
+        "rustc_output_prefix = lib\n"
+        "rustflags =\n"
+        "rustenv =\n"
+        "root_out_dir = .\n"
+        "target_out_dir = obj/bar\n"
+        "target_output_name = mylib\n"
+        "\n"
+        "build obj/bar/libmylib.rlib: rustc ../../bar/lib.rs | "
+        "../../bar/mylib.rs ../../bar/lib.rs\n"
+        "  edition = 2018\n";
+    std::string out_str = out.str();
+    EXPECT_EQ(expected, out_str) << out_str;
+  }
+
+  Target group(setup.settings(), Label(SourceDir("//baz/"), "group"));
+  group.set_output_type(Target::GROUP);
+  group.visibility().SetPublic();
+  group.public_deps().push_back(LabelTargetPair(&rlib));
+  group.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(group.OnResolved(&err));
+
+  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.rust_values().edition() = "2018";
+  target.private_deps().push_back(LabelTargetPair(&group));
+  target.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  {
+    std::ostringstream out;
+    NinjaRustBinaryTargetWriter writer(&target, out);
+    writer.Run();
+
+    const char expected[] =
+        "crate_name = foo_bar\n"
+        "crate_type = bin\n"
+        "output_dir = \n"
+        "rustc_output_extension = \n"
+        "rustflags =\n"
+        "rustenv =\n"
+        "root_out_dir = .\n"
+        "target_out_dir = obj/foo\n"
+        "target_output_name = bar\n"
+        "\n"
+        "build obj/foo/foo_bar: rustc ../../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"
+        "  edition = 2018\n";
+    std::string out_str = out.str();
+    EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+  }
+}