Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
Expand Down Expand Up @@ -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:
Expand Down
37 changes: 37 additions & 0 deletions test/linker_inputs_propagation/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ cc_library(
"foo_shared.cc",
],
hdrs = ["foo_shared.h"],
additional_linker_inputs = ["empty.so"],
linkopts = ["-L/doesnotexist"],
)

Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -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"],
)
9 changes: 9 additions & 0 deletions test/linker_inputs_propagation/resolver.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <assert.h>
#include <stdlib.h>

extern "C" const void* resolver_symbol();

int main() {
assert(resolver_symbol() != nullptr);
return EXIT_SUCCESS;
}
10 changes: 10 additions & 0 deletions test/linker_inputs_propagation/resolver.rs
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
Expand All @@ -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)):
Expand Down Expand Up @@ -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",
Expand All @@ -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.

Expand All @@ -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",
],
)
Loading