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();