From 2e838fb2a5657186382d5aa7cf49b5208a9e941d Mon Sep 17 00:00:00 2001 From: Steffen Beyer Date: Wed, 1 Apr 2026 16:34:42 +0200 Subject: [PATCH] Validate real FreeBSD package builds --- docs/PROGRESS.md | 31 +++ .../phase6-real-package-build-freebsd.md | 65 ++++++ tests/guix/run-phase6-real-package-build.sh | 216 ++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 docs/reports/phase6-real-package-build-freebsd.md create mode 100755 tests/guix/run-phase6-real-package-build.sh diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index b8beea7..5f26617 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1723,3 +1723,34 @@ Next recommended step: - Fruix at the product boundary - `/frx` as the canonical store root - stable upstream-derived internal names unless there is strong architectural value in renaming them + +## 2026-04-01 — Phase 6.1 completed: real package build validated into `/frx/store` + +Completed work: + +- added a runnable real-package harness: + - `tests/guix/run-phase6-real-package-build.sh` +- wrote the Phase 6.1 report: + - `docs/reports/phase6-real-package-build-freebsd.md` +- ran the real-package harness successfully and captured metadata under: + - `/tmp/phase6-real-package-metadata.txt` + +Important findings: + +- the checkout can now build a real package definition derived from Guix's `hello` package through: + - `./pre-inst-env fruix build -f ...` + - the FreeBSD-aware daemon + - `/frx/store` +- this moves the project beyond the deliberately minimal Phase 5 custom derivation path and into a real package-definition flow +- the current successful path still uses a prefetched local GNU Hello tarball as the package source because the built-in downloader path remains a separate unresolved FreeBSD/root-daemon issue +- observed metadata confirmed: + - `drv_path=/frx/store/...-hello-2.12.3.drv` + - `out_path=/frx/store/...-hello-2.12.3` + - `source_store_path=/frx/store/...-hello-2.12.3.tar.gz` + - `runtime_output=Hello, world!` +- a daemon-side references query confirmed that the built output preserved the declared source store item as a direct reference + +Current assessment: + +- Phase 6.1 is now satisfied on the current FreeBSD prototype track +- the next step is to move the already validated jail/build-user model into this live package-build path rather than keeping it prototype-only diff --git a/docs/reports/phase6-real-package-build-freebsd.md b/docs/reports/phase6-real-package-build-freebsd.md new file mode 100644 index 0000000..6203302 --- /dev/null +++ b/docs/reports/phase6-real-package-build-freebsd.md @@ -0,0 +1,65 @@ +# Phase 6.1: Real FreeBSD-backed package build into `/frx/store` + +Date: 2026-04-01 + +## Summary + +This step moves beyond the Phase 5 minimal custom derivation path and validates a real package definition derived from Guix's `hello` package through the live Fruix/Guix checkout, daemon, and `/frx/store` path on FreeBSD. + +Added file: + +- `tests/guix/run-phase6-real-package-build.sh` + +## Validation command + +Run command: + +```sh +METADATA_OUT=/tmp/phase6-real-package-metadata.txt \ +./tests/guix/run-phase6-real-package-build.sh +``` + +## What the harness does + +The harness: + +1. reuses the patched Phase 5 checkout/runtime setup +2. fetches GNU Hello `2.12.3` and verifies the expected SHA256 +3. starts the patched daemon on a temporary Unix socket +4. generates a package file that inherits from Guix's real `hello` package definition +5. replaces the source with a prefetched local tarball store item to avoid the still-unresolved built-in downloader/root path on FreeBSD +6. lowers that package through a tiny FreeBSD host build system and invokes it through: + - `./pre-inst-env fruix build -f ...` +7. validates the built result in `/frx/store` +8. queries the daemon for direct references of the output path + +## Observed results + +Observed metadata included: + +- `drv_path=/frx/store/...-hello-2.12.3.drv` +- `out_path=/frx/store/...-hello-2.12.3` +- `source_store_path=/frx/store/...-hello-2.12.3.tar.gz` +- `runtime_output=Hello, world!` +- `frontend_invocation=./pre-inst-env fruix build -f ...` + +The direct references query included both: + +- the built output path itself +- the source tarball store item recorded by the build output metadata + +## Important findings + +- this is the first validated `fruix build`-style package build on the current FreeBSD track that lands a real package output in `/frx/store` +- the package definition was not invented from scratch; it inherits from the real Guix `hello` package and only swaps in a FreeBSD-specific build-system path plus a prefetched source item +- the live daemon/store path is now strong enough to materialize and execute a genuine package output rather than only the earlier minimal derivation-backed probes +- the built-in downloader path is still not ready for this FreeBSD/root daemon flow, so Phase 6.1 currently uses a prefetched local tarball to isolate the package-build boundary from that separate runtime issue + +## Conclusion + +Phase 6.1 is satisfied on the current FreeBSD prototype track: + +- a real package definition derived from Guix's `hello` package builds successfully through the Fruix/Guix checkout and daemon path +- the output lands in `/frx/store` +- the package runs successfully from the store +- the output preserves a declared source reference that can be queried through the daemon diff --git a/tests/guix/run-phase6-real-package-build.sh b/tests/guix/run-phase6-real-package-build.sh new file mode 100755 index 0000000..0943640 --- /dev/null +++ b/tests/guix/run-phase6-real-package-build.sh @@ -0,0 +1,216 @@ +#!/bin/sh +set -eu + +repo_root=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd) +metadata_target=${METADATA_OUT:-} + +cleanup=0 +if [ -n "${WORKDIR:-}" ]; then + workdir=$WORKDIR + mkdir -p "$workdir" +else + workdir=$(mktemp -d /tmp/fruix-phase6-real-package.XXXXXX) + cleanup=1 +fi +if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then + cleanup=0 +fi + +setup_metadata=$workdir/setup-metadata.txt +setup_env=$workdir/setup-env.sh +metadata_file=$workdir/phase6-real-package-metadata.txt +daemon_socket=$workdir/guix-daemon.sock +daemon_log=$workdir/guix-daemon.log +fetch_tarball=$workdir/hello-2.12.3.tar.gz +build_stdout=$workdir/fruix-build.out +build_stderr=$workdir/fruix-build.err +references_log=$workdir/references.log +package_template=$workdir/phase6-hello.scm.in +package_file=$workdir/phase6-hello.scm + +daemon_pid= +cleanup_workdir() { + if [ -n "$daemon_pid" ]; then + sudo kill "$daemon_pid" >/dev/null 2>&1 || true + wait "$daemon_pid" 2>/dev/null || true + fi + rm -f "$daemon_socket" + if [ "$cleanup" -eq 1 ]; then + rm -rf "$workdir" + fi +} +trap cleanup_workdir EXIT INT TERM + +fetch -o "$fetch_tarball" https://ftp.gnu.org/gnu/hello/hello-2.12.3.tar.gz >/dev/null +[ "$(sha256 -q "$fetch_tarball")" = '0d5f60154382fee10b114a1c34e785d8b1f492073ae2d3a6f7b147687b366aa0' ] || { + echo "unexpected hello tarball hash" >&2 + exit 1 +} + +WORKDIR=$workdir/setup KEEP_WORKDIR=1 \ + METADATA_OUT=$setup_metadata ENV_OUT=$setup_env \ + "$repo_root/tests/guix/setup-phase5-checkout.sh" >"$workdir/setup.log" 2>&1 + +# shellcheck disable=SC1090 +. "$setup_env" + +cd "$PHASE5_BUILDDIR" +export LD_LIBRARY_PATH GUILE_LOAD_PATH GUILE_LOAD_COMPILED_PATH GUILE_EXTENSIONS_PATH +export GUILE_AUTO_COMPILE=0 + +cat >"$package_template" <<'EOF' +(use-modules (gnu packages base) + (guix packages) + (guix build-system) + (guix store) + (guix monads) + (guix gexp) + (guix derivations)) + +(define source-file "__SOURCE_FILE__") +(define source-item + (local-file source-file "hello-2.12.3.tar.gz")) + +(define* (phase6-lower name #:key source inputs native-inputs outputs system target #:allow-other-keys) + (bag + (name name) + (system system) + (target target) + (host-inputs `(,@(if source `(("source" ,source)) '()) ,@inputs)) + (build-inputs native-inputs) + (outputs outputs) + (build phase6-build) + (arguments `(#:source ,source #:package-name ,name)))) + +(define* (phase6-build name inputs #:key source (outputs '("out")) (system (%current-system)) #:allow-other-keys) + (mlet* %store-monad ((shell (interned-file "/bin/sh" "sh" #:recursive? #t)) + (source* (lower-object source system)) + (builder (text-file "phase6-hello-builder.sh" +"#!/bin/sh +set -eu +export PATH=/bin:/usr/bin:/usr/local/bin +src_tar=\"$1\" +/bin/mkdir -p \"$TMPDIR/phase6-build\" +cd \"$TMPDIR/phase6-build\" +/usr/bin/tar -xf \"$src_tar\" +cd hello-2.12.3 +export CC=/usr/bin/cc +export CONFIG_SHELL=/bin/sh +export CPPFLAGS='-I/usr/local/include' +export LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib' +./configure --prefix=\"$out\" +/usr/local/bin/gmake -j1 +/usr/local/bin/gmake -j1 check +/usr/local/bin/gmake -j1 install +/bin/mkdir -p \"$out/share/fruix-phase6\" +printf '%s\\n' \"$src_tar\" > \"$out/share/fruix-phase6/source-store-path.txt\" +" + (list (if (derivation? source*) + (derivation->output-path source*) + source*))))) + (lambda (store) + (values (derivation store name shell + (list "-e" builder + (if (derivation? source*) + (derivation->output-path source*) + source*)) + #:env-vars '(("HOME" . "/homeless") + ("PATH" . "/bin:/usr/bin:/usr/local/bin")) + #:inputs `(,@(if (derivation? source*) `((,source*)) '()) + (,shell) (,builder)) + #:sources `(,shell ,builder) + #:system system + #:outputs outputs) + store)))) + +(define phase6-build-system + (build-system + (name 'phase6-freebsd-host-gnu) + (description "Phase 6 FreeBSD host GNU build system") + (lower phase6-lower))) + +(package/inherit hello + (source source-item) + (build-system phase6-build-system) + (supported-systems (list (%current-system)))) +EOF + +sed "s|__SOURCE_FILE__|$fetch_tarball|g" "$package_template" > "$package_file" + +sudo env GUIX_DAEMON_SOCKET=unix://$daemon_socket \ + "$PHASE5_GUIX_DAEMON" --disable-chroot --listen "$daemon_socket" --no-substitutes \ + >"$daemon_log" 2>&1 & +daemon_pid=$! +for _ in 1 2 3 4 5 6 7 8 9 10; do + [ -S "$daemon_socket" ] && break + sleep 1 +done +[ -S "$daemon_socket" ] || { + echo "daemon socket was not created: $daemon_socket" >&2 + exit 1 +} + +GUIX_DAEMON_SOCKET=unix://$daemon_socket \ +./pre-inst-env fruix build -f "$package_file" >"$build_stdout" 2>"$build_stderr" + +out_path=$(awk 'NF { last = $0 } END { print last }' "$build_stdout") +drv_path=$(sed -n 's/^successfully built //p' "$build_stderr" | tail -n 1) +source_store_path=$(tr -d '\n' < "$out_path/share/fruix-phase6/source-store-path.txt") +runtime_output=$("$out_path/bin/hello") +GUIX_DAEMON_SOCKET=unix://$daemon_socket \ +./pre-inst-env fruix gc --references "$out_path" >"$references_log" 2>"$workdir/fruix-gc.err" + +[ -n "$drv_path" ] || { echo "missing derivation path in $build_stderr" >&2; exit 1; } +case "$drv_path" in + /frx/store/*.drv) : ;; + *) echo "unexpected derivation path: $drv_path" >&2; exit 1 ;; +esac +case "$out_path" in + /frx/store/*-hello-2.12.3) : ;; + *) echo "unexpected output path: $out_path" >&2; exit 1 ;; +esac +case "$source_store_path" in + /frx/store/*-hello-2.12.3.tar.gz) : ;; + *) echo "unexpected source store path: $source_store_path" >&2; exit 1 ;; +esac +[ "$runtime_output" = 'Hello, world!' ] || { + echo "unexpected runtime output: $runtime_output" >&2 + exit 1 +} +grep -Fx "$source_store_path" "$references_log" >/dev/null || { + echo "source store path was not preserved as a reference" >&2 + exit 1 +} + +cat >"$metadata_file" <