From 2b3389f5e224aefbc8a903b20cfcc97127262595 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Tue, 23 Dec 2025 08:58:04 -0500 Subject: [PATCH] Mingw fixes --- rust/platform/triple_mappings.bzl | 14 +- rust/private/repository_utils.bzl | 2 +- rust/private/rustc.bzl | 51 +++-- test/unit/windows_lib_name/BUILD.bazel | 3 + .../windows_lib_name_test.bzl | 185 ++++++++++++++++++ test/unit/windows_stdlib/BUILD.bazel | 3 + .../windows_stdlib/windows_stdlib_test.bzl | 122 ++++++++++++ 7 files changed, 350 insertions(+), 30 deletions(-) create mode 100644 test/unit/windows_lib_name/BUILD.bazel create mode 100644 test/unit/windows_lib_name/windows_lib_name_test.bzl create mode 100644 test/unit/windows_stdlib/BUILD.bazel create mode 100644 test/unit/windows_stdlib/windows_stdlib_test.bzl diff --git a/rust/platform/triple_mappings.bzl b/rust/platform/triple_mappings.bzl index 6070591e6b..85c8e36612 100644 --- a/rust/platform/triple_mappings.bzl +++ b/rust/platform/triple_mappings.bzl @@ -279,7 +279,12 @@ _SYSTEM_TO_STDLIB_LINKFLAGS = { "wasi": [], "wasip1": [], "wasip2": [], - "windows": ["advapi32.lib", "ws2_32.lib", "userenv.lib", "Bcrypt.lib"], + "windows": { + # see https://github.com/rust-lang/rust/blob/c4aa646f15e40bd3e64ddb5017b7b89b3646ac99/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs#L14-L23 + "gnu": ["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"], + "gnullvm": ["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"], + "msvc": ["advapi32.lib", "ws2_32.lib", "userenv.lib", "Bcrypt.lib"], + }, } def cpu_arch_to_constraints(cpu_arch, *, system = None): @@ -404,8 +409,11 @@ def system_to_staticlib_ext(system): def system_to_binary_ext(system): return _SYSTEM_TO_BINARY_EXT[system] -def system_to_stdlib_linkflags(system): - return _SYSTEM_TO_STDLIB_LINKFLAGS[system] +def system_to_stdlib_linkflags(target_triple): + val = _SYSTEM_TO_STDLIB_LINKFLAGS[target_triple.system] + if type(val) == "list": + return val + return val[target_triple.abi] def triple_to_constraint_set(target_triple): """Returns a set of constraints for a given platform triple diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl index 05b741947d..5547269d1c 100644 --- a/rust/private/repository_utils.bzl +++ b/rust/private/repository_utils.bzl @@ -393,7 +393,7 @@ def BUILD_for_rust_toolchain( str: A rendered template of a `rust_toolchain` declaration """ if stdlib_linkflags == None: - stdlib_linkflags = ", ".join(['"%s"' % x for x in system_to_stdlib_linkflags(target_triple.system)]) + stdlib_linkflags = ", ".join(['"%s"' % x for x in system_to_stdlib_linkflags(target_triple)]) rustfmt_label = None if include_rustfmt: diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index a28ad50b78..ac8d93a9e5 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -560,9 +560,9 @@ def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib): # Take the absolute value of hash() since it could be negative. path_hash = abs(hash(lib.path)) - lib_name = get_lib_name_for_windows(lib) if toolchain.target_os.startswith("windows") else get_lib_name_default(lib) + lib_name = get_lib_name_for_windows(lib) if toolchain.target_abi == "msvc" else get_lib_name_default(lib) - if toolchain.target_os.startswith("windows"): + if toolchain.target_abi == "msvc": prefix = "" extension = ".lib" elif lib_name.endswith(".pic"): @@ -1495,7 +1495,7 @@ def rustc_compile_action( pdb_file = None dsym_folder = None if crate_info.type in ("cdylib", "bin") and not experimental_use_cc_common_link: - if toolchain.target_os == "windows" and compilation_mode.strip_level == "none": + if toolchain.target_abi == "msvc" and compilation_mode.strip_level == "none": pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output) action_outputs.append(pdb_file) elif toolchain.target_os in ["macos", "darwin"]: @@ -2200,7 +2200,7 @@ def _get_crate_dirname(crate): """ return crate.output.dirname -def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows = False, for_darwin = False, flavor_msvc = False): +def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_darwin = False, flavor_msvc = False): artifact = get_preferred_artifact(lib, use_pic) if ambiguous_libs and artifact.path in ambiguous_libs: artifact = ambiguous_libs[artifact.path] @@ -2240,17 +2240,11 @@ def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows ): return [] if for_darwin else ["-lstatic=%s" % get_lib_name(artifact)] - if for_windows: - if flavor_msvc: - return [ - "-lstatic=%s" % get_lib_name(artifact), - "-Clink-arg={}".format(artifact.basename), - ] - else: - return [ - "-lstatic=%s" % get_lib_name(artifact), - "-Clink-arg=-l{}".format(artifact.basename), - ] + if flavor_msvc: + return [ + "-lstatic=%s" % get_lib_name(artifact), + "-Clink-arg={}".format(artifact.basename), + ] else: return [ "-lstatic=%s" % get_lib_name(artifact), @@ -2281,7 +2275,8 @@ def _make_link_flags_windows(make_link_flags_args, flavor_msvc, use_direct_drive ("-Clink-arg=%s--no-whole-archive" % prefix), ]) elif include_link_flags: - ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True, flavor_msvc = flavor_msvc)) + get_lib_name = get_lib_name_for_windows if flavor_msvc else get_lib_name_default + ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, flavor_msvc = flavor_msvc)) _add_user_link_flags(ret, linker_input) return ret @@ -2361,19 +2356,19 @@ def _get_make_link_flag_funcs(target_os, target_abi, use_direct_link_driver): - callable: The function for producing link args. - callable: The function for formatting link library names. """ + + get_lib_name = get_lib_name_default + if target_os == "windows": - make_link_flags_windows_msvc = _make_link_flags_windows_msvc_direct if use_direct_link_driver else _make_link_flags_windows_msvc_indirect - make_link_flags_windows_gnu = _make_link_flags_windows_gnu_direct if use_direct_link_driver else _make_link_flags_windows_gnu_indirect - make_link_flags = make_link_flags_windows_msvc if target_abi == "msvc" else make_link_flags_windows_gnu - get_lib_name = get_lib_name_for_windows + if target_abi == "msvc": + make_link_flags = _make_link_flags_windows_msvc_direct if use_direct_link_driver else _make_link_flags_windows_msvc_indirect + get_lib_name = get_lib_name_for_windows + else: + make_link_flags = _make_link_flags_windows_gnu_direct if use_direct_link_driver else _make_link_flags_windows_gnu_indirect elif target_os.startswith(("mac", "darwin", "ios")): - make_link_flags_darwin = _make_link_flags_darwin_direct if use_direct_link_driver else _make_link_flags_darwin_indirect - make_link_flags = make_link_flags_darwin - get_lib_name = get_lib_name_default + make_link_flags = _make_link_flags_darwin_direct if use_direct_link_driver else _make_link_flags_darwin_indirect else: - make_link_flags_default = _make_link_flags_default_direct if use_direct_link_driver else _make_link_flags_default_indirect - make_link_flags = make_link_flags_default - get_lib_name = get_lib_name_default + make_link_flags = _make_link_flags_default_direct if use_direct_link_driver else _make_link_flags_default_indirect return (make_link_flags, get_lib_name) @@ -2703,3 +2698,7 @@ no_std = rule( }, implementation = _no_std_impl, ) + +# Test-only exports for private helpers. +portable_link_flags_for_testing = _portable_link_flags +symlink_for_ambiguous_lib_for_testing = _symlink_for_ambiguous_lib diff --git a/test/unit/windows_lib_name/BUILD.bazel b/test/unit/windows_lib_name/BUILD.bazel new file mode 100644 index 0000000000..e2a5113ec1 --- /dev/null +++ b/test/unit/windows_lib_name/BUILD.bazel @@ -0,0 +1,3 @@ +load(":windows_lib_name_test.bzl", "windows_lib_name_test_suite") + +windows_lib_name_test_suite(name = "windows_lib_name_test_suite") diff --git a/test/unit/windows_lib_name/windows_lib_name_test.bzl b/test/unit/windows_lib_name/windows_lib_name_test.bzl new file mode 100644 index 0000000000..1749e16700 --- /dev/null +++ b/test/unit/windows_lib_name/windows_lib_name_test.bzl @@ -0,0 +1,185 @@ +"""Analysistests for Windows-specific library naming and link flags.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") + +# buildifier: disable=bzl-visibility +load("//rust/private:rustc.bzl", "portable_link_flags_for_testing", "symlink_for_ambiguous_lib_for_testing") + +# buildifier: disable=bzl-visibility +load("//rust/private:utils.bzl", "get_lib_name_default", "get_lib_name_for_windows") + +# buildifier: disable=provider-params +LinkFlagsInfo = provider(fields = {"flags": "List[str]"}) + +# buildifier: disable=provider-params +SymlinkInfo = provider(fields = {"symlink": "File"}) + +def _portable_link_flags_probe_impl(ctx): + lib_artifact = ctx.actions.declare_file(ctx.attr.lib_basename) + ctx.actions.write(lib_artifact, "", is_executable = False) + library_to_link = struct( + static_library = lib_artifact, + pic_static_library = None, + dynamic_library = None, + interface_library = None, + alwayslink = False, + ) + + get_lib_name = get_lib_name_for_windows if ctx.attr.flavor_msvc else get_lib_name_default + flags = portable_link_flags_for_testing( + lib = library_to_link, + use_pic = False, + ambiguous_libs = {}, + get_lib_name = get_lib_name, + for_windows = True, + flavor_msvc = ctx.attr.flavor_msvc, + ) + + return [ + DefaultInfo(files = depset([])), + LinkFlagsInfo(flags = flags), + ] + +portable_link_flags_probe = rule( + implementation = _portable_link_flags_probe_impl, + attrs = { + "flavor_msvc": attr.bool(default = False), + "lib_basename": attr.string(mandatory = True), + }, +) + +def _symlink_probe_impl(ctx): + lib_artifact = ctx.actions.declare_file(ctx.attr.lib_basename) + ctx.actions.write(lib_artifact, "", is_executable = False) + crate_output = ctx.actions.declare_file("crate.rlib") + ctx.actions.write(crate_output, "", is_executable = False) + symlink = symlink_for_ambiguous_lib_for_testing( + ctx.actions, + toolchain = struct(target_abi = ctx.attr.target_abi), + crate_info = struct(output = crate_output), + lib = lib_artifact, + ) + + return [ + SymlinkInfo(symlink = symlink), + DefaultInfo(files = depset([symlink])), + ] + +symlink_probe = rule( + implementation = _symlink_probe_impl, + attrs = { + "lib_basename": attr.string(mandatory = True), + "target_abi": attr.string(mandatory = True), + }, +) + +def _portable_link_flags_windows_gnu_test_impl(ctx): + env = analysistest.begin(ctx) + flags = analysistest.target_under_test(env)[LinkFlagsInfo].flags + + asserts.equals( + env, + ["-lstatic=foo.dll", "-Clink-arg=-lfoo.dll"], + flags, + ) + return analysistest.end(env) + +portable_link_flags_windows_gnu_test = analysistest.make( + _portable_link_flags_windows_gnu_test_impl, +) + +def _portable_link_flags_windows_msvc_test_impl(ctx): + env = analysistest.begin(ctx) + flags = analysistest.target_under_test(env)[LinkFlagsInfo].flags + + asserts.equals( + env, + ["-lstatic=libfoo.dll", "-Clink-arg=libfoo.dll.lib"], + flags, + ) + return analysistest.end(env) + +portable_link_flags_windows_msvc_test = analysistest.make( + _portable_link_flags_windows_msvc_test_impl, +) + +def _symlink_name_windows_gnu_test_impl(ctx): + env = analysistest.begin(ctx) + symlink = analysistest.target_under_test(env)[SymlinkInfo].symlink + + asserts.true(env, symlink.basename.startswith("libfoo.dll-")) + asserts.true(env, symlink.basename.endswith(".a")) + asserts.false(env, symlink.basename.startswith("liblib")) + + return analysistest.end(env) + +symlink_name_windows_gnu_test = analysistest.make(_symlink_name_windows_gnu_test_impl) + +def _symlink_name_windows_msvc_test_impl(ctx): + env = analysistest.begin(ctx) + symlink = analysistest.target_under_test(env)[SymlinkInfo].symlink + + asserts.true(env, symlink.basename.startswith("native_dep-")) + asserts.true(env, symlink.basename.endswith(".lib")) + + return analysistest.end(env) + +symlink_name_windows_msvc_test = analysistest.make(_symlink_name_windows_msvc_test_impl) + +def _define_targets(): + portable_link_flags_probe( + name = "portable_link_flags_windows_gnu_probe", + flavor_msvc = False, + lib_basename = "libfoo.dll.a", + ) + portable_link_flags_probe( + name = "portable_link_flags_windows_msvc_probe", + flavor_msvc = True, + lib_basename = "libfoo.dll.lib", + ) + + symlink_probe( + name = "symlink_windows_gnu_probe", + lib_basename = "libfoo.dll.a", + target_abi = "gnu", + ) + symlink_probe( + name = "symlink_windows_msvc_probe", + lib_basename = "native_dep.lib", + target_abi = "msvc", + ) + +def windows_lib_name_test_suite(name): + """Entry-point macro for Windows library naming tests. + + Args: + name: test suite name + """ + _define_targets() + + portable_link_flags_windows_gnu_test( + name = "portable_link_flags_windows_gnu_test", + target_under_test = ":portable_link_flags_windows_gnu_probe", + ) + portable_link_flags_windows_msvc_test( + name = "portable_link_flags_windows_msvc_test", + target_under_test = ":portable_link_flags_windows_msvc_probe", + ) + symlink_name_windows_gnu_test( + name = "symlink_name_windows_gnu_test", + target_under_test = ":symlink_windows_gnu_probe", + ) + symlink_name_windows_msvc_test( + name = "symlink_name_windows_msvc_test", + target_under_test = ":symlink_windows_msvc_probe", + ) + + native.test_suite( + name = name, + tests = [ + ":portable_link_flags_windows_gnu_test", + ":portable_link_flags_windows_msvc_test", + ":symlink_name_windows_gnu_test", + ":symlink_name_windows_msvc_test", + ], + ) diff --git a/test/unit/windows_stdlib/BUILD.bazel b/test/unit/windows_stdlib/BUILD.bazel new file mode 100644 index 0000000000..91b803e6a7 --- /dev/null +++ b/test/unit/windows_stdlib/BUILD.bazel @@ -0,0 +1,3 @@ +load(":windows_stdlib_test.bzl", "windows_stdlib_test_suite") + +windows_stdlib_test_suite(name = "windows_stdlib_test_suite") diff --git a/test/unit/windows_stdlib/windows_stdlib_test.bzl b/test/unit/windows_stdlib/windows_stdlib_test.bzl new file mode 100644 index 0000000000..7008a86aa6 --- /dev/null +++ b/test/unit/windows_stdlib/windows_stdlib_test.bzl @@ -0,0 +1,122 @@ +"""Analysistests covering Windows-specific stdlib link flags.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//rust/platform:triple.bzl", "triple") +load("//rust/platform:triple_mappings.bzl", "system_to_stdlib_linkflags") + +# buildifier: disable=bzl-visibility +load("//rust/private:repository_utils.bzl", "BUILD_for_rust_toolchain") + +def _stdlib_linkflags_windows_test_impl(ctx): + env = analysistest.begin(ctx) + analysistest.target_under_test(env) # Ensure target is configured. + + msvc_flags = system_to_stdlib_linkflags(triple("x86_64-pc-windows-msvc")) + gnu_flags = system_to_stdlib_linkflags(triple("x86_64-pc-windows-gnu")) + gnullvm_flags = system_to_stdlib_linkflags(triple("aarch64-pc-windows-gnullvm")) + + asserts.equals( + env, + ["advapi32.lib", "ws2_32.lib", "userenv.lib", "Bcrypt.lib"], + msvc_flags, + ) + asserts.equals( + env, + ["-ladvapi32", "-lws2_32", "-luserenv"], + gnu_flags, + ) + asserts.equals(env, gnu_flags, gnullvm_flags) + + return analysistest.end(env) + +stdlib_linkflags_windows_test = analysistest.make(_stdlib_linkflags_windows_test_impl) + +def _build_for_rust_toolchain_windows_flags_test_impl(ctx): + env = analysistest.begin(ctx) + analysistest.target_under_test(env) + + msvc_triple = triple("x86_64-pc-windows-msvc") + gnu_triple = triple("x86_64-pc-windows-gnu") + + rendered_msvc = BUILD_for_rust_toolchain( + name = "tc_msvc", + exec_triple = msvc_triple, + target_triple = msvc_triple, + version = "1.75.0", + allocator_library = None, + global_allocator_library = None, + default_edition = "2021", + include_rustfmt = False, + include_llvm_tools = False, + include_linker = False, + stdlib_linkflags = None, + extra_rustc_flags = None, + extra_exec_rustc_flags = None, + opt_level = None, + strip_level = None, + ) + rendered_gnu = BUILD_for_rust_toolchain( + name = "tc_gnu", + exec_triple = gnu_triple, + target_triple = gnu_triple, + version = "1.75.0", + allocator_library = None, + global_allocator_library = None, + default_edition = "2021", + include_rustfmt = False, + include_llvm_tools = False, + include_linker = False, + stdlib_linkflags = None, + extra_rustc_flags = None, + extra_exec_rustc_flags = None, + opt_level = None, + strip_level = None, + ) + + asserts.true( + env, + 'stdlib_linkflags = ["advapi32.lib", "ws2_32.lib", "userenv.lib", "Bcrypt.lib"],' in rendered_msvc, + "MSVC toolchain should render .lib stdlib linkflags:\n%s" % rendered_msvc, + ) + asserts.true( + env, + 'stdlib_linkflags = ["-ladvapi32", "-lws2_32", "-luserenv"],' in rendered_gnu, + "GNU toolchain should render -l stdlib linkflags:\n%s" % rendered_gnu, + ) + + return analysistest.end(env) + +build_for_rust_toolchain_windows_flags_test = analysistest.make( + _build_for_rust_toolchain_windows_flags_test_impl, +) + +def _define_targets(): + # Target under test is unused beyond satisfying analysistest requirements. + native.filegroup( + name = "dummy_target", + srcs = [], + ) + +def windows_stdlib_test_suite(name): + """Entry-point macro for Windows stdlib linkflag tests. + + Args: + name: test suite name""" + _define_targets() + + stdlib_linkflags_windows_test( + name = "stdlib_linkflags_windows_test", + target_under_test = ":dummy_target", + ) + build_for_rust_toolchain_windows_flags_test( + name = "build_for_rust_toolchain_windows_flags_test", + target_under_test = ":dummy_target", + ) + + native.test_suite( + name = name, + tests = [ + ":build_for_rust_toolchain_windows_flags_test", + ":stdlib_linkflags_windows_test", + ], + )