16 KiB
Working on FDS/OS
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 guide is for someone who has completed the first build and wants to change or inspect the working code. Run commands from the repository root, as a normal user. M0–M12 software acceptance is complete; consult AGENTS.md and the roadmap before adding components.
Choose the smallest useful build
| You changed or want to do | Run |
|---|---|
| See the available entry points | make help |
| Prepare a new checkout or restore missing host tools | make bootstrap |
| Edit the Rust smoketest | The Rust build and validation sequence below |
| Build/test the base display controller | make dasung, then make dasung-test; see Dasung |
| Change the CLI, shared contracts or stage0 | make tooling, then make tooling-test; rebuild initramfs and run make boot-test for boot-path changes |
| Change kernel configuration | make kernel, rebuild rootfs and boot images, then make boot-test; see Boot images |
| Change USB discovery, mapping or media writing | Build CLI images, then make stress-test; see Stress tests |
| Change shutdown or DATA safety | Rebuild both profiles; run make power-test against development, plus make data-test and make init-test; see Shutdown |
| Change init or boot services | make rootfs PROFILE=cli, make rootfs-test, then make init-test; see Native init |
| Change base packages or rootfs assembly | make rootfs PROFILE=cli, make rootfs-test, then make init-test; see Rootfs |
| Build the native development toolset | make rootfs PROFILE=development, make rootfs-test, make system-card PROFILE=development, then make development-test |
| Change internal storage or machine settings | Rebuild CLI and recovery, then make internal-test; see Internal storage |
| Change release signing or verification | make signing-test; see Release signatures |
| Rebuild the test XBPS package | ./tools/build-package hello |
| Verify an M0 build-system change | make bootstrap, then make smoke-test, then make check |
| Check Rust formatting and validation protections | make check, after building the Rust program |
| Read or edit documentation | Check examples, links, and claims against the implementation |
Run builds sequentially. The scripts share one Void masterdir and package cache;
starting bootstrap and package builds concurrently is not a supported workflow.
make packages builds every FDS base package. make all builds the complete
artifact set; two independent frozen offline builds passed with matching output. See
Frozen inputs and offline rebuilds. Use make clean-preview
and make clean to remove obsolete workspaces and Rust compilation output while
keeping published images, latest test fixtures, saved releases, inputs and
caches. See Cleanup for retention rules and rebuilding afterward.
There is no generic make test target.
Edit and rebuild the Rust program
The active program is rust/fds-smoketest/src/main.rs.
It is deliberately tiny: it establishes that the intended target and static
linking work. rust/dasungd/, rust/fds-common/, rust/fds-cli/, and
rust/fds-stage0/, rust/fds-boottrace/, and rust/fds-cartridged/ are also active. Directories absent from the workspace member
list remain reserved. Use the tooling guide for the FDS programs,
Cartridges for bay and metadata changes, and DATA
for writable storage and managed jobs.
Use the shared clap workspace dependency for every Rust command-line interface.
Declare commands and options with Parser, Subcommand, Args, and typed value
parsers; do not match or consume raw argument vectors. Shared media syntax lives
in rust/fds-burn/src/cli.rs; installed FDS aliases use Clap's multicall support
in rust/fds-cli/src/cli.rs. Keep authorization and filesystem validation in the
operation layer after parsing. Add parser tests when changing required options,
conflicts, private s6 flags, aliases, or child-argument forwarding.
The disposable Rust VM workloads in tests/helpers/ are a Cargo workspace
member and use the same locked Clap dependency. They are never installed in
the base packages. Their stress workload uses release optimization level 2;
production executables retain the size-oriented release settings.
After editing the program:
cargo fmt --all
cargo build --locked --offline --release \
--target aarch64-unknown-linux-musl -p fds-smoketest
./tools/verify-elf target/aarch64-unknown-linux-musl/release/fds-smoketest aarch64 static
make check
Cargo writes to target/aarch64-unknown-linux-musl/release/. It does not refresh
out/fds-smoketest or its manifest. Run make smoke-test when you need a freshly
exported artifact and matching checksums.
If you installed optional QEMU, execute the freshly built file with:
qemu-aarch64 target/aarch64-unknown-linux-musl/release/fds-smoketest
The smoke-test contract expects exactly FDS/OS M0: aarch64 static-musl OK.
Changing that text requires updating the runtime assertion and documentation as
part of the same change. The crate also rejects the wrong architecture, libc,
or missing static CRT at compile time. Do not bypass those checks to make an
x86_64 build appear to pass.
The release profile and target configuration live in Cargo.toml
and .cargo/config.toml. Rust and target versions are
selected through rust-toolchain.toml. This M0 crate has
no external crate dependencies. --offline works after bootstrap has installed
the toolchain; it is not a promise that the complete build pipeline is offline.
Build and inspect a package
./tools/build-package hello
This checks the Void source pin, prepares configuration and overlays, invokes
xbps-src's aarch64 cross build, and copies the resulting main package into
out/packages/. Its log is out/logs/xbps-hello.log. Follow
Packages for indexing, querying, and extracting the output.
hello is the verified M0 package. The helper accepts other template names, but
its exporter currently looks only for that main package in the top-level
hostdir/binpkgs/. Packages emitted into subrepositories and separately named
subpackages need additional handling; the helper is not yet a universal package
export interface.
For diagnosis, this is the underlying command in its correct host environment:
(
export PATH="$PWD/.host/xbps/usr/bin:$PATH"
export XBPS_ARCH=x86_64
cd vendor/void-packages
./xbps-src -a aarch64 pkg hello
)
The subshell keeps the directory and environment changes local. Prefer the FDS
helper for normal work: it also checks the pin and exports the output. The raw
command does neither. Do not run xbps-install against the Arch host or install
an ARM package into the x86_64 build container.
Change build configuration deliberately
The source of truth is config/xbps-src.conf.
XBPS_MAKEJOBS controls package build parallelism; the current value is 4.
XBPS_CHROOT_CMD=bwrap selects the supported container backend.
tools/prepare-void copies this file to the ignored
vendor/void-packages/etc/conf. If the two differ, it stops instead of silently
overwriting local settings. After intentionally editing the checked-in file,
review the difference:
diff -u vendor/void-packages/etc/conf config/xbps-src.conf
A difference makes diff exit 1; that is expected. Once the checked-in version
contains every setting you intend to keep, synchronize and prepare:
cp config/xbps-src.conf vendor/void-packages/etc/conf
./tools/prepare-void
This overwrites the local generated configuration, so reconcile any intentional local edits before copying. Re-run the three M0 validation commands after a build configuration change. A mirror change does not lock the packages served by that mirror; see input limitations.
How future package overlays fit
FDS-owned package sources belong in packages/fds-*/, outside the upstream
submodule. An overlay becomes active only when its directory contains a
template. fds-base, fds-base-files, fds-init, fds-dasungd, fds-cli, fds-kernel,
fds-cartridged, fds-eink, and fds-dhcpcd are active.
For active overlays and future templates, tools/prepare-void copies each
active directory into vendor/void-packages/srcpkgs/. It allows an identical
repeat copy, rejects a name that collides with a tracked upstream package, and
stops if a previous generated copy differs. Edit the source under packages/,
then reconcile the specific stale generated copy. Never put the only copy of an
FDS change inside vendor/.
The troubleshooting guide explains how to preserve and inspect a conflicting copy. Empty future directories are intentional; adding a fake template to silence a missing-package error does not implement that milestone.
Preserve logs and identify inputs
The normal log filenames are reused. Before reproducing a failure that you need to retain, save the existing logs:
log_archive=$(mktemp -d "$PWD/out/saved-logs.XXXXXX")
cp -a out/logs/. "$log_archive/"
printf 'Saved logs to %s\n' "$log_archive"
After a successful smoke-test:
cat VOID_PACKAGES_COMMIT
rustc --version
cargo --version
cat out/manifests/void-build-packages.txt
sha256sum -c out/manifests/artifacts.sha256
The package inventory lists what remains installed in the build container after xbps-src cleans up temporary dependencies. Cached package digests and build logs provide additional input records. These records do not constitute a complete, immutable upstream package repository snapshot.
Source updates and changes for review
Keep upstream tracked files unchanged. Updating Void is a deliberate change to
both the submodule gitlink and VOID_PACKAGES_COMMIT, followed by the full M0
checks. Updating host XBPS requires a matching archive URL and checksum. Updating
Rust requires considering the pinned toolchain, target, and generated artifacts
together. Do not use an unreviewed git pull inside the submodule as a repair.
For a change report, state what changed, which commands actually passed, and what was skipped. Include the failing command and relevant log when reporting a bug. Do not describe ELF inspection as execution, QEMU userspace execution as a Pi boot, or a target timing as a measured result. Use English for documentation, comments, diagnostics, and project discussions.
Desktop verification
Build make rootfs PROFILE=development, then run make desktop-test. This
executes the actual ARM WindowMaker, terminal, cartridge daemon, and DHCP client
in disposable VMs. Its Xvfb backend and root test console exist only in generated
fixtures. The normal SYSTEM configuration uses Xorg and the ordinary FDS user.
VM helpers place QEMU in a private network namespace. The test's virtual DHCP
server can advertise DNS while remaining unable to reach host interfaces or the
Internet. QEMU diagnostics have separate *.stderr.log files, keeping host
emulator messages out of guest serial/JSON responses. Screenshots and measured
activation timestamps are retained under out/m8-vm.*.
To return to a console image after desktop development, rebuild
make rootfs PROFILE=cli and make system-card PROFILE=cli. A SYSTEM image cannot
be relabeled as another profile without rebuilding the matching filesystem.
Use the development SYSTEM
profiles/development.list extends the same immutable base with native aarch64
glibc development tools. FDS control programs retain their static-musl build.
Build and verify the profile on the x86_64 workstation:
make rootfs PROFILE=development
make rootfs-test
make system-card PROFILE=development
make development-test
These commands do not install compilers on the Pi's read-only SYSTEM at runtime.
The profile must be built into a new SYSTEM image. Store source code and build
outputs on an active DATA cartridge, or under /tmp for disposable experiments.
The ordinary user's home is temporary in this profile too.
At native startup, FDS advances an older wall clock to the image's fixed source
timestamp in /usr/share/fds/build-epoch. A newer kernel/RTC clock is retained.
This prevents immutable compiler and CMake inputs from appearing to be in the
future when a machine boots without a usable RTC. It requires no network,
time server or waiting. /run/fds/clock.json records which case occurred.
The floor is an approximate lower bound, not the current date. Set the date
explicitly from a trusted time source when accurate timestamps or certificate
validity matter. Physical Pi RTC behavior remains to be tested. Boot-duration
measurements continue to use CLOCK_BOOTTIME, independent of wall-clock changes.
make development-test checks the older-clock case while compiling real projects;
make clock-test checks preservation of a newer clock in a disposable VM.
| Selected package(s) | Purpose |
|---|---|
gcc, glibc-devel |
Native C/C++ compilation, libc headers and startup files; GCC selects its matching support libraries and C++ headers |
make, cmake, meson, ninja, pkg-config |
Common project build systems and dependency discovery; Meson brings its Python runtime |
rust, cargo |
Native Rust development and package builds; Rust selects its standard library and LLVM runtime dependencies |
git |
Local source history and optional remote checkout when networking is explicitly enabled |
gdb, strace |
Interactive debugging and syscall inspection |
vim |
Terminal editing, with no graphical session required |
| Xvfb and the existing X11 diagnostic tools | Virtual desktop integration tests; they remain opt-in |
No development package is added to the boot service bundle. The profile uses GCC for C/C++ and Rust for Rust; additional compilers and physical performance profilers can be considered separately when there is a concrete need.
For a disposable first C program at FDS>:
mkdir -p /tmp/hello
cd /tmp/hello
printf '#include <stdio.h>\nint main(void) { puts("Hello from FDS"); }\n' >hello.c
cc -g -o hello hello.c
./hello
gdb ./hello
Within GDB, use break main, run, next, and quit. For persistent work,
use /data/projects after selecting a healthy DATA cartridge. Stop programs and
leave that directory before fds eject BAY.
For a dependency-free Rust example:
cargo new --vcs none /tmp/hello-rust
cd /tmp/hello-rust
cargo run --offline
--offline avoids contacting a registry. Projects with third-party crates need
those sources prepared beforehand or an explicit network connection. To preserve
Cargo downloads, put CARGO_HOME on DATA and stop related builds before ejecting.
An ordinary development program uses the target's glibc environment; rebuilding
the FDS distribution and its static control plane still follows the documented
x86_64 cross-build workflow.
make development-test runs native C, C++ and Rust compiler/linker executions,
CMake/Meson/Ninja/Make builds, GDB and strace, a local Git commit, and a headless Vim
edit in a disposable ARM VM. It verifies read-only SYSTEM, absent optional
services and native shutdown. Test results are recorded in
M12 validation; they do not establish Pi compiler throughput.