Validate FreeBSD derivation generation

This commit is contained in:
2026-04-01 14:10:14 +02:00
parent d4f1fedcb8
commit 12afb2bab3
3 changed files with 324 additions and 0 deletions

View File

@@ -1615,3 +1615,34 @@ Current assessment:
- Phase 5.1 is now satisfied on the current FreeBSD prototype track
- the key boundary has shifted from “the checkout still crashes immediately” to “the checkout runs, and can now be used as the basis for real derivation/store experiments”
- the next step is to prove that a real derivation can be emitted against `/frx/store` from the now-runnable checkout
## 2026-04-01 — Phase 5.2 completed: real derivation generation validated against `/frx/store`
Completed work:
- added a runnable derivation-generation harness:
- `tests/guix/run-phase5-derivation-generation.sh`
- wrote the Phase 5.2 report:
- `docs/reports/phase5-derivation-generation-freebsd.md`
- ran the derivation-generation harness successfully and captured metadata under:
- `/tmp/phase5-derivation-metadata.txt`
Important findings:
- the now-runnable checkout can successfully use a real daemon/store connection on FreeBSD to lower a package through:
- `package->bag`
- `bag->derivation`
- the emitted derivation is a real `/frx/store` derivation path rather than an ad hoc placeholder or shell metadata artifact
- the validation used a deliberately minimal custom package with a custom low-level build system so that this subphase isolates the real lowering/store boundary without being dominated by still-unresolved upstream bootstrap assumptions for full native FreeBSD package graphs
- observed metadata confirmed:
- `bag_name=phase5-freebsd-lowering-0`
- `bag_host_inputs=("source")`
- `drv_path=/frx/store/...-phase5-freebsd-lowering-0.drv`
- `out_path=/frx/store/...-phase5-freebsd-lowering-0`
- this means the key architectural step is now real and no longer hypothetical:
- a package object in the checkout can be lowered to a real derivation targeting `/frx/store` on FreeBSD
Current assessment:
- Phase 5.2 is now satisfied on the current FreeBSD prototype track
- the next step is no longer “can we emit a derivation at all?” but “can the same daemon/store path accept and execute a derivation-backed build request successfully?”

View File

@@ -0,0 +1,90 @@
# Phase 5.2: Real derivation generation validated on FreeBSD against `/frx/store`
Date: 2026-04-01
## Summary
This step validates that the now-runnable checkout can perform real package lowering and emit a real derivation targeting `/frx/store` on FreeBSD.
Added file:
- `tests/guix/run-phase5-derivation-generation.sh`
## Approach
Rather than jumping immediately to a full upstream package such as GNU Hello, this step uses a deliberately minimal custom package defined inside the harness. The goal is to validate the real Guix lowering layers first, while avoiding unrelated bootstrap baggage that is still not fully adapted to FreeBSD.
The custom package still exercises the layers that matter for this subphase:
- `package->bag`
- `bag->derivation`
- real store connection through the daemon socket
- derivation emission into `/frx/store`
- source lowering as a real store item
## Why a custom low-level package was used
Attempting to lower a representative upstream package now gets past the earlier `leave-on-EPIPE` command-path failure, but still runs into deeper platform/bootstrap issues on this FreeBSD path, such as missing bootstrap binaries for the native system string.
That means the meaningful next proof point was:
> can the real checkout lower a package and emit a real derivation at all on FreeBSD?
The answer is now yes.
## Validation command
Run command:
```sh
METADATA_OUT=/tmp/phase5-derivation-metadata.txt \
./tests/guix/run-phase5-derivation-generation.sh
```
## What the harness does
The harness:
1. reuses the patched checkout/runtime setup from Phase 5.1
2. builds the patched `guix-daemon` for FreeBSD
3. starts that daemon on a temporary Unix socket
4. runs the checkout through the user-facing frontend boundary:
- `./pre-inst-env fruix repl -- ...`
5. defines a minimal custom FreeBSD test package with a custom low-level build system
6. lowers that package through:
- `package->bag`
- `bag->derivation`
7. records the resulting derivation path and output path
## Observed results
Observed metadata included:
- `bag_name=phase5-freebsd-lowering-0`
- `bag_host_inputs=("source")`
- `drv_path=/frx/store/...-phase5-freebsd-lowering-0.drv`
- `out_path=/frx/store/...-phase5-freebsd-lowering-0`
This demonstrates that:
- the checkout can now talk to a real daemon socket on FreeBSD
- a real package object can be lowered to a bag
- that bag can be lowered to a real derivation
- the derivation is registered under `/frx/store`
- the output path is also a normal `/frx/store` path
## Important findings
- the FreeBSD daemon path does not need to be fully feature-complete before derivation generation becomes useful; a narrow but real lowering path already works
- the custom package approach avoided conflating Phase 5.2 with the still-unresolved upstream bootstrap-package assumptions for native FreeBSD package graphs
- the result is materially beyond the earlier builder-phase and profile prototypes because the derivation is now emitted by the real checkout and store machinery rather than by an ad hoc stand-in
## Conclusion
Phase 5.2 is satisfied on the current FreeBSD prototype track:
- a real derivation is now emitted by the checkout on FreeBSD
- it targets `/frx/store`
- it comes from a real `package->bag` and `bag->derivation` path rather than from a shell-only approximation
The next step is to go one layer deeper and submit an actual derivation-backed build request through the same FreeBSD-aware daemon/store path.

