|
| 1 | +# Windows Pack Design |
| 2 | + |
| 3 | +**Date:** 2026-05-19 |
| 4 | +**Status:** Planned (stub guard in place, implementation not yet started) |
| 5 | + |
| 6 | +## Current state |
| 7 | + |
| 8 | +`mcpp pack` is fully functional on Linux and macOS. On Windows it exits early |
| 9 | +with a clear error message directing users to the CI workflow: |
| 10 | + |
| 11 | +``` |
| 12 | +error: `mcpp pack` is not yet supported on Windows. |
| 13 | + Use the CI workflow (ci-windows.yml) to produce Windows zip packages. |
| 14 | + Windows PE packaging (DLL collection + zip) is planned. |
| 15 | +``` |
| 16 | + |
| 17 | +The guard lives at the top of `mcpp::pack::run()` in `src/pack/pack.cppm`. |
| 18 | + |
| 19 | +## Why the current implementation cannot run on Windows |
| 20 | + |
| 21 | +The POSIX implementation relies on three Linux/macOS-only mechanisms: |
| 22 | + |
| 23 | +| Mechanism | POSIX usage | Windows equivalent | |
| 24 | +|---|---|---| |
| 25 | +| `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` | |
| 26 | +| `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 | |
| 27 | +| `tar -czf` | GNU tar — not universally present on Windows before Win11 22H2 | `Compress-Archive` (PowerShell), `7z`, or Win32 `CreateFile`/`MiniZip` | |
| 28 | + |
| 29 | +## Planned Windows pack implementation |
| 30 | + |
| 31 | +### Goal |
| 32 | + |
| 33 | +Produce a self-contained `.zip` archive (not `.tar.gz`) that users can |
| 34 | +extract and run with no additional setup: |
| 35 | + |
| 36 | +``` |
| 37 | +<name>-<version>-x86_64-pc-windows-msvc.zip |
| 38 | +└── <name>-<version>-x86_64-pc-windows-msvc/ |
| 39 | + ├── <name>.exe |
| 40 | + ├── *.dll (bundled DLLs, if any) |
| 41 | + └── README.md / LICENSE (if present) |
| 42 | +``` |
| 43 | + |
| 44 | +### DLL discovery |
| 45 | + |
| 46 | +Replace `ldd_parse()` with a Win32 equivalent: |
| 47 | + |
| 48 | +1. **Primary: `dumpbin /dependents <binary>`** — available when MSVC tools are |
| 49 | + on `PATH`. Produces a list of DLL names; resolve each against `PATH` / |
| 50 | + `%SystemRoot%\System32` / side-by-side assemblies. |
| 51 | + |
| 52 | +2. **Fallback: `PE header walk`** — open the PE file, walk the Import Directory, |
| 53 | + extract DLL names. Can be implemented with `<windows.h>` + `ImageNtHeader`. |
| 54 | + |
| 55 | +3. **Skip-list**: mirror the manylinux skip-list concept for Windows: |
| 56 | + `kernel32.dll`, `user32.dll`, `ntdll.dll`, `vcruntime*.dll` (Redist), |
| 57 | + `api-ms-win-*.dll` (API sets), `ucrtbase.dll`. |
| 58 | + |
| 59 | +### Archive creation |
| 60 | + |
| 61 | +Use `std::filesystem` to copy files into a staging directory, then produce |
| 62 | +the zip with one of: |
| 63 | + |
| 64 | +- **PowerShell** `Compress-Archive` — available on all modern Windows. |
| 65 | + Invoke via `run_capture("powershell -Command \"Compress-Archive ..."`)`. |
| 66 | + Slow for large trees; fine for typical release packages. |
| 67 | +- **libzip / minizip** — statically linkable; avoid the PowerShell dependency. |
| 68 | + Preferred long-term. |
| 69 | + |
| 70 | +### Format |
| 71 | + |
| 72 | +- Output file: `.zip` (not `.tar.gz`) on Windows. |
| 73 | +- `pack::Format` enum needs a new `Zip` variant (or auto-select by platform). |
| 74 | +- `make_plan()` should derive the output extension from the target platform. |
| 75 | + |
| 76 | +### Entry point |
| 77 | + |
| 78 | +No shell wrapper needed on Windows — users double-click `<name>.exe` or run |
| 79 | +it from `cmd.exe` / PowerShell directly. If DLLs are bundled, they should be |
| 80 | +placed in the **same directory** as the executable (the Win32 loader checks |
| 81 | +`%EXE_DIR%` first, before `%PATH%`). |
| 82 | + |
| 83 | +### Implementation checklist (for the future PR) |
| 84 | + |
| 85 | +- [ ] Add `Format::Zip` (or `Format::ZipAuto`) to `pack::Format` |
| 86 | +- [ ] Implement `dumpbin_parse()` (or PE header walk fallback) in `pack.cppm` |
| 87 | + under `#if defined(_WIN32)` |
| 88 | +- [ ] Implement `make_zip()` (PowerShell or libzip) in `pack.cppm` |
| 89 | +- [ ] Remove the `#if defined(_WIN32)` early-return guard from `pack::run()` |
| 90 | + once the above are ready |
| 91 | +- [ ] Add a Windows-specific integration test to `ci-windows.yml` |
| 92 | + |
| 93 | +### CI workflow (current workaround) |
| 94 | + |
| 95 | +Until this is implemented, `ci-windows.yml` zips the raw build output with |
| 96 | +PowerShell `Compress-Archive`. This is good enough for CI artifacts but does |
| 97 | +not collect/bundle DLLs or apply the staging-directory layout. |
0 commit comments