Pass ldflags to Rust tools.

Rust tools may involve ldd or some other linker, which may require
access to the ldflags used for a given toolchain. Example instances
might be:
* building against an Android NDK or some other platform with a special
  sysroot
* absorbing ldflags required for ASAN configurations.

Previously, these ldflags weren't passed to rust tools. With this
change, they are.

Bug: 170
Change-Id: I7e8dd150740ab7a4a61d0b9c919fbe28ed736ac1
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/11940
Commit-Queue: Adrian Taylor <adetaylor@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/ninja_binary_target_writer.cc b/src/gn/ninja_binary_target_writer.cc
index 50e3e29..a5dcd66 100644
--- a/src/gn/ninja_binary_target_writer.cc
+++ b/src/gn/ninja_binary_target_writer.cc
@@ -294,18 +294,22 @@
   out_ << std::endl;
 }
 
-void NinjaBinaryTargetWriter::WriteLinkerFlags(
+void NinjaBinaryTargetWriter::WriteCustomLinkerFlags(
     std::ostream& out,
-    const Tool* tool,
-    const SourceFile* optional_def_file) {
-  if (tool->AsC()) {
+    const Tool* tool) {
+
+  if (tool->AsC() || (tool->AsRust() && tool->AsRust()->MayLink())) {
     // First the ldflags from the target and its config.
     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
                                          target_, &ConfigValues::ldflags,
                                          GetFlagOptions(), out);
   }
+}
 
-  // Followed by library search paths that have been recursively pushed
+void NinjaBinaryTargetWriter::WriteLibrarySearchPath(
+    std::ostream& out,
+    const Tool* tool) {
+  // Write library search paths that have been recursively pushed
   // through the dependency tree.
   const UniqueVector<SourceDir>& all_lib_dirs = target_->all_lib_dirs();
   if (!all_lib_dirs.empty()) {
@@ -334,6 +338,16 @@
                                      PathOutput::DIR_NO_LAST_SLASH);
     }
   }
