diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 1e7616cd5e..ae45d5f22f 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -925,6 +925,22 @@ def _args_map_bin_dir(file): """ return "/".join(file.path.split("/", 3)[:3]) +def dlltool_path_from_linker_path(linker_path): + """Derives the path to `dlltool`, which MinGW ships next to the linker. + + Args: + linker_path (str): Path to the cc_toolchain's linker executable. + + Returns: + str: The derived path, or None if `linker_path` has no directory component. + """ + sep = max(linker_path.rfind("/"), linker_path.rfind("\\")) + if sep < 0: + return None + + suffix = linker_path[-4:].lower() + return "{}dlltool{}".format(linker_path[:sep + 1], suffix if suffix == ".exe" else "") + def construct_arguments( *, ctx, @@ -1291,6 +1307,17 @@ def construct_arguments( # If linker_type is not explicitly set, infer from which linker is actually being used ld_is_direct_driver = False + # gcc runs dlltool internally when acting as the linker, but rustc drives the linker directly so + # it needs dlltool's path: apply outside the link-emit gate below since rlib compiles need it too. + if cc_toolchain and toolchain.target_os == "windows" and toolchain.target_abi != "msvc": + linker_path = cc_common.get_tool_for_action( + feature_configuration = feature_configuration, + action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, + ) + dlltool_path = dlltool_path_from_linker_path(linker_path) + if dlltool_path: + rustc_flags.add(dlltool_path, format = "--codegen=dlltool=%s") + # Link! if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or add_flags_for_binary: # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc diff --git a/test/unit/windows_lib_name/windows_lib_name_test.bzl b/test/unit/windows_lib_name/windows_lib_name_test.bzl index 83d272649a..c65c55f9e4 100644 --- a/test/unit/windows_lib_name/windows_lib_name_test.bzl +++ b/test/unit/windows_lib_name/windows_lib_name_test.bzl @@ -1,9 +1,9 @@ -"""Analysistests for Windows-specific library naming and link flags.""" +"""Tests for Windows-specific library naming, link flags, and dlltool derivation.""" -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts", "unittest") # buildifier: disable=bzl-visibility -load("//rust/private:rustc.bzl", "portable_link_flags", "symlink_for_ambiguous_lib") +load("//rust/private:rustc.bzl", "dlltool_path_from_linker_path", "portable_link_flags", "symlink_for_ambiguous_lib") # buildifier: disable=bzl-visibility load("//rust/private:utils.bzl", "get_lib_name_default", "get_lib_name_for_windows") @@ -125,6 +125,30 @@ def _symlink_name_windows_msvc_test_impl(ctx): symlink_name_windows_msvc_test = analysistest.make(_symlink_name_windows_msvc_test_impl) +def _dlltool_path_from_linker_path_test_impl(ctx): + env = unittest.begin(ctx) + + # Cross-compiling from a Unix host (e.g. llvm-mingw or a Debian mingw-w64 package): + asserts.equals( + env, + "/usr/x86_64-w64-mingw32/bin/dlltool", + dlltool_path_from_linker_path("/usr/x86_64-w64-mingw32/bin/x86_64-w64-mingw32-gcc"), + ) + + # Windows-style linker path (native MinGW toolchain): + asserts.equals( + env, + "C:\\mingw64\\bin\\dlltool.exe", + dlltool_path_from_linker_path("C:\\mingw64\\bin\\GCC.EXE"), + ) + + # No directory component: nothing to derive from: + asserts.equals(env, None, dlltool_path_from_linker_path("gcc")) + + return unittest.end(env) + +dlltool_path_from_linker_path_test = unittest.make(_dlltool_path_from_linker_path_test_impl) + def _define_targets(): portable_link_flags_probe( name = "portable_link_flags_windows_gnu_probe", @@ -172,6 +196,9 @@ def windows_lib_name_test_suite(name): name = "symlink_name_windows_msvc_test", target_under_test = ":symlink_windows_msvc_probe", ) + dlltool_path_from_linker_path_test( + name = "dlltool_path_from_linker_path_test", + ) native.test_suite( name = name, @@ -180,5 +207,6 @@ def windows_lib_name_test_suite(name): ":portable_link_flags_windows_msvc_test", ":symlink_name_windows_gnu_test", ":symlink_name_windows_msvc_test", + ":dlltool_path_from_linker_path_test", ], )