A trivial executable target that consumes generated source from a .buildTool() plugin fails during compilation when launching the build plugin when using -static-stdlib SDKROOT=...WindowsExperimental.sdk.
CI matrix:
| arch |
link |
result |
| x86_64 |
dynamic |
✅ |
| arm64 |
dynamic |
✅ |
| x86_64 |
static (-static-stdlib, SDKROOT=…WindowsExperimental.sdk) |
❌ |
| arm64 |
static (same) |
❌ |
Minimal reproducer: https://github.com/tothambrus11/spm-plugin-windows-static
Failed run: https://github.com/tothambrus11/spm-plugin-windows-static/actions/runs/25639157545
Inaccurate (AI) interpretation
Summary
When SDKROOT points at WindowsExperimental.sdk, building any SwiftPM target that uses a .buildTool() plugin produces a plugin executable that fails to start with STATUS_DLL_NOT_FOUND (0xC0000135) because the Windows loader cannot resolve the PackagePlugin.dll it depends on. SwiftPM swallows the loader failure and surfaces only:
error: build planning stopped due to build-tool plugin failures
— the diagnostic-quality side of which is the subject of a companion issue.
Environment
- Toolchain:
swift-DEVELOPMENT-SNAPSHOT-2026-04-30-a (Windows x86_64 and arm64), installed via compnerd/gha-setup-swift
- SDK:
WindowsExperimental.sdk (via SDKROOT=…\Windows.platform\Developer\SDKs\WindowsExperimental.sdk)
- Build driver: default ("Swift Build") - EDIT:
--build-system native doesn't fix this either.
- Reproduces with both
swift build and swift build -Xswiftc -static-stdlib
Reproducer
Public minimal repository: https://github.com/tothambrus11/spm-plugin-windows-static
A trivial executable target consumes generated source from a .buildTool() plugin. CI matrix:
| arch |
link |
result |
| x86_64 |
dynamic |
✅ |
| arm64 |
dynamic |
✅ |
| x86_64 |
static (-static-stdlib, SDKROOT=…WindowsExperimental.sdk) |
❌ |
| arm64 |
static (same) |
❌ |
Failed run: https://github.com/tothambrus11/spm-plugin-windows-static/actions/runs/25639157545
Observed
The "Inspect plugin executable after failure" step in CI shows, on the produced .build\plugins\cache\BuildPlugin.exe:
ExitCode: -1073741515 ← 0xC0000135 STATUS_DLL_NOT_FOUND
--- stdout --- (empty)
--- stderr --- (empty)
llvm-readobj --coff-imports enumerates the imported DLLs and resolves each against the PATH the runner has (the same PATH SwiftPM uses to invoke the plugin):
[FOUND] FoundationEssentials.dll -> …\Swift\Runtimes\0.0.0\usr\bin\FoundationEssentials.dll
[FOUND] KERNEL32.dll -> C:\Windows\system32\kernel32.dll
[MISSING] PackagePlugin.dll ← root cause
[FOUND] swiftCore.dll -> …\Swift\Runtimes\0.0.0\usr\bin\swiftCore.dll
[FOUND] swift_Concurrency.dll -> …\Swift\Runtimes\0.0.0\usr\bin\swift_Concurrency.dll
[FOUND] VCRUNTIME140.dll -> …\vcruntime140.dll
[FOUND] api-ms-win-crt-*-l1-1-0.dll …
The toolchain does ship PackagePlugin.dll, but at usr\lib\swift\pm\PluginAPI\PackagePlugin.dll — a directory not on PATH and not in the loader's standard search.
Reasoning
- The plugin executable's exit code is
0xC0000135 and both stdout and stderr are empty. By Windows semantics this can only originate from the loader, i.e. before any user main() or PackagePlugin runtime entry point runs. So the failure is not a plugin-protocol error or a SwiftPM/plugin handshake problem.
llvm-readobj enumerates the exact DLL imports of the produced binary, and Get-Command resolves each against the live PATH. Exactly one entry is missing — PackagePlugin.dll — and it is the only Swift runtime DLL the plugin imports that is not under …\Runtimes\0.0.0\usr\bin.
- The matrix isolates the trigger: same package, same toolchain, same plugin target builds and runs successfully in the
dynamic cells (no WindowsExperimental.sdk redirect) and fails in the static cells (with SDKROOT repointed). The only variable is the SDK.
Expected behavior
Either:
- (a) the build-tool plugin executable produced when targeting
WindowsExperimental.sdk should not depend on a PackagePlugin.dll whose location is not in the loader search path (e.g. by linking PackagePlugin statically into plugin executables, or by emitting a manifest / rpath equivalent), or
- (b) SwiftPM should prepend the toolchain
usr\lib\swift\pm\PluginAPI directory to the plugin process's PATH (or use AddDllDirectory / SetDefaultDllDirectories) before invoking the plugin.
(b) appears to be the smaller change and would also benefit any future SDK that ships a similarly placed PackagePlugin.lib import library.
A trivial executable target that consumes generated source from a
.buildTool()plugin fails during compilation when launching the build plugin when using-static-stdlib SDKROOT=...WindowsExperimental.sdk.CI matrix:
-static-stdlib,SDKROOT=…WindowsExperimental.sdk)Minimal reproducer: https://github.com/tothambrus11/spm-plugin-windows-static
Failed run: https://github.com/tothambrus11/spm-plugin-windows-static/actions/runs/25639157545
Inaccurate (AI) interpretation
Summary
When
SDKROOTpoints atWindowsExperimental.sdk, building any SwiftPM target that uses a.buildTool()plugin produces a plugin executable that fails to start withSTATUS_DLL_NOT_FOUND(0xC0000135) because the Windows loader cannot resolve thePackagePlugin.dllit depends on. SwiftPM swallows the loader failure and surfaces only:— the diagnostic-quality side of which is the subject of a companion issue.
Environment
swift-DEVELOPMENT-SNAPSHOT-2026-04-30-a(Windows x86_64 and arm64), installed viacompnerd/gha-setup-swiftWindowsExperimental.sdk(viaSDKROOT=…\Windows.platform\Developer\SDKs\WindowsExperimental.sdk)--build-system nativedoesn't fix this either.swift buildandswift build -Xswiftc -static-stdlibReproducer
Public minimal repository: https://github.com/tothambrus11/spm-plugin-windows-static
A trivial executable target consumes generated source from a
.buildTool()plugin. CI matrix:-static-stdlib,SDKROOT=…WindowsExperimental.sdk)Failed run: https://github.com/tothambrus11/spm-plugin-windows-static/actions/runs/25639157545
Observed
The "Inspect plugin executable after failure" step in CI shows, on the produced
.build\plugins\cache\BuildPlugin.exe:llvm-readobj --coff-importsenumerates the imported DLLs and resolves each against thePATHthe runner has (the samePATHSwiftPM uses to invoke the plugin):The toolchain does ship
PackagePlugin.dll, but atusr\lib\swift\pm\PluginAPI\PackagePlugin.dll— a directory not onPATHand not in the loader's standard search.Reasoning
0xC0000135and both stdout and stderr are empty. By Windows semantics this can only originate from the loader, i.e. before any usermain()or PackagePlugin runtime entry point runs. So the failure is not a plugin-protocol error or a SwiftPM/plugin handshake problem.llvm-readobjenumerates the exact DLL imports of the produced binary, andGet-Commandresolves each against the livePATH. Exactly one entry is missing —PackagePlugin.dll— and it is the only Swift runtime DLL the plugin imports that is not under…\Runtimes\0.0.0\usr\bin.dynamiccells (noWindowsExperimental.sdkredirect) and fails in thestaticcells (withSDKROOTrepointed). The only variable is the SDK.Expected behavior
Either:
WindowsExperimental.sdkshould not depend on aPackagePlugin.dllwhose location is not in the loader search path (e.g. by linking PackagePlugin statically into plugin executables, or by emitting a manifest / rpath equivalent), orusr\lib\swift\pm\PluginAPIdirectory to the plugin process'sPATH(or useAddDllDirectory/SetDefaultDllDirectories) before invoking the plugin.(b) appears to be the smaller change and would also benefit any future SDK that ships a similarly placed
PackagePlugin.libimport library.