+}
+
+void NinjaBinaryTargetWriter::WriteLinkerFlags(
+    std::ostream& out,
+    const Tool* tool,
+    const SourceFile* optional_def_file) {
+  // First any ldflags
+  WriteCustomLinkerFlags(out, tool);
+  // Then the library search path
+  WriteLibrarySearchPath(out, tool);
 
   if (optional_def_file) {
     out_ << " /DEF:";
diff --git a/src/gn/ninja_binary_target_writer.h b/src/gn/ninja_binary_target_writer.h
index 76a8a4e..66e0b6a 100644
--- a/src/gn/ninja_binary_target_writer.h
+++ b/src/gn/ninja_binary_target_writer.h
@@ -69,6 +69,10 @@
   void WriteLinkerFlags(std::ostream& out,
                         const Tool* tool,
                         const SourceFile* optional_def_file);
+  void WriteCustomLinkerFlags(std::ostream& out,
+                        const Tool* tool);
+  void WriteLibrarySearchPath(std::ostream& out,
+                        const Tool* tool);
   void WriteLibs(std::ostream& out, const Tool* tool);
   void WriteFrameworks(std::ostream& out, const Tool* tool);
   void WriteSwiftModules(std::ostream& out,
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index 82149bf..73cc9e8 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -320,8 +320,10 @@
     out_ << " -Clink-arg=";
     path_output_.WriteFile(out_, nonrustdep);
   }
-
-  WriteLinkerFlags(out_, tool_, nullptr);
+  WriteLibrarySearchPath(out_, tool_);
   WriteLibs(out_, tool_);
   out_ << std::endl;
+  out_ << "  ldflags =";
+  WriteCustomLinkerFlags(out_, tool_);
+  out_ << std::endl;
 }
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index a65a961..d130abb 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -42,6 +42,7 @@
   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().ldflags().push_back("-fsanitize=address");
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
@@ -65,6 +66,7 @@
         "../../foo/main.rs\n"
         "  externs =\n"
         "  rustdeps =\n"
+        "  ldflags = -fsanitize=address\n"
         "  sources = ../../foo/input3.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -107,6 +109,7 @@
         "../../bar/mylib.rs ../../bar/lib.rs\n"
         "  externs =\n"
         "  rustdeps =\n"
+        "  ldflags =\n"
         "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -158,6 +161,7 @@
         "../../foo/main.rs obj/foo/libdirect.rlib\n"
         "  externs = --extern direct=obj/foo/libdirect.rlib\n"
         "  rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -201,6 +205,7 @@
         "../../bar/mylib.rs ../../bar/lib.rs\n"
         "  externs =\n"
         "  rustdeps =\n"
+        "  ldflags =\n"
         "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -247,6 +252,7 @@
         "obj/baz/group.stamp\n"
         "  externs = --extern mymacro=obj/bar/libmymacro.so\n"
         "  rustdeps = -Ldependency=obj/bar\n"
+        "  ldflags =\n"
         "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -285,6 +291,7 @@
         "../../foo/source.rs ../../foo/main.rs obj/bar/libmylib.rlib\n"
         "  externs = --extern mylib=obj/bar/libmylib.rlib\n"
         "  rustdeps = -Ldependency=obj/bar\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -341,6 +348,7 @@
         "../../foo/main.rs obj/foo/libdirect.rlib\n"
         "  externs = --extern direct_renamed=obj/foo/libdirect.rlib\n"
         "  rustdeps = -Ldependency=obj/foo\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -443,6 +451,7 @@
         "-Clink-arg=obj/baz/sourceset.csourceset.o "
         "-Clink-arg=obj/foo/libstatic.a -Clink-arg=./libshared.so "
         "-Clink-arg=./libshared_with_toc.so\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -480,6 +489,7 @@
         "../../foo/main.rs obj/foo/libstatic.a\n"
         "  externs =\n"
         "  rustdeps = -Lnative=obj/foo -Clink-arg=obj/foo/libstatic.a\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -518,6 +528,7 @@
         "obj/foo/libstatic.a\n"
         "  externs =\n"
         "  rustdeps = -Lnative=obj/foo -Clink-arg=obj/foo/libstatic.a\n"
+        "  ldflags =\n"
         "  sources = ../../baz/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -562,6 +573,7 @@
         "../../foo/main.rs\n"
         "  externs =\n"
         "  rustdeps =\n"
+        "  ldflags =\n"
         "  sources = ../../foo/input3.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -607,6 +619,7 @@
         "../../foo/main.rs\n"
         "  externs =\n"
         "  rustdeps = -Lnative=../../baz -lquux\n"
+        "  ldflags =\n"
         "  sources = ../../foo/input.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -666,6 +679,7 @@
         "../../bar/mylib.rs ../../bar/lib.rs obj/baz/libmymacrodep.rlib\n"
         "  externs = --extern mymacrodep=obj/baz/libmymacrodep.rlib\n"
         "  rustdeps = -Ldependency=obj/baz\n"
+        "  ldflags =\n"
         "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -704,6 +718,7 @@
         "../../foo/main.rs obj/bar/libmymacro.so\n"
         "  externs = --extern mymacro=obj/bar/libmymacro.so\n"
         "  rustdeps = -Ldependency=obj/bar\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -746,6 +761,7 @@
         "../../bar/mylib.rs ../../bar/lib.rs\n"
         "  externs =\n"
         "  rustdeps =\n"
+        "  ldflags =\n"
         "  sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -791,6 +807,7 @@
         "../../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"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -838,6 +855,7 @@
         "  externs = --extern lib1=../../foo/lib1.rlib --extern "
         "lib2=lib2.rlib\n"
         "  rustdeps =\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -884,6 +902,7 @@
         "|| obj/foo/bar.inputs.stamp\n"
         "  externs =\n"
         "  rustdeps =\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs "
         "../../foo/config.json ../../foo/template.h\n";
     std::string out_str = out.str();
@@ -924,6 +943,7 @@
         "../../bar/lib.rs\n"
         "  externs =\n"
         "  rustdeps =\n"
+        "  ldflags =\n"
         "  sources = ../../bar/lib.rs\n";
     std::string out_str = out.str();
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
@@ -962,6 +982,7 @@
         "  externs =\n"
         "  rustdeps = -Ldependency=obj/bar -Lnative=obj/bar "
         "-Clink-arg=obj/bar/libmylib.so\n"
+        "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\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 c417019..69e6cf6 100644
--- a/src/gn/rust_substitution_type.cc
+++ b/src/gn/rust_substitution_type.cc
@@ -9,6 +9,7 @@
 
 #include "gn/err.h"
 #include "gn/substitution_type.h"
+#include "gn/c_substitution_type.h"
 
 const SubstitutionTypes RustSubstitutions = {
     &kRustSubstitutionCrateName,       &kRustSubstitutionCrateType,
@@ -40,3 +41,8 @@
          type == &kRustSubstitutionRustFlags ||
          type == &kRustSubstitutionSources;
 }
+
+bool IsValidRustLinkerSubstitution(const Substitution* type) {
+  return IsValidRustSubstitution(type) ||
+         type == &CSubstitutionLdFlags;
+}
diff --git a/src/gn/rust_substitution_type.h b/src/gn/rust_substitution_type.h
index 2eeb7dc..36ba953 100644
--- a/src/gn/rust_substitution_type.h
+++ b/src/gn/rust_substitution_type.h
@@ -23,5 +23,6 @@
 extern const Substitution kRustSubstitutionSources;
 
 bool IsValidRustSubstitution(const Substitution* type);
+bool IsValidRustLinkerSubstitution(const Substitution* type);
 
 #endif  // TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
diff --git a/src/gn/rust_tool.cc b/src/gn/rust_tool.cc
index 0b8921f..eaffb5b 100644
--- a/src/gn/rust_tool.cc
+++ b/src/gn/rust_tool.cc
@@ -38,6 +38,11 @@
          name == kRsToolStaticlib;
 }
 
+bool RustTool::MayLink() const {
+  return name_ == kRsToolBin || name_ == kRsToolCDylib || name_ == kRsToolDylib ||
+         name_ == kRsToolMacro;
+}
+
 void RustTool::SetComplete() {
   SetToolComplete();
 }
@@ -114,9 +119,9 @@
 }
 
 bool RustTool::ValidateSubstitution(const Substitution* sub_type) const {
-  if (name_ == kRsToolBin || name_ == kRsToolCDylib || name_ == kRsToolDylib ||
-      name_ == kRsToolMacro || name_ == kRsToolRlib ||
-      name_ == kRsToolStaticlib)
+  if (MayLink())
+    return IsValidRustLinkerSubstitution(sub_type);
+  if (ValidateName(name_))
     return IsValidRustSubstitution(sub_type);
   NOTREACHED();
   return false;
diff --git a/src/gn/rust_tool.h b/src/gn/rust_tool.h
index 8eec377..836191c 100644
--- a/src/gn/rust_tool.h
+++ b/src/gn/rust_tool.h
@@ -38,6 +38,7 @@
   bool ValidateName(const char* name) const override;
   void SetComplete() override;
   bool ValidateSubstitution(const Substitution* sub_type) const override;
+  bool MayLink() const;
 
   RustTool* AsRust() override;
   const RustTool* AsRust() const override;