diff --git a/rust/extensions.bzl b/rust/extensions.bzl index 06b1592ed3..c9fa69ec91 100644 --- a/rust/extensions.bzl +++ b/rust/extensions.bzl @@ -292,17 +292,19 @@ rust = module_extension( }, ) +_RUST_HOST_TOOLS_TAG_ATTRS = { + "name": attr.string( + doc = "The name of the module to create", + default = "rust_host_tools", + ), + "version": attr.string( + doc = "The version of Rust to use for tools executed on the Bazel host.", + default = rust_common.default_version, + ), +} | _COMMON_TAG_KWARGS + _RUST_HOST_TOOLS_TAG = tag_class( - attrs = { - "name": attr.string( - doc = "The name of the module to create", - default = "rust_host_tools", - ), - "version": attr.string( - doc = "The version of Rust to use for tools executed on the Bazel host.", - default = rust_common.default_version, - ), - } | _COMMON_TAG_KWARGS, + attrs = _RUST_HOST_TOOLS_TAG_ATTRS, ) # This is a separate module extension so that only the host tools are @@ -312,7 +314,7 @@ def _rust_host_tools_impl(module_ctx): for mod in module_ctx.modules: for host_tools in mod.tags.host_tools: - attrs = {key: getattr(host_tools, key) for key in dir(host_tools)} + attrs = {key: getattr(host_tools, key) for key in _RUST_HOST_TOOLS_TAG_ATTRS.keys()} # Label attrs must be stringified for the repository rule. for label_attr in ("allocator_library", "global_allocator_library"): diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index c8f6ecc9e6..a4e99b173b 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -2293,7 +2293,8 @@ def portable_link_flags( ambiguous_libs, get_lib_name, for_darwin = False, - flavor_msvc = False): + flavor_msvc = False, + link_static_library_by_path = False): """_summary_ Args: @@ -2303,6 +2304,8 @@ def portable_link_flags( get_lib_name (_type_): _description_ for_darwin (bool, optional): _description_. Defaults to False. flavor_msvc (bool, optional): _description_. Defaults to False. + link_static_library_by_path (bool, optional): Use the selected static artifact path for the + trailing `-Clink-arg` copy instead of `-l`. Defaults to False. Returns: _type_: _description_ @@ -2352,9 +2355,12 @@ def portable_link_flags( "-Clink-arg={}".format(artifact.basename), ] else: + # Rustc may restore dynamic linker mode before appending -Clink-arg + # values, so use the selected archive path where supported. + late_static_link_arg = artifact.path if link_static_library_by_path else "-l{}".format(get_lib_name(artifact)) return [ "-lstatic=%s" % get_lib_name(artifact), - "-Clink-arg=-l{}".format(get_lib_name(artifact)), + "-Clink-arg={}".format(late_static_link_arg), ] elif _is_dylib(lib): return [ @@ -2409,7 +2415,14 @@ def _make_link_flags_darwin(make_link_flags_args, use_direct_driver): ("-Clink-arg=%s%s" % (prefix, get_preferred_artifact(lib, use_pic).path)), ]) elif include_link_flags: - ret.extend(portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_darwin = True)) + ret.extend(portable_link_flags( + lib, + use_pic, + ambiguous_libs, + get_lib_name_default, + for_darwin = True, + link_static_library_by_path = True, + )) _add_user_link_flags(ret, linker_input) return ret @@ -2425,7 +2438,13 @@ def _make_link_flags_default(make_link_flags_args, use_direct_driver): ("-Clink-arg=%s--no-whole-archive" % prefix), ]) elif include_link_flags: - ret.extend(portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default)) + ret.extend(portable_link_flags( + lib, + use_pic, + ambiguous_libs, + get_lib_name_default, + link_static_library_by_path = True, + )) _add_user_link_flags(ret, linker_input) return ret 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 ba8f4166ff..a84fc5ef4b 100644 --- a/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl +++ b/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl @@ -30,7 +30,13 @@ def _dependency_linkopts_are_propagated_test_impl(ctx): # Expect a library's own linkopts to come after the flags we create to link them. # This is required, because linkopts are ordered and the linker will only apply later ones when resolving symbols required for earlier ones. # This means that if one of our transitive deps has a linkopt like `-lfoo`, the dep will see the symbols of foo at link time. - _assert_contains_in_order(env, link_action.argv, ["-lstatic=foo_with_linkopts", "-Clink-arg=-lfoo_with_linkopts", "--codegen=link-arg=-L/doesnotexist"]) + native_link_arg = _find_arg_with_prefix_and_suffix( + env, + link_action.argv, + "-Clink-arg=", + "/test/linker_inputs_propagation/libfoo_with_linkopts.a", + ) + _assert_contains_in_order(env, link_action.argv, ["-lstatic=foo_with_linkopts", native_link_arg, "--codegen=link-arg=-L/doesnotexist"]) return analysistest.end(env) def _assert_contains_input(env, inputs, name): @@ -47,6 +53,13 @@ def _assert_contains_in_order(env, haystack, needle): return unittest.fail(env, "Expected {} to contain {}".format(haystack, needle)) +def _find_arg_with_prefix_and_suffix(env, haystack, prefix, suffix): + for arg in haystack: + if arg.startswith(prefix) and arg.endswith(suffix): + return arg + unittest.fail(env, "Expected {} to contain an argument starting with {} and ending with {}".format(haystack, prefix, suffix)) + return None + def _get_lib_name(ctx, name): if ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo]): return name diff --git a/test/unit/native_deps/native_deps_test.bzl b/test/unit/native_deps/native_deps_test.bzl index 37755a21c7..1c1a7818e1 100644 --- a/test/unit/native_deps/native_deps_test.bzl +++ b/test/unit/native_deps/native_deps_test.bzl @@ -80,6 +80,20 @@ def _assert_bin_dir_structure(env, ctx, bin_dir, toolchain): "darwin component should start with 'darwin', got '{}'".format(darwin_component), ) +def _native_link_arg(ctx, action, pic_suffix = "", stripped = False): + toolchain = _get_toolchain(ctx) + if toolchain.target_os == "windows": + if toolchain.target_triple.abi == "msvc": + link_arg = "-Clink-arg=native_dep.lib" + else: + link_arg = "-Clink-arg=-lnative_dep.lib" + else: + link_arg = "-Clink-arg={}/test/unit/native_deps/libnative_dep{}.a".format( + _get_bin_dir_from_action(action), + pic_suffix, + ) + return link_arg.removeprefix("-Clink-arg=") if stripped else link_arg + def _rlib_has_no_native_libs_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) @@ -94,20 +108,12 @@ def _cdylib_has_native_libs_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) action = tut.actions[0] - toolchain = _get_toolchain(ctx) compilation_mode = ctx.var["COMPILATION_MODE"] pic_suffix = _get_pic_suffix(ctx, compilation_mode) assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") assert_argv_contains(env, action, "--crate-type=cdylib") assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix) - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains(env, action, _native_link_arg(ctx, action, pic_suffix)) assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -115,18 +121,10 @@ def _staticlib_has_native_libs_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) action = tut.actions[0] - toolchain = _get_toolchain(ctx) assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") assert_argv_contains(env, action, "--crate-type=staticlib") assert_argv_contains(env, action, "-lstatic=native_dep") - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep" - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains(env, action, _native_link_arg(ctx, action)) assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -134,20 +132,12 @@ def _proc_macro_has_native_libs_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) action = tut.actions[0] - toolchain = _get_toolchain(ctx) compilation_mode = ctx.var["COMPILATION_MODE"] pic_suffix = _get_pic_suffix(ctx, compilation_mode) assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") assert_argv_contains(env, action, "--crate-type=proc-macro") assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix) - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains(env, action, _native_link_arg(ctx, action, pic_suffix)) assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -155,17 +145,9 @@ def _bin_has_native_libs_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) action = tut.actions[0] - toolchain = _get_toolchain(ctx) assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") assert_argv_contains(env, action, "-lstatic=native_dep") - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep" - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains(env, action, _native_link_arg(ctx, action)) assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -197,13 +179,14 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): # Validate bin_dir structure (ignoring ST-{hash} suffix from config transitions) _assert_bin_dir_structure(env, ctx, bin_dir, toolchain) + native_link_arg = _native_link_arg(ctx, action, stripped = True) if toolchain.target_os in ["macos", "darwin"]: if use_cc_linker: # When using CC linker, args are passed with -Wl, prefix as separate arguments want = [ "-lstatic=native_dep", - "-lnative_dep", + native_link_arg, "-Wl,-force_load", "-Wl,{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), ] @@ -211,7 +194,7 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): # When using rust-lld directly, args are passed without prefix as separate arguments want = [ "-lstatic=native_dep", - "-lnative_dep", + native_link_arg, "-force_load", "{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), ] @@ -242,6 +225,7 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): elif toolchain.target_arch == "s390x": want = [ "-lstatic=native_dep", + native_link_arg, "link-arg=-Wl,--whole-archive", "link-arg={}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), "link-arg=-Wl,--no-whole-archive", @@ -249,7 +233,7 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): elif use_cc_linker: want = [ "-lstatic=native_dep", - "-lnative_dep", + native_link_arg, "-Wl,--whole-archive", "{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), "-Wl,--no-whole-archive", @@ -257,7 +241,7 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): else: want = [ "-lstatic=native_dep", - "-lnative_dep", + native_link_arg, "--whole-archive", "{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), "--no-whole-archive", @@ -280,13 +264,14 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): compilation_mode = ctx.var["COMPILATION_MODE"] pic_suffix = _get_pic_suffix(ctx, compilation_mode) + native_link_arg = _native_link_arg(ctx, action, pic_suffix, stripped = True) if toolchain.target_os in ["macos", "darwin"]: if use_cc_linker: # When using CC linker, args are passed with -Wl, prefix as separate arguments want = [ "-lstatic=native_dep{}".format(pic_suffix), - "-lnative_dep{}".format(pic_suffix), + native_link_arg, "-Wl,-force_load", "-Wl,{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), ] @@ -294,7 +279,7 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): # When using rust-lld directly, args are passed without prefix as separate arguments want = [ "-lstatic=native_dep{}".format(pic_suffix), - "-lnative_dep{}".format(pic_suffix), + native_link_arg, "-force_load", "{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), ] @@ -324,6 +309,7 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): elif toolchain.target_arch == "s390x": want = [ "-lstatic=native_dep{}".format(pic_suffix), + native_link_arg, "link-arg=-Wl,--whole-archive", "link-arg={}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), "link-arg=-Wl,--no-whole-archive", @@ -332,7 +318,7 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): # CC linker uses -Wl, prefix but arguments are separate want = [ "-lstatic=native_dep{}".format(pic_suffix), - "-lnative_dep{}".format(pic_suffix), + native_link_arg, "-Wl,--whole-archive", "{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), "-Wl,--no-whole-archive", @@ -341,7 +327,7 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): # rust-lld doesn't use -Wl, prefix, so flags and path are separate want = [ "-lstatic=native_dep{}".format(pic_suffix), - "-lnative_dep{}".format(pic_suffix), + native_link_arg, "--whole-archive", "{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), "--no-whole-archive", diff --git a/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl b/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl index f246e1ddc0..988661c450 100644 --- a/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl +++ b/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl @@ -35,7 +35,11 @@ def _proc_macro_does_not_leak_deps_impl(ctx): else: native_deps = [arg for arg in rustc_action.argv if arg == "-Clink-arg=-lnative.lib"] else: - native_deps = [arg for arg in rustc_action.argv if arg == "-Clink-arg=-lnative"] + native_deps = [ + arg + for arg in rustc_action.argv + if arg.startswith("-Clink-arg=") and arg.endswith("/test/unit/proc_macro/leaks_deps/native/libnative.a") + ] asserts.equals(env, 1, len(native_deps)) return analysistest.end(env)