Date: 2026-05-19
Status: SUPERSEDED and implemented, 2026-08-17. See
2026-08-16-windows-toolchain-three-axes-design.md §4 for the design that
shipped, and src/pack/binfmt.cppm / src/pack/zip.cppm for the code.
Everything below assumes pack runs on Windows. That assumption is visible in every proposal:
dumpbin /dependents,ImageNtHeaderfrom<windows.h>, PowerShell'sCompress-Archive, and an implementation "under#if defined(_WIN32)".The assumption came from the guard it was trying to remove.
packrefused Windows, so the problem looked like "pack has no Windows branch". It was not: the ELF closure is derived by RUNNING the artifact (LD_TRACE_LOADED_OBJECTS=1 '<binary>'), so it can cross neither an OS nor an ARCHITECTURE — a Linux box cannot trace a PE, and an x86_64 box cannot trace an aarch64 ELF either. The#if defined(_WIN32)was that limitation surfacing at the nearest place a user would hit it.Reading the import table statically removes both limits at once, and then cross-OS packaging is not a feature that had to be added — it is what remains when the obstacle is gone. Each Windows-only tool above would have reintroduced the obstacle one layer down.
What did survive from here: the
.zipoutput, the flat DLLs-beside-the-.exelayout, no wrapper script, and the skip-list concept (with one correction —vcruntime*.dllis listed below as a system DLL, and it is not: it belongs to the TOOLSET, and whether it travels iscxx_runtime's decision).
mcpp pack is fully functional on Linux and macOS. On Windows it exits early
with a clear error message directing users to the CI workflow:
error: `mcpp pack` is not yet supported on Windows.
Use the CI workflow (ci-windows.yml) to produce Windows zip packages.
Windows PE packaging (DLL collection + zip) is planned.
The guard lives at the top of mcpp::pack::run() in src/pack/pack.cppm.
The POSIX implementation relies on three Linux/macOS-only mechanisms:
| Mechanism | POSIX usage | Windows equivalent |
|---|---|---|
LD_TRACE_LOADED_OBJECTS=1 |
Tells the ELF dynamic linker to print deps without executing main() |
No direct equivalent. Would need dumpbin /dependents (MSVC) or ldd emulation via LoadLibraryEx |
patchelf |
Rewrites RUNPATH / PT_INTERP ELF headers in-place |
Not applicable to PE/COFF. DLL search order is controlled by the OS loader and manifest, not embedded paths |
tar -czf |
GNU tar — not universally present on Windows before Win11 22H2 | Compress-Archive (PowerShell), 7z, or Win32 CreateFile/MiniZip |
Produce a self-contained .zip archive (not .tar.gz) that users can
extract and run with no additional setup:
<name>-<version>-x86_64-pc-windows-msvc.zip
└── <name>-<version>-x86_64-pc-windows-msvc/
├── <name>.exe
├── *.dll (bundled DLLs, if any)
└── README.md / LICENSE (if present)
Replace ldd_parse() with a Win32 equivalent:
-
Primary:
dumpbin /dependents <binary>— available when MSVC tools are onPATH. Produces a list of DLL names; resolve each againstPATH/%SystemRoot%\System32/ side-by-side assemblies. -
Fallback:
PE header walk— open the PE file, walk the Import Directory, extract DLL names. Can be implemented with<windows.h>+ImageNtHeader. -
Skip-list: mirror the manylinux skip-list concept for Windows:
kernel32.dll,user32.dll,ntdll.dll,vcruntime*.dll(Redist),api-ms-win-*.dll(API sets),ucrtbase.dll.
Use std::filesystem to copy files into a staging directory, then produce
the zip with one of:
- PowerShell
Compress-Archive— available on all modern Windows. Invoke viarun_capture("powershell -Command \"Compress-Archive ...")`. Slow for large trees; fine for typical release packages. - libzip / minizip — statically linkable; avoid the PowerShell dependency. Preferred long-term.
- Output file:
.zip(not.tar.gz) on Windows. pack::Formatenum needs a newZipvariant (or auto-select by platform).make_plan()should derive the output extension from the target platform.
No shell wrapper needed on Windows — users double-click <name>.exe or run
it from cmd.exe / PowerShell directly. If DLLs are bundled, they should be
placed in the same directory as the executable (the Win32 loader checks
%EXE_DIR% first, before %PATH%).
- Add
Format::Zip(orFormat::ZipAuto) topack::Format - Implement
dumpbin_parse()(or PE header walk fallback) inpack.cppmunder#if defined(_WIN32) - Implement
make_zip()(PowerShell or libzip) inpack.cppm - Remove the
#if defined(_WIN32)early-return guard frompack::run()once the above are ready - Add a Windows-specific integration test to
ci-windows.yml
Until this is implemented, ci-windows.yml zips the raw build output with
PowerShell Compress-Archive. This is good enough for CI artifacts but does
not collect/bundle DLLs or apply the staging-directory layout.