Link Rust binaries against transitive public_deps An earlier commit, 646a62e, changed how Rust links against native dependencies. Rather than a `-l...` argument to instruct rustc to link against the specified library, GN uses `-Clink-arg=` to bypass rustc and directly instruct the linker to link the native library. One implication of this change is that Rust binaries no longer link against native libraries that they transitively depend on through a Rust library dependency's public dependency. More concretely, say Rust binary A depends on Rust library B, which depends on native library C, which also declares a public_dep on D. B is therefore allowed to use D, and must link against it. However, B isn't actually linked -- A is the binary that gets linked. If A uses a method from B that uses a method from D, A must link against D. After the previously mentioned change, A no longer links against D. If B were to explicitly depend on D (rather than depend on it through C's public_deps), this would work. This commit fixes the issue by ensuring that public dependencies on shared libraries are propagated up through the list of Rust transitive library dependencies, allowing the final Rust binary to link against all necessary native libraries. Change-Id: I374167db1b9338f42426f1cbd6f15dd535358d94 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/12001 Reviewed-by: Tyler Mandry <tmandry@google.com> Commit-Queue: Tyler Mandry <tmandry@google.com>
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc index 8d3f07e..b03b2cb 100644 --- a/src/gn/ninja_rust_binary_target_writer_unittest.cc +++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -990,3 +990,78 @@ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str; } } + +TEST_F(NinjaRustBinaryTargetWriterTest, TransitivePublicNonRustDeps) { + Err err; + TestWithScope setup; + + // This test verifies that the Rust binary "target" links against this lib. + Target implicitlib(setup.settings(), Label(SourceDir("//foo/"), "implicit")); + implicitlib.set_output_type(Target::SHARED_LIBRARY); + implicitlib.visibility().SetPublic(); + implicitlib.sources().push_back(SourceFile("//foo/implicit.cpp")); + implicitlib.source_types_used().Set(SourceFile::SOURCE_CPP); + implicitlib.SetToolchain(setup.toolchain()); + ASSERT_TRUE(implicitlib.OnResolved(&err)); + + Target sharedlib(setup.settings(), Label(SourceDir("//foo/"), "shared")); + sharedlib.set_output_type(Target::SHARED_LIBRARY); + sharedlib.visibility().SetPublic(); + sharedlib.sources().push_back(SourceFile("//foo/shared.cpp")); + sharedlib.source_types_used().Set(SourceFile::SOURCE_CPP); + sharedlib.SetToolchain(setup.toolchain()); + sharedlib.public_deps().push_back(LabelTargetPair(&implicitlib)); + ASSERT_TRUE(sharedlib.OnResolved(&err)); + + Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib")); + rlib.set_output_type(Target::RUST_LIBRARY); + rlib.visibility().SetPublic(); + SourceFile barlib("//bar/lib.rs"); + rlib.sources().push_back(SourceFile("//bar/mylib.rs")); + rlib.sources().push_back(barlib); + rlib.source_types_used().Set(SourceFile::SOURCE_RS); + rlib.rust_values().set_crate_root(barlib); + rlib.rust_values().crate_name() = "mylib"; + rlib.SetToolchain(setup.toolchain()); + rlib.private_deps().push_back(LabelTargetPair(&sharedlib)); + ASSERT_TRUE(rlib.OnResolved(&err)); + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::EXECUTABLE); + target.visibility().SetPublic(); + SourceFile main("//foo/main.rs"); + target.sources().push_back(main); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(main); + target.rust_values().crate_name() = "foo_bar"; + target.private_deps().push_back(LabelTargetPair(&rlib)); + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); + + { + std::ostringstream out; + NinjaRustBinaryTargetWriter writer(&target, out); + writer.Run(); + + const char expected[] = + "crate_name = foo_bar\n" + "crate_type = bin\n" + "output_extension = \n" + "output_dir = \n" + "rustflags =\n" + "rustenv =\n" + "root_out_dir = .\n" + "target_out_dir = obj/foo\n" + "target_output_name = bar\n" + "\n" + "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/main.rs " + "obj/bar/libmylib.rlib ./libshared.so ./libimplicit.so\n" + " externs = --extern mylib=obj/bar/libmylib.rlib\n" + " rustdeps = -Ldependency=obj/bar -Lnative=. -Clink-arg=-Bdynamic " + "-Clink-arg=./libshared.so -Clink-arg=./libimplicit.so\n" + " ldflags =\n" + " sources = ../../foo/main.rs\n"; + std::string out_str = out.str(); + EXPECT_EQ(expected, out_str) << expected << "\n" << out_str; + } +}
diff --git a/src/gn/target.cc b/src/gn/target.cc index bd3fb58..5600f46 100644 --- a/src/gn/target.cc +++ b/src/gn/target.cc
@@ -656,6 +656,16 @@ inherited_libraries_.Append(dep, is_public); } + // Propagate public dependent libraries. + for (const auto& transitive : + dep->rust_values().transitive_libs().GetOrderedAndPublicFlag()) { + if (transitive.second && + (dep->output_type() == STATIC_LIBRARY || + dep->output_type() == SHARED_LIBRARY)) { + rust_values().transitive_libs().Append(transitive.first, is_public); + } + } + if (dep->output_type() == RUST_LIBRARY) { rust_values().transitive_libs().Append(dep, is_public); rust_values().transitive_libs().AppendInherited(