diff --git a/docs/PLAN_4.md b/docs/PLAN_4.md new file mode 100644 index 0000000..f0f7d0b --- /dev/null +++ b/docs/PLAN_4.md @@ -0,0 +1,406 @@ +# Fruix on FreeBSD, Path D: Declarative Source Acquisition, Installation Artifacts, and the Road to Self-Hosting + +This document extends `docs/PLAN_3.md` after the completion of Phases 1 through 15. + +The project has now crossed another important threshold: + +- Fruix builds native FreeBSD base artifacts from `/usr/src` into `/frx/store` +- the validated boot/runtime path is host-base-free at the deployed system layer +- the FreeBSD base is now an explicit declarative Fruix input +- side-by-side base versions can coexist in `/frx/store` +- Fruix can rebuild, redeploy, and roll back between declared base versions + +That means the next question is no longer whether Fruix can assemble and boot a native FreeBSD system. It can. + +The next question is how to make that native base path **more reproducible, more source-declarative, and more installable**. + +The biggest remaining impurity is now clear: + +- the native base build path still relies on the builder host's ambient `/usr/src` + +That was the right bridge through Phases 13 to 15, but it should not be the long-term source story. + +Accordingly, Plan 4 focuses on three connected goals: + +1. make FreeBSD source acquisition and source identity more declarative +2. turn the current image-generation path into a real installation story +3. only then move carefully toward self-hosted base builds + +This preserves the Guix-inspired core while continuing to embrace FreeBSD semantics rather than trying to imitate Linux-specific workflows. + +Throughout this plan, the canonical Fruix roots remain: + +- `/frx/store` +- `/frx/var` +- `/frx/etc` + +The user-facing system remains **Fruix**, with CLI `fruix`, while internal upstream-derived names continue to be renamed selectively rather than mechanically. + +The current real-VM validation constraints still apply unless explicitly relaxed later: + +- approved VM: `90490f2e-e8fc-4b7a-388e-5c26f0157289` +- approved A/B VDIs: + - `0f1f90d3-48ca-4fa2-91d8-fc6339b95743` + - `7061d761-3639-4bec-87f7-2ba1af924eaa` + +--- + +## Current State at the Start of Plan 4 + +Fruix on FreeBSD already has: + +- a functioning content-addressed store and derivation path under `/frx/store` +- daemon-mediated builds with validated FreeBSD isolation concepts +- a working declarative FreeBSD operating-system model +- validated system closure, rootfs, and image generation through `fruix system ...` +- real QEMU and XCP-ng boot validation +- a host-base-free native boot/runtime composition built from: + - `freebsd-native-kernel` + - `freebsd-native-bootloader` + - `freebsd-native-runtime` +- a declarative `freebsd-base` model that records: + - base identity + - version label + - branch + - source root + - target and kernel configuration +- side-by-side native base versions in `/frx/store` +- rollback-friendly redeploy across those declared base versions +- a documented decision to continue using host-built native base artifacts for now rather than jumping immediately to guest self-hosting + +The main remaining architectural compromise is now not the runtime/boot artifact split, but the **source boundary**: + +- native FreeBSD base builds still use ambient `/usr/src` +- source acquisition is not yet a first-class Fruix-managed input +- therefore reproducibility and provenance still stop short of a fully declared source story + +At the same time, Fruix still lacks a real installation workflow beyond image generation and validation harnesses. + +--- + +## Guiding Decision for the Next Milestone + +The next major milestone should be: + +- **declarative FreeBSD source acquisition and source pinning first**, +- then **native base builds from fetched or materialized source inputs**, +- then a **real installation story**, +- and only after that, a controlled re-evaluation of self-hosted base builds. + +This means the next work should optimize for: + +- explicit source identity +- stronger provenance +- side-by-side source revisions +- repeatable rebuilds from fetched or materialized source trees +- installation artifacts that are operator-usable beyond current validation harnesses + +It should **not** optimize for: + +- jumping straight into guest self-hosting while source identity is still weak +- building a large interactive installer before the installation primitives are clear +- replacing the current validated host-built native-base path prematurely + +--- + +## Phase 16: Make FreeBSD Source Acquisition Declarative + +This is the next architectural pivot after the native base/runtime split. + +The goal is to move from: + +- “build from whatever `/usr/src` is on the host” + +into: + +- “build from a declared FreeBSD source input with explicit provenance.” + +### Intermediate Goal 16.1: Model FreeBSD Source Inputs Explicitly + +**Verification Goal 16.1:** Introduce a first-class Fruix model for FreeBSD source inputs. + +This can be a new `freebsd-source` record, an extension of `freebsd-base`, or another explicit source object, but it should represent at least: + +- source kind: + - local checkout snapshot + - fetched Git checkout + - fetched `src.txz` archive +- source URL or local path +- branch or release line +- commit, tag, or revision when applicable +- expected tree or archive hash +- any intended patch queue or source transformation layer + +This step should also document how that source object relates to: + +- `freebsd-base` +- native kernel/world/runtime/bootloader/headers builds +- output identity in `/frx/store` + +**Success Criteria:** Fruix can describe FreeBSD source inputs as explicit declared objects rather than only relying on ambient `/usr/src`. + +### Intermediate Goal 16.2: Fetch or Materialize Source Trees Under Fruix Control + +**Verification Goal 16.2:** Materialize a clean FreeBSD source tree from a declared source input using Fruix-managed fetch/materialization logic. + +The first implementation may be intentionally modest, such as: + +- fetching a Git checkout at a pinned revision from `https://git.FreeBSD.org/src.git` +- or downloading a `src.txz` archive and verifying its hash + - for example from `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz` + - or from snapshot paths such as `https://download.freebsd.org/snapshots/amd64/15.0-STABLE/src.txz` +- or snapshotting a local source tree into a content-addressed source artifact with explicit metadata + +This phase should also define where downloaded or temporary source material lives, for example under: + +- `/frx/var/cache/fruix/freebsd-source` + +The important point is that the eventual build input should no longer be “the current mutable host tree” but a **materialized source snapshot with recorded identity**. + +**Success Criteria:** Fruix can fetch or materialize a pinned FreeBSD source tree with recorded provenance and a stable identity suitable for native base builds. + +### Intermediate Goal 16.3: Build Native Base Artifacts From the Declared Source Input + +**Verification Goal 16.3:** Teach the native FreeBSD base build path to consume the declared source artifact rather than ambient `/usr/src`. + +This should preserve the validated Plan-3/Plan-4 deployment story: + +- native kernel +- native bootloader slice +- native runtime slice +- host-base-free validated path + +The goal is not yet to change the successful boot/runtime model, only the **source input boundary**. + +**Success Criteria:** Fruix builds native FreeBSD base artifacts from a declared source input, and the resulting store artifacts record that source identity explicitly. + +--- + +## Phase 17: Support Multiple FreeBSD Source Revisions Side by Side + +Once source acquisition is explicit, Fruix should prove that it can treat source revisions the way it already treats declared base versions: as side-by-side inputs rather than in-place mutations. + +### Intermediate Goal 17.1: Support Side-by-Side FreeBSD Source Revisions in `/frx/store` + +**Verification Goal 17.1:** Demonstrate that at least two distinct declared FreeBSD source inputs can coexist and produce distinct native base artifacts in `/frx/store`. + +These can differ by: + +- Git revision +- source archive hash +- local source snapshot hash +- or another explicit source identity boundary + +The goal is to prove that the source declaration, not ambient host state, now drives the base build outputs. + +**Success Criteria:** Fruix can hold at least two distinct FreeBSD source revisions side by side as meaningful native base inputs and outputs. + +### Intermediate Goal 17.2: Rebuild and Boot From Two Distinct Source Revisions + +**Verification Goal 17.2:** Build and boot systems from at least two distinct declared source revisions using the validated native base path. + +This should preserve the current validation strategy: + +- local QEMU/UEFI/TCG where useful +- real XCP-ng validation on the approved VM and approved A/B VDI path + +The booted systems do not necessarily need to differ in obvious runtime behavior; the important point is that their source identity and native base outputs differ and are tracked correctly. + +**Success Criteria:** Fruix boots systems built from two distinct declared FreeBSD source revisions and records those revisions in system metadata. + +### Intermediate Goal 17.3: Clarify Source Provenance, Caching, and Update Policy + +**Verification Goal 17.3:** Document and encode the intended policy for: + +- source caching +- source refresh/invalidation +- patch application if any +- tree hashing rules +- when a new source identity should or should not invalidate native base outputs + +This step should reduce ambiguity before installation artifacts and later self-hosting experiments depend on these source objects. + +**Success Criteria:** the repo clearly explains how FreeBSD source objects are fetched, cached, identified, invalidated, and consumed by native base builds. + +--- + +## Phase 18: Turn System Images Into a Real Installation Story + +Fruix can already produce bootable images. The next step is to make installation itself a first-class Fruix story rather than a manual consequence of image generation. + +### Intermediate Goal 18.1: Define a Minimal Non-Interactive Installation Flow + +**Verification Goal 18.1:** Introduce a minimal installation workflow that can take a Fruix system artifact and install it to a target disk or image in a repeatable way. + +The first version should prefer clarity and automation over interactivity. For example, it may: + +- partition a target disk +- create required filesystems +- copy or materialize the selected system closure +- install boot assets +- stage activation/runtime metadata +- configure root SSH key or basic operator access + +This first step should be VM-friendly and serial-console-friendly. + +**Success Criteria:** Fruix can perform a repeatable installation of a declarative system onto a target disk or image without relying on ad hoc manual assembly. + +### Intermediate Goal 18.2: Produce a Minimal Installer Environment + +**Verification Goal 18.2:** Produce a small installer environment that can boot into a Fruix-managed install context and perform the installation workflow. + +This environment may initially be one of: + +- a special-purpose disk image +- a minimal recovery/install rootfs +- a proto-installer medium with only the tools needed for partitioning, filesystem creation, and deployment + +The target here is not a polished live environment. It is a reliable installer substrate. + +**Success Criteria:** Fruix can boot a minimal installer environment and use it to install a selected Fruix system to a target disk. + +### Intermediate Goal 18.3: Build a Bootable Installer ISO + +**Verification Goal 18.3:** Produce a bootable installer ISO for UEFI systems. + +The first ISO can be intentionally narrow in scope. It should be enough to: + +- boot under QEMU and, where practical, on the validated virtualization path +- expose the install workflow +- install a target Fruix system +- leave the machine bootable into the installed system + +A polished graphical or highly interactive installer is not required here. + +**Success Criteria:** Fruix can build a bootable installer ISO that installs a declarative Fruix-on-FreeBSD system. + +--- + +## Phase 19: Strengthen System Deployment and Generation Management + +The current deployment model already has the important Guix-like semantic properties at the closure/store layer. This phase is about making that story more operator-facing and less harness-specific. + +### Intermediate Goal 19.1: Define a First-Class Fruix Deployment Workflow + +**Verification Goal 19.1:** Define and document the canonical user-facing deployment workflow for system rebuild, image generation, installation, and rollback. + +This may introduce or refine user-facing commands such as: + +- `fruix system build` +- `fruix system image` +- future deployment/install/switch subcommands if justified + +The key goal is clarity: operators should have an obvious Fruix way to move between generations and deployments. + +**Success Criteria:** the repo documents a coherent user-facing deployment workflow for system build, install, roll-forward, and rollback. + +### Intermediate Goal 19.2: Model System Generations More Explicitly + +**Verification Goal 19.2:** Make the system-generation story more explicit in metadata and deployment roots. + +This should include deciding how Fruix wants to represent: + +- current system generation +- previous system generation +- rollback target +- GC roots associated with installed systems + +This does not have to copy Guix System mechanically, but it should preserve the same important properties. + +**Success Criteria:** Fruix has a clearer model for installed system generations and rollback roots rather than relying mainly on test-harness knowledge. + +### Intermediate Goal 19.3: Validate Installed-System Rollback as an Operator Workflow + +**Verification Goal 19.3:** Validate rollback through the intended installed-system workflow, not only through build/image test harnesses. + +This step should prove that the installation and generation model work together coherently. + +**Success Criteria:** an installed Fruix system can move between generations using the intended operator-facing deployment model. + +--- + +## Phase 20: Controlled Steps Toward Self-Hosted Base Builds + +Only after source identity and installation/deployment boundaries are stronger should Fruix seriously revisit self-hosted base builds. + +### Intermediate Goal 20.1: Validate a Fruix-Managed Development Environment for Native Base Work + +**Verification Goal 20.1:** Ensure that a Fruix-managed system can expose the development/runtime/toolchain environment needed for deeper FreeBSD-native build work. + +This should build on the cleaner runtime/development split already established in Phase 14. + +The goal is not yet full self-hosting; it is to prove that the system can host the tools and profiles needed for that work in a controlled way. + +**Success Criteria:** a Fruix-managed system can expose a usable development environment for native FreeBSD build tasks. + +### Intermediate Goal 20.2: Run Host-Initiated Native Base Builds Inside a Fruix-Managed Environment + +**Verification Goal 20.2:** As an intermediate step, perform native base builds inside a Fruix-managed environment or jail while still using the host as the outer orchestrator. + +This narrows the remaining gap without immediately demanding full guest self-hosting. + +**Success Criteria:** Fruix can build native FreeBSD base artifacts from inside a Fruix-managed build environment, with the host still orchestrating the outer loop. + +### Intermediate Goal 20.3: Reassess and Potentially Prototype Guest Self-Hosted Base Builds + +**Verification Goal 20.3:** Revisit guest self-hosting only after the earlier source/install/deployment goals are complete enough to make that experiment meaningful. + +At that point, the question should be answered with real evidence: + +- what exactly self-hosting would improve +- what it would cost in complexity +- how it would fit with the source/deployment model already established + +**Success Criteria:** the project either: + +- validates a first controlled guest self-hosted base build, or +- records a clear evidence-based decision to continue preferring host-orchestrated native builds. + +--- + +## Strategic Notes + +### Why this order? + +This sequence is intended to preserve the most important win from Plan 3 and Phase 15: + +- Fruix already has a native, store-based, rollback-friendly FreeBSD base path + +The next improvement should therefore be to make the **source input** as explicit as the **deployment output** already is. + +After that, Fruix can turn its successful image-generation path into a real installation story. Only then will self-hosting be judged in the right context. + +### Why not jump straight to self-hosting? + +Because at the end of Phase 15, the biggest remaining weakness is not “where the build runs”, but “how explicitly the source is declared and acquired”. + +If self-hosting were attempted immediately, it would mix together: + +- source acquisition problems +- toolchain/profile maturity problems +- build-environment questions +- deployment/installation questions +- and virtualization/operator constraints + +That would make debugging and design less clear. + +### Why add installation work before self-hosting? + +Because Fruix should become more usable as a system even if self-hosting remains a later milestone. + +A real installation story would: + +- make the current system model more operator-meaningful +- improve validation of generation/deployment semantics +- prepare the project for broader VM and eventually hardware use +- make later self-hosting experiments easier to stage and reproduce + +### What success will mean + +Success under this plan means the project moves from: + +- “Fruix builds native FreeBSD base artifacts from the host’s current `/usr/src` and boots them successfully” + +into: + +- “Fruix acquires FreeBSD sources declaratively, builds native base artifacts from pinned source inputs, installs systems through a real Fruix workflow, and approaches self-hosting from a much cleaner architectural position.” diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 257a98b..18d24ae 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,5 +1,83 @@ # Progress +## 2026-04-03 — Phase 16.1 completed: FreeBSD source inputs are now explicit Fruix objects + +Completed work: + +- added `docs/PLAN_4.md` to define the post-Phase-15 roadmap around: + - declarative FreeBSD source acquisition + - installation artifacts + - the controlled path toward self-hosting +- introduced a first-class `freebsd-source` record in `modules/fruix/packages/freebsd.scm` with: + - supported kinds: + - `local-tree` + - `git` + - `src-txz` + - exported accessors for: + - `name` + - `kind` + - `url` + - `path` + - `ref` + - `commit` + - `sha256` + - new `%default-freebsd-source` +- extended `freebsd-base` so it now records both: + - transitional `source-root` + - declarative `source` +- added/exported: + - `freebsd-base-source` +- threaded declared source fields into native package plans so native build outputs can record them +- in `modules/fruix/system/freebsd.scm`: + - added source validation for: + - `local-tree` + - `git` + - `src-txz` + - added `freebsd-source-spec` + - `freebsd-base-spec` now nests the declared source + - native manifests and `.freebsd-native-build-info.scm` now include: + - `declared-source` + - closures now generate: + - `metadata/freebsd-source.scm` + - `metadata/store-layout.scm` now records: + - `freebsd-source` +- in `scripts/fruix.scm`, `build` and `image` metadata now emit: + - `freebsd_source_name` + - `freebsd_source_kind` + - `freebsd_source_url` + - `freebsd_source_path` + - `freebsd_source_ref` + - `freebsd_source_commit` + - `freebsd_source_sha256` + - `freebsd_source_file` +- added validation artifacts: + - `tests/system/phase16-declarative-source-operating-system.scm.in` + - `tests/system/run-phase16-declarative-source-build.sh` + - `tests/system/validate-phase16-freebsd-source.scm` +- compared Fruix's new source model with Guix's source modeling via: + - `~/repos/guix/guix/packages.scm` + - `~/repos/guix/guix/git-download.scm` +- wrote: + - `docs/reports/phase16-declarative-source-model-freebsd.md` + +Validation: + +- `PASS phase16-declarative-source-build` +- source model probe confirmed support for: + - local-tree `/usr/src` + - Git refs such as `stable/15` at `https://git.FreeBSD.org/src.git` + - canonical `src.txz` URLs such as: + - `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz` + - `https://download.freebsd.org/snapshots/amd64/15.0-STABLE/src.txz` +- closure/native metadata now records the declared source explicitly while preserving the current validated `/usr/src` build path + +Current assessment: + +- Phase 16.1 is complete +- Fruix can now describe FreeBSD source inputs explicitly, but it does not fetch/materialize them yet +- the next step is Phase 16.2: + - fetch or materialize declared FreeBSD source inputs under Fruix control and use their stable identity as the next reproducibility boundary + ## 2026-04-01 — Phase 1.1 started: Guile verified on FreeBSD amd64 Completed work: diff --git a/docs/reports/phase16-declarative-source-model-freebsd.md b/docs/reports/phase16-declarative-source-model-freebsd.md new file mode 100644 index 0000000..4b4031f --- /dev/null +++ b/docs/reports/phase16-declarative-source-model-freebsd.md @@ -0,0 +1,202 @@ +# Phase 16.1: model FreeBSD source inputs explicitly + +Date: 2026-04-03 + +## Goal + +Phase 16.1 introduces a first-class Fruix model for FreeBSD source inputs so the native base path is no longer described only as "whatever `/usr/src` is on the host". + +This step does **not** yet fetch or materialize remote source trees. It makes the source declaration explicit and records it through the package, system, and CLI metadata layers so later phases can replace ambient `/usr/src` with fetched or materialized inputs cleanly. + +## Implementation + +### New declarative FreeBSD source record + +Added in `modules/fruix/packages/freebsd.scm`: + +- `freebsd-source` +- `freebsd-source?` +- accessors for: + - `name` + - `kind` + - `url` + - `path` + - `ref` + - `commit` + - `sha256` +- `%default-freebsd-source` + +Supported source kinds are now modeled explicitly as: + +- `local-tree` +- `git` +- `src-txz` + +The default source remains a local-tree declaration for: + +- `/usr/src` + +### FreeBSD bases now carry a declared source object + +Extended `freebsd-base` so it now records: + +- the transitional `source-root` still used by the current native build path +- a new `source` record describing the declared FreeBSD source input + +Added/exported: + +- `freebsd-base-source` + +This keeps the validated `/usr/src` path working while giving the base a source object that can later point at fetched Git or `src.txz` materializations. + +### Native package plans and metadata now record the declared source + +`modules/fruix/packages/freebsd.scm` now threads source fields into native package install plans. + +`modules/fruix/system/freebsd.scm` now records a `declared-source` block in: + +- native package manifests +- `.freebsd-native-build-info.scm` + +This means native kernel, bootloader, and runtime outputs now remember both: + +- the declared base +- the declared source input + +### Operating-system validation now checks source declarations + +Added source validation in `modules/fruix/system/freebsd.scm`. + +Current accepted source declarations are: + +- `local-tree` + - requires a path +- `git` + - requires a URL and at least a ref or commit +- `src-txz` + - requires a URL + +This is intentionally enough for Phase 16.1 modeling without yet enforcing the later fetch/materialization policy. + +### Closure metadata now includes a dedicated source file + +System closures now generate: + +- `metadata/freebsd-source.scm` + +and embed source information in: + +- `metadata/freebsd-base.scm` +- `metadata/store-layout.scm` +- `parameters.scm` + +### CLI metadata now exposes the declared FreeBSD source + +`scripts/fruix.scm` now emits the following for `fruix system build` and `image`: + +- `freebsd_source_name` +- `freebsd_source_kind` +- `freebsd_source_url` +- `freebsd_source_path` +- `freebsd_source_ref` +- `freebsd_source_commit` +- `freebsd_source_sha256` +- `freebsd_source_file` + +## Guix comparison + +This follows the same broad idea as Guix source modeling: + +- Guix uses `origin` in `guix/packages.scm` +- Git-backed sources are modeled with `git-reference` in `guix/git-download.scm` + +Phase 16.1 does not copy Guix mechanically, but it adopts the same useful architectural boundary: + +- source identity should be a declarative object +- builds should record that source object explicitly + +For Fruix, the source kinds are FreeBSD-oriented: + +- local source tree snapshots for development +- FreeBSD Git refs/commits +- official FreeBSD `src.txz` archives + +## Upstream source forms verified during this step + +Verified usable upstream source forms include: + +- Git: + - `https://git.FreeBSD.org/src.git` +- release archive: + - `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz` +- snapshot archive: + - `https://download.freebsd.org/snapshots/amd64/15.0-STABLE/src.txz` + +The shorter release URL form above is treated as the canonical example in this phase. + +## New files + +Added: + +- `docs/PLAN_4.md` +- `tests/system/phase16-declarative-source-operating-system.scm.in` +- `tests/system/run-phase16-declarative-source-build.sh` +- `tests/system/validate-phase16-freebsd-source.scm` + +## Validation + +Passing run: + +- `PASS phase16-declarative-source-build` +- workdir: + - `/tmp/fruix-phase16-declarative-source.0LRvaC` + +The source model probe confirmed: + +```text +local_kind=local-tree +local_path=/usr/src +git_kind=git +git_url=https://git.FreeBSD.org/src.git +git_ref=stable/15 +txz_kind=src-txz +txz_url=https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz +txz_sha256=example-sha256 +base_source_accessor=ok +``` + +The closure build confirmed: + +```text +closure_path=/frx/store/ac73a2a89c3c3f794462ccde0d9f0952362dc2ae32e631a7e99e07e7363e6118-fruix-system-fruix-freebsd +kernel_store=/frx/store/c2f771a794f94cf168ae26a421ffab33e0ae4765f23882257d5bb7b2947d493d-freebsd-native-kernel-15.0-STABLE-source-model +bootloader_store=/frx/store/a146c9c2fcacdc02454fe3bfd3afb5c5dbea54b6514242d53df0783ef4ee34fd-freebsd-native-bootloader-15.0-STABLE-source-model +runtime_store=/frx/store/530e96440a518821a701db03f9437d7646c0447f8ea55c5df08417e9274dead1-freebsd-native-runtime-15.0-STABLE-source-model +native_base_store_count=3 +host_base_store_count=0 +freebsd_source_name=host-usr-src +freebsd_source_kind=local-tree +freebsd_source_path=/usr/src +freebsd_source_file=/frx/store/ac73a2a89c3c3f794462ccde0d9f0952362dc2ae32e631a7e99e07e7363e6118-fruix-system-fruix-freebsd/metadata/freebsd-source.scm +declarative_source_model=ok +``` + +The harness also verified that: + +- `metadata/freebsd-source.scm` exists +- `metadata/freebsd-base.scm` contains a nested source block +- `metadata/store-layout.scm` records the declared source explicitly +- native build info files contain a `declared-source` block with: + - `kind . local-tree` + - `path . "/usr/src"` + +## Result + +Phase 16.1 is complete. + +Fruix now has an explicit source declaration layer for FreeBSD bases while preserving the current validated `/usr/src`-driven native build path. + +That means Phase 16.2 can focus narrowly on the next real boundary: + +- fetching or materializing declared FreeBSD source inputs under Fruix control +- instead of just describing them diff --git a/modules/fruix/packages/freebsd.scm b/modules/fruix/packages/freebsd.scm index 6da5b03..2ff31bb 100644 --- a/modules/fruix/packages/freebsd.scm +++ b/modules/fruix/packages/freebsd.scm @@ -3,6 +3,16 @@ #:use-module (srfi srfi-9) #:use-module (srfi srfi-13) #:export (freebsd-release + freebsd-source + freebsd-source? + freebsd-source-name + freebsd-source-kind + freebsd-source-url + freebsd-source-path + freebsd-source-ref + freebsd-source-commit + freebsd-source-sha256 + %default-freebsd-source freebsd-base freebsd-base? freebsd-base-name @@ -10,6 +20,7 @@ freebsd-base-release freebsd-base-branch freebsd-base-source-root + freebsd-base-source freebsd-base-target freebsd-base-target-arch freebsd-base-kernconf @@ -87,8 +98,32 @@ (define freebsd-release "15.0-STABLE") +(define-record-type + (make-freebsd-source name kind url path ref commit sha256) + freebsd-source? + (name freebsd-source-name) + (kind freebsd-source-kind) + (url freebsd-source-url) + (path freebsd-source-path) + (ref freebsd-source-ref) + (commit freebsd-source-commit) + (sha256 freebsd-source-sha256)) + +(define* (freebsd-source #:key + (name "default") + (kind 'local-tree) + (url (and (eq? kind 'git) "https://git.FreeBSD.org/src.git")) + (path (and (eq? kind 'local-tree) "/usr/src")) + (ref #f) + (commit #f) + (sha256 #f)) + (make-freebsd-source name kind url path ref commit sha256)) + +(define %default-freebsd-source + (freebsd-source)) + (define-record-type - (make-freebsd-base name version-label release branch source-root target + (make-freebsd-base name version-label release branch source-root source target target-arch kernconf make-flags) freebsd-base? (name freebsd-base-name) @@ -96,6 +131,7 @@ (release freebsd-base-release) (branch freebsd-base-branch) (source-root freebsd-base-source-root) + (source freebsd-base-source) (target freebsd-base-target) (target-arch freebsd-base-target-arch) (kernconf freebsd-base-kernconf) @@ -123,13 +159,18 @@ (version-label freebsd-release) (release freebsd-release) (branch (default-freebsd-branch release)) - (source-root "/usr/src") + (source #f) + (source-root #f) (target "amd64") (target-arch "amd64") (kernconf "GENERIC") (make-flags default-native-make-flags)) - (make-freebsd-base name version-label release branch source-root target - target-arch kernconf make-flags)) + (let* ((source (or source %default-freebsd-source)) + (source-root (or source-root + (freebsd-source-path source) + "/usr/src"))) + (make-freebsd-base name version-label release branch source-root source target + target-arch kernconf make-flags))) (define %default-freebsd-base (freebsd-base)) @@ -574,6 +615,13 @@ library for profile experiments." (base-version-label . ,(freebsd-base-version-label base)) (base-release . ,(freebsd-base-release base)) (base-branch . ,(freebsd-base-branch base)) + (base-source-name . ,(freebsd-source-name (freebsd-base-source base))) + (base-source-kind . ,(freebsd-source-kind (freebsd-base-source base))) + (base-source-url . ,(freebsd-source-url (freebsd-base-source base))) + (base-source-path . ,(freebsd-source-path (freebsd-base-source base))) + (base-source-ref . ,(freebsd-source-ref (freebsd-base-source base))) + (base-source-commit . ,(freebsd-source-commit (freebsd-base-source base))) + (base-source-sha256 . ,(freebsd-source-sha256 (freebsd-base-source base))) (source-root . ,(freebsd-base-source-root base)) (target . ,(freebsd-base-target base)) (target-arch . ,(freebsd-base-target-arch base)) diff --git a/modules/fruix/system/freebsd.scm b/modules/fruix/system/freebsd.scm index b1843b2..1361afb 100644 --- a/modules/fruix/system/freebsd.scm +++ b/modules/fruix/system/freebsd.scm @@ -313,10 +313,20 @@ (release . ,(build-plan-ref plan 'base-release freebsd-release)) (branch . ,(build-plan-ref plan 'base-branch "unknown")))) +(define (native-build-declared-source plan) + `((name . ,(build-plan-ref plan 'base-source-name "default")) + (kind . ,(build-plan-ref plan 'base-source-kind 'local-tree)) + (url . ,(build-plan-ref plan 'base-source-url #f)) + (path . ,(build-plan-ref plan 'base-source-path #f)) + (ref . ,(build-plan-ref plan 'base-source-ref #f)) + (commit . ,(build-plan-ref plan 'base-source-commit #f)) + (sha256 . ,(build-plan-ref plan 'base-source-sha256 #f)))) + (define (native-build-manifest-string package input-paths) (let* ((plan (freebsd-package-install-plan package)) (common (native-build-common-manifest plan)) (declared-base (native-build-declared-base plan)) + (declared-source (native-build-declared-source plan)) (keep-paths (build-plan-ref plan 'keep-paths '())) (prune-paths (build-plan-ref plan 'prune-paths '()))) (string-append @@ -326,6 +336,8 @@ "inputs=" (string-join input-paths ",") "\n" "declared-base=\n" (object->string declared-base) + "\ndeclared-source=\n" + (object->string declared-source) "\nnative-build-common=\n" (object->string common) "\nkeep-paths=\n" @@ -479,6 +491,7 @@ `((package . ,(freebsd-package-name package)) (version . ,(freebsd-package-version package)) (declared-base . ,(native-build-declared-base plan)) + (declared-source . ,(native-build-declared-source plan)) (build-system . ,(freebsd-package-build-system package)) (source-root . ,(assoc-ref common 'source-root)) (source-tree-sha256 . ,(assoc-ref common 'source-tree-sha256)) @@ -682,12 +695,22 @@ (define (package-names packages) (map freebsd-package-name packages)) +(define (freebsd-source-spec source) + `((name . ,(freebsd-source-name source)) + (kind . ,(freebsd-source-kind source)) + (url . ,(freebsd-source-url source)) + (path . ,(freebsd-source-path source)) + (ref . ,(freebsd-source-ref source)) + (commit . ,(freebsd-source-commit source)) + (sha256 . ,(freebsd-source-sha256 source)))) + (define (freebsd-base-spec base) `((name . ,(freebsd-base-name base)) (version-label . ,(freebsd-base-version-label base)) (release . ,(freebsd-base-release base)) (branch . ,(freebsd-base-branch base)) (source-root . ,(freebsd-base-source-root base)) + (source . ,(freebsd-source-spec (freebsd-base-source base))) (target . ,(freebsd-base-target base)) (target-arch . ,(freebsd-base-target-arch base)) (kernconf . ,(freebsd-base-kernconf base)) @@ -702,6 +725,31 @@ (loop tail seen (if (member head duplicates) duplicates (cons head duplicates))) (loop tail (cons head seen) duplicates)))))) +(define (non-empty-string? value) + (and (string? value) + (not (string-null? value)))) + +(define (validate-freebsd-source source) + (unless (freebsd-source? source) + (error "freebsd base source must be a record")) + (let ((kind (freebsd-source-kind source))) + (unless (member kind '(local-tree git src-txz)) + (error "unsupported freebsd source kind" kind)) + (case kind + ((local-tree) + (unless (non-empty-string? (freebsd-source-path source)) + (error "local-tree freebsd source must declare a path" source))) + ((git) + (unless (non-empty-string? (freebsd-source-url source)) + (error "git freebsd source must declare a URL" source)) + (unless (or (non-empty-string? (freebsd-source-ref source)) + (non-empty-string? (freebsd-source-commit source))) + (error "git freebsd source must declare a ref or commit" source))) + ((src-txz) + (unless (non-empty-string? (freebsd-source-url source)) + (error "src-txz freebsd source must declare a URL" source))))) + #t) + (define (validate-operating-system os) (let* ((host-name (operating-system-host-name os)) (base (operating-system-freebsd-base os)) @@ -716,6 +764,7 @@ (error "operating-system host-name must not be empty")) (unless (freebsd-base? base) (error "operating-system freebsd-base must be a record")) + (validate-freebsd-source (freebsd-base-source base)) (let ((dups (duplicate-elements user-names))) (unless (null? dups) (error "duplicate user names in operating-system" dups))) @@ -1386,11 +1435,14 @@ (metadata-files `(("metadata/freebsd-base.scm" . ,(object->string (freebsd-base-spec (operating-system-freebsd-base os)))) + ("metadata/freebsd-source.scm" + . ,(object->string (freebsd-source-spec (freebsd-base-source (operating-system-freebsd-base os))))) ("metadata/host-base-provenance.scm" . ,(object->string (host-freebsd-provenance))) ("metadata/store-layout.scm" . ,(object->string `((freebsd-base . ,(freebsd-base-spec (operating-system-freebsd-base os))) + (freebsd-source . ,(freebsd-source-spec (freebsd-base-source (operating-system-freebsd-base os)))) (host-base-store-count . ,(length host-base-stores)) (host-base-stores . ,host-base-stores) (native-base-store-count . ,(length native-base-stores)) @@ -1466,6 +1518,7 @@ (native-base-stores . ,native-base-stores) (fruix-runtime-stores . ,fruix-runtime-stores) (freebsd-base-file . ,(string-append closure-path "/metadata/freebsd-base.scm")) + (freebsd-source-file . ,(string-append closure-path "/metadata/freebsd-source.scm")) (host-base-provenance-file . ,(string-append closure-path "/metadata/host-base-provenance.scm")) (store-layout-file . ,(string-append closure-path "/metadata/store-layout.scm")) (generated-files . ,(map car generated-files)) diff --git a/scripts/fruix.scm b/scripts/fruix.scm index 43fe5a3..df65466 100644 --- a/scripts/fruix.scm +++ b/scripts/fruix.scm @@ -176,6 +176,7 @@ Options:\n\ (native-base-stores (assoc-ref result 'native-base-stores)) (fruix-runtime-stores (assoc-ref result 'fruix-runtime-stores)) (base (operating-system-freebsd-base os)) + (source (freebsd-base-source base)) (host-provenance (call-with-input-file (assoc-ref result 'host-base-provenance-file) read))) (emit-metadata `((action . "build") @@ -192,6 +193,14 @@ Options:\n\ (freebsd_base_target_arch . ,(freebsd-base-target-arch base)) (freebsd_base_kernconf . ,(freebsd-base-kernconf base)) (freebsd_base_file . ,(assoc-ref result 'freebsd-base-file)) + (freebsd_source_name . ,(freebsd-source-name source)) + (freebsd_source_kind . ,(freebsd-source-kind source)) + (freebsd_source_url . ,(or (freebsd-source-url source) "")) + (freebsd_source_path . ,(or (freebsd-source-path source) "")) + (freebsd_source_ref . ,(or (freebsd-source-ref source) "")) + (freebsd_source_commit . ,(or (freebsd-source-commit source) "")) + (freebsd_source_sha256 . ,(or (freebsd-source-sha256 source) "")) + (freebsd_source_file . ,(assoc-ref result 'freebsd-source-file)) (ready_marker . ,(operating-system-ready-marker os)) (kernel_store . ,(assoc-ref result 'kernel-store)) (bootloader_store . ,(assoc-ref result 'bootloader-store)) @@ -246,6 +255,7 @@ Options:\n\ (native-base-stores (assoc-ref result 'native-base-stores)) (fruix-runtime-stores (assoc-ref result 'fruix-runtime-stores)) (base (operating-system-freebsd-base os)) + (source (freebsd-base-source base)) (host-provenance (call-with-input-file (assoc-ref result 'host-base-provenance-file) read))) (emit-metadata `((action . "image") @@ -261,6 +271,14 @@ Options:\n\ (freebsd_base_target_arch . ,(freebsd-base-target-arch base)) (freebsd_base_kernconf . ,(freebsd-base-kernconf base)) (freebsd_base_file . ,(assoc-ref result 'freebsd-base-file)) + (freebsd_source_name . ,(freebsd-source-name source)) + (freebsd_source_kind . ,(freebsd-source-kind source)) + (freebsd_source_url . ,(or (freebsd-source-url source) "")) + (freebsd_source_path . ,(or (freebsd-source-path source) "")) + (freebsd_source_ref . ,(or (freebsd-source-ref source) "")) + (freebsd_source_commit . ,(or (freebsd-source-commit source) "")) + (freebsd_source_sha256 . ,(or (freebsd-source-sha256 source) "")) + (freebsd_source_file . ,(assoc-ref result 'freebsd-source-file)) (disk_capacity . ,(assoc-ref image-spec 'disk-capacity)) (root_size . ,(assoc-ref image-spec 'root-size)) (image_store_path . ,(assoc-ref result 'image-store-path)) diff --git a/tests/system/phase16-declarative-source-operating-system.scm.in b/tests/system/phase16-declarative-source-operating-system.scm.in new file mode 100644 index 0000000..fb52120 --- /dev/null +++ b/tests/system/phase16-declarative-source-operating-system.scm.in @@ -0,0 +1,90 @@ +(use-modules (fruix system freebsd) + (fruix packages freebsd)) + +(define phase16-source + (freebsd-source + #:name "__SOURCE_NAME__" + #:kind 'local-tree + #:path "/usr/src")) + +(define phase16-base + (freebsd-base + #:name "__BASE_NAME__" + #:version-label "__BASE_VERSION_LABEL__" + #:release "__BASE_RELEASE__" + #:branch "__BASE_BRANCH__" + #:source phase16-source + #:source-root "/usr/src" + #:target "amd64" + #:target-arch "amd64" + #:kernconf "GENERIC")) + +(define phase16-operating-system + (operating-system + #:host-name "fruix-freebsd" + #:freebsd-base phase16-base + #:kernel (freebsd-native-kernel-for phase16-base) + #:bootloader (freebsd-native-bootloader-for phase16-base) + #:base-packages (freebsd-native-system-packages-for phase16-base) + #: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-phase16-declarative-source-build.sh b/tests/system/run-phase16-declarative-source-build.sh new file mode 100755 index 0000000..b0871b5 --- /dev/null +++ b/tests/system/run-phase16-declarative-source-build.sh @@ -0,0 +1,245 @@ +#!/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/phase16-declarative-source-operating-system.scm.in} +source_probe=${SOURCE_PROBE:-$script_dir/validate-phase16-freebsd-source.scm} +system_name=${SYSTEM_NAME:-phase16-operating-system} +store_dir=${STORE_DIR:-/frx/store} +base_name=${BASE_NAME:-source-model} +base_version_label=${BASE_VERSION_LABEL:-15.0-STABLE-source-model} +base_release=${BASE_RELEASE:-15.0-STABLE} +base_branch=${BASE_BRANCH:-stable/15} +source_name=${SOURCE_NAME:-host-usr-src} +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 "$source_probe" ] || { + echo "missing source probe script: $source_probe" >&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-phase16-declarative-source.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 + +phase16_os_file=$workdir/phase16-declarative-source-operating-system.scm +source_probe_out=$workdir/source-probe.txt +build_out=$workdir/build.txt +metadata_file=$workdir/phase16-declarative-source-build-metadata.txt +root_authorized_key=$(tr -d '\n' < "$root_authorized_key_file") +sed \ + -e "s|__BASE_NAME__|$base_name|g" \ + -e "s|__BASE_VERSION_LABEL__|$base_version_label|g" \ + -e "s|__BASE_RELEASE__|$base_release|g" \ + -e "s|__BASE_BRANCH__|$base_branch|g" \ + -e "s|__SOURCE_NAME__|$source_name|g" \ + -e "s|__ROOT_AUTHORIZED_KEY__|$root_authorized_key|g" \ + "$os_template" > "$phase16_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}" \ + "$@" +} + +action_env "${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}" \ + -L "$project_root/modules" \ + -c "(primitive-load \"$source_probe\")" >"$source_probe_out" + +for expected in \ + 'local_kind=local-tree' \ + 'local_path=/usr/src' \ + 'git_kind=git' \ + 'git_url=https://git.FreeBSD.org/src.git' \ + 'git_ref=stable/15' \ + 'txz_kind=src-txz' \ + 'txz_url=https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz' \ + 'txz_sha256=example-sha256' \ + 'base_source_accessor=ok' +do + grep -F "$expected" "$source_probe_out" >/dev/null || { + echo "source probe missing expected line: $expected" >&2 + exit 1 + } +done + +action_env "$fruix_cmd" system build "$phase16_os_file" --system "$system_name" --store "$store_dir" >"$build_out" + +closure_path=$(sed -n 's/^closure_path=//p' "$build_out") +kernel_store=$(sed -n 's/^kernel_store=//p' "$build_out") +bootloader_store=$(sed -n 's/^bootloader_store=//p' "$build_out") +native_base_store_count=$(sed -n 's/^native_base_store_count=//p' "$build_out") +native_base_stores=$(sed -n 's/^native_base_stores=//p' "$build_out") +host_base_store_count=$(sed -n 's/^host_base_store_count=//p' "$build_out") +freebsd_base_name_out=$(sed -n 's/^freebsd_base_name=//p' "$build_out") +freebsd_base_version_label_out=$(sed -n 's/^freebsd_base_version_label=//p' "$build_out") +freebsd_base_release_out=$(sed -n 's/^freebsd_base_release=//p' "$build_out") +freebsd_base_branch_out=$(sed -n 's/^freebsd_base_branch=//p' "$build_out") +freebsd_base_source_root_out=$(sed -n 's/^freebsd_base_source_root=//p' "$build_out") +freebsd_base_file=$(sed -n 's/^freebsd_base_file=//p' "$build_out") +freebsd_source_name_out=$(sed -n 's/^freebsd_source_name=//p' "$build_out") +freebsd_source_kind_out=$(sed -n 's/^freebsd_source_kind=//p' "$build_out") +freebsd_source_url_out=$(sed -n 's/^freebsd_source_url=//p' "$build_out") +freebsd_source_path_out=$(sed -n 's/^freebsd_source_path=//p' "$build_out") +freebsd_source_ref_out=$(sed -n 's/^freebsd_source_ref=//p' "$build_out") +freebsd_source_commit_out=$(sed -n 's/^freebsd_source_commit=//p' "$build_out") +freebsd_source_sha256_out=$(sed -n 's/^freebsd_source_sha256=//p' "$build_out") +freebsd_source_file=$(sed -n 's/^freebsd_source_file=//p' "$build_out") +store_layout_file=$(sed -n 's/^store_layout_file=//p' "$build_out") + +[ -n "$closure_path" ] || { echo "missing closure path" >&2; exit 1; } +[ "$host_base_store_count" = 0 ] || { echo "expected zero host base stores, got: $host_base_store_count" >&2; exit 1; } +[ "$native_base_store_count" = 3 ] || { echo "expected three native base stores, got: $native_base_store_count" >&2; exit 1; } +[ "$freebsd_base_name_out" = "$base_name" ] || { echo "unexpected freebsd base name: $freebsd_base_name_out" >&2; exit 1; } +[ "$freebsd_base_version_label_out" = "$base_version_label" ] || { echo "unexpected freebsd base version label: $freebsd_base_version_label_out" >&2; exit 1; } +[ "$freebsd_base_release_out" = "$base_release" ] || { echo "unexpected freebsd base release: $freebsd_base_release_out" >&2; exit 1; } +[ "$freebsd_base_branch_out" = "$base_branch" ] || { echo "unexpected freebsd base branch: $freebsd_base_branch_out" >&2; exit 1; } +[ "$freebsd_base_source_root_out" = /usr/src ] || { echo "unexpected freebsd base source root: $freebsd_base_source_root_out" >&2; exit 1; } +[ "$freebsd_source_name_out" = "$source_name" ] || { echo "unexpected freebsd source name: $freebsd_source_name_out" >&2; exit 1; } +[ "$freebsd_source_kind_out" = local-tree ] || { echo "unexpected freebsd source kind: $freebsd_source_kind_out" >&2; exit 1; } +[ "$freebsd_source_url_out" = "" ] || { echo "unexpected freebsd source URL: $freebsd_source_url_out" >&2; exit 1; } +[ "$freebsd_source_path_out" = /usr/src ] || { echo "unexpected freebsd source path: $freebsd_source_path_out" >&2; exit 1; } +[ "$freebsd_source_ref_out" = "" ] || { echo "unexpected freebsd source ref: $freebsd_source_ref_out" >&2; exit 1; } +[ "$freebsd_source_commit_out" = "" ] || { echo "unexpected freebsd source commit: $freebsd_source_commit_out" >&2; exit 1; } +[ "$freebsd_source_sha256_out" = "" ] || { echo "unexpected freebsd source sha256: $freebsd_source_sha256_out" >&2; exit 1; } +[ -f "$freebsd_base_file" ] || { echo "missing freebsd base file: $freebsd_base_file" >&2; exit 1; } +[ -f "$freebsd_source_file" ] || { echo "missing freebsd source file: $freebsd_source_file" >&2; exit 1; } +[ -f "$store_layout_file" ] || { echo "missing store layout file: $store_layout_file" >&2; exit 1; } + +case "$kernel_store" in + /frx/store/*-freebsd-native-kernel-$base_version_label) : ;; + *) echo "unexpected kernel store path: $kernel_store" >&2; exit 1 ;; +esac +case "$bootloader_store" in + /frx/store/*-freebsd-native-bootloader-$base_version_label) : ;; + *) echo "unexpected bootloader store path: $bootloader_store" >&2; exit 1 ;; +esac +runtime_store=$(printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep "freebsd-native-runtime-$base_version_label$" | head -n 1) +[ -n "$runtime_store" ] || { echo "failed to recover runtime store" >&2; exit 1; } + +for path in "$kernel_store/.freebsd-native-build-info.scm" "$bootloader_store/.freebsd-native-build-info.scm" "$runtime_store/.freebsd-native-build-info.scm"; do + [ -f "$path" ] || { + echo "missing native build info file: $path" >&2 + exit 1 + } + grep -F "(declared-source" "$path" >/dev/null || { + echo "native build info missing declared source block in $path" >&2 + exit 1 + } + grep -F "(kind . local-tree)" "$path" >/dev/null || { + echo "native build info missing declared source kind in $path" >&2 + exit 1 + } + grep -F "(path . \"/usr/src\")" "$path" >/dev/null || { + echo "native build info missing declared source path in $path" >&2 + exit 1 + } +done + +grep -F "(name . \"$source_name\")" "$freebsd_source_file" >/dev/null || { + echo "freebsd source file missing name" >&2 + exit 1 +} +grep -F "(kind . local-tree)" "$freebsd_source_file" >/dev/null || { + echo "freebsd source file missing kind" >&2 + exit 1 +} +grep -F "(path . \"/usr/src\")" "$freebsd_source_file" >/dev/null || { + echo "freebsd source file missing path" >&2 + exit 1 +} +grep -F "(source" "$freebsd_base_file" >/dev/null || { + echo "freebsd base file missing nested source block" >&2 + exit 1 +} +if ! grep -F "(source" "$closure_path/parameters.scm" >/dev/null; then + echo "closure parameters do not record the declared source" >&2 + exit 1 +fi +grep -F "(freebsd-source" "$store_layout_file" >/dev/null || { + echo "store layout file missing declared source block" >&2 + exit 1 +} + +closure_base=$(basename "$closure_path") +cat >"$metadata_file" <