From 246dda7f1caca85164c103801e6f7b6d3c7f62f8 Mon Sep 17 00:00:00 2001 From: roboomp Date: Thu, 13 Aug 2026 16:00:33 +0000 Subject: [PATCH] fix(natives): static-link win32 MSVC CRT so addon needs no VC++ redist The shipped win32-x64 pi_natives addon linked the dynamic MSVC CRT (/MD) and imported VCRUNTIME140.dll from the Visual C++ Redistributable, which is absent on a clean Windows install. LoadLibrary of the extracted .node then failed with error 126 ("The specified module could not be found"), so omp could not start after a fresh `irm install.ps1 | iex`. Static-link the CRT for the win32 addon: +crt-static for rustc (crate BUILD select) plus the static_link_msvcrt cc feature enabled for win32 in the native_addon transition, so its C deps (opus/cmake, tree-sitter, blake3, ring) compile /MT in lock-step. The rebuilt .node imports only core Windows system DLLs -- no VCRUNTIME140.dll, no api-ms-win-crt-*. Fixes #8439 --- bazel/defs.bzl | 15 ++++++++++++++- bazel/toolchains/msvc/NOTES.md | 18 +++++++++++++----- crates/pi-natives/BUILD.bazel | 7 +++++++ docs/natives-build-release-debugging.md | 4 ++-- packages/natives/CHANGELOG.md | 4 ++++ 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/bazel/defs.bzl b/bazel/defs.bzl index ca2619218..3cace6060 100644 --- a/bazel/defs.bzl +++ b/bazel/defs.bzl @@ -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", ], diff --git a/bazel/toolchains/msvc/NOTES.md b/bazel/toolchains/msvc/NOTES.md index 5bbb76d32..1ba1177ab 100644 --- a/bazel/toolchains/msvc/NOTES.md +++ b/bazel/toolchains/msvc/NOTES.md @@ -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 @@ -103,8 +110,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). diff --git a/crates/pi-natives/BUILD.bazel b/crates/pi-natives/BUILD.bazel index 1150d423f..a6594ff68 100644 --- a/crates/pi-natives/BUILD.bazel +++ b/crates/pi-natives/BUILD.bazel @@ -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", diff --git a/docs/natives-build-release-debugging.md b/docs/natives-build-release-debugging.md index 2dc045618..41985996e 100644 --- a/docs/natives-build-release-debugging.md +++ b/docs/natives-build-release-debugging.md @@ -64,7 +64,7 @@ Notes: This mirrors the old cargo `ci` profile. Because the profile lives **in the transition**, a bare `bazel build //:natives-` 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.-[-].node` name, scoped under the rule name (`bazel-bin/natives-/…`) 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). diff --git a/packages/natives/CHANGELOG.md b/packages/natives/CHANGELOG.md index 9231ee751..872c3f561 100644 --- a/packages/natives/CHANGELOG.md +++ b/packages/natives/CHANGELOG.md @@ -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