diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 1ad6a5f5be..d9f72bacaa 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -2245,20 +2245,24 @@ def _is_dylib(dep): """ return not bool(dep.static_library or dep.pic_static_library) -def _collect_nonstatic_linker_inputs(cc_info): - shared_linker_inputs = [] +def _collect_nonstatic_linker_inputs(cc_info, include_final_link_requirements): + nonstatic_linker_inputs = [] for linker_input in cc_info.linking_context.linker_inputs.to_list(): dylibs = [ lib for lib in linker_input.libraries if _is_dylib(lib) ] - if dylibs: - shared_linker_inputs.append(cc_common.create_linker_input( + user_link_flags = linker_input.user_link_flags if include_final_link_requirements else [] + additional_inputs = linker_input.additional_inputs if include_final_link_requirements else [] + if dylibs or user_link_flags or additional_inputs: + nonstatic_linker_inputs.append(cc_common.create_linker_input( owner = linker_input.owner, libraries = depset(dylibs), + user_link_flags = depset(user_link_flags), + additional_inputs = depset(additional_inputs), )) - return shared_linker_inputs + return nonstatic_linker_inputs def _add_lto_flags(ctx, toolchain, args, crate): """Adds flags to an Args object to configure LTO for 'rustc'. @@ -2392,13 +2396,16 @@ def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_co # Flattening is okay since crate_info.deps only records direct deps. for dep in crate_info.deps.to_list(): if dep.cc_info: - # A Rust staticlib or shared library doesn't need to propagate linker inputs - # of its dependencies, except for shared libraries. + # Static dependencies are bundled into both crate types. Shared libraries + # remain final-link dependencies, as do a staticlib's user link flags. if crate_info.type in ["cdylib", "staticlib"]: - shared_linker_inputs = _collect_nonstatic_linker_inputs(dep.cc_info) - if shared_linker_inputs: + nonstatic_linker_inputs = _collect_nonstatic_linker_inputs( + dep.cc_info, + include_final_link_requirements = crate_info.type == "staticlib", + ) + if nonstatic_linker_inputs: linking_context = cc_common.create_linking_context( - linker_inputs = depset(shared_linker_inputs), + linker_inputs = depset(nonstatic_linker_inputs), ) cc_infos.append(CcInfo(linking_context = linking_context)) else: diff --git a/test/linker_inputs_propagation/BUILD.bazel b/test/linker_inputs_propagation/BUILD.bazel index e088e19285..c619d3b5c1 100644 --- a/test/linker_inputs_propagation/BUILD.bazel +++ b/test/linker_inputs_propagation/BUILD.bazel @@ -29,6 +29,7 @@ cc_library( "foo_shared.cc", ], hdrs = ["foo_shared.h"], + additional_linker_inputs = ["empty.so"], linkopts = ["-L/doesnotexist"], ) @@ -91,6 +92,13 @@ rust_library( deps = [":foo_with_linkopts"], ) +rust_static_library( + name = "staticlib_uses_foo_with_linkopts", + srcs = ["bar_uses_shared_foo.rs"], + edition = "2018", + deps = [":foo_with_linkopts"], +) + rust_shared_library( name = "sharedlib_uses_foo", srcs = ["bar_uses_foo.rs"], @@ -168,3 +176,32 @@ rust_binary( }), deps = [":rlib_uses_foo_with_redundant_linkopts"], ) + +cc_binary( + name = "depends_on_foo_with_linkopts_via_staticlib", + srcs = ["baz.cc"], + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), + deps = [":staticlib_uses_foo_with_linkopts"], +) + +cc_library( + name = "resolver_linkopts", + linkopts = ["-lresolv"], +) + +rust_static_library( + name = "staticlib_uses_resolver_linkopts", + srcs = ["resolver.rs"], + edition = "2021", + link_deps = [":resolver_linkopts"], +) + +cc_test( + name = "depends_on_resolver_linkopts_via_staticlib", + srcs = ["resolver.cc"], + target_compatible_with = ["@platforms//os:linux"], + deps = [":staticlib_uses_resolver_linkopts"], +) diff --git a/test/linker_inputs_propagation/resolver.cc b/test/linker_inputs_propagation/resolver.cc new file mode 100644 index 0000000000..ba0874d140 --- /dev/null +++ b/test/linker_inputs_propagation/resolver.cc @@ -0,0 +1,9 @@ +#include +#include + +extern "C" const void* resolver_symbol(); + +int main() { + assert(resolver_symbol() != nullptr); + return EXIT_SUCCESS; +} diff --git a/test/linker_inputs_propagation/resolver.rs b/test/linker_inputs_propagation/resolver.rs new file mode 100644 index 0000000000..b078d81823 --- /dev/null +++ b/test/linker_inputs_propagation/resolver.rs @@ -0,0 +1,10 @@ +use std::ffi::c_void; + +extern "C" { + fn ns_initparse(message: *const u8, message_length: i32, handle: *mut c_void) -> i32; +} + +#[no_mangle] +pub extern "C" fn resolver_symbol() -> *const c_void { + ns_initparse as *const c_void +} diff --git a/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl b/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl index f46bee6b0d..011f5d1ec6 100644 --- a/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl +++ b/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl @@ -18,7 +18,7 @@ def _static_lib_is_not_propagated_test_impl(ctx): link_action = [action for action in tut.actions if action.mnemonic == "CppLink"][0] lib_name = _get_lib_name(ctx, name = "foo") - asserts.false(env, _assert_contains_input(env, link_action.inputs, lib_name)) + asserts.false(env, _contains_input(link_action.inputs, lib_name)) return analysistest.end(env) @@ -39,6 +39,20 @@ def _dependency_linkopts_are_propagated_test_impl(ctx): ]) return analysistest.end(env) +def _staticlib_dependency_nonstatic_inputs_are_propagated_test_impl(ctx): + env = analysistest.begin(ctx) + tut = analysistest.target_under_test(env) + link_action = [action for action in tut.actions if action.mnemonic == "CppLink"][0] + + _assert_contains_in_order(env, link_action.argv, ["-L/doesnotexist"]) + _assert_contains_input(env, link_action.inputs, "empty.so") + + # The dependency's static library is already bundled into the Rust staticlib. + lib_name = _get_lib_name(ctx, name = "foo_with_linkopts") + asserts.false(env, _contains_input(link_action.inputs, lib_name)) + + return analysistest.end(env) + def _get_pic_suffix(ctx): # cc_library only produces .pic.a artifacts on linux-ish platforms in opt mode # (mac/win produce a single variant regardless of mode). Mirrors the same logic @@ -50,12 +64,17 @@ def _get_pic_suffix(ctx): return ".pic" if ctx.var["COMPILATION_MODE"] == "opt" else "" def _assert_contains_input(env, inputs, name): + if _contains_input(inputs, name): + return + unittest.fail(env, "Expected {} to contain a library starting with {}".format(inputs.to_list(), name)) + +def _contains_input(inputs, name): for input in inputs.to_list(): # We cannot check for name equality because rlib outputs contain # a hash in their name. if input.basename.startswith(name): - return - unittest.fail(env, "Expected {} to contain a library starting with {}".format(inputs.to_list(), name)) + return True + return False def _assert_contains_in_order(env, haystack, needle): for i in range(len(haystack)): @@ -92,6 +111,13 @@ dependency_linkopts_are_propagated_test = analysistest.make( }, ) +staticlib_dependency_nonstatic_inputs_are_propagated_test = analysistest.make( + _staticlib_dependency_nonstatic_inputs_are_propagated_test_impl, + attrs = { + "_windows_constraint": attr.label(default = Label("@platforms//os:windows")), + }, +) + def _linker_inputs_propagation_test(): static_lib_is_not_propagated_test( name = "depends_on_foo_via_staticlib", @@ -118,6 +144,11 @@ def _linker_inputs_propagation_test(): target_under_test = "//test/linker_inputs_propagation:depends_on_foo_with_redundant_linkopts", ) + staticlib_dependency_nonstatic_inputs_are_propagated_test( + name = "staticlib_dependency_nonstatic_inputs_are_propagated", + target_under_test = "//test/linker_inputs_propagation:depends_on_foo_with_linkopts_via_staticlib", + ) + def linker_inputs_propagation_test_suite(name): """Entry-point macro called from the BUILD file. @@ -134,5 +165,6 @@ def linker_inputs_propagation_test_suite(name): ":depends_on_foo_via_sharedlib", ":depends_on_shared_foo_via_sharedlib", ":dependency_linkopts_are_propagated", + ":staticlib_dependency_nonstatic_inputs_are_propagated", ], )