Merge PR #8443: fix(natives): static-link win32 MSVC CRT so addon needs no VC++ redist (@roboomp)

This commit is contained in:
can1357
2026-08-13 18:46:08 +02:00
6 changed files with 55 additions and 17 deletions
+14 -1
View File
@@ -16,19 +16,32 @@ _ADDON_RUSTC_FLAGS = [
]
def _addon_transition_impl(settings, attr):
# Statically link the MSVC CRT for the shipped win32 addon: rustc gets
# +crt-static via the crate's rustc_flags select, and the C dependencies
# (opus/cmake, tree-sitter, blake3, ring) must move to /MT in lock-step so
# the final .node imports no VCRUNTIME140.dll from the Visual C++
# Redistributable (absent on a clean Windows install -> dlopen error 126).
# The static_link_msvcrt cc feature flips the toolchain compile flags that
# rules_rust forwards to cc-rs/cmake as CFLAGS/CXXFLAGS; it is inert for the
# zig/darwin toolchains, so scoping it to win32 is belt-and-suspenders.
features = list(settings["//command_line_option:features"])
if "win32" in str(attr.platform):
features = features + ["static_link_msvcrt"]
return {
"//command_line_option:platforms": str(attr.platform),
"//command_line_option:compilation_mode": "opt",
"//command_line_option:features": features,
"@rules_rust//rust/settings:lto": "thin",
"@rules_rust//rust/settings:extra_rustc_flags": _ADDON_RUSTC_FLAGS,
}
_addon_transition = transition(
implementation = _addon_transition_impl,
inputs = [],
inputs = ["//command_line_option:features"],
outputs = [
"//command_line_option:platforms",
"//command_line_option:compilation_mode",
"//command_line_option:features",
"@rules_rust//rust/settings:lto",
"@rules_rust//rust/settings:extra_rustc_flags",
],
+15 -6
View File
@@ -22,9 +22,16 @@ exec hosts. Replaces cargo-xwin.
work from Bazel actions (cwd = execroot) *and* from build scripts, where
rules_rust `${pwd}`-expands `CC`/`AR` to absolute paths and cc-rs/cmake spawn
tools from other cwds.
- **CRT: dynamic `/MD`** (rules_cc's msvc branch default outside `dbg` without
the `static_link_msvcrt` feature) — matches what napi/cc-rs produced under
cargo-xwin (rust msvc targets default to dynamic CRT without `+crt-static`).
- **CRT: static `/MT` for the shipped addon.** The toolchain *default* is
dynamic `/MD` (rules_cc's msvc branch default outside `dbg` without the
`static_link_msvcrt` feature), matching what napi/cc-rs produced under
cargo-xwin. But `//:natives-win32-x64-baseline` overrides to static CRT:
`-Ctarget-feature=+crt-static` for rustc (crate BUILD select) plus the
`static_link_msvcrt` cc feature (enabled for win32 in the `native_addon`
transition, `bazel/defs.bzl`) so the C deps compile `/MT` in lock-step.
Without this the `.node` imports `VCRUNTIME140.dll` from the Visual C++
Redistributable, which is absent on a clean Windows install and makes the
loader's dlopen fail with error 126 (issue #8439).
- **SSE floor in the wrapper, not annotations**: `-msse4.1 -msse4.2` live in the
clang-cl wrapper, which only ever targets win32-x64 (baseline = x86-64-v2 ⊇
SSE4.2). This is the old build-native.ts CFLAGS hack, windows-only by
@@ -95,7 +102,8 @@ exec hosts. Replaces cargo-xwin.
defaults to the Debug config → `/MDd` → `msvcrtd.lib`, which the lean splat
(like cargo-xwin's) does not carry; toolchain.cmake pins
`CMAKE_TRY_COMPILE_CONFIGURATION=Release`, `CMAKE_POLICY_DEFAULT_CMP0091=NEW`
and `CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` (/MD everywhere).
and `CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded` (static release `/MT`
everywhere, matching the addon's static-CRT policy — issue #8439).
Verified on darwin: scratch `project(C)` + `add_executable` configures with
"Clang 20.1.7 with MSVC-like command-line" and links a valid PE32+ exe
through vs_link_exe with the wrapper rc/mt/linker.
@@ -103,8 +111,9 @@ exec hosts. Replaces cargo-xwin.
## What to verify on can.internal (linux-x64)
1. `bazel build //:natives-win32-x64-baseline` end-to-end link; check the
produced `pi_natives.win32-x64-baseline.node` imports (dumpbin/llvm-readobj:
expect VCRUNTIME140/api-ms-win-crt-* → `/MD`, no static CRT).
produced `pi_natives.win32-x64-baseline.node` imports (dumpbin/llvm-readobj):
expect **no** `VCRUNTIME140.dll` and **no** `api-ms-win-crt-*` (static CRT);
only core Windows system DLLs (kernel32, ntdll, advapi32, …) should remain.
2. LLVM 20.1.7 Linux-X64 binaries are built on a newish Ubuntu: confirm the
kata runner image's glibc is ≥ 2.35-ish and has `libtinfo6`/`libstdc++6`
(usual LLVM release-binary runtime deps).
+12 -7
View File
@@ -124,15 +124,20 @@ set(CMAKE_CXX_COMPILER "${CMAKE_CURRENT_LIST_DIR}/bin/clang-cl")
set(CMAKE_LINKER "${CMAKE_CURRENT_LIST_DIR}/bin/lld-link")
set(CMAKE_RC_COMPILER "${CMAKE_CURRENT_LIST_DIR}/bin/llvm-rc")
set(CMAKE_MT "${CMAKE_CURRENT_LIST_DIR}/bin/llvm-mt")
# The xwin splat carries release CRT import libs only (msvcrt.lib, no
# msvcrtd.lib — same as cargo-xwin). try_compile defaults to the Debug
# configuration, whose /MDd would demand the debug CRT; pin try_compile to
# Release and the runtime library to dynamic release /MD for every config
# (CMP0091 NEW makes CMAKE_MSVC_RUNTIME_LIBRARY authoritative even for
# projects with ancient cmake_minimum_required, e.g. bundled opus).
# The xwin splat carries release CRT import + static libs only (msvcrt.lib /
# libcmt.lib, no debug msvcrtd.lib / libcmtd.lib — same as cargo-xwin).
# try_compile defaults to the Debug configuration, whose debug CRT the splat
# lacks; pin try_compile to Release. The shipped win32 addon statically links
# the CRT (rustc +crt-static + the static_link_msvcrt cc feature; see
# bazel/defs.bzl and crates/pi-natives/BUILD.bazel), so pin the runtime library
# to the static release CRT /MT for every config as well — otherwise CMake's
# authoritative CMAKE_MSVC_RUNTIME_LIBRARY (CMP0091 NEW) would emit /MD for the
# bundled opus objects, which then import VCRUNTIME140.dll and conflict with the
# static CRT the rest of the addon links (issue #8439). Keep this in lock-step
# with the static_link_msvcrt feature: both must select the static CRT together.
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
set(CMAKE_POLICY_DEFAULT_CMP0091 NEW)
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
"""
_BUILD = """\
+7
View File
@@ -40,6 +40,13 @@ rust_shared_library(
# cdylib at all; napi musl addons have always linked the dynamic CRT.
"//bazel/triples:x86_64-unknown-linux-musl": ["-Ctarget-feature=-crt-static"],
"//bazel/triples:aarch64-unknown-linux-musl": ["-Ctarget-feature=-crt-static"],
# Statically link the MSVC CRT so the shipped .node does not import
# VCRUNTIME140.dll from the Visual C++ Redistributable, which is absent
# on a clean Windows install and makes the loader's dlopen fail with
# "The specified module could not be found" (error 126). The C deps are
# switched to /MT in lock-step via the static_link_msvcrt cc feature in
# the native_addon transition (bazel/defs.bzl).
"//bazel/triples:x86_64-pc-windows-msvc": ["-Ctarget-feature=+crt-static"],
"//conditions:default": [],
}),
version = "17.1.5",
+3 -3
View File
@@ -64,7 +64,7 @@ Notes:
This mirrors the old cargo `ci` profile. Because the profile lives **in the transition**, a bare `bazel build //:natives-<t>` is always release-grade regardless of `-c`, and every addon shares one cache entry per (platform, source) pair. The rule then symlinks the produced shared library to the loader's canonical `pi_natives.<platform>-<arch>[-<variant>].node` name, scoped under the rule name (`bazel-bin/natives-<t>/…`) so gnu/musl outputs with identical basenames cannot collide at the package level.
Per-target codegen that is not part of the transition lives in `crates/pi-natives/BUILD.bazel` `rustc_flags` selects: `-Ctarget-cpu=x86-64-v2` (baseline) / `x86-64-v3` (modern) via `//bazel/variants`, the napi link args (`-Wl,-undefined,dynamic_lookup` on macOS, `-Wl,-z,nodelete` on linux — `build.rs`/`napi_build::setup()` is deliberately not wired in), and `-Ctarget-feature=-crt-static` for musl.
Per-target codegen that is not part of the transition lives in `crates/pi-natives/BUILD.bazel` `rustc_flags` selects: `-Ctarget-cpu=x86-64-v2` (baseline) / `x86-64-v3` (modern) via `//bazel/variants`, the napi link args (`-Wl,-undefined,dynamic_lookup` on macOS, `-Wl,-z,nodelete` on linux — `build.rs`/`napi_build::setup()` is deliberately not wired in), `-Ctarget-feature=-crt-static` for musl, and `-Ctarget-feature=+crt-static` for win32-x64 msvc (paired with the `static_link_msvcrt` cc feature enabled in the `native_addon` transition so the C deps compile `/MT` in lock-step — the shipped `.node` then imports no `VCRUNTIME140.dll` from the VC++ Redistributable).
### 3) Platforms and toolchains
@@ -73,7 +73,7 @@ Per-target codegen that is not part of the transition lives in `crates/pi-native
| linux gnu (x64/arm64) | `@zig_sdk//libc_aware/toolchain:linux_*_gnu.2.17` (hermetic zig cc) | glibc **2.17** portability floor — same floor the previous cross builds used |
| linux musl (x64/arm64) | `@zig_sdk//libc_aware/toolchain:linux_*_musl` | dynamic CRT (`-Ctarget-feature=-crt-static` in the crate BUILD) |
| darwin (x64/arm64) | host Xcode toolchain | Apple frameworks aren't redistributable; darwin addons build on mac hosts only |
| win32-x64 msvc | `//bazel/toolchains/msvc` (`@msvc_cc`): clang-cl + lld-link + xwin CRT/SDK | hermetic cross-link from linux-x64 CI pods and darwin dev hosts; see `bazel/toolchains/msvc/NOTES.md` |
| win32-x64 msvc | `//bazel/toolchains/msvc` (`@msvc_cc`): clang-cl + lld-link + xwin CRT/SDK | hermetic cross-link from linux-x64 CI pods and darwin dev hosts; **static CRT** (`+crt-static` + `static_link_msvcrt`) so the addon needs no VC++ Redistributable; see `bazel/toolchains/msvc/NOTES.md` |
Rust toolchains are nightly (pinned in `MODULE.bazel`), with repo-local musl re-registrations in `//bazel/toolchains` carrying an explicit `@zig_sdk//libc:musl` constraint (rules_rust's generated gnu and musl toolchains otherwise share (os, cpu) constraints).
@@ -217,7 +217,7 @@ bazelisk build --nobuild //:natives-win32-x64-baseline
| rstest macro: "Cargo.toml not found" in a vendored test | rstest verifies `Cargo.toml` exists in the manifest dir | `compile_data = ["Cargo.toml"]` on the `rust_test` (see `crates/vendor/uu-tail/BUILD.bazel`) |
| vendored tests fail on bare `test_data/...` paths / symlink into srcs | tests assume cargo's cwd, incompatible with runfiles execution | `tags = ["manual"]`; run via `cargo nextest` when touching the fork; hermetic sibling test covers the contract |
| blake3 msvc: `ml64.exe` not found | cc-rs resolves MASM from build-script PATH on non-windows hosts | `bin/ml64.exe → llvm-ml -m64` shim in `@msvc_cc`, prepended via the `blake3` annotation PATH |
| audiopus_sys msvc: cmake demands VS generator / rc+mt tools; `try_compile` wants `msvcrtd.lib` | cross cmake on linux/mac hosts; Debug config → `/MDd` which the lean xwin splat lacks | `CMAKE_GENERATOR_x86_64_pc_windows_msvc=Ninja` + `@msvc_cc`'s `toolchain.cmake` (`CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_msvc`) pinning wrappers + Release try-compile + `/MD` |
| audiopus_sys msvc: cmake demands VS generator / rc+mt tools; `try_compile` wants `msvcrtd.lib` | cross cmake on linux/mac hosts; Debug config → `/MDd` which the lean xwin splat lacks | `CMAKE_GENERATOR_x86_64_pc_windows_msvc=Ninja` + `@msvc_cc`'s `toolchain.cmake` (`CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_msvc`) pinning wrappers + Release try-compile + `/MT` (static CRT, matches the addon policy) |
| win32 link oddities generally | — | read `bazel/toolchains/msvc/NOTES.md` first: wrapper self-location, `lld-link` flavor/driver-link behavior, `LIB`, `/MD` CRT choice, xwin splat caveats |
| `rust_test(crate = ...)` "can't find crate" at macro expansion | rmeta-only pipelined deps break macro_rules re-export harness compiles | rust pipelined_compilation stays OFF (`.bazelrc` note) |
| build script can't find cmake/ninja | `--incompatible_strict_action_env` — no host env leaks | explicit `PATH` in the crate annotation (`MODULE.bazel`), not host env |
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed `omp` failing to start on a clean Windows install with `Failed to load pi_natives native addon for win32-x64 ... The specified module could not be found` (LoadLibrary error 126). The shipped win32-x64 addon linked the dynamic MSVC CRT (`/MD`) and imported `VCRUNTIME140.dll` from the Visual C++ Redistributable, which is absent on a fresh Windows install. The addon now statically links the CRT (`+crt-static` for rustc plus the `static_link_msvcrt` cc feature for its C dependencies), so the `.node` imports only core Windows system DLLs ([#8439](https://github.com/can1357/oh-my-pi/issues/8439)).
## [17.3.0] - 2026-08-13
### Fixed