Make dynamic link switch... dynamic.

Previously gn hard-coded the linker switch -Bdynamic
which is incorrect on platforms where MSVC tools are used for linking.
This CL parameterizes that flag.

Bug: https://crbug.com/1271215
Change-Id: I379c86b5eb166d973bc502cdae2552b9e1c6a6a7
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/12460
Commit-Queue: Tyler Mandry <tmandry@google.com>
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Reviewed-by: Tyler Mandry <tmandry@google.com>
diff --git a/docs/reference.md b/docs/reference.md
index eca3032..d04912a 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -3870,6 +3870,20 @@
         process, but may be used when generating metadata for rust-analyzer.
         (See --export-rust-project). It enables such metadata to include
         information about the Rust standard library.
+
+    dynamic_link_switch
+        Valid for: Rust tools which link
+
+        A switch to be optionally inserted into linker command lines
+        to indicate that subsequent items may be dynamically linked.
+        For ld-like linkers, -Clink-arg=-Bdynamic may be a good choice.
+        This switch is inserted by gn into rustc command lines before
+        listing any non-Rust dependencies. This may be necessary because
+        sometimes rustc puts the linker into a mode where it would otherwise
+        link against static libraries by default. This flag will be
+        inserted into the {{rustdeps}} variable at the appropriate place;
+        {{ldflags}} can't be used for the same purpose because the flags
+        may not be inserted at the desired place in the command line.
 ```
 
 #### **Expansions for tool variables**
diff --git a/src/gn/function_toolchain.cc b/src/gn/function_toolchain.cc
index b0fc5e1..b36b1e9 100644
--- a/src/gn/function_toolchain.cc
+++ b/src/gn/function_toolchain.cc
@@ -592,6 +592,20 @@
         (See --export-rust-project). It enables such metadata to include
         information about the Rust standard library.
 
+    dynamic_link_switch
+        Valid for: Rust tools which link
+
+        A switch to be optionally inserted into linker command lines
+        to indicate that subsequent items may be dynamically linked.
+        For ld-like linkers, -Clink-arg=-Bdynamic may be a good choice.
+        This switch is inserted by gn into rustc command lines before
+        listing any non-Rust dependencies. This may be necessary because
+        sometimes rustc puts the linker into a mode where it would otherwise
+        link against static libraries by default. This flag will be
+        inserted into the {{rustdeps}} variable at the appropriate place;
+        {{ldflags}} can't be used for the same purpose because the flags
+        may not be inserted at the desired place in the command line.
+
 )"  // String break to prevent overflowing the 16K max VC string length.
     R"(Expansions for tool variables
 
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index 06bce2f..694f332 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -363,7 +363,7 @@
   // that allows dynamic linking, as rustc may have previously put it into
   // static-only mode.
   if (nonrustdeps.size() > 0) {
-    out_ << " -Clink-arg=-Bdynamic";
+    out_ << " " << tool_->dynamic_link_switch();
   }
   for (const auto& nonrustdep : nonrustdeps) {
     out_ << " -Clink-arg=";
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index 41a9fdd..21b104f 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -896,7 +896,7 @@
         "  source_file_part = lib.rs\n"
         "  source_name_part = lib\n"
         "  externs =\n"
-        "  rustdeps = -Lnative=obj/foo -Clink-arg=-Bdynamic "
+        "  rustdeps = -Lnative=obj/foo -Clink-arg=-Balternative-dynamic "
         "-Clink-arg=obj/foo/libstatic.a\n"
         "  ldflags =\n"
         "  sources = ../../baz/lib.rs\n";
diff --git a/src/gn/rust_tool.cc b/src/gn/rust_tool.cc
index eaffb5b..5f2c2de 100644
--- a/src/gn/rust_tool.cc
+++ b/src/gn/rust_tool.cc
@@ -21,6 +21,7 @@
   set_lib_dir_switch("-Lnative=");
   set_lib_switch("-l");
   set_linker_arg("-Clink-arg=");
+  set_dynamic_link_switch("-Clink-arg=-Bdynamic");
 }
 
 RustTool::~RustTool() = default;
@@ -115,6 +116,13 @@
   if (!ReadString(scope, "rust_sysroot", &rust_sysroot_, err)) {
     return false;
   }
+
+  if (MayLink()) {
+    if (!ReadString(scope, "dynamic_link_switch", &dynamic_link_switch_, err)) {
+      return false;
+    }
+  }
+
   return true;
 }
 
diff --git a/src/gn/rust_tool.h b/src/gn/rust_tool.h
index 7b4f7ac..341c48f 100644
--- a/src/gn/rust_tool.h
+++ b/src/gn/rust_tool.h
@@ -44,8 +44,15 @@
 
   std::string_view GetSysroot() const;
 
+  const std::string& dynamic_link_switch() const { return dynamic_link_switch_; }
+  void set_dynamic_link_switch(std::string s) {
+    DCHECK(!complete_);
+    dynamic_link_switch_ = std::move(s);
+  }
+
  private:
   std::string rust_sysroot_;
+  std::string dynamic_link_switch_;
 
   bool SetOutputExtension(const Value* value, std::string* var, Err* err);
   bool ReadOutputsPatternList(Scope* scope,
diff --git a/src/gn/test_with_scope.cc b/src/gn/test_with_scope.cc
index 757dd34..638bf32 100644
--- a/src/gn/test_with_scope.cc
+++ b/src/gn/test_with_scope.cc
@@ -242,7 +242,7 @@
       "{{target_out_dir}}/{{source_name_part}}.o"));
   toolchain->SetTool(std::move(swift_tool));
 
-  // CDYLIB
+  // RUST CDYLIB
   std::unique_ptr<Tool> cdylib_tool = Tool::CreateTool(RustTool::kRsToolCDylib);
   SetCommandForTool(
       "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
@@ -255,7 +255,7 @@
       "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
   toolchain->SetTool(std::move(cdylib_tool));
 
-  // DYLIB
+  // RUST DYLIB
   std::unique_ptr<Tool> dylib_tool = Tool::CreateTool(RustTool::kRsToolDylib);
   SetCommandForTool(
       "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
@@ -295,7 +295,7 @@
       "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
   toolchain->SetTool(std::move(rlib_tool));
 
-  // STATICLIB
+  // RUST STATICLIB
   std::unique_ptr<Tool> staticlib_tool =
       Tool::CreateTool(RustTool::kRsToolStaticlib);
   SetCommandForTool(
@@ -307,6 +307,7 @@
   staticlib_tool->set_default_output_extension(".a");
   staticlib_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
+  static_cast<RustTool*>(staticlib_tool.get())->set_dynamic_link_switch("-Clink-arg=-Balternative-dynamic");
   toolchain->SetTool(std::move(staticlib_tool));
 
   toolchain->ToolchainSetupComplete();