2
Developer Development
Felis edited this page 2026-09-27 00:29:40 +08:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Table of Contents
include_toc
include_toc
true

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 .host/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.