Always link dependencies into Rust static libraries
Rust handling of static libraries differs from C/C++ where we always
need to link all dependencies, but they are not considered final.
Change-Id: I843b0d0b9db435560b5b6da75f0696852a11ace8
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/10500
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Petr Hosek <phosek@google.com>
diff --git a/src/gn/ninja_binary_target_writer.cc b/src/gn/ninja_binary_target_writer.cc
index 08e8c93..e4b0652 100644
--- a/src/gn/ninja_binary_target_writer.cc
+++ b/src/gn/ninja_binary_target_writer.cc
@@ -159,14 +159,22 @@
if (can_link_libs && dep->swift_values().builds_module())
classified_deps->swiftmodule_deps.push_back(dep);
- if (dep->output_type() == Target::SOURCE_SET ||
- // If a complete static library depends on an incomplete static library,
- // manually link in the object files of the dependent library as if it
- // were a source set. This avoids problems with braindead tools such as
- // ar which don't properly link dependent static libraries.
- (target_->complete_static_lib() &&
- (dep->output_type() == Target::STATIC_LIBRARY &&
- !dep->complete_static_lib()))) {
+ if (target_->source_types_used().RustSourceUsed() &&
+ (target_->output_type() == Target::RUST_LIBRARY ||
+ target_->output_type() == Target::STATIC_LIBRARY) &&
+ dep->IsLinkable()) {
+ // Rust libraries and static libraries aren't final, but need to have the
+ // link lines of all transitive deps specified.
+ classified_deps->linkable_deps.push_back(dep);
+ } else if (dep->output_type() == Target::SOURCE_SET ||
+ // If a complete static library depends on an incomplete static
+ // library, manually link in the object files of the dependent
+ // library as if it were a source set. This avoids problems with
+ // braindead tools such as ar which don't properly link dependent
+ // static libraries.
+ (target_->complete_static_lib() &&
+ (dep->output_type() == Target::STATIC_LIBRARY &&
+ !dep->complete_static_lib()))) {
// Source sets have their object files linked into final targets
// (shared libraries, executables, loadable modules, and complete static
// libraries). Intermediate static libraries and other source sets
@@ -182,11 +190,6 @@
// can be complete. Otherwise, these will be skipped since this target
// will depend only on the source set's object files.
classified_deps->non_linkable_deps.push_back(dep);
- } else if (target_->output_type() == Target::RUST_LIBRARY &&
- dep->IsLinkable()) {
- // Rust libraries aren't final, but need to have the link lines of all
- // transitive deps specified.
- classified_deps->linkable_deps.push_back(dep);
} else if (target_->complete_static_lib() && dep->IsFinal()) {
classified_deps->non_linkable_deps.push_back(dep);
} else if (can_link_libs && dep->IsLinkable()) {
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index 34d87f0..e58572b 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -351,6 +351,14 @@
Err err;
TestWithScope setup;
+ Target staticlib(setup.settings(), Label(SourceDir("//foo/"), "static"));
+ staticlib.set_output_type(Target::STATIC_LIBRARY);
+ staticlib.visibility().SetPublic();
+ staticlib.sources().push_back(SourceFile("//foo/static.cpp"));
+ staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(staticlib.OnResolved(&err));
+
Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
rlib.set_output_type(Target::RUST_LIBRARY);
rlib.visibility().SetPublic();
@@ -363,14 +371,6 @@
rlib.SetToolchain(setup.toolchain());
ASSERT_TRUE(rlib.OnResolved(&err));
- Target staticlib(setup.settings(), Label(SourceDir("//foo/"), "static"));
- staticlib.set_output_type(Target::STATIC_LIBRARY);
- staticlib.visibility().SetPublic();
- staticlib.sources().push_back(SourceFile("//foo/static.cpp"));
- staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
- staticlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(staticlib.OnResolved(&err));
-
Target sharedlib(setup.settings(), Label(SourceDir("//foo/"), "shared"));
sharedlib.set_output_type(Target::SHARED_LIBRARY);
sharedlib.visibility().SetPublic();
@@ -482,6 +482,43 @@
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
+
+ Target rstaticlib(setup.settings(), Label(SourceDir("//baz/"), "baz"));
+ rstaticlib.set_output_type(Target::STATIC_LIBRARY);
+ rstaticlib.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ rstaticlib.sources().push_back(bazlib);
+ rstaticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rstaticlib.rust_values().set_crate_root(bazlib);
+ rstaticlib.rust_values().crate_name() = "baz";
+ rstaticlib.private_deps().push_back(LabelTargetPair(&staticlib));
+ rstaticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rstaticlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&rstaticlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = baz\n"
+ "crate_type = staticlib\n"
+ "output_extension = .a\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/baz\n"
+ "target_output_name = libbaz\n"
+ "\n"
+ "build obj/baz/libbaz.a: rust_staticlib ../../baz/lib.rs | ../../baz/lib.rs "
+ "obj/foo/libstatic.a\n"
+ " externs =\n"
+ " rustdeps = -Lnative=obj/foo -lstatic\n"
+ " sources = ../../baz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
}
TEST_F(NinjaRustBinaryTargetWriterTest, RustOutputExtensionAndDir) {