From 246dda7f1caca85164c103801e6f7b6d3c7f62f8 Mon Sep 17 00:00:00 2001 From: roboomp Date: Thu, 13 Aug 2026 16:00:33 +0000 Subject: [PATCH 1/2] 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 From 2e2bf1f3a52c6ec6be0d6dc972693e540e4093a2 Mon Sep 17 00:00:00 2001 From: roboomp Date: Thu, 13 Aug 2026 16:21:23 +0000 Subject: [PATCH 2/2] fix(natives): pin cmake opus build to static MSVC CRT (/MT) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The win32 addon static-CRT switch enabled the static_link_msvcrt cc feature, but audiopus_sys's bundled opus is built through the generated CMake toolchain, which pinned CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL (/MD) as authoritative under CMP0091 NEW. The opus objects could then still emit /MD and pull VCRUNTIME140.dll / conflict with the static CRT the rest of the addon links, leaving the outcome dependent on compile- flag ordering. Pin CMAKE_MSVC_RUNTIME_LIBRARY to MultiThreaded (static release /MT) in the msvc toolchain.cmake so opus deterministically matches rustc's +crt-static and the static_link_msvcrt feature. Verified with a fully cold `bazel build //:natives-win32-x64-baseline` (opus recompiled): the produced .node imports no VCRUNTIME140.dll and no api-ms-win-crt-* — only core Windows system DLLs. Fixes #8439 --- bazel/toolchains/msvc/NOTES.md | 3 ++- bazel/toolchains/msvc/cc.bzl | 19 ++++++++++++------- docs/natives-build-release-debugging.md | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/bazel/toolchains/msvc/NOTES.md b/bazel/toolchains/msvc/NOTES.md index 1ba1177ab..6b1b33d15 100644 --- a/bazel/toolchains/msvc/NOTES.md +++ b/bazel/toolchains/msvc/NOTES.md @@ -102,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. diff --git a/bazel/toolchains/msvc/cc.bzl b/bazel/toolchains/msvc/cc.bzl index 564aa0baf..627ae2906 100644 --- a/bazel/toolchains/msvc/cc.bzl +++ b/bazel/toolchains/msvc/cc.bzl @@ -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 = """\ diff --git a/docs/natives-build-release-debugging.md b/docs/natives-build-release-debugging.md index 41985996e..02b77e1b1 100644 --- a/docs/natives-build-release-debugging.md +++ b/docs/natives-build-release-debugging.md @@ -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 |