build: add Guix reproducible release flow for Linux, MacOS, Windows#5149
build: add Guix reproducible release flow for Linux, MacOS, Windows#5149eval-exec wants to merge 10 commits intonervosnetwork:developfrom
Linux, MacOS, Windows#5149Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a Guix-based “pinned environment” release builder intended to produce reproducible Linux release tarballs (Bitcoin Core–style), alongside a Guix package recipe for ckb.
Changes:
- Add
contrib/guix/guix-build+ supporting scripts to vendor Rust crates and build a deterministicx86_64-unknown-linux-gnurelease tarball in aguix time-machine shell --container --pureenvironment. - Pin the Guix channel revision (
contrib/guix/channels.scm) and define the build toolchain manifest (contrib/guix/manifest.scm). - Add a Guix package recipe (
contrib/guix/guix.scm) and generated Rust crate inputs (contrib/guix/rust-crates.scm), plus documentation and.gitignoreupdates.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
contrib/guix/guix-build |
New top-level entrypoint orchestrating source archive creation + containerized build. |
contrib/guix/libexec/vendor-rust-sources.sh |
Vendors Cargo dependencies into a deterministic local directory and writes .cargo/config.toml. |
contrib/guix/libexec/build.sh |
Container build script producing the staged install tree + deterministic tarball and SHA256SUMS. |
contrib/guix/manifest.scm |
Declares pinned build tools (Rust, GCC toolchain, patchelf, etc.) for the container environment. |
contrib/guix/channels.scm |
Pins the Guix channel commit used by guix time-machine. |
contrib/guix/guix.scm |
Adds a Guix package definition for building ckb as a Guix package. |
contrib/guix/rust-crates.scm |
Generated crate source definitions + lookup-cargo-inputs list used by guix.scm. |
contrib/guix/README.md |
Documents the new release builder flow and Guix package recipe usage. |
.gitignore |
Ignores guix-build-* output directories. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
02a3b1d to
8e9ff40
Compare
Linux, MacOS, Windows
|
Invite @doitian @chenyukang @driftluo @quake @Officeyutong @zhangsoledad to test guix reproducible build on x86_64 Linux platform( If you have guix machine, execute: If you have no guix machine, execute: It may cost ❯ cat guix-build-0.205.0/output/x86_64-unknown-linux-gnu/SHA256SUMS
3c7d003ca216fbb31173e0da5d965f472243ef4f8b6e1288d37e6053eb566d2d ckb_0.205.0_x86_64-unknown-linux-gnu.tar.gz❯ cat guix-build-0.205.0/output/aarch64-apple-darwin/SHA256SUMS
7efa63c5b25b28e19c7cbfb143d45da2d10b05af8f427ac7e12097c40e20a937 ./ckb_0.205.0_aarch64-apple-darwin.tar.gz
❯ cat guix-build-0.205.0/output/x86_64-pc-windows-gnu/SHA256SUMS
39901c3e5f27559f6d30150c8dfbe2604cbef893981f5e5c1834df8652c6a1f8 ./ckb_0.205.0_x86_64-pc-windows-gnu.zip❯ cat guix-build-0.205.0/output/aarch64-unknown-linux-gnu/SHA256SUMS
37b1b69faa879ab0f00188c51ab156df0f011912550708a5e400677c30443633 ./ckb_0.205.0_aarch64-unknown-linux-gnu.tar.gz❯ cat guix-build-0.205.0/output/x86_64-pc-windows-msvc/SHA256SUMS
e31f5af8e04efde939f09d66cc3b1a77534f1cf575f5f9afc36894bdbbdd6226 ./ckb_0.205.0_x86_64-pc-windows-msvc.zip |
There was a problem hiding this comment.
Pull request overview
This PR introduces a pinned Guix time-machine/container-based build flow to produce reproducible CKB release artifacts (Linux tarball, Windows zip), including toolchain patching and post-build binary validation for Linux.
Changes:
- Added
contrib/guix/guix-build+libexec/build.shpipeline to generate a vendored source archive and build it in a pure Guix container. - Added a pinned Guix manifest (
manifest.scm+channels.scm) with an older-glibc cross toolchain, plus patches to improve toolchain reproducibility. - Added Linux ELF ABI/dependency validation (
symbol-check.py) and a Docker-based convenience runner (guix-docker-build+Dockerfile).
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| contrib/guix/symbol-check.py | New Linux ELF ABI/dependency checker (glibc symbol cap, interpreter, rpath/runpath, allowed libs). |
| contrib/guix/patches/rocksdb-cross-compile-platform.patch | Patch intended for cross-compiling rocksdb-sys platform detection. |
| contrib/guix/patches/glibc-fixed-store-remap.patch | Adds -ffile-prefix-map to glibc build flags to reduce Guix store path leakage. |
| contrib/guix/patches/gcc-ssa-generation.patch | GCC patch to make SSA generation deterministic across architectures. |
| contrib/guix/patches/gcc-fixed-store-remap.patch | Adds -ffile-prefix-map to GCC libgcc/libunwind build flags for reproducible debug paths. |
| contrib/guix/patches/ckb-disable-jemalloc-on-windows.patch | Patch intended to disable jemalloc/memory tracker on all Windows targets. |
| contrib/guix/manifest.scm | Pinned build manifest including cross toolchains and cross-compiled OpenSSL/sysroot selection. |
| contrib/guix/libexec/vendor-rust-sources.sh | Vendors crates from Cargo.lock via Guix and writes .cargo/config.toml for offline builds. |
| contrib/guix/libexec/rust-linker.sh | Linker wrapper to route host vs target link steps and enforce deterministic/linker flags. |
| contrib/guix/libexec/build.sh | Main in-container build script: toolchain env setup, source patching, build, packaging, and Linux validation. |
| contrib/guix/guix.scm | Guix package recipe for building ckb as a Guix package (separate from release-tarball flow). |
| contrib/guix/guix-docker-build | Docker wrapper that runs guix-daemon and executes the Guix build inside a container. |
| contrib/guix/guix-build | Top-level driver to create source archive + run per-host builds in a pure Guix container. |
| contrib/guix/channels.scm | Pins the Guix revision for time-machine builds. |
| contrib/guix/README.md | Documents the Guix release builder entry points and usage. |
| contrib/guix/Dockerfile | Alpine-based builder image that installs Guix and runs guix-daemon. |
| .gitignore | Ignores guix-build-* output directories. |
Comments suppressed due to low confidence (1)
contrib/guix/patches/rocksdb-cross-compile-platform.patch:29
- This patch file is not referenced by
manifest.scmor applied bylibexec/build.sh(which currently performs an inlinesedreplacement instead). Either wire this patch into the build flow (so it’s the single source of truth and fails cleanly on mismatch) or remove it to avoid future confusion about what is actually being applied.
Fix ckb-librocksdb-sys cross-compilation platform detection.
When cross-compiling from Linux to Windows, cfg!(target_os = "windows")
evaluates on the BUILD host (Linux), not the TARGET. This causes
build_detect_platform (a Linux script) to run and set POSIX/Linux defines,
while the TARGET-based check also adds Windows defines -- resulting in
conflicting platform flags like -DROCKSDB_PLATFORM_POSIX and -DOS_WIN.
Fix: check the Cargo TARGET env var instead of cfg!() for platform
detection in build.rs, which correctly reflects the cross-compilation
target.
--- a/build.rs
+++ b/build.rs
@@ -6,8 +6,11 @@ use std::path::PathBuf;
use std::process::Command;
fn get_flags_from_detect_platform_script() -> Option<Vec<String>> {
- if !cfg!(target_os = "windows") {
- let mut cmd = Command::new("bash");
+ // Use the Cargo TARGET env var (not cfg!()) to detect the target platform.
+ // cfg!(target_os) reflects the BUILD host, which is wrong when cross-compiling.
+ let target = env::var("TARGET").unwrap_or_default();
+ if !target.contains("windows") {
+ let mut cmd = std::process::Command::new("bash");
// if ROCKSDB_USE_IO_URING is not set, treat as enable
// we use pkg_config probe library, more friendly for rust.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add pinned Guix packaging and a Bitcoin-style shell-based release builder under contrib/guix, using cargo-inputs-from-lockfile for Rust crate sources.
Major changes to the Guix reproducible build system: manifest.scm: - Add glibc-2.31 cross-toolchain (x86_64-linux-gnu-toolchain) using cross-gcc, cross-libc, and cross-kernel-headers from Guix - Cross-compile OpenSSL 3.0.8 against glibc-2.31 as a Guix package (openssl-glibc-2.31), avoiding the bootstrap issues of package-with-c-toolchain by using our existing cross-toolchain - Use standard gcc-toolchain-14 for native build tools and rust-1.92 for the Rust compiler build.sh: - Add cross-compilation environment setup (CC, CXX, AR, RANLIB, etc.) using the cross-toolchain from the Guix profile - Create CC/CXX wrappers that inject -Wl,-rpath for the cross-glibc so autoconf test programs (e.g., jemalloc) can execute in the container, with -static-libgcc to avoid native libgcc_s mismatch - Set GUIX_LD_WRAPPER_DISABLE_RPATH only in rust-linker.sh for the final target link, not globally, so intermediate binaries get rpath - Route host (build-script) linking through CKB_RUST_HOST_LIBRARY_PATH to avoid cross-glibc contamination in the Guix profile - Use patchelf --remove-rpath alongside --set-interpreter - Reference symbol-check.py from /ckb mount, not source tree rust-linker.sh (new): - Cargo linker wrapper that distinguishes host vs target linking - Target links: cross-gcc with --dynamic-linker, -static-libstdc++, -static-libgcc, and GUIX_LD_WRAPPER_DISABLE_RPATH=yes - Host links: native gcc with LIBRARY_PATH set to native glibc - Path detection uses /target/<triple>/ to avoid false matches guix-build: - Support explicit git ref: ./contrib/guix/guix-build v0.205.0 - Archives the exact commit, not the worktree - Add --help, usage text, and build progress message - Keep HEAD mode with dirty-worktree check as default symbol-check.py: - Allow non-GLIBC version namespaces (GLIBCXX, GCC, CXXABI, OPENSSL) - Fix LIEF interpreter check with fallback for single-entry arch maps
Add Dockerfile and guix-docker-build script for running reproducible Guix builds inside Docker containers, following fanquake's approach used by Bitcoin Core: - guix-daemon runs as the container's main process (PID 1) - Builds are executed via `docker exec`, not in the same shell - Docker volumes cache /gnu/store across builds for performance - Alpine-based image with Guix 1.5.0 binary and 32 build users Usage: ./contrib/guix/guix-docker-build v0.205.0 Verified bit-for-bit identical output between host and Docker: 3c7d003ca216fbb31173e0da5d965f472243ef4f8b6e1288d37e6053eb566d2d Also fix tar extraction in build.sh with --no-same-owner to avoid "Cannot change ownership" errors inside nested Guix containers.
Add reference comments linking to the original Bitcoin Core files that this Guix build system is derived from. Each file now credits its Bitcoin Core counterpart with a direct GitHub URL. Files annotated: - guix-build → bitcoin/contrib/guix/guix-build - build.sh → bitcoin/contrib/guix/libexec/build.sh - manifest.scm → bitcoin/contrib/guix/manifest.scm - symbol-check.py → bitcoin/contrib/guix/symbol-check.py - Dockerfile → fanquake/core-review/guix - guix-docker-build → fanquake/core-review/guix - rust-linker.sh → no Bitcoin equivalent (Rust-specific) - gcc/glibc patches → bitcoin/contrib/guix/patches
Add MinGW-w64 cross-toolchain support for building CKB Windows binaries reproducibly from Linux, following Bitcoin Core's approach. manifest.scm: - Add mingw-w64-base-gcc with POSIX thread support - Add make-mingw-pthreads-cross-toolchain (MinGW-w64 + winpthreads) - Add openssl-mingw-w64 (OpenSSL cross-compiled for Windows, static) - Make package list HOST-dependent: Linux gets patchelf + glibc OpenSSL, Windows gets zip + MinGW OpenSSL guix-build: - Map Rust triples to GNU triples (MANIFEST_HOST) for the manifest - Pass RUST_TARGET alongside HOST into the container - Allow x86_64-pc-windows-gnu in SUPPORTED_HOSTS build.sh: - Add x86_64-w64-mingw32 case for cross-toolchain paths (winpthreads instead of glibc, no kernel headers) - Windows CC wrapper: plain cross-gcc (no rpath injection) - Windows OpenSSL: static linking (OPENSSL_STATIC=1) - Windows packaging: deterministic .zip (not .tar.gz) - Skip patchelf and ELF symbol checks for PE binaries rust-linker.sh: - Add *windows* case: -Wl,--no-insert-timestamp for deterministic PE headers, no --dynamic-linker Usage: HOSTS=x86_64-pc-windows-gnu ./contrib/guix/guix-build v0.205.0 HOSTS="x86_64-unknown-linux-gnu x86_64-pc-windows-gnu" ./contrib/guix/guix-build v0.205.0 Reference: https://github.com/bitcoin/bitcoin/blob/master/contrib/guix/manifest.scm
Add macOS Apple Silicon cross-compilation support, following Bitcoin
Core's approach for the toolchain while building Rust's library/std
from source (a world first — no existing project does this).
manifest.scm:
- Add clang-toolchain-19, lld-19, make-lld-wrapper for darwin targets
guix-build:
- Add aarch64-apple-darwin to SUPPORTED_HOSTS
- Add SDK_PATH check and sharing into the Guix container
- Reference Bitcoin's SDK extraction docs
build.sh:
- Build Rust library/std from source using x.py with Guix's
bootstrapped rustc + Apple SDK (no pre-built binaries)
- Clang CC/CXX wrappers with -isysroot, -nostdlibinc, -fuse-ld=lld
- LLVM tools (llvm-ar, llvm-ranlib, llvm-strip, llvm-nm)
- ZERO_AR_DATE=1 for deterministic archives
- Vendor OpenSSL (no system OpenSSL in Apple SDK)
rust-linker.sh:
- Darwin case: -fuse-ld=lld, -Wl,-no_adhoc_codesign,
-Wl,--threads=1 (deterministic LC_UUID),
-Wl,-platform_version,macos,14.0,14.0
Usage:
SDK_PATH=/path/to/SDKs HOSTS=aarch64-apple-darwin \
./contrib/guix/guix-build v0.205.0
Requires macOS SDK extracted from Xcode (not redistributable).
See: https://github.com/bitcoin/bitcoin/blob/master/contrib/macdeploy/README.md
Reference: https://github.com/bitcoin/bitcoin/blob/master/contrib/guix/manifest.scm
Three fixes surfaced during host-vs-docker reproducibility verification: * guix-build: add --preserve='^HOST$' so the HOST env var is actually kept through `guix shell --pure`. The comment in manifest.scm already referenced this flag, but it was never present, so manifest evaluation sometimes picked up a stale profile for the wrong target. * rust-linker.sh: add -Wl,-s on the Windows link to strip the COFF symbol table. Rustc embeds DLL import-lib symbols whose names contain its random temp directory (e.g. _head__tmp_rustc9s2NHV_kernel32_dll_imports_lib), which made each build differ. * ckb-disable-jemalloc-on-windows.patch: regenerate via `git diff` so the hunk headers carry correct line counts and context lines are space-prefixed. The hand-written version was rejected as malformed by the Guix-bundled `patch`. Verified reproducible across two Docker runs and one host run: 39901c3e5f27559f6d30150c8dfbe2604cbef893981f5e5c1834df8652c6a1f8
Adds a new supported target triple, cross-compiled from x86_64 Linux
using Guix's old-glibc toolchain machinery (same approach as the
existing x86_64-linux-gnu build):
* manifest.scm: aarch64-linux-gnu-toolchain built via
make-ckb-cross-toolchain (glibc 2.31); openssl-aarch64-glibc-2.31
cross-compiled against that toolchain; the host-dependent branch
now selects x86_64 or aarch64 packages based on the target, and
aarch64 pulls in a Rust sysroot via make-rust-sysroot/implementation
(rustc only ships libstd for the build host).
* guix-build: aarch64-unknown-linux-gnu added to SUPPORTED_HOSTS and
mapped to the aarch64-linux-gnu GNU triple.
* libexec/build.sh: new HOST case with
DYNAMIC_LINKER=/lib/ld-linux-aarch64.so.1 and the right
TARGET_ENV_SUFFIX. Because cross-arch cross-compile differs from
the existing same-arch glibc-downgrade, build scripts (build.rs,
proc-macros) get CC_x86_64_unknown_linux_gnu/HOST_CC pointed at
native gcc, and AARCH64_UNKNOWN_LINUX_GNU_OPENSSL_{INCLUDE,LIB}_DIR
is set directly so openssl-sys doesn't try to use the host
pkg-config for a differing target arch. Hardcoded x86_64
interpreter in the patchelf call replaced with ${DYNAMIC_LINKER}.
* symbol-check.py: recognise AARCH64 for the glibc 2.31 cap, the
expected interpreter, and the ld-linux-aarch64.so.1 allow-list
entry.
Verified reproducible:
x86_64-unknown-linux-gnu: 3c7d003ca216fbb31173e0da5d965f472243ef4f8b6e1288d37e6053eb566d2d
aarch64-unknown-linux-gnu: 37b1b69faa879ab0f00188c51ab156df0f011912550708a5e400677c30443633
x86_64 hash is identical with and without this commit applied.
Adds a fourth reproducibility target: the MSVC ABI Windows build that
CKB's CI currently produces via a Visual Studio runner. Cross-compiled
from Linux using clang-cl + lld-link and an xwin-extracted Microsoft SDK
(user-provided at depends/SDKs/msvc-vs17-sdk10.0.22621, same pattern as
the Apple SDK for darwin).
What changes:
* manifest.scm — a (string-suffix? "-pc-windows-msvc" target) cond
branch that pulls in clang-toolchain-20, lld-20, rust-src-pkg, and
zip. rust-src-pkg was inlined in the darwin branch; it's now a
top-level definition shared between darwin and windows-msvc because
both need the Rust source to build library/std from scratch via x.py.
* guix-build — x86_64-pc-windows-msvc in SUPPORTED_HOSTS; MSVC SDK
detection block modelled on OSX_SDK; --share the SDK into the guix
container and pass MSVC_SDK into the environment.
* libexec/build.sh — new HOST case with clang-cl as REAL_CC and an
lld-link wrapper (cross-lld-link) that injects /LIBPATH: for the
xwin SDK lib directories so neither x.py nor rustc has to know the
paths. x.py builds library/std from source using the same pattern
as darwin (config.toml, checksum regen, host rustlib copy). The
host rustlib copy now chmod -R u+w so subsequent `rm -rf distsrc`
can clean up the /gnu/store-read-only files (latent bug: also
affected darwin on reruns). RUSTFLAGS carries /Brepro and
/DEBUG:NONE for a deterministic PE.
* libexec/rust-linker.sh — dedicated *-pc-windows-msvc case that
forwards rustc's linker args verbatim to lld-link. The previous
*windows* branch is kept for MinGW and now explicitly excludes msvc
(the two take different flag dialects — GNU-style -Wl,... vs MSVC
/FLAG).
Patch pipeline refactor (prerequisite):
* Replace the sed-based patch_vendored_crate with a helper,
apply_vendored_patch, that applies a real .patch file to a vendored
crate directory and refreshes .cargo-checksum.json afterwards so
cargo --frozen accepts the modified files. The existing
rocksdb-cross-compile-platform.patch was stale (wrong hunk line
counts, also semantically incorrect for darwin) — replaced with
ckb-librocksdb-sys-cross-compile.patch, regenerated from a real
git diff.
* New patches/ckb-vm-msvc-cc-env.patch: ckb-vm 0.24.14's build.rs
hardcodes build.compiler("gcc") for its is_msvc branch (to route
its GNU AT&T-syntax execute_x64.S through a GNU assembler, which
works on a native Windows machine that also has MinGW-w64's gcc
installed alongside cl.exe). For cross-compile from Linux, "gcc"
resolves to the host's Linux cc which cannot emit MSVC COFF. The
patch adds a CKB_VM_ASM_CC env-var override — build.sh sets it to
clang-cl, which handles GNU AT&T .S via LLVM's integrated assembler
and produces valid MSVC-ABI COFF.
Reproducibility verified:
x86_64-pc-windows-msvc: e31f5af8e04efde939f09d66cc3b1a77534f1cf575f5f9afc36894bdbbdd6226
- host run 1, host run 2, docker run: all identical
The existing four targets are unaffected by the patch-pipeline refactor
(mingw re-verified post-refactor at 39901c3e..., matching the pre-refactor
hash byte-for-byte; linux/aarch64-linux never hit that code path).
The xwin SDK is user-provided. The release flow documents pinning
\`xwin --manifest-version 17 --sdk-version 10.0.22621\` — two xwin
extractions from the same pin produce byte-identical trees (5547 files
+ 2895 symlinks, all matching), so users can either re-run xwin or
consume a pre-extracted tarball. Microsoft's Visual Studio Build Tools
EULA applies to the SDK; xwin's --accept-license flag handles it.
What problem does this PR solve?
Problem Summary:
CKB does not have a Bitcoin-style Guix release flow for producing bit-for-bit reproducible Linux release tarballs from a pinned build environment.
What is changed and how it works?
What's Changed:
contrib/guix/guix-build,manifest.scm, andlibexec/build.shto build in a pinnedguix time-machine shell --container --pureenvironmentcontrib/guix/channels.scmCargo.lockvia Guixcargo-inputs-from-lockfilecontrib/guix/guix.scmpath remapping for package reproducibilitySupport:
x86_64-apple-darwinRelated changes
owner/repo: NoneCheck List
Tests
Commands:
FORCE_DIRTY_WORKTREE=1 ./contrib/guix/guix-build./contrib/guix/guix-build v0.205.03c7d003ca216fbb31173e0da5d965f472243ef4f8b6e1288d37e6053eb566d2d