Include library search paths when compiling rlibs

in the command line.

Bug: 340
Change-Id: I5ae0ad38b5ac26ae8474ae2ba47334491a0d304a
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/15640
Reviewed-by: David Turner <digit@google.com>
Reviewed-by: Dana Jansens <danakj@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: danakj <danakj@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md
index 310d7e8..02295b7 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -5650,6 +5650,10 @@
   of targets, and public_configs are always propagated across public deps of
   all types of targets.
 
+  For Rust targets, deps ensures that Rust code can refer to the dependency
+  target. If the dependency is a C/C++ target, the path to that target will
+  be made available to Rust for `#[link]` directives.
+
   Data dependencies are propagated differently. See "gn help data_deps" and
   "gn help runtime_deps".
 
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index 554f88c..21ae6d0 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -349,20 +349,23 @@
     path_output_.WriteDir(out_, dir, PathOutput::DIR_NO_LAST_SLASH);
   }
 
+  UniqueVector<SourceDir> nonrustdep_dirs;
+
+  // Non-Rust native dependencies. A dependency from Rust implies the ability
+  // to specify it in #[link], and GN will ensure that rustc can find it by
+  // adding it to the native library search paths.
+  for (const auto& nonrustdep : nonrustdeps) {
+    nonrustdep_dirs.push_back(
+        nonrustdep.AsSourceFile(settings_->build_settings()).GetDir());
+  }
+  for (const auto& nonrustdep_dir : nonrustdep_dirs) {
+    out_ << " -Lnative=";
+    path_output_.WriteDir(out_, nonrustdep_dir, PathOutput::DIR_NO_LAST_SLASH);
+  }
+
+  // If rustc will invoke a linker, then pass linker arguments to include those
+  // non-Rust native dependencies in the linking step.
   if (target_is_final) {
-    // Non-Rust native dependencies.
-    UniqueVector<SourceDir> nonrustdep_dirs;
-    for (const auto& nonrustdep : nonrustdeps) {
-      nonrustdep_dirs.push_back(
-          nonrustdep.AsSourceFile(settings_->build_settings()).GetDir());
-    }
-    // First -Lnative to specify the search directories.
-    // This is necessary for #[link(...)] directives to work properly.
-    for (const auto& nonrustdep_dir : nonrustdep_dirs) {
-      out_ << " -Lnative=";
-      path_output_.WriteDir(out_, nonrustdep_dir,
-                            PathOutput::DIR_NO_LAST_SLASH);
-    }
     // Before outputting any libraries to link, ensure the linker is in a mode
     // that allows dynamic linking, as rustc may have previously put it into
     // static-only mode.
@@ -373,11 +376,22 @@
       out_ << " -Clink-arg=";
       path_output_.WriteFile(out_, nonrustdep);
     }
-    WriteLibrarySearchPath(out_, tool_);
+  }
+
+  // Library search paths are required to find system libraries named in #[link]
+  // directives, which will not be specified in non-Rust native dependencies.
+  WriteLibrarySearchPath(out_, tool_);
+  // If rustc will invoke a linker, all libraries need the passed through to the
+  // linker.
+  if (target_is_final) {
     WriteLibs(out_, tool_);
   }
   out_ << std::endl;
   out_ << "  ldflags =";
-  WriteCustomLinkerFlags(out_, tool_);
+  // If rustc will invoke a linker, linker flags need to be forwarded through to
+  // the linker.
+  if (target_is_final) {
+    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 5eda4a7..deb2bb6 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 =\n"
+        "  rustdeps = -Lnative=obj/foo\n"
         "  ldflags =\n"
         "  sources = ../../baz/lib.rs\n";
     std::string out_str = out.str();
@@ -1227,6 +1227,14 @@
   public_rlib.SetToolchain(setup.toolchain());
   ASSERT_TRUE(public_rlib.OnResolved(&err));
 
+  Target staticlib(setup.settings(), Label(SourceDir("//clib/"), "static"));
+  staticlib.set_output_type(Target::STATIC_LIBRARY);
+  staticlib.visibility().SetPublic();
+  staticlib.sources().push_back(SourceFile("//foo/clib.cpp"));
+  staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+  staticlib.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(staticlib.OnResolved(&err));
+
   Target rlib(setup.settings(), Label(SourceDir("//foo/"), "rlibcrate"));
   rlib.set_output_type(Target::RUST_LIBRARY);
   rlib.visibility().SetPublic();
@@ -1243,18 +1251,31 @@
   // `rustdeps` for a `rust_rlib`, though they would for a `rust_bin` (as tested
   // for above).
   //
-  // 1. A dependency on an archive file directly as happens with a `deps` rule
-  //    pointing to a `static_library` target.
+  // 1. A dependency on an archive file directly as happens with a `libs` rule,
+  //    requesting a system library. The path to that library must be specified
+  //    separately with `-L` in ldflags, the library does not appear in the
+  //    rustc compilation of an rlib.
   rlib.config_values().libs().push_back(
       LibFile(SourceFile("//dir1/ar.a")));
-  // 2. A dependency on a library name as happens with a `libs` rule.
+  // 2. A dependency on a library name as happens with a `libs` rule. Libraries
+  //    need only be named when linking, they do not need to appear in an rlib
+  //    compilation.
   rlib.config_values().libs().push_back(LibFile("quux"));
   // 3. A dependency on a library path which will be used for linking, which is
-  //    separate from the dependency paths for finding Rust crates.
+  //    separate from the dependency paths for finding Rust crates. But it may
+  //    be needed to resolve the path to a native library in a #[link]
+  //    directive.
   rlib.config_values().lib_dirs().push_back(SourceDir("//baz/"));
   // 4. A framework search directory will be used for linking frameworks, which
-  //    is also separate from finding Rust crates.
+  //    is also separate from finding Rust crates. Again a #[link] directive can
+  //    point to a framework, so these paths need to be present during
+  //    compilation
   rlib.config_values().framework_dirs().push_back(SourceDir("//fwdir/"));
+  // 5. A dependency on a C library through a `deps` rule, which points to a
+  //    `static_library` target. GN guarantees that Rust can refer to that
+  //    library through #[link] without having to specify the path in ldflags
+  //    as well.
+  rlib.private_deps().push_back(LabelTargetPair(&staticlib));
 
   ASSERT_TRUE(rlib.OnResolved(&err));
 
@@ -1274,11 +1295,12 @@
         "target_out_dir = obj/foo\n"
         "target_output_name = librlibcrate\n"
         "\n"
-        "build obj/foo/librlibcrate.rlib: rust_rlib ../../foo/input.rs | ../../foo/input.rs obj/bar/libpubliclib.rlib\n"
+        "build obj/foo/librlibcrate.rlib: rust_rlib ../../foo/input.rs | ../../foo/input.rs obj/bar/libpubliclib.rlib obj/clib/libstatic.a\n"
         "  source_file_part = input.rs\n"
         "  source_name_part = input\n"
         "  externs = --extern publiccrate=obj/bar/libpubliclib.rlib\n"
-        "  rustdeps = -Ldependency=obj/bar\n"
+        "  rustdeps = -Ldependency=obj/bar -Lnative=obj/clib "
+"-Lnative=../../baz -Lframework=../../fwdir\n"
         "  ldflags =\n"
         "  sources = ../../foo/input.rs\n";
     std::string out_str = out.str();