diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index fcff5a2..bf473fd 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -2959,3 +2959,102 @@ Next recommended step: 1. build the first concrete `freebsd-native-kernel` and `freebsd-native-world` outputs from `/usr/src` 2. inspect/document their staged contents in `/frx/store` 3. then wire a bootable system closure/image around those native outputs + +## 2026-04-03 — Phase 13.2: first native FreeBSD world/kernel outputs built from `/usr/src` + +Completed work: + +- wrote the Phase 13.2 report: + - `docs/reports/phase13-native-world-kernel-build-freebsd.md` +- exercised the new native build path for real and produced the first concrete store outputs from `/usr/src`: + - native kernel output under `/frx/store` + - native world output under `/frx/store` +- added a dedicated Phase 13.2 template/harness: + - `tests/system/phase13-native-base-pid1-operating-system.scm.in` + - `tests/system/run-phase13-native-base-build.sh` +- fixed the first real native-build failure: + - `MAKEOBJDIRPREFIX` cannot be passed as a make command-line variable for this FreeBSD build path + - changed the native builder to invoke make as: + - `env MAKEOBJDIRPREFIX=... make ...` +- fixed a subtle output-identity bug in the first `/usr/src` hash implementation: + - the initial `mtree`-based source hash accidentally included unstable header comments such as date/user/machine lines + - now strips `# ...` header lines before hashing + - this stabilized the source-tree identity and stopped the native output/build-root identity from drifting on each run +- verified that native world and kernel now share the same: + - `source-tree-sha256` + - `build-root` +- the first native world split remains intentionally runtime-oriented and prunes at least: + - `usr/share/doc` + - `usr/share/examples` + - `usr/share/info` + - `usr/share/man` + - `usr/tests` + +Concrete validated outputs: + +- native kernel store path: + - `/frx/store/93f35ddcb9a03f63f83c9e8ae29788685d339789da664f881822b4a1914f5ff6-freebsd-native-kernel-15.0-STABLE` +- native world store path: + - `/frx/store/3f6f7f8c06ed8dad4cae21a1e8ac8ba4823bdb7cf54328c9bbcccaeb858beb77-freebsd-native-world-15.0-STABLE` +- shared native build root: + - `/var/tmp/fruix-freebsd-native-build-c59b1b8128b305d9bad9cf3d654771c941c4e8b6a2732f6bc959df96d1d32f58` + +Validated native-world contents include at least: + +- `/bin/sh` +- `/sbin/init` +- `/etc/rc` +- `/usr/sbin/sshd` +- `/sbin/dhclient` +- `/usr/bin/cap_mkdb` +- `/usr/sbin/pwd_mkdb` +- `/usr/share/locale/C.UTF-8/LC_CTYPE` + +Validated pruned paths are absent from the world output: + +- `/usr/share/man` +- `/usr/tests` + +Validation: + +- real native-base `fruix system build` succeeded with an operating-system using: + - `freebsd-native-kernel` + - `freebsd-native-world` + - host-staged `freebsd-bootloader` + - `shepherd-pid1` +- `tests/system/run-phase13-native-base-build.sh` passes: + - workdir: `/tmp/phase13-2-build-1775173551` + - result: `PASS phase13-native-base-build` +- the harness confirmed: + - closure rebuild path reproducibility + - native kernel/world store paths exist + - native build info files exist + - world/kernel source-tree hashes match + - world/kernel build roots match + - native build logs exist + - `native-base-stores` is present in closure metadata + - host/native store boundary now looks as expected for this mixed system: + - `host_base_store_count=1` + - `native_base_store_count=2` + +Important findings: + +- the native build path is now real, not just modeled +- the first mixed Phase-13 system boundary is explicit and sensible: + - host-staged bootloader only + - native kernel + native world + - Fruix runtime stores unchanged +- the `mtree` preamble bug would have made native output identity drift across runs; fixing it was essential before treating these as reproducible store artifacts +- the shared build-root result is important: the kernel/world pair now reuses the same `/usr/src` build state instead of acting like two unrelated builds + +Current assessment: + +- Phase 13.2 is complete +- Fruix can now build and stage native FreeBSD base artifacts from `/usr/src` in `/frx/store` +- the next step is to boot a system using those native outputs rather than stopping at build-time inspection + +Next recommended step: + +1. wire the image/boot path to use the native kernel/world outputs end-to-end +2. validate locally with QEMU/UEFI +3. validate on the approved XCP-ng VM and VDI path diff --git a/docs/reports/phase13-native-world-kernel-build-freebsd.md b/docs/reports/phase13-native-world-kernel-build-freebsd.md new file mode 100644 index 0000000..fdc8b18 --- /dev/null +++ b/docs/reports/phase13-native-world-kernel-build-freebsd.md @@ -0,0 +1,176 @@ +# Phase 13.2: built the first native FreeBSD world and kernel artifacts from `/usr/src` + +Date: 2026-04-03 + +## Goal + +After Phase 13.1 introduced native Fruix package/materialization support for FreeBSD base artifacts, the next step was to exercise that path for real: + +- build a kernel from `/usr/src` +- build and stage a minimal runtime-oriented world from `/usr/src` +- place both outputs under `/frx/store` +- document the exact split of this first native base target + +## Implementation + +### 1. Fixed two important builder issues found during the first real run + +#### `MAKEOBJDIRPREFIX` had to move to the environment + +The first real `buildworld` attempt failed with: + +```text +MAKEOBJDIRPREFIX can only be set in environment or src-env.conf(5) +``` + +The native build path originally passed `MAKEOBJDIRPREFIX=...` as a make command-line variable. That was corrected so the generated build command now uses: + +- `env MAKEOBJDIRPREFIX=... make ...` + +instead. + +#### The initial `/usr/src` tree hash was accidentally unstable + +The first source-tree hashing attempt hashed the raw `mtree -c` output. That was subtly wrong because `mtree` includes header comments such as: + +- date +- user +- machine +- tree path banner + +As a result, successive hash computations differed even when `/usr/src` had not changed. + +This was corrected by stripping the leading `# ...` comment lines before hashing the `mtree` content. After that fix: + +- native world and native kernel reported the same `source-tree-sha256` +- both reused the same native build root +- output identity stopped drifting on each run + +## First concrete native outputs + +A native Fruix system definition using: + +- `freebsd-native-kernel` +- `freebsd-native-world` +- host-staged `freebsd-bootloader` +- `shepherd-pid1` + +was built successfully. + +Resulting store outputs: + +- kernel: + - `/frx/store/93f35ddcb9a03f63f83c9e8ae29788685d339789da664f881822b4a1914f5ff6-freebsd-native-kernel-15.0-STABLE` +- world: + - `/frx/store/3f6f7f8c06ed8dad4cae21a1e8ac8ba4823bdb7cf54328c9bbcccaeb858beb77-freebsd-native-world-15.0-STABLE` + +Native build metadata records for both outputs show: + +- `source-root=/usr/src` +- the same `source-tree-sha256` +- `kernconf=GENERIC` +- the same shared `build-root` + +Shared build root: + +- `/var/tmp/fruix-freebsd-native-build-c59b1b8128b305d9bad9cf3d654771c941c4e8b6a2732f6bc959df96d1d32f58` + +Key log files recorded in the output metadata: + +- `buildworld.log` +- `buildkernel-GENERIC.log` +- `install-freebsd-native-world.log` +- `install-freebsd-native-kernel.log` + +## First native world split + +The first `freebsd-native-world` output is intentionally runtime-oriented. Validation confirmed that it contains the core pieces needed by the existing Fruix guest model, including: + +- `/bin/sh` +- `/sbin/init` +- `/etc/rc` +- `/usr/sbin/sshd` +- `/sbin/dhclient` +- `/usr/bin/cap_mkdb` +- `/usr/sbin/pwd_mkdb` +- `/usr/share/locale/C.UTF-8/LC_CTYPE` + +The first prune list is also active. Validation confirmed the world output does **not** contain: + +- `/usr/share/man` +- `/usr/tests` + +This keeps the first native world output closer to the current boot/runtime target instead of a fully unpruned base install tree. + +## Store-boundary result + +The generated closure metadata now shows the intended transitional boundary clearly: + +- `host_base_store_count=1` +- `host_base_stores=` bootloader only +- `native_base_store_count=2` +- `native_base_stores=` native kernel + native world + +That means Fruix now has a mixed but explicit Phase-13 system boundary: + +- bootloader still comes from the old host-staged path +- kernel and core runtime world now come from native `/usr/src`-built store outputs + +## Validation + +### Direct build through `fruix system build` + +A real native-base system build succeeded and produced: + +- native kernel store output +- native world store output +- a system closure referencing those outputs + +### Dedicated Phase 13.2 harness + +Added: + +- `tests/system/phase13-native-base-pid1-operating-system.scm.in` +- `tests/system/run-phase13-native-base-build.sh` + +Passing run: + +- `PASS phase13-native-base-build` +- workdir: `/tmp/phase13-2-build-1775173551` + +Key metadata from that run: + +```text +kernel_store=/frx/store/93f35ddcb9a03f63f83c9e8ae29788685d339789da664f881822b4a1914f5ff6-freebsd-native-kernel-15.0-STABLE +world_store=/frx/store/3f6f7f8c06ed8dad4cae21a1e8ac8ba4823bdb7cf54328c9bbcccaeb858beb77-freebsd-native-world-15.0-STABLE +host_base_store_count=1 +native_base_store_count=2 +world_source_tree_sha256=72a451e2ea47c4c4777035f797dc35f8d905eaabb2a717bc1fd71019f3021f72 +kernel_source_tree_sha256=72a451e2ea47c4c4777035f797dc35f8d905eaabb2a717bc1fd71019f3021f72 +world_build_root=/var/tmp/fruix-freebsd-native-build-c59b1b8128b305d9bad9cf3d654771c941c4e8b6a2732f6bc959df96d1d32f58 +kernel_build_root=/var/tmp/fruix-freebsd-native-build-c59b1b8128b305d9bad9cf3d654771c941c4e8b6a2732f6bc959df96d1d32f58 +``` + +The harness also verified: + +- closure rebuild path reproducibility +- existence of required kernel/world runtime files +- absence of the pruned world paths +- presence of native build metadata and logs +- presence of `native-base-stores` in closure store-layout metadata + +## Assessment + +Phase 13.2 is complete. + +Fruix now does more than describe native FreeBSD base artifacts: it actually builds them from `/usr/src`, stores them under `/frx/store`, and records enough metadata to inspect how they were produced. + +This is the first point where Fruix can honestly say that part of the FreeBSD base is no longer just a curated copy of the builder host. + +## Next recommended step + +Proceed to Phase 13.3: + +- wire the operating-system/image path to boot using these native kernel/world outputs +- validate locally with QEMU/UEFI +- validate on the approved XCP-ng VM/VDI path diff --git a/modules/fruix/system/freebsd.scm b/modules/fruix/system/freebsd.scm index 0212a65..d6944bb 100644 --- a/modules/fruix/system/freebsd.scm +++ b/modules/fruix/system/freebsd.scm @@ -276,8 +276,11 @@ (build-plan-ref plan 'kernconf "GENERIC")))) (define (native-build-source-tree-sha256 source-root) - (string-hash - (command-output "mtree" "-c" "-k" "type,link,size,mode,sha256digest" "-p" source-root))) + (let* ((mtree-output (command-output "mtree" "-c" "-k" "type,link,size,mode,sha256digest" "-p" source-root)) + (stable-lines (filter (lambda (line) + (not (string-prefix? "#" line))) + (string-split mtree-output #\newline)))) + (string-hash (string-join stable-lines "\n")))) (define (native-build-common-manifest plan) (let* ((source-root (build-plan-ref plan 'source-root "/usr/src")) @@ -383,10 +386,9 @@ (string-append "/var/tmp/fruix-freebsd-native-build-" (string-hash (object->string common)))) -(define (native-make-arguments common build-root) +(define (native-make-arguments common _build-root) (append (list "-C" (assoc-ref common 'source-root) - (string-append "MAKEOBJDIRPREFIX=" build-root "/obj") (string-append "TARGET=" (assoc-ref common 'target)) (string-append "TARGET_ARCH=" (assoc-ref common 'target-arch)) (string-append "KERNCONF=" (assoc-ref common 'kernconf))) @@ -395,7 +397,7 @@ (define* (make-command-string common build-root target #:key (parallel? #f) (destdir #f)) (string-join (append - (list "make") + (list "env" (string-append "MAKEOBJDIRPREFIX=" build-root "/obj") "make") (if parallel? (list (string-append "-j" (current-build-jobs))) '()) diff --git a/tests/system/phase13-native-base-pid1-operating-system.scm.in b/tests/system/phase13-native-base-pid1-operating-system.scm.in new file mode 100644 index 0000000..2c75d99 --- /dev/null +++ b/tests/system/phase13-native-base-pid1-operating-system.scm.in @@ -0,0 +1,71 @@ +(use-modules (fruix system freebsd) + (fruix packages freebsd)) + +(define phase13-operating-system + (operating-system + #:host-name "fruix-freebsd" + #:kernel freebsd-native-kernel + #:bootloader freebsd-bootloader + #:base-packages (list freebsd-native-world) + #:groups (list (user-group #:name "wheel" #:gid 0 #:system? #t) + (user-group #:name "sshd" #:gid 22 #:system? #t) + (user-group #:name "_dhcp" #:gid 65 #:system? #t) + (user-group #:name "operator" #:gid 1000 #:system? #f)) + #:users (list (user-account #:name "root" + #:uid 0 + #:group "wheel" + #:comment "Charlie &" + #:home "/root" + #:shell "/bin/sh" + #:system? #t) + (user-account #:name "sshd" + #:uid 22 + #:group "sshd" + #:comment "Secure Shell Daemon" + #:home "/var/empty" + #:shell "/usr/sbin/nologin" + #:system? #t) + (user-account #:name "_dhcp" + #:uid 65 + #:group "_dhcp" + #:comment "dhcp programs" + #:home "/var/empty" + #:shell "/usr/sbin/nologin" + #:system? #t) + (user-account #:name "operator" + #:uid 1000 + #:group "operator" + #:supplementary-groups '("wheel") + #:comment "Fruix Operator" + #:home "/home/operator" + #:shell "/bin/sh" + #:system? #f)) + #:file-systems (list (file-system #:device "/dev/gpt/fruix-root" + #:mount-point "/" + #:type "ufs" + #:options "rw" + #:needed-for-boot? #t) + (file-system #:device "devfs" + #:mount-point "/dev" + #:type "devfs" + #:options "rw" + #:needed-for-boot? #t) + (file-system #:device "tmpfs" + #:mount-point "/tmp" + #:type "tmpfs" + #:options "rw,size=64m")) + #:services '(shepherd ready-marker sshd) + #:loader-entries '(("autoboot_delay" . "1") + ("boot_multicons" . "YES") + ("boot_serial" . "YES") + ("console" . "comconsole,vidconsole")) + #:rc-conf-entries '(("clear_tmp_enable" . "NO") + ("hostid_enable" . "NO") + ("sendmail_enable" . "NONE") + ("sshd_enable" . "YES") + ("ifconfig_xn0" . "SYNCDHCP") + ("ifconfig_em0" . "SYNCDHCP") + ("ifconfig_vtnet0" . "SYNCDHCP")) + #:init-mode 'shepherd-pid1 + #:ready-marker "/var/lib/fruix/ready" + #:root-authorized-keys '("__ROOT_AUTHORIZED_KEY__"))) diff --git a/tests/system/run-phase13-native-base-build.sh b/tests/system/run-phase13-native-base-build.sh new file mode 100755 index 0000000..afc0e90 --- /dev/null +++ b/tests/system/run-phase13-native-base-build.sh @@ -0,0 +1,224 @@ +#!/bin/sh +set -eu + +project_root=${PROJECT_ROOT:-$(pwd)} +script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd) +fruix_cmd=$project_root/bin/fruix +os_template=${OS_TEMPLATE:-$script_dir/phase13-native-base-pid1-operating-system.scm.in} +system_name=${SYSTEM_NAME:-phase13-operating-system} +store_dir=${STORE_DIR:-/frx/store} +metadata_target=${METADATA_OUT:-} +root_authorized_key_file=${ROOT_AUTHORIZED_KEY_FILE:-$HOME/.ssh/id_ed25519.pub} + +[ -x "$fruix_cmd" ] || { + echo "fruix command is not executable: $fruix_cmd" >&2 + exit 1 +} +[ -f "$os_template" ] || { + echo "missing operating-system template: $os_template" >&2 + exit 1 +} +[ -f "$root_authorized_key_file" ] || { + echo "missing root authorized key file: $root_authorized_key_file" >&2 + exit 1 +} + +cleanup=0 +if [ -n "${WORKDIR:-}" ]; then + workdir=$WORKDIR + mkdir -p "$workdir" +else + workdir=$(mktemp -d /tmp/fruix-phase13-native-build.XXXXXX) + cleanup=1 +fi +if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then + cleanup=0 +fi + +cleanup_workdir() { + if [ "$cleanup" -eq 1 ]; then + rm -rf "$workdir" 2>/dev/null || sudo rm -rf "$workdir" + fi +} +trap cleanup_workdir EXIT INT TERM + +phase13_os_file=$workdir/phase13-native-base-operating-system.scm +build_out_a=$workdir/build-a.txt +build_out_b=$workdir/build-b.txt +metadata_file=$workdir/phase13-native-base-build-metadata.txt +root_authorized_key=$(tr -d '\n' < "$root_authorized_key_file") +sed "s|__ROOT_AUTHORIZED_KEY__|$root_authorized_key|g" "$os_template" > "$phase13_os_file" + +action_env() { + sudo env \ + HOME="$HOME" \ + GUILE_AUTO_COMPILE=0 \ + FRUIX_FREEBSD_BUILD_JOBS="${FRUIX_FREEBSD_BUILD_JOBS:-8}" \ + GUIX_SOURCE_DIR="${GUIX_SOURCE_DIR:-$HOME/repos/guix}" \ + GUILE_BIN="${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}" \ + GUILE_EXTRA_PREFIX="${GUILE_EXTRA_PREFIX:-/tmp/guile-gnutls-freebsd-validate-install}" \ + SHEPHERD_PREFIX="${SHEPHERD_PREFIX:-/tmp/shepherd-freebsd-validate-install}" \ + "$@" +} + +printf 'Using fruix command: %s\n' "$fruix_cmd" +printf 'Working directory: %s\n' "$workdir" +printf 'Store directory: %s\n' "$store_dir" +printf 'OS template: %s\n' "$os_template" + +action_env "$fruix_cmd" system build "$phase13_os_file" --system "$system_name" --store "$store_dir" >"$build_out_a" +action_env "$fruix_cmd" system build "$phase13_os_file" --system "$system_name" --store "$store_dir" >"$build_out_b" + +closure_path=$(sed -n 's/^closure_path=//p' "$build_out_a") +closure_rebuild_path=$(sed -n 's/^closure_path=//p' "$build_out_b") +kernel_store=$(sed -n 's/^kernel_store=//p' "$build_out_a") +bootloader_store=$(sed -n 's/^bootloader_store=//p' "$build_out_a") +host_base_store_count=$(sed -n 's/^host_base_store_count=//p' "$build_out_a") +host_base_stores=$(sed -n 's/^host_base_stores=//p' "$build_out_a") +native_base_store_count=$(sed -n 's/^native_base_store_count=//p' "$build_out_a") +native_base_stores=$(sed -n 's/^native_base_stores=//p' "$build_out_a") +fruix_runtime_store_count=$(sed -n 's/^fruix_runtime_store_count=//p' "$build_out_a") +fruix_runtime_stores=$(sed -n 's/^fruix_runtime_stores=//p' "$build_out_a") +host_base_provenance_file=$(sed -n 's/^host_base_provenance_file=//p' "$build_out_a") +store_layout_file=$(sed -n 's/^store_layout_file=//p' "$build_out_a") +reference_count=$(sed -n 's/^reference_count=//p' "$build_out_a") +generated_file_count=$(sed -n 's/^generated_file_count=//p' "$build_out_a") + +[ -n "$closure_path" ] || { echo "missing closure path" >&2; exit 1; } +[ "$closure_path" = "$closure_rebuild_path" ] || { + echo "native-base closure path was not reproducible: $closure_path != $closure_rebuild_path" >&2 + exit 1 +} +case "$kernel_store" in + /frx/store/*-freebsd-native-kernel-15.0-STABLE) : ;; + *) echo "unexpected native kernel store path: $kernel_store" >&2; exit 1 ;; +esac +case "$bootloader_store" in + /frx/store/*-freebsd-bootloader-15.0-STABLE) : ;; + *) echo "unexpected bootloader store path: $bootloader_store" >&2; exit 1 ;; +esac +[ "$host_base_store_count" = 1 ] || { echo "expected exactly one host-staged base store, got: $host_base_store_count" >&2; exit 1; } +[ "$native_base_store_count" = 2 ] || { echo "expected exactly two native base stores, got: $native_base_store_count" >&2; exit 1; } +[ -n "$fruix_runtime_store_count" ] || { echo "missing Fruix runtime store count" >&2; exit 1; } +[ -n "$reference_count" ] || { echo "missing reference count" >&2; exit 1; } +[ -n "$generated_file_count" ] || { echo "missing generated file count" >&2; exit 1; } + +world_store=$(printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep 'freebsd-native-world-15.0-STABLE$' | head -n 1) +[ -n "$world_store" ] || { echo "failed to recover native world store from metadata" >&2; exit 1; } + +world_build_info=$world_store/.freebsd-native-build-info.scm +kernel_build_info=$kernel_store/.freebsd-native-build-info.scm +[ -f "$world_build_info" ] || { echo "missing world build info: $world_build_info" >&2; exit 1; } +[ -f "$kernel_build_info" ] || { echo "missing kernel build info: $kernel_build_info" >&2; exit 1; } +[ -f "$host_base_provenance_file" ] || { echo "missing host base provenance file: $host_base_provenance_file" >&2; exit 1; } +[ -f "$store_layout_file" ] || { echo "missing store layout file: $store_layout_file" >&2; exit 1; } + +for path in \ + "$kernel_store/boot/kernel/kernel" \ + "$kernel_store/boot/kernel/linker.hints" \ + "$world_store/bin/sh" \ + "$world_store/sbin/init" \ + "$world_store/etc/rc" \ + "$world_store/usr/sbin/sshd" \ + "$world_store/sbin/dhclient" \ + "$world_store/usr/bin/cap_mkdb" \ + "$world_store/usr/sbin/pwd_mkdb" \ + "$world_store/usr/share/locale/C.UTF-8/LC_CTYPE" +do + [ -e "$path" ] || { + echo "required native world/kernel path missing: $path" >&2 + exit 1 + } +done + +[ ! -e "$world_store/usr/share/man" ] || { echo "native world still contains pruned man pages" >&2; exit 1; } +[ ! -e "$world_store/usr/tests" ] || { echo "native world still contains pruned tests" >&2; exit 1; } + +grep -F 'native-base-stores' "$store_layout_file" >/dev/null || { + echo "store layout metadata is missing native-base-stores" >&2 + exit 1 +} + +grep -F 'source-root . "/usr/src"' "$world_build_info" >/dev/null || { + echo "world build info is missing /usr/src provenance" >&2 + exit 1 +} + +grep -F 'source-root . "/usr/src"' "$kernel_build_info" >/dev/null || { + echo "kernel build info is missing /usr/src provenance" >&2 + exit 1 +} + +world_source_tree_sha256=$(grep -o 'source-tree-sha256 . "[^"]*"' "$world_build_info" | head -n 1 | cut -d'"' -f2) +kernel_source_tree_sha256=$(grep -o 'source-tree-sha256 . "[^"]*"' "$kernel_build_info" | head -n 1 | cut -d'"' -f2) +world_build_root=$(grep -o 'build-root . "[^"]*"' "$world_build_info" | head -n 1 | cut -d'"' -f2) +kernel_build_root=$(grep -o 'build-root . "[^"]*"' "$kernel_build_info" | head -n 1 | cut -d'"' -f2) +world_build_log=$(grep -o 'buildworld-log . "[^"]*"' "$world_build_info" | head -n 1 | cut -d'"' -f2) +kernel_build_log=$(grep -o 'buildkernel-log . "[^"]*"' "$kernel_build_info" | head -n 1 | cut -d'"' -f2) +world_install_log=$(grep -o 'install-log . "[^"]*"' "$world_build_info" | head -n 1 | cut -d'"' -f2) +kernel_install_log=$(grep -o 'install-log . "[^"]*"' "$kernel_build_info" | head -n 1 | cut -d'"' -f2) + +[ -n "$world_source_tree_sha256" ] || { echo "missing world source-tree hash" >&2; exit 1; } +[ -n "$kernel_source_tree_sha256" ] || { echo "missing kernel source-tree hash" >&2; exit 1; } +[ "$world_source_tree_sha256" = "$kernel_source_tree_sha256" ] || { + echo "native world/kernel source-tree hashes differ: $world_source_tree_sha256 != $kernel_source_tree_sha256" >&2 + exit 1 +} +[ "$world_build_root" = "$kernel_build_root" ] || { + echo "native world/kernel build roots differ: $world_build_root != $kernel_build_root" >&2 + exit 1 +} + +for path in "$world_build_log" "$kernel_build_log" "$world_install_log" "$kernel_install_log"; do + [ -f "$path" ] || { + echo "expected native build log missing: $path" >&2 + exit 1 + } +done + +closure_base=$(basename "$closure_path") +cat >"$metadata_file" <