Disclosure: this project was human-directed and AI-written.
This repo checks whether small CLIs can be built into the same Linux ELF binary from two host operating systems:
Windows cross-build output
vs
WSL/Linux native-build output
The test compares final executable SHA256 hashes byte-for-byte.
For me, reproducible builds reduce the overhead of preparing to contribute to an open-source project.
Before submitting a change to an unfamiliar project, I usually rebuild the published release from its release commit. If my local binary does not match the published binary, several questions appear:
- Did the release build use different compiler flags that may affect performance, behavior, or compatibility?
- Did the maintainer intentionally add a post-build step such as signing? If that is the only difference, I can safely ignore it, even if I cannot produce the exact same binary locally.
- Was the published binary accidentally replaced with something dangerous?
Without reproducibility, I have to separate these cases myself. That is painful when the release artifact is not linked to a CI run, or when the CI logs are no longer available.
When a project can honestly say its release artifacts are reproducible, it removes uncertainty for first-time contributors before they change code.
Toolchain versions used in this run:
| Toolchain | Version |
|---|---|
| Go | 1.26.3 |
| .NET SDK | 10.0.203 |
| Zig | 0.16.0 |
| Rust | 1.95.0 |
| cargo-zigbuild | 0.22.3 |
| Language | Windows SHA256 | WSL SHA256 | Result |
|---|---|---|---|
| Go | 345bd67403a21347542fbe2127c08b6f33e4e0d655260bac7cdf3c14ce3a13f8 |
345bd67403a21347542fbe2127c08b6f33e4e0d655260bac7cdf3c14ce3a13f8 |
PASS |
| Zig | b7fe8d63e367c449a4924ef45652b98e2a663043a3007d108e3e6af5a3fc05d7 |
b7fe8d63e367c449a4924ef45652b98e2a663043a3007d108e3e6af5a3fc05d7 |
PASS |
| Rust | ebf9533d736e2bfa92ed32c09f3bdcf17782b51d19e502f75e662b8538d9a4dc |
ebf9533d736e2bfa92ed32c09f3bdcf17782b51d19e502f75e662b8538d9a4dc |
PASS |
| C#/.NET | 9e51e2291c70a47e2d5d1eb6e371ba2a97b4c997a515e709052c4ef95444a8a2 |
135532386a409415a940c335f36e6134908ee884e5d2b3bb3f5d43c426cfcb98 |
FAIL |
For this exact hello-world test, with no third-party or native dependencies:
- Go and Zig: best result here. Direct cross-build commands produced identical Linux binaries.
- Rust: also produced identical Linux binaries, but only after both hosts used
cargo-zigbuildto keep the linker path consistent. Plaincargo build --target ...still depends on host linker configuration. - C#/.NET: the final self-contained single-file artifact differed across Windows and WSL.
This ranking is not a guarantee for real projects. Native C/C++ dependencies,
generated code, pkg-config, system libraries, libc/sysroot contents, debug
info, archive metadata, build IDs, host CPU detection, and embedded timestamps
can all become build inputs.
Yes. The languages at the top show that reproducible Linux binaries are possible when dependencies and build flags are controlled.
The language at the bottom shows a different case: even careful settings did not produce identical final artifacts in this experiment.
Each language builds a minimal hello-world CLI:
go/ -> hello-go -> linux/amd64
cs/ -> hello-cs -> linux-x64 self-contained single-file
zig/ -> hello-zig -> x86_64-linux-musl
rust/ -> hello-rust -> x86_64-unknown-linux-musl
The WSL build runs from a Linux filesystem clone such as
~/work/reproducible-cross-build, not directly from /mnt/c or /mnt/d. That
keeps host OS, filesystem, and absolute path differences in scope.
The scripts remove common nondeterminism:
- Go uses
-trimpath,-buildvcs=false, and an empty build id. - C# disables debug symbols, ReadyToRun, trimming, and source revision metadata.
- Zig uses
ReleaseSmalland-fstrip. - Rust uses
SOURCE_DATE_EPOCH, path remapping, stripping, andcargo-zigbuild.
Prompt:
Reproduce this repo's experiment: build the Go, C#/.NET, Zig, and Rust hello-world CLIs into Linux ELF binaries from Windows and from a separate WSL/Linux filesystem clone, then compare SHA256 hashes byte-for-byte.
Expand reproduce steps
Build on Windows:
.\scripts\bootstrap-windows-tools.ps1
rustup target add x86_64-unknown-linux-musl
cargo install cargo-zigbuild --locked
.\scripts\build-windows.ps1Build on WSL/Linux from a separate Linux filesystem clone:
cd ~/work/reproducible-cross-build
bash scripts/bootstrap-wsl-tools.sh
rustup target add x86_64-unknown-linux-musl
cargo install cargo-zigbuild --locked
bash scripts/build-wsl.shIf the builds are in separate clones, copy WSL outputs back to Windows:
cp -f out/wsl/hello-go /mnt/d/path/to/reproducible-cross-build/out/wsl/hello-go
cp -f out/wsl/hello-cs /mnt/d/path/to/reproducible-cross-build/out/wsl/hello-cs
cp -f out/wsl/hello-zig /mnt/d/path/to/reproducible-cross-build/out/wsl/hello-zig
cp -f out/wsl/hello-rust /mnt/d/path/to/reproducible-cross-build/out/wsl/hello-rustThen compare on Windows:
.\scripts\compare.ps1The scripts write diagnostics to logs/, which Git ignores because it can
contain host paths, usernames, locale-specific output, and other local details.
Expected output for the recorded run:
[PASS] go 345bd67403a21347542fbe2127c08b6f33e4e0d655260bac7cdf3c14ce3a13f8
[FAIL] cs
Windows: 9e51e2291c70a47e2d5d1eb6e371ba2a97b4c997a515e709052c4ef95444a8a2
WSL: 135532386a409415a940c335f36e6134908ee884e5d2b3bb3f5d43c426cfcb98
[PASS] zig b7fe8d63e367c449a4924ef45652b98e2a663043a3007d108e3e6af5a3fc05d7
[PASS] rust ebf9533d736e2bfa92ed32c09f3bdcf17782b51d19e502f75e662b8538d9a4dc
This is a minimal experiment, not a broad guarantee. Passing here only means this project, with these commands and toolchain versions, produced identical Linux binaries across Windows and WSL.
It does not cover complex dependency graphs, code generation, build scripts, proc-macros, native libraries, embedded timestamps, usernames, hostnames, or other build-time metadata.