Add explicit MinGW Windows targets - #10637
Conversation
|
Validated this downstream in roc-platform-template-go#13 using this PR at Results:
One downstream detail found during validation: Go Windows/ARM64 c-archives contained the required exported callbacks but lacked the archive symbol index needed by The downstream PR remains draft until these target changes are available in a nightly, at which point its Roc pin can be advanced and the new Windows x64 consumer jobs can run. |
Greptile SummaryThis PR separates MSVC and MinGW Windows target identities and carries that ABI choice through target selection, builtins, platform inputs, and linking.
Confidence Score: 4/5The PR does not yet appear safe to merge because unsupported Windows ABIs can still abort target detection and build configuration.
Files Needing Attention: src/target/mod.zig, build.zig
|
| Filename | Overview |
|---|---|
| src/target/mod.zig | Adds explicit Windows ABI-aware target identities, but the previously reported unsupported-ABI forced unwrap remains. |
| src/cli/target_selection.zig | Extends CLI target parsing and selection to distinguish MinGW targets from existing MSVC targets. |
| src/cli/linker.zig | Carries the selected Windows ABI into linking and separates MSVC discovery from explicit MinGW runtime inputs. |
| build.zig | Builds MinGW target artifacts and distributes vendored runtime inputs across test platforms, while still calling the panic-prone target conversion path. |
| ci/vendor_mingw_runtime.py | Reproducibly discovers and vendors fixed MinGW startup objects, runtime archives, and import libraries. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Selection[Target selection] --> ABI{Windows ABI}
ABI -->|MSVC| MSVCTarget[x64win / arm64win]
ABI -->|GNU| MinGWTarget[x64mingw / arm64mingw]
MSVCTarget --> MSVCInputs[SDK and MSVC discovery]
MinGWTarget --> MinGWInputs[Platform-declared startup and runtime inputs]
MSVCInputs --> Linker[Windows linker]
MinGWInputs --> LLD[LLD MinGW mode]
Linker --> Artifact[Windows artifact]
LLD --> Artifact
Reviews (3): Last reviewed commit: "Make the dylib test host a multi-member ..." | Re-trigger Greptile
| switch (classifyOs(os)) { | ||
| .macos => return .x64mac, | ||
| .windows => return .x64win, | ||
| .windows => return switch (windowsAbiFromStd(abi).?) { |
On Windows, `roc <app>` fails to link any app with a platform that carries
its own compiler-rt:
lld-link: error: duplicate symbol: memcpy
>>> defined at roc_machine_code_shim.lib(compiler_rt.obj)
In run mode the machine-code shim is substituted at the `app` position of the
platform's `targets:` inputs, so it lands next to a platform host archive that
is already a compiler-rt carrier. COFF rejects the overlapping definitions
outright -- the same hazard already noted on the boxy object in this file.
`roc build` is unaffected because it never links the shim.
The shim does not need the bundled copy. Its own objects reference only
memcpy, memmove and memset:
roc_machine_code_shim_zcu.obj -> memcpy, memmove, memset
roc_builtins.obj -> memcpy, memmove, memset
host_trampoline.obj -> (none)
`bundle_compiler_rt` additionally defines __extendhfsf2, floor, fmaxl,
__fixdfti, __modti3, __netf2 and the rest, none of which the shim calls, and
those surplus definitions are exactly what collide.
Applied to every target rather than just Windows. Keeping it ABI-conditional
would get harder as more Windows targets land (#10637), and the surplus
definitions are no more wanted on ELF and Mach-O than on COFF -- those formats
just tolerate the overlap today.
Verified on Windows with a rebuilt roc:
- running a roc-ray app against its released platform bundle links, opens a
window and exits cleanly
- headerless default-platform runs still work, including a runtime app doing
I128 division, Dec.sqrt and F64.sqrt -- this is the path where the shim was
the sole compiler-rt carrier, since default_platform_runtime_obj and
builtins_obj both set bundle_compiler_rt = false
The Linux shim is -nostdlib with link_libc = false, so its mem* references now
have to resolve from the rest of the link; Linux and macOS coverage here is
CI's, not local.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A stock `zig build` on Windows targets the gnu ABI, so the native target is now x64mingw rather than x64win, and a MinGW link only sees what the platform declares. Give every test platform x64mingw and arm64mingw entries that list Zig 0.16.0's mingw-w64 startup objects, runtime archives, and UCRT/Win32 import libraries, and check those files in under test/fx (regenerate with ci/vendor_mingw_runtime.py), the same way the musl crt1.o/libc.a are. build.zig copies them into the other test platforms next to host.lib, and the hosts that were hard-coded to the MSVC target dir now follow the native ABI. Shared outputs list dllcrt2.obj (DllMainCRTStartup) instead of crt2.obj, archives list no startup object, and http-headers adds ws2_32.lib for its Winsock host. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
matchesHostOsAndArch ignores the ABI, so on a mingw-native host both x64win and x64mingw matched and the platform's listing order decided the default. The interpreter backend only builds for the exact native target, so a platform listing x64win first failed `roc build` outright. Skip a host target in the other ABI whenever the platform lists one in the native ABI (in either CPU-level spelling). Listing order still decides among wasm and cross targets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LLD's MinGW-mode DLL entry is _DllMainCRTStartup while mingw-w64's dllcrt2 defines DllMainCRTStartup; alias them the way `zig cc -shared` does. Vtable-ABI links wrapped every Windows platform input in /wholearchive. A MinGW platform lists its C runtime and import libraries after `app`, and force-loading every member of libmingw32 and of each import library collides on overlapping definitions (__NULL_IMPORT_DESCRIPTOR, the delay-load helpers, ...). Link the post-app inputs lazily on MinGW; host archives that need full inclusion come before `app`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The LLVM backend references hosted functions weakly, and a weak reference alone never extracts an archive member. LLD's COFF symbol table goes further: a weak reference that reaches a lazy archive entry before a strong one replaces it with a plain undefined, so with a multi-member host archive (a Go c-archive, say) every hosted symbol the app used resolved to null and the program called through a null pointer. Whether it happened depended on which other symbols the host's own startup object still had to drag in. Pass every hosted symbol the app references as a link root (/include on COFF, --undefined on ELF, -u on Mach-O) so the link must resolve it from the platform inputs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every test platform host was a single object, so its hosted functions always came along with whatever pulled that object in, and the hosted symbol rooting in the previous commit had no in-repo test. Move the dylib platform's hosted functions into host_hosted.zig, compiled as a second archive member that nothing but the app references. Without the rooting, that member is never extracted: the shared DCE canary is missing from the DLL and the loader dies calling through null. With it, run-test-dylib passes unchanged, including its section-GC assertions, which still hold within the member. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
149cc4f to
0d0276a
Compare
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
On Windows, `roc <app>` fails to link any app with a platform that carries
its own compiler-rt:
lld-link: error: duplicate symbol: memcpy
>>> defined at roc_machine_code_shim.lib(compiler_rt.obj)
In run mode the machine-code shim is substituted at the `app` position of the
platform's `targets:` inputs, so it lands next to a platform host archive that
is already a compiler-rt carrier. COFF rejects the overlapping definitions
outright -- the same hazard already noted on the boxy object in this file.
`roc build` is unaffected because it never links the shim.
The shim does not need the bundled copy. Its own objects reference only
memcpy, memmove and memset:
roc_machine_code_shim_zcu.obj -> memcpy, memmove, memset
roc_builtins.obj -> memcpy, memmove, memset
host_trampoline.obj -> (none)
`bundle_compiler_rt` additionally defines __extendhfsf2, floor, fmaxl,
__fixdfti, __modti3, __netf2 and the rest, none of which the shim calls, and
those surplus definitions are exactly what collide.
Applied to every target rather than just Windows. Keeping it ABI-conditional
would get harder as more Windows targets land (#10637), and the surplus
definitions are no more wanted on ELF and Mach-O than on COFF -- those formats
just tolerate the overlap today.
Verified on Windows with a rebuilt roc:
- running a roc-ray app against its released platform bundle links, opens a
window and exits cleanly
- headerless default-platform runs still work, including a runtime app doing
I128 division, Dec.sqrt and F64.sqrt -- this is the path where the shim was
the sole compiler-rt carrier, since default_platform_runtime_obj and
builtins_obj both set bundle_compiler_rt = false
The Linux shim is -nostdlib with link_libc = false, so its mem* references now
have to resolve from the rest of the link; Linux and macOS coverage here is
CI's, not local.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
roc-lang#10637 added x64mingw, arm64mingw, x64v1mingw and arm64v1mingw to RocTarget and roc-lang#10904 added a switch over RocTarget for the default platform's compiler-rt object. Neither branch saw the other, so both merged cleanly and left the switch missing four cases, which stops roc compiling at all on every platform. The default platform only carries a compiler-rt object for musl and glibc, so the MinGW targets join the other Windows targets in returning null.
Summary
x64win,x64v1win,arm64win, andarm64v1winas MSVC targetsx64mingw,x64v1mingw,arm64mingw, andarm64v1mingwtargets using GNU Windows triples and target-specific embedded objectsdesign.mdRoot cause
Roc represented Windows as one target family. Both target triples were MSVC triples, and the Windows linker path unconditionally resolved an MSVC libc installation, added Windows SDK/MSVC library paths, and linked MSVC default libraries. COFF alone cannot identify whether a platform archive expects the MSVC or MinGW runtime, so a Go cgo archive built with a GNU Windows toolchain could not be linked correctly.
This change makes that ABI choice explicit at target selection and preserves it through code generation, embedded builtins, platform input lookup, and final linking. MinGW linking uses
lld-link -lldmingw, disables implicit default libraries, and consumes the startup objects, runtime archives, and import libraries declared by the platform.Validation
zig build minici --summary failures— 75/75 phases passedc-archivehosts forwindows/amd64andwindows/arm64using Zig GNU Windows targets--opt=speedand--opt=devCloses #8779.