View File

@@ -0,0 +1,203 @@
#!/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-phase5-derivation.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/phase5-derivation-metadata.txt
daemon_socket=$workdir/guix-daemon.sock
daemon_log=$workdir/guix-daemon.log
scheme_log=$workdir/phase5-derivation.log
scheme_template=$workdir/phase5-derivation.scm.in
scheme_file=$workdir/phase5-derivation.scm
source_file=$workdir/phase5-source.txt
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
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
printf 'phase5-derivation-source\n' > "$source_file"
cat >"$scheme_template" <<'EOF'
(use-modules (guix store)
(guix monads)
(guix gexp)
(guix packages)
(guix build-system)
(guix derivations)
(ice-9 match)
(srfi srfi-1))
(define source-file "__SOURCE_FILE__")
(call-with-output-file source-file
(lambda (port)
(display "phase5-derivation-source\n" port)))
(define source-item
(local-file source-file "__SOURCE_NAME__"))
(define* (phase5-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 phase5-build)
(arguments `(#:source ,source))))
(define* (phase5-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 "phase5-builder.sh"
"#!/bin/sh\nset -eu\n/bin/mkdir -p \"$out\"\n/bin/cp \"$1\" \"$out/payload.txt\"\nprintf '%s\\n' \"$1\" > \"$out/source-path.txt\"\n"
(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"))
#:inputs `(,@(if (derivation? source*)
`((,source*))
'())
(,shell) (,builder))
#:sources `(,shell ,builder)
#:system system
#:outputs outputs)
store))))
(define phase5-build-system
(build-system
(name 'phase5-freebsd)
(description "Phase 5 FreeBSD lowering test build system")
(lower phase5-lower)))
(define phase5-package
(package
(name "phase5-freebsd-lowering")
(version "0")
(source source-item)
(build-system phase5-build-system)
(synopsis "Phase 5 lowering test package")
(description "Minimal package used to validate lowering on FreeBSD.")
(home-page "https://example.invalid/fruix")
(license #f)))
(run-with-store (open-connection)
(mlet* %store-monad ((bag -> (package->bag phase5-package (%current-system) #f))
(drv (bag->derivation bag phase5-package)))
(return (begin
(format #t "bag-name=~a~%" (bag-name bag))
(format #t "bag-host-inputs=~s~%" (map car (bag-host-inputs bag)))
(format #t "drv-path=~a~%" (derivation-file-name drv))
(format #t "out-path=~a~%" (derivation->output-path drv))))))
EOF
sed \
-e "s|__SOURCE_FILE__|$source_file|g" \
-e "s|__SOURCE_NAME__|$(basename "$source_file")|g" \
"$scheme_template" > "$scheme_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 repl -- "$scheme_file" >"$scheme_log" 2>&1
bag_name=$(sed -n 's/^bag-name=//p' "$scheme_log")
bag_inputs=$(sed -n 's/^bag-host-inputs=//p' "$scheme_log")
drv_path=$(sed -n 's/^drv-path=//p' "$scheme_log")
out_path=$(sed -n 's/^out-path=//p' "$scheme_log")
[ -n "$bag_name" ] || { echo "missing bag-name in $scheme_log" >&2; exit 1; }
[ -n "$drv_path" ] || { echo "missing drv-path in $scheme_log" >&2; exit 1; }
[ -n "$out_path" ] || { echo "missing out-path in $scheme_log" >&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/*) : ;;
*) echo "unexpected output path: $out_path" >&2; exit 1 ;;
esac
printf '%s' "$bag_inputs" | grep -q 'source' || {
echo "expected source input in bag-host-inputs: $bag_inputs" >&2
exit 1
}
cat >"$metadata_file" <<EOF
workdir=$workdir
setup_metadata=$setup_metadata
setup_env=$setup_env
phase5_builddir=$PHASE5_BUILDDIR
daemon_socket=$daemon_socket
daemon_log=$daemon_log
scheme_log=$scheme_log
source_file=$source_file
scheme_file=$scheme_file
bag_name=$bag_name
bag_host_inputs=$bag_inputs
drv_path=$drv_path
out_path=$out_path
EOF
if [ -n "$metadata_target" ]; then
mkdir -p "$(dirname "$metadata_target")"
cp "$metadata_file" "$metadata_target"
fi
printf 'PASS phase5-derivation-generation\n'
printf 'Work directory: %s\n' "$workdir"
printf 'Metadata file: %s\n' "$metadata_file"
if [ -n "$metadata_target" ]; then
printf 'Copied metadata to: %s\n' "$metadata_target"
fi
printf '%s\n' '--- metadata ---'
cat "$metadata_file"