diff --git a/MODULE.bazel b/MODULE.bazel index e38c02825d4a..b6352b5ca13b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -93,25 +93,25 @@ git_override( remote = "https://github.com/bazelbuild/rules_pkg.git", ) -# Temporary until rules_rust > 0.69.0 ships with Windows GNU ABI support (PR #3940). -# The CI runner uses MinGW as its cc_toolchain; rules_rust 0.69.0 defaults to -# x86_64-pc-windows-msvc whose linker flags are incompatible with MinGW's g++. +# Pinned past the 0.69.0 release to pick up the Windows-GNU ABI support from +# bazelbuild/rules_rust#3940 (lib name resolution, stdlib link flags, PDB +# emission split by abi). Required because our Windows cc_toolchain is MinGW +# (@winlibs_mingw64), and the 0.69.0 stable release still hard-codes MSVC +# behavior for `target_os == "windows"`. git_override( module_name = "rules_rust", - commit = "82506df3f31240f97194b2e9453022125eaa07f4", # main as of April 21, 2026 + commit = "f31db8b6f124dd5eebdd0ed8de4daf20d4d9685f", # main as of April 27, 2026 (includes #3940 + #3990) patch_strip = 1, patches = [ - # Stops rustc_compile_action from leaking default_shell_env into - # CrateInfo.rustc_env, which otherwise clobbers cc_toolchain link_env - # for rust_test(crate = ...). Upstream issue pending. - "//bazel/patches:rules_rust-crateinfo-env-leak.patch", + # Derives `-Cdlltool=/dlltool.exe` automatically + "//bazel/patches:rules_rust-derive-dlltool-from-linker.patch", ], remote = "https://github.com/bazelbuild/rules_rust.git", ) git_override( module_name = "rules_rust_prost", - commit = "82506df3f31240f97194b2e9453022125eaa07f4", + commit = "f31db8b6f124dd5eebdd0ed8de4daf20d4d9685f", remote = "https://github.com/bazelbuild/rules_rust.git", strip_prefix = "extensions/prost", ) @@ -282,11 +282,26 @@ llvm.toolchain( llvm_version = "19.1.7", # https://github.com/bazel-contrib/toolchains_llvm/blob/master/toolchain/internal/llvm_distributions.bzl ) +# Hermetic MinGW-w64 toolchain sourced from a pinned WinLibs distribution. +# Variant: GCC 14.2.0 + MinGW-w64 12.0.0, POSIX threads, MSVCRT runtime, +# no LLVM bundle. URL, sha256 and gcc_version must move together when bumping. +winlibs_mingw_repository = use_repo_rule( + "//bazel/toolchains/mingw:winlibs.bzl", + "winlibs_mingw_repository", +) + +winlibs_mingw_repository( + name = "winlibs_mingw64", + gcc_version = "14.2.0", + sha256 = "ff475e985a98c5f3785129baf7460db14fee27708bce35f2833db5009507f1b9", + url = "https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.7-12.0.0-msvcrt-r3/winlibs-x86_64-posix-seh-gcc-14.2.0-mingw-w64msvcrt-12.0.0-r3.zip", +) + # TODO{agent-build}: Find a way to register platform-specific toolchains dynamically register_toolchains( "@gcc_toolchain_x86_64//:cc_toolchain", "@gcc_toolchain_aarch64//:cc_toolchain", - "//bazel/toolchains/mingw:mingw_cc_toolchain", + "@winlibs_mingw64//:mingw_cc_toolchain", "@llvm_toolchain//:all", # last to avoid taking precedence over GCC toolchains ) @@ -334,21 +349,11 @@ rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") # Register a GNU-ABI Rust toolchain for Windows so that rustc generates # GNU-style linker flags (-lkernel32, -o, etc.) compatible with the MinGW -# cc_toolchain used on the Windows CI runner. Processed before the default -# toolchains, so Bazel's toolchain resolution picks it over the auto-registered -# x86_64-pc-windows-msvc toolchain. +# cc_toolchain used on the Windows CI runner. rust.repository_set( name = "rust_windows_gnu_x86_64", edition = "2024", exec_triple = "x86_64-pc-windows-gnu", - extra_exec_rustc_flags = [ - "-Clinker=C:/tools/msys64/mingw64/bin/gcc.exe", - "-Cdlltool=C:/tools/msys64/mingw64/bin/dlltool.exe", - ], - extra_rustc_flags = [ - "-Clinker=C:/tools/msys64/mingw64/bin/gcc.exe", - "-Cdlltool=C:/tools/msys64/mingw64/bin/dlltool.exe", - ], target_compatible_with = [ "@platforms//os:windows", "@platforms//cpu:x86_64", diff --git a/bazel/patches/rules_rust-crateinfo-env-leak.patch b/bazel/patches/rules_rust-crateinfo-env-leak.patch deleted file mode 100644 index 1e8129375763..000000000000 --- a/bazel/patches/rules_rust-crateinfo-env-leak.patch +++ /dev/null @@ -1,34 +0,0 @@ -Do not leak ctx.configuration.default_shell_env into CrateInfo.rustc_env. - -At the tail of rustc_compile_action, CrateInfo is re-created with -`rustc_env = env`. But `env` was built as `dict(default_shell_env) + -env_from_args`, so it carries the host's default_shell_env (PATH, LIB, -INCLUDE, ...) on top of the rustc-specific env. Every downstream consumer -that inherits crate_info.rustc_env (notably rust_test(crate = ...)) then -picks up the host env, and at its own compile time the inherited values -clobber cc_toolchain's link_env -- because construct_arguments applies -link_env first (line ~1183) and crate_info.rustc_env after. - -Persisting only env_from_args keeps CrateInfo honest: Bazel re-injects -default_shell_env at action execution time anyway, so there is no reason -to bake it into provider state. - -Upstream issue: https://github.com/bazelbuild/rules_rust/issues/TBD - -diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl -index f43ddaba67abb9b27f840b7d984c4cafd3147c29..dc30e62545a6778315c30237a3895a63911babb9 100644 ---- a/rust/private/rustc.bzl -+++ b/rust/private/rustc.bzl -@@ -1797,8 +1797,11 @@ def rustc_compile_action( - ) - - if crate_info_dict != None: -+ # Persist only rustc-specific env; `env` above also carries default_shell_env -+ # which must not leak through CrateInfo (it would clobber cc_toolchain -+ # link_env in downstream rust_test(crate = ...)). - crate_info_dict.update({ -- "rustc_env": env, -+ "rustc_env": env_from_args, - }) - crate_info = rust_common.create_crate_info( - deps = depset(deps), diff --git a/bazel/patches/rules_rust-derive-dlltool-from-linker.patch b/bazel/patches/rules_rust-derive-dlltool-from-linker.patch new file mode 100644 index 000000000000..ee04cfec0238 --- /dev/null +++ b/bazel/patches/rules_rust-derive-dlltool-from-linker.patch @@ -0,0 +1,35 @@ +diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl +index c8f6ecc9..c7319735 100644 +--- a/rust/private/rustc.bzl ++++ b/rust/private/rustc.bzl +@@ -1158,6 +1158,30 @@ def construct_arguments( + # If linker_type is not explicitly set, infer from which linker is actually being used + ld_is_direct_driver = False + ++ # rustc on Windows-GNU shells out to dlltool to synthesize import ++ # libraries -- both for #[link(... kind = "raw-dylib")] declarations ++ # in *any* crate (rlib included; windows-sys 0.61+ is the common case) ++ # and for the *.dll.a companion that cdylib crates emit alongside ++ # their .dll. cc_toolchain has no action / tool slot for dlltool ++ # because gcc-as-linker invokes it internally -- but rustc drives the ++ # linker directly, so it needs an explicit pointer. ++ # ++ # Inject the derived path here, *outside* the link-emit gate below, so ++ # rlib compiles also see it. Every MinGW distribution (winlibs, MSYS2 ++ # mingw64, llvm-mingw) ships dlltool.exe next to the linker, so reuse ++ # the cc_toolchain's CPP_LINK_EXECUTABLE_ACTION_NAME tool path and ++ # swap the basename. User-supplied `-Cdlltool=` via extra_rustc_flags ++ # is appended later and still wins because rustc honors the last ++ # `-C=` for a given key. ++ if cc_toolchain and toolchain.target_os == "windows" and toolchain.target_abi != "msvc": ++ dlltool_linker = cc_common.get_tool_for_action( ++ feature_configuration = feature_configuration, ++ action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, ++ ) ++ dlltool_sep = max(dlltool_linker.rfind("/"), dlltool_linker.rfind("\\")) ++ if dlltool_sep >= 0: ++ rustc_flags.add(dlltool_linker[:dlltool_sep] + "/dlltool.exe", 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/bazel/rules/ebpf/cgo_godefs.bzl b/bazel/rules/ebpf/cgo_godefs.bzl index 7b30148cdc9b..58e43719f821 100644 --- a/bazel/rules/ebpf/cgo_godefs.bzl +++ b/bazel/rules/ebpf/cgo_godefs.bzl @@ -73,10 +73,9 @@ def _cgo_godefs_impl(ctx): package_name = ctx.label.package.split("/")[-1] genpost_args = "$ROOT/{test} {pkg}".format(test = test_path_no_ext, pkg = package_name) - # TODO(ABLD-410): uses the system clang rather than a hermetic toolchain. - # On Windows, Go defaults to gcc (MinGW) — no CC override needed, matching - # the old ninja behavior. - cc_prefix = "CC=clang " if platform == "linux" else "" + # TODO(ABLD-410): Linux still shells out to the system clang. + # Windows points cgo at the hermetic MinGW gcc from the cc_toolchain. + cc_prefix = "CC=clang " if platform == "linux" else "CC=$ROOT/{} ".format(go.cgo_tools.c_compiler_path) cmd = ( "set -euo pipefail && ROOT=$PWD && cd {src_dir} && " + diff --git a/bazel/rules/windows_resources.bzl b/bazel/rules/windows_resources.bzl index 744aa23603d6..6d8beb013ad7 100644 --- a/bazel/rules/windows_resources.bzl +++ b/bazel/rules/windows_resources.bzl @@ -13,7 +13,9 @@ load("@rules_cc//cc:action_names.bzl", "C_COMPILE_ACTION_NAME") load("@rules_cc//cc:defs.bzl", "cc_common") load("@rules_cc//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_ATTRS", "find_cc_toolchain", "use_cc_toolchain") load("//bazel/rules:version_info.bzl", "agent_version_defines") -load("//bazel/toolchains/mingw:paths.bzl", "MINGW_PATH") + +_WINDRES = "@winlibs_mingw64//:windres" +_WINDMC = "@winlibs_mingw64//:windmc" def _cc_env(ctx): """Returns (env, cc_toolchain) from the resolved CC toolchain.""" @@ -48,7 +50,7 @@ def _win_messagetable_impl(ctx): windmc_args.add(src) ctx.actions.run( - executable = MINGW_PATH + "/bin/windmc", + executable = ctx.executable._windmc, arguments = [windmc_args], inputs = [src], outputs = [rc_out, h_out, bin_out], @@ -67,7 +69,7 @@ def _win_messagetable_impl(ctx): windres_args.add("-o", syso_out) ctx.actions.run( - executable = MINGW_PATH + "/bin/windres", + executable = ctx.executable._windres, arguments = [windres_args], env = env, inputs = depset([rc_out, bin_out], transitive = [cc_toolchain.all_files]), @@ -83,6 +85,8 @@ _win_messagetable = rule( doc = "Compiles a .mc message file into a .syso resource and .h header via windmc + windres.", attrs = { "src": attr.label(mandatory = True, allow_single_file = [".mc"]), + "_windmc": attr.label(default = _WINDMC, executable = True, cfg = "exec", allow_single_file = True), + "_windres": attr.label(default = _WINDRES, executable = True, cfg = "exec", allow_single_file = True), } | CC_TOOLCHAIN_ATTRS, toolchains = use_cc_toolchain(), fragments = ["cpp"], @@ -129,7 +133,7 @@ def _win_resource_impl(ctx): windres_args.add("-o", syso_out) ctx.actions.run( - executable = MINGW_PATH + "/bin/windres", + executable = ctx.executable._windres, arguments = [windres_args], env = env, inputs = depset([src] + ctx.files.deps, transitive = [cc_toolchain.all_files]), @@ -147,6 +151,7 @@ _win_resource = rule( "src": attr.label(mandatory = True, allow_single_file = [".rc"]), "deps": attr.label_list(allow_files = True), "defines": attr.string_dict(), + "_windres": attr.label(default = _WINDRES, executable = True, cfg = "exec", allow_single_file = True), } | CC_TOOLCHAIN_ATTRS, toolchains = use_cc_toolchain(), fragments = ["cpp"], diff --git a/bazel/toolchains/mingw/BUILD.bazel b/bazel/toolchains/mingw/BUILD.bazel index 92ea1f55f483..83739850964f 100644 --- a/bazel/toolchains/mingw/BUILD.bazel +++ b/bazel/toolchains/mingw/BUILD.bazel @@ -1,50 +1,3 @@ -load("@rules_cc//cc:defs.bzl", "cc_toolchain") -load("//bazel/toolchains/mingw:paths.bzl", "MINGW_PATH", "MSYS2_PATH") -load("//bazel/toolchains/mingw:toolchain.bzl", "mingw_cc_toolchain_config") - -mingw_cc_toolchain_config( - name = "mingw-cc-toolchain-config", - GCC_VERSION = "14.2.0", - MINGW_PATH = MINGW_PATH, - MSYS2_PATH = MSYS2_PATH, -) - -filegroup( - name = "toolchain_files", -) - -# Define our cc_toolchain -# (https://bazel.build/reference/be/c-cpp#cc_toolchain). -# The cc_toolchain rule is pre-defined by the C++ rule owners. It uses these -# parameters to construct a ToolchainInfo provider, as required by Bazel's -# platform/toolchain APIs. -cc_toolchain( - name = "mingw_cc_toolchain_definition", - all_files = ":toolchain_files", - compiler_files = ":toolchain_files", - dwp_files = ":toolchain_files", - linker_files = ":toolchain_files", - objcopy_files = ":toolchain_files", - strip_files = ":toolchain_files", - toolchain_config = ":mingw-cc-toolchain-config", -) - -# Bazel's platform/toolchain APIs require this wrapper around the actual -# toolchain defined above. It serves two purposes: declare which -# constraint_values it supports (which can be matched to appropriate platforms) -# and tell Bazel what language this toolchain is for. -# -# So when you're building a cc_binary, Bazel has all the info it needs to give -# that cc_binary the right toolchain: it knows cc_binary requires a "C++-type -# toolchain" (this is encoded in the cc_binary rule definition) and needs to -# use a toolchain that matches whatever you set --platforms to at the command -# line. -toolchain( - name = "mingw_cc_toolchain", - target_compatible_with = [ - "@platforms//os:windows", - "@platforms//cpu:x86_64", - ], - toolchain = ":mingw_cc_toolchain_definition", - toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", -) +# MinGW cc_toolchain lives in @winlibs_mingw64//: (see winlibs.BUILD.bazel). +# This file exists only to mark the package; the toolchain config rule is +# loaded from //bazel/toolchains/mingw:toolchain.bzl by the WinLibs repo. diff --git a/bazel/toolchains/mingw/paths.bzl b/bazel/toolchains/mingw/paths.bzl deleted file mode 100644 index a53c3e8c80d2..000000000000 --- a/bazel/toolchains/mingw/paths.bzl +++ /dev/null @@ -1,4 +0,0 @@ -"""MinGW/MSYS2 installation paths shared across toolchain and resource rules.""" - -MSYS2_PATH = "C:/tools/msys64" -MINGW_PATH = MSYS2_PATH + "/mingw64" diff --git a/bazel/toolchains/mingw/toolchain.bzl b/bazel/toolchains/mingw/toolchain.bzl index 7b8a803caea4..ede60f1c88db 100644 --- a/bazel/toolchains/mingw/toolchain.bzl +++ b/bazel/toolchains/mingw/toolchain.bzl @@ -1,9 +1,18 @@ +"""Hermetic MinGW-w64 cc_toolchain_config. + +All tool paths are relative to the package containing the cc_toolchain target +(see @winlibs_mingw64//:winlibs.BUILD.bazel), so this rule has no host-path +attrs and works in sandboxed / remote-exec contexts. + +WinLibs gcc.exe locates its sibling tools (as, ld, cc1, ...) via relative path +lookup against argv[0], which is why no PATH env_entry is needed once the full +WinLibs tree is staged as the cc_toolchain's `all_files`. +""" + load( "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "action_config", "artifact_name_pattern", - "env_entry", - "env_set", "feature", "flag_group", "flag_set", @@ -13,40 +22,27 @@ load( load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load("@rules_cc//cc:defs.bzl", "CcToolchainConfigInfo", "cc_common") -def _impl(ctx): - tools = [ - "ar", - "cpp", - "gcc", - "gcov", - "ld", - "nm", - "objdump", - "strip", - ] - - gpp_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/g++", - ) - - gcc_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/gcc", - ) - - # For assembly files, GCC can act as the assembler (preprocesses and assembles .S files) - as_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/as", - ) - - # For creating static libraries (.a files) - ar_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/ar", - ) +_TOOL_NAMES = [ + "ar", + "cpp", + "gcc", + "gcov", + "ld", + "nm", + "objdump", + "strip", +] - tool_paths = [] +def _impl(ctx): + gpp_tool = tool(path = "bin/g++.exe") + gcc_tool = tool(path = "bin/gcc.exe") + as_tool = tool(path = "bin/as.exe") + ar_tool = tool(path = "bin/ar.exe") - for mingw_tool in tools: - tool_paths.append(tool_path(name = mingw_tool, path = "{}/bin/{}".format(ctx.attr.MINGW_PATH, mingw_tool))) + tool_paths = [ + tool_path(name = name, path = "bin/{}.exe".format(name)) + for name in _TOOL_NAMES + ] default_feature = feature( name = "default_env_feature", @@ -68,25 +64,8 @@ def _impl(ctx): ], ), ], - env_sets = [ - env_set( - actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_link_executable, - ACTION_NAMES.cpp_link_dynamic_library, - ACTION_NAMES.cpp_link_static_library, - ], - env_entries = [ - env_entry("PATH", "{}/usr/bin;{}/bin".format(ctx.attr.MSYS2_PATH, ctx.attr.MINGW_PATH)), - ], - ), - ], ) - # Feature for archiver flags (ar) archiver_flags_feature = feature( name = "archiver_flags", enabled = True, @@ -96,9 +75,8 @@ def _impl(ctx): flag_groups = [ flag_group( flags = ["rcsD", "%{output_execpath}"], - # This is needed to make rules_foreign_cc work - # as it cannot find output_execpath. This way we - # don't expand this flag group if variable is not available. + # Needed for rules_foreign_cc which doesn't expose + # output_execpath; skip the group when absent. expand_if_available = "output_execpath", ), flag_group( @@ -125,37 +103,32 @@ def _impl(ctx): target_libc = "nothing", cc_target_os = "windows", compiler = "mingw-gcc", - abi_version = "gcc-" + ctx.attr.GCC_VERSION, + abi_version = "gcc-" + ctx.attr.gcc_version, abi_libc_version = "nothing", action_configs = [ - # C compilation - use gcc action_config( action_name = ACTION_NAMES.c_compile, enabled = True, tools = [gcc_tool], ), - # C++ compilation - use g++ action_config( action_name = ACTION_NAMES.cpp_compile, enabled = True, tools = [gpp_tool], ), - # Assembly actions - # preprocess_assemble: for .S files (assembly with C preprocessor) + # .S files (assembly with C preprocessor) go through gcc. action_config( action_name = ACTION_NAMES.preprocess_assemble, enabled = True, - tools = [gcc_tool], # GCC can preprocess and assemble .S files + tools = [gcc_tool], ), - # assemble: for .s files (pure assembly, no preprocessing) + # .s files (pure assembly) go straight to as. action_config( action_name = ACTION_NAMES.assemble, enabled = True, - tools = [as_tool], # Use 'as' directly for pure assembly + tools = [as_tool], ), - # Linking actions (shared between C and C++) - # For C projects, gcc is typically used; for C++ projects, g++ is used - # We'll use g++ for linking to handle both cases properly + # g++ for linking covers both C and C++ link cases. action_config( action_name = ACTION_NAMES.cpp_link_executable, enabled = True, @@ -166,7 +139,6 @@ def _impl(ctx): enabled = True, tools = [gpp_tool], ), - # Static library archiving - use ar (archiver) action_config( action_name = ACTION_NAMES.cpp_link_static_library, enabled = True, @@ -174,12 +146,17 @@ def _impl(ctx): ), ], tool_paths = tool_paths, + # Paths are relative to the cc_toolchain's package (the root of + # @winlibs_mingw64//:), so rules_cc resolves them via crosstool_top + # and never constructs a Label("@winlibs_mingw64//...") from inside + # @@rules_cc+'s repo_mapping. Using %package(@winlibs_mingw64//)% + # here trips rules_cc 0.2.18 on Bazel 9 with an invalid-Label error. cxx_builtin_include_directories = [ - ctx.attr.MINGW_PATH + "/include", - ctx.attr.MINGW_PATH + "/lib/gcc/x86_64-w64-mingw32/" + ctx.attr.GCC_VERSION + "/include-fixed", - ctx.attr.MINGW_PATH + "/lib/gcc/x86_64-w64-mingw32/" + ctx.attr.GCC_VERSION + "/include", - ctx.attr.MINGW_PATH + "/lib/gcc/x86_64-w64-mingw32/" + ctx.attr.GCC_VERSION + "/install-tools/include", - ctx.attr.MINGW_PATH + "/x86_64-w64-mingw32/include", + "include", + "lib/gcc/x86_64-w64-mingw32/" + ctx.attr.gcc_version + "/include-fixed", + "lib/gcc/x86_64-w64-mingw32/" + ctx.attr.gcc_version + "/include", + "lib/gcc/x86_64-w64-mingw32/" + ctx.attr.gcc_version + "/install-tools/include", + "x86_64-w64-mingw32/include", ], artifact_name_patterns = [ artifact_name_pattern( @@ -204,9 +181,7 @@ def _impl(ctx): mingw_cc_toolchain_config = rule( implementation = _impl, attrs = { - "MSYS2_PATH": attr.string(mandatory = True), - "MINGW_PATH": attr.string(mandatory = True), - "GCC_VERSION": attr.string(mandatory = True), + "gcc_version": attr.string(mandatory = True), }, provides = [CcToolchainConfigInfo], ) diff --git a/bazel/toolchains/mingw/winlibs.BUILD.bazel b/bazel/toolchains/mingw/winlibs.BUILD.bazel new file mode 100644 index 000000000000..2217eb1f7272 --- /dev/null +++ b/bazel/toolchains/mingw/winlibs.BUILD.bazel @@ -0,0 +1,80 @@ +"""BUILD template instantiated by winlibs_mingw_repository. + +%GCC_VERSION% is substituted at fetch time so the URL pin in MODULE.bazel +stays the single source of truth for which WinLibs zip we use. + +After strip_prefix the layout is `bin/`, `include/`, `lib/`, +`x86_64-w64-mingw32/` at the repo root. +""" + +load("@//bazel/toolchains/mingw:toolchain.bzl", "mingw_cc_toolchain_config") +load("@rules_cc//cc:defs.bzl", "cc_toolchain") + +package(default_visibility = ["//visibility:public"]) + +GCC_VERSION = "%GCC_VERSION%" + +filegroup( + name = "all", + srcs = glob( + ["**"], + exclude = [ + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), +) + +[ + filegroup( + name = tool_name, + srcs = ["bin/" + tool_name + ".exe"], + ) + for tool_name in [ + "ar", + "as", + "cpp", + "dlltool", + "g++", + "gcc", + "gcov", + "ld", + "nm", + "objdump", + "strip", + "windmc", + "windres", + ] +] + +filegroup( + name = "make", + srcs = ["bin/mingw32-make.exe"], +) + +mingw_cc_toolchain_config( + name = "mingw-cc-toolchain-config", + gcc_version = GCC_VERSION, +) + +cc_toolchain( + name = "mingw_cc_toolchain_definition", + all_files = ":all", + compiler_files = ":all", + dwp_files = ":all", + linker_files = ":all", + objcopy_files = ":all", + strip_files = ":all", + toolchain_config = ":mingw-cc-toolchain-config", +) + +toolchain( + name = "mingw_cc_toolchain", + target_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + toolchain = ":mingw_cc_toolchain_definition", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) diff --git a/bazel/toolchains/mingw/winlibs.bzl b/bazel/toolchains/mingw/winlibs.bzl new file mode 100644 index 000000000000..50bee4a68024 --- /dev/null +++ b/bazel/toolchains/mingw/winlibs.bzl @@ -0,0 +1,48 @@ +"""Repository rule that materializes a hermetic MinGW-w64 toolchain from a +pinned WinLibs distribution. + +Pins URL, SHA256 and GCC version together in MODULE.bazel so bumping the zip +and the include-dir layout it implies stays a single atomic change. +""" + +def _winlibs_mingw_repository_impl(ctx): + ctx.download_and_extract( + url = ctx.attr.url, + sha256 = ctx.attr.sha256, + stripPrefix = ctx.attr.strip_prefix, + ) + ctx.template( + "BUILD.bazel", + ctx.attr._build_file_template, + substitutions = { + "%GCC_VERSION%": ctx.attr.gcc_version, + }, + executable = False, + ) + +winlibs_mingw_repository = repository_rule( + implementation = _winlibs_mingw_repository_impl, + doc = "Downloads a pinned WinLibs MinGW-w64 zip and exposes it as a hermetic cc_toolchain.", + attrs = { + "url": attr.string( + mandatory = True, + doc = "Direct URL to the WinLibs .zip release asset.", + ), + "sha256": attr.string( + mandatory = True, + doc = "SHA256 of the zip, pinned for supply-chain integrity.", + ), + "strip_prefix": attr.string( + default = "mingw64", + doc = "Top-level directory stripped from the zip (WinLibs ships everything under mingw64/).", + ), + "gcc_version": attr.string( + mandatory = True, + doc = "GCC version baked into the WinLibs zip; must match the URL or builtin include dirs will be wrong.", + ), + "_build_file_template": attr.label( + default = "//bazel/toolchains/mingw:winlibs.BUILD.bazel", + allow_single_file = True, + ), + }, +)