14 KiB
M0: Arch Linux x86_64 build host
Development reference and historical context. For current operating instructions, use the user manual. Acceptance applies only to the source and artifacts identified in each record.
Documentation index · First build · Troubleshooting
This is the technical reference for the build foundation used by later milestones. The rootfs guide adds assembly, Python and isolated ARM execution requirements. If you are setting up the repository for the first time, follow Your first build for an ordered walkthrough with expected output. For editing, rebuilding, and configuration changes after setup, use Development.
Scope and pinned inputs
M0 validates the cross-build environment. The scoped Dasung integration adds a controller package and service definitions without implementing a full OS. M1 adds the configured FDS rootfs; M2 adds native init and the generic ARM VM. M3 adds static FDS tools. M4 cross-builds the Pi kernel and creates initramfs, SYSTEM GPT/EROFS and a FAT32 boot partition; see Boot images. M6 adds cartridge management and M7 adds writable DATA. M8 adds the optional WindowMaker stack and on-demand Ethernet. M12 adds the complete internal disk, independent recovery and persistent machine settings. The signature tools have passed acceptance; the frozen-input workflow and signed local 0.1.0 release have passed two complete offline builds and artifact/archive verification. The desktop guide explains the new dependencies and the development image used for virtual display tests. See Native init for the additional test dependencies. An ARM host and a connected Pi are not required.
| Input | Pin |
|---|---|
| void-packages | Git submodule and VOID_PACKAGES_COMMIT: 02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1 |
| Static XBPS host tools | Versioned 0.60.4_1.x86_64-musl URL and a SHA-256 digest stored in this repository |
| Rust | rust-toolchain.toml: 1.98.0, minimal profile, rustfmt |
| Rust target | aarch64-unknown-linux-musl |
| Rust crate dependencies | Smoketest has none; all workspace dependencies, including shared Clap CLI parsing, are pinned in Cargo.lock, fetched at bootstrap, and built with --locked --offline |
| XBPS test package | srcpkgs/hello/template in the pinned Void tree: GNU hello 2.12.3_1 |
| Pi kernel | packages/fds-kernel/template: 6.12.87, pinned Raspberry Pi source archive and FDS config delta |
| Pi firmware | config/pi-firmware.conf: signed 20260715_1 package with a pinned SHA-256 |
The musl ABI of the static host tools does not determine the target ABI. Scripts
explicitly use XBPS_ARCH=x86_64 for the glibc build container, then run
./xbps-src -a aarch64 pkg hello to build the ARM glibc package. Local ARM package
indexing also explicitly sets XBPS_ARCH=aarch64.
Generic Linux workstation tools and desktop additions
make workstation builds native fds-cartridge and fds-emulator. Runtime
requirements are QEMU, erofs-utils, bubblewrap and native XBPS with a prepared
Void source checkout for software creation. fds-cartridge invokes xbps-src,
installs runtime dependencies and creates direct EROFS trees. The Rust tar reader
and xz utility remain for legacy cartridges and package/source archives. See
the current workstation guide.
The target fds-control panel uses x11rb 0.13.2's pure-Rust core X11 connection.
It adds no shared GUI toolkit or background service. Its protocol, hostname and
OS-access dependencies are locked and included in Rust license notices. Both
the panel and foreground launcher are packaged in fds-cli as static ARM binaries.
See the revision rationale.
Host dependencies
Run as an ordinary user on x86_64 Arch Linux with unprivileged user namespaces.
Rootfs construction now requires Linux 6.7+ with namespaced binfmt_misc support.
The M5 build uses a private handler and static QEMU for child execution; it does
not register an ARM interpreter in the workstation's global registry.
The checkout path must not contain whitespace, a limitation of upstream
xbps-src. Allow several GB of disk space and provide a network connection.
sudo pacman -S --needed bash coreutils binutils git curl make file tar xz gzip zstd \
bubblewrap rustup ca-certificates findutils diffutils grep sed gawk util-linux
Alternatively, run ./tools/bootstrap-host --install-deps. Only this explicit
option invokes sudo/pacman. Default make bootstrap checks dependencies and
exits nonzero when one is missing, without installing host system packages.
Dependency rationale:
- bash, coreutils, findutils, diffutils, grep, sed, gawk, and util-linux provide the build scripts and upstream build system with their required shell tools.
- git pins upstream source; curl and CA certificates download HTTPS inputs.
- tar, xz, gzip, and zstd extract archives and XBPS packages.
- file and binutils inspect ELF artifacts; make provides the build entry points.
- bubblewrap runs the build container as an ordinary user.
- rustup installs the pinned Rust toolchain and target. rustfmt checks source formatting and is not a runtime dependency.
Rust's bundled rust-lld and target-provided static musl/CRT are sufficient for
the current pure-Rust smoketest. No Zig, Docker, OpenSSL crate, or additional
musl runtime is required. The imported Dasung controller bundles libusb and uses a Void musl C cross
compiler inside the container; see its dependency rationale.
Glibc C libraries cannot simply be reused in static-musl binaries.
Exact commands
Run sequentially from the repository root:
make bootstrap
make smoke-test
make check
Bootstrap initializes the pinned submodule, verifies and extracts XBPS, checks
the tools' x86_64 static ELF properties, installs the pinned Rust toolchain and
target, fetches the locked workspace crates, copies config/xbps-src.conf into the local Void checkout, then runs the
equivalent of:
export PATH="$PWD/.host/xbps/usr/bin:$PATH"
export XBPS_ARCH=x86_64
cd vendor/void-packages
./xbps-src -A x86_64 binary-bootstrap
binary-bootstrap installs upstream base-chroot as a compilation environment.
It is separate from the FDS fds-base target rootfs built in M1. Builds run through
bubblewrap without starting Void services on Arch. Repeated bootstrap reuses the
masterdir and verified downloads. The script checks the container architecture,
installed base-chroot package, and execution of GCC inside the container.
To reproduce the two builds individually from the repository root:
./tools/cargo-build --locked --offline --release \
--target aarch64-unknown-linux-musl -p fds-smoketest
./tools/build-package hello
The latter command sets the local XBPS PATH, checks the Void pin, and executes this command within the submodule:
./xbps-src -a aarch64 pkg hello
xbps-src installs the aarch64 GNU cross toolchain that runs on x86_64, together with the target glibc sysroot. No Pi compilation is needed. The pinned template's checksum verifies the package source archive.
Validation
file target/aarch64-unknown-linux-musl/release/fds-smoketest
readelf -hW target/aarch64-unknown-linux-musl/release/fds-smoketest
readelf -lW target/aarch64-unknown-linux-musl/release/fds-smoketest
readelf -dW target/aarch64-unknown-linux-musl/release/fds-smoketest
readelf -VW target/aarch64-unknown-linux-musl/release/fds-smoketest
ldd target/aarch64-unknown-linux-musl/release/fds-smoketest
Rust acceptance requires ELF64, little endian, AArch64, executable type EXEC/DYN,
static linkage reported by file, no PT_INTERP, no DT_NEEDED, and no
GLIBC_* version requirements. Static PIE is also acceptable. The pinned Rust
target, self-contained linking configuration, and compile-time target checks
establish musl provenance; an ELF's static label alone does not identify its libc.
ldd is supplemental diagnostic output. A static executable normally produces
not a dynamic executable with exit status 1. An x86_64 host's ldd alone cannot
prove that an ARM executable is static.
XBPS validation checks architecture=aarch64 in the local package index, then
extracts usr/bin/hello from the generated package. It requires AArch64,
/lib/ld-linux-aarch64.so.1, and libc.so.6. An architecture suffix in a package
filename is not sufficient evidence.
make check verifies rejection of the wrong architecture, dynamic executables
passed as static, a static-musl executable passed as a glibc package, non-ELF
input, missing artifacts, invalid arguments, Void pin drift, and modified
upstream files. It also tests overlay protection and runs rustfmt. Pipelines
enable pipefail so tee cannot hide a failed build.
Optionally install sudo pacman -S --needed qemu-user to execute the ARM Rust
binary during smoke-test and compare its output. When qemu-aarch64 is absent,
the script explicitly reports SKIP. M0's required acceptance is actual
cross-compilation plus ELF/package inspection. Static inspection is not an ARM
runtime or Pi boot test.
Inputs, caches, and overlays
Use make clean-preview followed by make clean when repeated builds and VM
tests accumulate. Cleanup explains the retained current images,
release/input archives and caches. Full image builds and VM suites need tens of
GiB per working tree, beyond the initial M0 smoke test's requirements.
.host/xbps/: project-local host tools, not installed into/usr.vendor/void-packages/masterdir-x86_64/: glibc build container.vendor/void-packages/hostdir/: download caches, cross packages, and build output.out/packages/: exported aarch64 test package and its repository index.out/manifests/void-build-packages.txt: packages remaining installed after the build. Cross toolchains and temporary dependencies removed by xbps-src remain traceable through cache digests and build logs.out/manifests/void-package-inputs.sha256: digests of cached.xbpsinputs.out/manifests/artifacts.sha256: validation artifact digests.out/logs/: actual command logs, including logs from failed attempts.
Place FDS package overlays at packages/fds-*/template. tools/prepare-void
copies only directories containing a template and refuses to replace tracked
upstream packages. These copies are visible inside the build container. Base,
base-files, init, CLI, kernel and Dasung overlays are active; future directories
without templates remain inactive.
If an overlay changes and preparation reports a stale copy, inspect and preserve
any unique edits before moving the specific generated copy out of srcpkgs/ and
retrying. Modified upstream tracked files cause immediate failure. Conflicting
local build configuration also causes failure and must be reconciled explicitly.
See configuration and overlay troubleshooting
for the procedure.
Updating Void requires an explicit change to both the submodule gitlink and
VOID_PACKAGES_COMMIT, followed by validation and a commit containing both.
Do not fetch master and silently change the pin. Replacing the static host XBPS
archive also requires updating both its versioned URL and checksum.
Known limitations
- Source templates, Rust, and XBPS tools are pinned; the complete Void binary repository is not snapshotted. Initial bootstrap, xbps-src build-container updates, and missing build dependencies still use signed rolling repositories. Cached inputs, version inventories, and digests support auditing but cannot guarantee future upstream retention. M12 preserves the exact selected packages, sources and build tools through the accepted snapshot and offline workflow. Its two fresh builds produced 27 matching artifacts. This freezes the release inputs, not the entire rolling Void repository; the original M0 check alone does not establish reproducibility.
- Bootstrap supports x86_64 Arch Linux. Other host distributions and ARM hosts have not been validated. Unprivileged user namespaces must be available.
- M4 tests the actual Pi kernel and initramfs-to-EROFS handoff in a generic ARM VM. This does not emulate Pi firmware, RP1, physical bays or the display. Media burning and physical boot-performance measurements remain separate work.
opt-level=zis the plan's initial setting, not a proven fastest setting. Compare optimization levels later using real programs and hardware.- During the initial upstream bootstrap, bubblewrap's mounted
/tmpproducedrm: cannot remove '//tmp': Device or resource busyin the cleanup phase. Upstream returned success; the subsequent container execution, cross build, and public bootstrap command passed. The upstream source was left unchanged.