Skip to content

OpenFusionist/reproducible-cross-build

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reproducible Cross-Build: Windows vs Linux

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.

Why This Comparison Matters

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.

Results

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

Language Ranking

For this exact hello-world test, with no third-party or native dependencies:

  1. Go and Zig: best result here. Direct cross-build commands produced identical Linux binaries.
  2. Rust: also produced identical Linux binaries, but only after both hosts used cargo-zigbuild to keep the linker path consistent. Plain cargo build --target ... still depends on host linker configuration.
  3. 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.

Is This Ranking Still Meaningful?

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.

Test Design

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 ReleaseSmall and -fstrip.
  • Rust uses SOURCE_DATE_EPOCH, path remapping, stripping, and cargo-zigbuild.

Reproduce

For AI Agents

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.

For Humans

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.ps1

Build 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.sh

If 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-rust

Then compare on Windows:

.\scripts\compare.ps1

The 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

Scope

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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors