Skip to content

build: bundle MSVC runtime DLLs into win32 zips - #6

Open
greatcoat wants to merge 1 commit into
masterfrom
fix/bundle-msvc-runtime-dlls
Open

build: bundle MSVC runtime DLLs into win32 zips#6
greatcoat wants to merge 1 commit into
masterfrom
fix/bundle-msvc-runtime-dlls

Conversation

@greatcoat

Copy link
Copy Markdown

Problem: Every win32 zip ships a bare exe that imports the dynamic MSVC runtime; machines without the VC++ redistributable kill it at load with 0xC0000135, breaking OpenWhispr local transcription.

Fix: Copy msvcp140/vcruntime140/vcruntime140_1/vcomp140 from the runner's VC redist dir (globbed toolset path) into dist for the CPU, CUDA, and Vulkan jobs, include them in every Compress-Archive, and fail CI if any zip lacks them.

Root cause

The build passes -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded intending a static CRT, but that variable is a silent no-op while CMake policy CMP0091 is unset — so the exes link the dynamic CRT — and MSVC OpenMP links VCOMP140.DLL dynamically regardless. The packaging steps then zip the bare exe (CPU, Vulkan) or exe + CUDA DLLs only (CUDA). Nothing else in OpenWhispr's shipping path provides the runtime: the Windows loader terminates the process with 0xC0000135 (STATUS_DLL_NOT_FOUND) before main(), with empty stderr.

PE import tables of the shipped 0.0.8/0.0.9 exes (parsed from the release assets — dumpbin-equivalent): all five win32 exes (whisper-cpp-cpu/-cuda, whisper-server-cpu/-cuda/-vulkan) import MSVCP140.dll, VCOMP140.DLL, VCRUNTIME140.dll, VCRUNTIME140_1.dll; none of the zips contains any of them (0.0.8 + 0.0.9 asset listings verified). The Vulkan packaging comment claiming "no other runtime DLLs are needed" was factually wrong and is corrected.

Fix details

  • build-windows-x64-cpu — copy the 4 DLLs from $env:VCToolsRedistDir\x64\Microsoft.VC*.CRT / ...\Microsoft.VC*.OpenMP into dist, include them in both Compress-Archive calls (the whisper-cpp-*.zip cli asset had the same defect).
  • build-windows-x64-cuda — same 4 DLLs appended to the existing $bundledDlls list (after the CUDA-DLL count check, so that check keeps its meaning).
  • build-windows-x64-vulkan — same 4 DLLs added to the zip; vulkan-1.dll stays unbundled (GPU driver provides it).
  • New "Verify zips contain the MSVC runtime DLLs" step per Windows job: lists each zip's entries and fails the build if any of the four DLLs is missing.
  • Release-notes body updated to say the MSVC runtime is bundled.
  • Toolset dir is globbed (Microsoft.VC*.CRT), not hardcoded to VC143 — the toolset version moves with the runner image.

Deliberate non-changes: not switching to a real static CRT (CMP0091=NEW + GGML_OPENMP=OFF) — bundling app-locally is the approved scope; the static alternative is documented in the RCA and needs an explicit go. CUDA DLL bundling logic untouched.

Tests

  • RED: the new verify-step logic (same semantics, run against the real release assets) fails every shipped win32 zip: 0.0.8 cpu/cuda/vulkan × cli/server and 0.0.9 cpu-server all report missing: msvcp140.dll, vcomp140.dll, vcruntime140.dll, vcruntime140_1.dll. A simulated dist with the four DLLs passes.
  • Field repro: customer machine (Win10, no redist) reproduced 0xC0000135 on every whisper-server spawn; installing the VC++ redistributable — the only change — immediately fixed transcription (2026-08-17). Clean-VM verification of the packaged zips runs when the next release is cut, before it ships.
  • CI on this PR does not exercise the workflow (it triggers on workflow_dispatch/tag only); dispatching it publishes a release, so first real run = the 0.0.10 cut after merge.

Provenance

  • RCA: Titan wiki/areas/engineering/whisper-server-dll-not-found-rca.md; Linear CUS-113 (evidence chain in RCA comment).
  • Customer: Brian, gmail thread 1a00fc15dfac3569, debug-2026-08-17T12-23-22-137Z.log (14 spawns, all exit 3221225781 in ~50 ms, empty stderr).
  • Note: release 0.0.9 already exists (2026-08-12, adds Pascal sm_61; still DLL-less) — the fixed release is 0.0.10, and openwhispr-desktop's WHISPER_CPP_TAG bump targets 0.0.10 (companion PR).

🤖 Generated with Claude Code

The win32 exes link the dynamic MSVC runtime (CMAKE_MSVC_RUNTIME_LIBRARY=
MultiThreaded is a no-op while CMP0091 is unset, and OpenMP links vcomp140
dynamically regardless), but every win32 zip shipped a bare exe. On machines
without the VC++ 2015-2022 redistributable the loader kills whisper-server
with 0xC0000135 (STATUS_DLL_NOT_FOUND) before main(), breaking local
transcription in OpenWhispr (CUS-113).

Bundle msvcp140/vcruntime140/vcruntime140_1/vcomp140 from the runner's VC
redist dir (globbed, not pinned to VC143) into the CPU, CUDA, and Vulkan
zips, and fail CI if any zip is missing them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant