Skip to content

fix(win): ship the OpenMP runtime the ggml backends actually import - #330

Merged
EtienneLescot merged 2 commits into
mainfrom
fix/win-openmp-runtime
Aug 10, 2026
Merged

fix(win): ship the OpenMP runtime the ggml backends actually import#330
EtienneLescot merged 2 commits into
mainfrom
fix/win-openmp-runtime

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

The bug the guard was supposed to prevent, still shipping

1.9.1 was meant to end the Visual C++ Redistributable dependency. It did not.

ggml-base.dll  →  VCOMP140.DLL
ggml-cpu.dll   →  VCOMP140.DLL

vcomp140.dll is Microsoft's OpenMP runtime. It comes with the Visual C++ Redistributable and is not part of Windows — verified on this machine: version 14.51.36247, product "Microsoft Visual Studio", the same version as the redistributable entries in Programs and Features.

On a clean machine whisper-stt-server.exe therefore still dies in the loader before main(), and transcription and captions fail with the unactionable timeout before-pack.cjs describes. It is in the 1.9.1 release and in the package currently in Store certification.

Why the guard missed it

Two defects, and the second is the more useful lesson.

It enumerated the members that had already bitten, not the family. msvcp/vcruntime/concrt — and vcomp starts with none of them. Now: msvcp, vcruntime, concrt, vcomp, vcamp, mfc.

It tested the wrong property. It asked "does this import a redistributable DLL?" when what matters is "does this import a redistributable DLL that will not be there?" Those differ in exactly one case — when the DLL ships alongside — which is the remedy. So the check as written forbade its own fix. It now ignores an import satisfied from the same directory, the same colocation that already carries the ffmpeg DLLs.

The fix

scripts/stage-vcomp-runtime.mjs copies vcomp140.dll out of the Visual Studio redistributable directory into the payload. It globs the toolset tag, which moves with the compiler (Microsoft.VC143.OpenMP on the 2022 runners, VC145 on a 2026 install), and prefers the redistributable copy over System32 because that is the one Microsoft licenses for redistribution. win.extraResources carries it like everything else in that folder. Wired into build:win and build:win:store.

Shipping it rather than -DGGML_OPENMP=OFF is deliberate. The DLL leaves the computation identical. Dropping OpenMP swaps its scheduler for ggml's own and changes transcription throughput by an amount nobody has measured — 200 KB against that unknown is a cheap trade, and the alternative stays open. Measure first.

Verified both directions

With the DLL staged, the check passes. With it removed:

Refusing to package binaries that need the Visual C++ Redistributable.
  - ggml-base.dll imports VCOMP140.DLL
  - ggml-cpu.dll imports VCOMP140.DLL

The negative case is the one that matters — it is precisely what the previous version could not see. Run against the real payload, not a fixture.

How it was found, and what else the sweep turned up

By walking the full import closure of the shipped payload and asking which DLLs come from outside it. That is the only method available for an absence: no amount of running the app on a machine that has the redistributable can reveal a machine that does not.

71 distinct external DLLs. After this change every one of them is either shipped beside the payload or present on every Windows edition — with one exception, recorded in the documentation and deliberately not fixed here:

mf.dll, mfplat.dll, mfreadwrite.dll  →  wgc-capture.exe

Media Foundation is absent from Windows N editions unless the user installs the Media Feature Pack. Recording would fail there with the same 0xC0000135 that rejected 1.9.1. Untested and unhandled; N editions are sold in Europe.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Windows packaging to include required Visual C++ runtime libraries, preventing failures caused by missing OpenMP components.
    • Enhanced dependency checks to recognize bundled runtime files and provide clearer remediation guidance.
  • Documentation

    • Added licensing and attribution details for the bundled OpenMP runtime.
    • Documented Windows packaging requirements, troubleshooting steps, and the remaining Windows N-edition limitation.

1.9.1 was supposed to have ended the Visual C++ Redistributable dependency. It
did not. ggml-base.dll and ggml-cpu.dll are compiled with OpenMP and import
vcomp140.dll, which is part of that redistributable and not of Windows, so on a
clean machine whisper-stt-server still dies in the loader before main() and
transcription and captions fail with the unactionable timeout before-pack.cjs
describes.

The guard added with that fix reported the payload clean throughout, because it
matched msvcp/vcruntime/concrt and `vcomp` starts with none of the three. Two
things were wrong with it, and the second matters more than the missing prefix:

It enumerated the members that had already bitten rather than the family, so it
now covers msvcp, vcruntime, concrt, vcomp, vcamp and mfc.

And it tested "imports a redistributable DLL" when the property worth testing is
"imports a redistributable DLL that will not be there". Those differ exactly when
the DLL ships alongside — which is the remedy — so the old wording forbade its own
fix. It now ignores an import that is satisfied from the same directory, the same
colocation that already carries the ffmpeg DLLs.

stage-vcomp-runtime.mjs copies vcomp140.dll out of the Visual Studio
redistributable directory, globbing the toolset tag because it moves with the
compiler (VC143 on the 2022 runners, VC145 on a 2026 install), and preferring the
redistributable copy over System32 because that is the one Microsoft licenses for
redistribution. win.extraResources carries it like everything else in that folder.

Shipping it rather than rebuilding whisper with -DGGML_OPENMP=OFF is deliberate.
The DLL leaves the computation identical; dropping OpenMP swaps its scheduler for
ggml's own and changes transcription throughput by an amount nobody has measured.
200 KB against that unknown is a cheap trade, and the alternative stays open —
measure first.

Verified both directions against the real payload: with the DLL staged the check
passes, and with it removed it names ggml-base.dll and ggml-cpu.dll. The negative
case is the one that matters, since it is precisely what the previous version
could not see.

Found by walking the full import closure of the shipped payload rather than by
running anything, which is the only method available for an absence. The same
sweep leaves one unresolved item recorded in the documentation and not addressed
here: wgc-capture.exe imports mf.dll, mfplat.dll and mfreadwrite.dll, and Media
Foundation is absent from Windows N editions unless the user installs the Media
Feature Pack. Recording would fail there with the same 0xC0000135. N editions are
sold in Europe.
…it is

Two review findings, both real, and the first turned out to have a second half.

A directory answers `readdirSync` by name exactly as a file does, so an entry
called `vcomp140.dll` marked the import satisfied while the loader would still
have found nothing — a guard reporting clean for the one reason it exists. That
was the finding. The same confusion sat on the scanning side, where such an entry
was opened as a binary and killed the hook with a raw `EISDIR`: the build stopped,
which is right, on a message naming nothing, which is not. One list of regular
files now feeds both questions.

This directory really does hold subdirectories — the vendored ffmpeg SDK — so the
distinction is unlikely rather than hypothetical.

Root discovery no longer assumes the two default install paths. It reuses
`findVcVarsAll` from scripts/msvcEnv.mjs, the same lookup the two native build
scripts already run, which consults VCVARSALL, then vswhere, then VSINSTALLDIR,
then sweeps for the pre-release channels vswhere does not enumerate. That covers
the custom installation path the review raised, without adding a vswhere
dependency this repo had already decided it could not rely on — vswhere is not on
PATH on the machine this was written on, which is why msvcEnv grew its fallbacks
in the first place. The derived root is searched alone when it yields anything,
which prefers the toolchain that actually compiled the helpers and avoids
re-walking a large tree twice.

Verified all three states against the real payload: with the DLL present the
check passes; with a directory of that name it refuses and names ggml-base.dll and
ggml-cpu.dll; with the file simply absent, likewise. Staging still resolves the
redistributable copy, now through discovery rather than a fixed path.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b75c9213-0de6-4bb9-8266-aaa709d71884

📥 Commits

Reviewing files that changed from the base of the PR and between 2bb038a and 6a87b61.

📒 Files selected for processing (3)
  • scripts/before-pack.cjs
  • scripts/stage-vcomp-runtime.mjs
  • technical-documentation/engineering/build-and-packaging.md

📝 Walkthrough

Walkthrough

The Windows build stages vcomp140.dll from Visual Studio installations. Packaging validation recognizes more Visual C++ runtime families and accepts DLLs shipped beside native binaries. Notices and build documentation describe the runtime and packaging behavior.

Changes

Windows runtime packaging

Layer / File(s) Summary
Runtime discovery and staging
scripts/stage-vcomp-runtime.mjs
The Windows-only script finds x64 vcomp140.dll candidates, excludes onecore copies, selects the newest version, reports missing installations, and copies the DLL beside the native payload.
Packaging integration and dependency validation
package.json, scripts/before-pack.cjs, technical-documentation/engineering/build-and-packaging.md, THIRD-PARTY-NOTICES.md
Windows build scripts invoke runtime staging. Packaging checks cover additional redistributable families and accept matching DLLs shipped in the payload. Documentation and notices describe the runtime and packaging requirements.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant WindowsBuild
  participant RuntimeStager
  participant VisualStudio
  participant NativePayload
  participant PackagingCheck
  WindowsBuild->>RuntimeStager: Run stage:vcomp
  RuntimeStager->>VisualStudio: Find x64 vcomp140.dll candidates
  VisualStudio-->>RuntimeStager: Return candidates
  RuntimeStager->>NativePayload: Copy newest supported DLL
  WindowsBuild->>PackagingCheck: Scan native binaries
  PackagingCheck->>NativePayload: Check shipped redistributable DLLs
  NativePayload-->>PackagingCheck: Return colocated DLL names
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: shipping the OpenMP runtime required by the ggml backends.
Description check ✅ Passed The description provides a detailed summary, rationale, implementation details, Windows impact, testing results, and the documented N-edition limitation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/win-openmp-runtime

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/stage-vcomp-runtime.mjs (1)

45-50: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Resolve Visual Studio installations instead of relying on fixed roots.

If Visual Studio or Build Tools uses a custom installation path, the native build can succeed but findRedistCopies() returns no candidates and stops packaging. Use vswhere.exe to discover installed instances and scan their installation paths. If custom paths are unsupported, document and enforce that constraint in CI.

# Run in PowerShell on a Windows build agent.
$vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe"
$roots = & $vswhere -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
$roots | ForEach-Object {
	Get-ChildItem -Path $_ -Filter "vcomp140.dll" -Recurse -File -ErrorAction SilentlyContinue |
		Where-Object { $_.FullName -match "\\Redist\\" -and $_.FullName -match "\\x64\\" } |
		Select-Object -ExpandProperty FullName
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/stage-vcomp-runtime.mjs` around lines 45 - 50, Update
findRedistCopies() to discover Visual Studio and Build Tools installations
through vswhere.exe, including custom installation paths, instead of only
scanning the two hard-coded roots. Query instances requiring
Microsoft.VisualStudio.Component.VC.Tools.x86.x64, use each returned
installationPath as a scan root, and preserve filtering for the required
vcomp140.dll redist candidates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/before-pack.cjs`:
- Around line 366-377: Update the shipped-entry collection near
scanned/offenders to include only directory entries that are regular files, not
directories or other filesystem types. Use the resulting regular-file name set
when applying the !shipped.has(d.toLowerCase()) exclusion in the offenders
calculation.

---

Nitpick comments:
In `@scripts/stage-vcomp-runtime.mjs`:
- Around line 45-50: Update findRedistCopies() to discover Visual Studio and
Build Tools installations through vswhere.exe, including custom installation
paths, instead of only scanning the two hard-coded roots. Query instances
requiring Microsoft.VisualStudio.Component.VC.Tools.x86.x64, use each returned
installationPath as a scan root, and preserve filtering for the required
vcomp140.dll redist candidates.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c90e91e-a421-4fcc-a672-e7c7b03ba216

📥 Commits

Reviewing files that changed from the base of the PR and between 1617c93 and 2bb038a.

📒 Files selected for processing (5)
  • THIRD-PARTY-NOTICES.md
  • package.json
  • scripts/before-pack.cjs
  • scripts/stage-vcomp-runtime.mjs
  • technical-documentation/engineering/build-and-packaging.md

Comment thread scripts/before-pack.cjs
@EtienneLescot
EtienneLescot force-pushed the fix/win-openmp-runtime branch from 2bb038a to 3cef11a Compare August 10, 2026 16:26
@EtienneLescot
EtienneLescot merged commit f3902dc into main Aug 10, 2026
14 of 15 checks passed
@EtienneLescot
EtienneLescot deleted the fix/win-openmp-runtime branch August 10, 2026 17:54
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