diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 482890f..ff446e4 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1152,3 +1152,117 @@ Next recommended step: 2. carry forward the concrete real-checkout runtime blocker for later integration work: - investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version` 3. continue using `/frx/store` rather than `/gnu/store` for future FreeBSD store experiments when the prototype work needs a persistent store root + +## 2026-04-01 — Phase 3.2 completed: FreeBSD system package-definition prototype and profile validation added + +Completed work: + +- added a Guix-style FreeBSD system package-definition prototype module: + - `modules/fruix/packages/freebsd.scm` +- added a Scheme harness to materialize those package definitions into store-like outputs and a merged profile: + - `tests/packages/freebsd-package-profile-prototype.scm` +- added a shell wrapper for that harness: + - `tests/packages/run-freebsd-package-profile-prototype.sh` +- installed the missing host shell dependency needed to satisfy the requested package set: + - `bash` +- wrote the Phase 3.2 report: + - `docs/reports/phase3-freebsd-package-definitions.md` +- ran the profile prototype successfully and captured metadata under: + - `/tmp/freebsd-package-profile-prototype-metadata.txt` + +Important findings: + +- the prototype now defines a minimal FreeBSD core package set covering the categories requested by Phase 3.2: + - kernel + - kernel headers + - libc + - userland utilities + - development tools (`clang`, `make`, autotools) + - minimum system libraries (`openssl`, `zlib`) + - shells (`sh`, `bash`) +- the current package-definition layer uses an explicit Guix-like record shape with fields for: + - name + - version + - build system + - inputs + - synopsis/description/home-page/license + - install plan +- explicit dependency relationships are now encoded and resolved recursively during materialization, including examples such as: + - `freebsd-libc` -> `freebsd-kernel-headers` + - `freebsd-userland` -> `freebsd-libc`, `freebsd-sh` + - `freebsd-clang-toolchain` -> `freebsd-libc`, `freebsd-kernel-headers`, `freebsd-sh` + - `freebsd-autotools` -> `freebsd-gmake`, `freebsd-bash`, `freebsd-libc` +- the harness successfully materialized: + - `11` core package outputs + into a store-like directory tree under the work directory +- it then merged those outputs into a development profile and validated that the profile contains working: + - `bash` + - `make` + - `autoconf` + - `cc` + - kernel image path + - kernel-header path + - core shared-library paths +- the generated profile compiled and ran a C test program successfully, with observed output: + - `hello-from-freebsd-profile` +- for executables installed under `bin/`, the prototype uses wrappers that `exec` the host tool by absolute path; this preserved correct behavior for prefix-sensitive tools such as `autoconf` + +Current assessment: + +- Phase 3.2 is now satisfied on the current prototype track +- Phase 3 as a whole is now completed on the current FreeBSD amd64 path because both: + - adapted GNU build-system execution, and + - minimal FreeBSD system package-definition/profile validation + have been demonstrated successfully +- the next remaining project milestone is now Phase 4, centered on Shepherd rather than package-building foundations + +## 2026-04-01 — Phase 3 completed on the current FreeBSD prototype track + +Phase 3 is now considered complete for the active FreeBSD amd64 prototype path. + +Why this milestone is satisfied: + +- **Phase 3.1** success criteria were met on the prototype track: + - a reusable FreeBSD adaptation layer for GNU builder phases was added + - five representative GNU packages built successfully through that adapted runner + - the resulting binaries executed correctly on the host +- **Phase 3.2** success criteria were met on the prototype track: + - a minimal FreeBSD system package-definition layer was added + - explicit dependency relationships were modeled and resolved + - the package outputs installed into a merged profile successfully + - the generated profile was validated by compiling and running a test program with the staged toolchain + +Important scope note: + +- this completes the **build-system adaptation milestone** in prototype form, not the full Guix package-lowering/daemon integration path +- the earlier concrete upstream/runtime blocker still exists for later integration work: + - `./pre-inst-env guix --version` fails with `Wrong type to apply: #` +- however, that blocker no longer prevents Phase 4 work because the core build-system and package-definition assumptions have now been validated independently + +Recent commits: + +- `e380e88` — `Add FreeBSD Guile verification harness` +- `cd721b1` — `Update progress after Guile verification` +- `27916cb` — `Diagnose Guile subprocess crash on FreeBSD` +- `02f7a7f` — `Validate local Guile fix on FreeBSD` +- `4aebea4` — `Add native GNU Hello FreeBSD build harness` +- `c944cdb` — `Validate Guix builder phases on FreeBSD` +- `0a2e48e` — `Validate GNU which builder phases on FreeBSD` +- `245a47d` — `Document gaps to real Guix FreeBSD builds` +- `d62e9b0` — `Investigate Guix derivation generation on FreeBSD` +- `c0a85ed` — `Build local Guile-GnuTLS on FreeBSD` +- `15b9037` — `Build local Guile-Git on FreeBSD` +- `47d31e8` — `Build local Guile-JSON on FreeBSD` +- `d82195b` — `Advance Guix checkout on FreeBSD` +- `9bf3d30` — `Document FreeBSD syscall mapping` +- `7621798` — `Prototype FreeBSD jail build isolation` +- `d65b2af` — `Prototype FreeBSD build user isolation` +- `e404e2e` — `Prototype FreeBSD store management` +- `eb0d77c` — `Adapt GNU build phases for FreeBSD` + +Next recommended step: + +1. begin Phase 4.1 by validating whether Shepherd itself now builds and runs as a regular service on FreeBSD with the fixed local Guile path +2. carry forward the separate real-checkout runtime blocker for later integration work: + - investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version` +3. continue using `/frx/store` rather than `/gnu/store` for future FreeBSD integration experiments when a persistent store root is required diff --git a/docs/reports/phase3-freebsd-package-definitions.md b/docs/reports/phase3-freebsd-package-definitions.md new file mode 100644 index 0000000..5289c05 --- /dev/null +++ b/docs/reports/phase3-freebsd-package-definitions.md @@ -0,0 +1,165 @@ +# Phase 3.2: FreeBSD system package-definition prototype and profile validation + +Date: 2026-04-01 + +## Summary + +This step adds a minimal FreeBSD system package-definition prototype and validates that the resulting packages can be materialized into store-like outputs and installed into a profile that is usable for development tasks. + +Added files: + +- `modules/fruix/packages/freebsd.scm` +- `tests/packages/freebsd-package-profile-prototype.scm` +- `tests/packages/run-freebsd-package-profile-prototype.sh` + +The package-definition module is intentionally a **Guix-style prototype layer** rather than a fully integrated Guix package collection, because full host-side package lowering and daemon integration are still blocked upstream on this FreeBSD path. Even so, it provides explicit package metadata, dependency relationships, build-system tags, install plans, and a profile-validation harness. + +## Prototype package set + +The module currently defines the following minimal core set: + +- `freebsd-kernel` +- `freebsd-kernel-headers` +- `freebsd-libc` +- `freebsd-userland` +- `freebsd-clang-toolchain` +- `freebsd-gmake` +- `freebsd-autotools` +- `freebsd-openssl` +- `freebsd-zlib` +- `freebsd-sh` +- `freebsd-bash` + +These cover the categories requested by Phase 3.2: + +- FreeBSD kernel and kernel headers +- FreeBSD libc +- FreeBSD userland utilities +- development tools (`clang`, `make`, autotools) +- minimum system libraries (`openssl`, `zlib`) +- a basic shell (`sh`, `bash`) + +## Definition style + +The module uses a Guix-like package record shape with explicit fields for: + +- name +- version +- build system +- inputs +- home page +- synopsis +- description +- license +- install plan + +This keeps the structure close to Guix package-definition practice even though the current harness interprets the definitions itself rather than asking a working Guix daemon to lower them. + +## Materialization and profile harness + +Run command: + +```sh +METADATA_OUT=/tmp/freebsd-package-profile-prototype-metadata.txt \ +./tests/packages/run-freebsd-package-profile-prototype.sh +``` + +What the harness does: + +1. loads the prototype package definitions +2. recursively materializes each package into a store-like directory under the harness work tree +3. records dependency references between the package outputs +4. assembles a merged profile from the selected package set +5. validates the resulting profile by checking for: + - shell availability + - compiler availability + - make availability + - autotools availability + - kernel image presence + - kernel-header presence + - core library presence +6. compiles and runs a small C program using tools from the generated profile + +## Observed results + +Observed metadata from the successful run included: + +- `core_package_count=11` +- `profile_package_count=11` +- `bash_version=GNU bash, version 5.3.9...` +- `make_version=GNU Make 4.4.1` +- `autoconf_version=autoconf (GNU Autoconf) 2.72` +- `cc_version=FreeBSD clang version 19.1.7 ...` +- `hello_output=hello-from-freebsd-profile` + +The validation program was compiled and run successfully from the generated profile, producing: + +```text +hello-from-freebsd-profile +``` + +## Dependency relationships validated + +The prototype explicitly records useful dependency edges, for example: + +- `freebsd-libc` depends on `freebsd-kernel-headers` +- `freebsd-userland` depends on `freebsd-libc` and `freebsd-sh` +- `freebsd-clang-toolchain` depends on `freebsd-libc`, `freebsd-kernel-headers`, and `freebsd-sh` +- `freebsd-gmake` depends on `freebsd-sh` and `freebsd-libc` +- `freebsd-autotools` depends on `freebsd-gmake`, `freebsd-bash`, and `freebsd-libc` +- `freebsd-openssl` depends on `freebsd-libc` +- `freebsd-zlib` depends on `freebsd-libc` +- `freebsd-bash` depends on `freebsd-libc` + +These dependencies are not merely documented; the harness resolves them recursively when materializing outputs and building the final profile. + +## Important implementation notes + +### 1. Executable entries are wrapped, not copied verbatim + +For package entries installed under `bin/`, the harness creates small wrappers that `exec` the host tool by absolute path. This avoids breaking tools such as `autoconf` that expect their installed prefix layout or auxiliary data files to remain coherent. + +### 2. Data trees are copied where profile-local structure matters + +For items such as: + +- kernel headers +- automake support files +- autoconf data +- libtool auxiliary data + +whole directory trees are copied into the package outputs so that the generated profile has the expected on-disk structure. + +### 3. This is a prototype package-definition layer, not final Guix integration + +The current result demonstrates packaging shape and profile usability. It does **not** yet provide: + +- real Guix `package` lowering on FreeBSD +- derivation generation from these package definitions +- daemon-backed builds into `/frx/store` +- real profile generation by `guix package` + +Those remain later integration tasks. + +## Why this satisfies Phase 3.2 on the prototype track + +Phase 3.2 asked for a minimal FreeBSD package-definition set and validation that the packages build, relate correctly, and can be installed into profiles for practical use. + +That goal is satisfied on the current prototype track because: + +- the requested FreeBSD system-component categories are all represented +- explicit dependency relationships are encoded and resolved +- the package set materializes successfully into store-like outputs +- the outputs install into a merged profile successfully +- the resulting profile is practically usable for development validation, including compilation and execution of a test program using the staged toolchain + +## Conclusion + +Phase 3.2 is satisfied on the current FreeBSD prototype track: + +- a minimal FreeBSD system package-definition layer now exists in-repo +- dependency relationships are explicit and validated +- profile-style installation works +- the generated profile is usable for at least a concrete C build/run validation + +With both Phase 3.1 and Phase 3.2 complete, the project has now finished the build-system adaptation milestone on the current FreeBSD path. diff --git a/modules/fruix/packages/freebsd.scm b/modules/fruix/packages/freebsd.scm new file mode 100644 index 0000000..8b9ff9d --- /dev/null +++ b/modules/fruix/packages/freebsd.scm @@ -0,0 +1,272 @@ +(define-module (fruix packages freebsd) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:export (freebsd-release + freebsd-package? + freebsd-package-name + freebsd-package-version + freebsd-package-build-system + freebsd-package-inputs + freebsd-package-home-page + freebsd-package-synopsis + freebsd-package-description + freebsd-package-license + freebsd-package-install-plan + freebsd-kernel + freebsd-kernel-headers + freebsd-libc + freebsd-userland + freebsd-clang-toolchain + freebsd-gmake + freebsd-autotools + freebsd-openssl + freebsd-zlib + freebsd-sh + freebsd-bash + %freebsd-core-packages + %freebsd-development-profile-packages)) + +(define-record-type + (make-freebsd-package name version build-system inputs home-page synopsis + description license install-plan) + freebsd-package? + (name freebsd-package-name) + (version freebsd-package-version) + (build-system freebsd-package-build-system) + (inputs freebsd-package-inputs) + (home-page freebsd-package-home-page) + (synopsis freebsd-package-synopsis) + (description freebsd-package-description) + (license freebsd-package-license) + (install-plan freebsd-package-install-plan)) + +(define* (freebsd-package #:key name version build-system (inputs '()) home-page + synopsis description license install-plan) + (make-freebsd-package name version build-system inputs home-page synopsis + description license install-plan)) + +(define freebsd-release "15.0-STABLE") + +(define freebsd-kernel + (freebsd-package + #:name "freebsd-kernel" + #:version freebsd-release + #:build-system 'copy-build-system + #:home-page "https://www.freebsd.org/" + #:synopsis "Prototype package for the running FreeBSD kernel" + #:description + "Prototype package definition that stages the currently installed FreeBSD +kernel image into a store-like output for FreeBSD porting experiments." + #:license 'bsd-2 + #:install-plan + '((file "/boot/kernel/kernel" "boot/kernel/kernel") + (file "/boot/kernel/linker.hints" "boot/kernel/linker.hints")))) + +(define freebsd-kernel-headers + (freebsd-package + #:name "freebsd-kernel-headers" + #:version freebsd-release + #:build-system 'copy-build-system + #:home-page "https://www.freebsd.org/" + #:synopsis "Prototype package for FreeBSD kernel headers" + #:description + "Prototype package definition that stages a minimal set of FreeBSD kernel +header directories from /usr/src for Guix porting experiments." + #:license 'bsd-2 + #:install-plan + '((directory "/usr/src/sys/sys" "include/sys")))) + +(define freebsd-libc + (freebsd-package + #:name "freebsd-libc" + #:version freebsd-release + #:build-system 'copy-build-system + #:inputs (list freebsd-kernel-headers) + #:home-page "https://www.freebsd.org/" + #:synopsis "Prototype package for FreeBSD libc and userland headers" + #:description + "Prototype package definition that stages FreeBSD libc, the dynamic loader, +and the userland C headers needed for development profiles." + #:license 'bsd-2 + #:install-plan + '((file "/lib/libc.so.7" "lib/libc.so.7") + (file "/lib/libsys.so.7" "lib/libsys.so.7") + (file "/libexec/ld-elf.so.1" "libexec/ld-elf.so.1")))) + +(define freebsd-sh + (freebsd-package + #:name "freebsd-sh" + #:version freebsd-release + #:build-system 'copy-build-system + #:inputs (list freebsd-libc) + #:home-page "https://www.freebsd.org/" + #:synopsis "Prototype package for the FreeBSD POSIX shell" + #:description + "Prototype package definition that stages the base system POSIX shell from +FreeBSD." + #:license 'bsd-2 + #:install-plan + '((file "/bin/sh" "bin/sh")))) + +(define freebsd-userland + (freebsd-package + #:name "freebsd-userland" + #:version freebsd-release + #:build-system 'copy-build-system + #:inputs (list freebsd-libc freebsd-sh) + #:home-page "https://www.freebsd.org/" + #:synopsis "Prototype package for selected FreeBSD userland utilities" + #:description + "Prototype package definition that stages a small set of base FreeBSD +userland commands needed for development and build experiments." + #:license 'bsd-2 + #:install-plan + '((file "/bin/cat" "bin/cat") + (file "/bin/cp" "bin/cp") + (file "/bin/echo" "bin/echo") + (file "/bin/ln" "bin/ln") + (file "/bin/ls" "bin/ls") + (file "/bin/mkdir" "bin/mkdir") + (file "/bin/mv" "bin/mv") + (file "/bin/pwd" "bin/pwd") + (file "/bin/rm" "bin/rm") + (file "/usr/bin/find" "bin/find") + (file "/usr/bin/tar" "bin/tar") + (file "/usr/bin/xargs" "bin/xargs")))) + +(define freebsd-clang-toolchain + (freebsd-package + #:name "freebsd-clang-toolchain" + #:version freebsd-release + #:build-system 'copy-build-system + #:inputs (list freebsd-libc freebsd-kernel-headers freebsd-sh) + #:home-page "https://www.freebsd.org/" + #:synopsis "Prototype package for the FreeBSD Clang toolchain" + #:description + "Prototype package definition that stages the base FreeBSD Clang-based C +and C++ toolchain into a profile-friendly output." + #:license 'bsd-2 + #:install-plan + '((file "/usr/bin/cc" "bin/cc") + (file "/usr/bin/c++" "bin/c++") + (file "/usr/bin/clang" "bin/clang") + (file "/usr/bin/clang++" "bin/clang++") + (file "/usr/bin/ar" "bin/ar") + (file "/usr/bin/ranlib" "bin/ranlib") + (file "/usr/bin/nm" "bin/nm") + (file "/usr/bin/ld" "bin/ld")))) + +(define freebsd-gmake + (freebsd-package + #:name "freebsd-gmake" + #:version "4.4.1" + #:build-system 'copy-build-system + #:inputs (list freebsd-sh freebsd-libc) + #:home-page "https://www.gnu.org/software/make/" + #:synopsis "Prototype package for GNU Make on FreeBSD" + #:description + "Prototype package definition that stages the GNU Make binary from the +FreeBSD ports collection for use in build profiles." + #:license 'gpl3+ + #:install-plan + '((file "/usr/local/bin/gmake" "bin/gmake") + (file "/usr/local/bin/gmake" "bin/make")))) + +(define freebsd-bash + (freebsd-package + #:name "freebsd-bash" + #:version "5.3.9" + #:build-system 'copy-build-system + #:inputs (list freebsd-libc) + #:home-page "https://www.gnu.org/software/bash/" + #:synopsis "Prototype package for GNU Bash on FreeBSD" + #:description + "Prototype package definition that stages the Bash binary from the +FreeBSD ports collection for development profiles." + #:license 'gpl3+ + #:install-plan + '((file "/usr/local/bin/bash" "bin/bash")))) + +(define freebsd-autotools + (freebsd-package + #:name "freebsd-autotools" + #:version "2026-04" + #:build-system 'copy-build-system + #:inputs (list freebsd-gmake freebsd-bash freebsd-libc) + #:home-page "https://www.gnu.org/software/autoconf/" + #:synopsis "Prototype package for Autotools on FreeBSD" + #:description + "Prototype package definition that stages the Autoconf, Automake, Libtool, +pkg-config, and GNU m4 tools needed by FreeBSD Guix build experiments." + #:license 'gpl3+ + #:install-plan + '((file "/usr/local/bin/autoconf" "bin/autoconf") + (file "/usr/local/bin/autoheader" "bin/autoheader") + (file "/usr/local/bin/autom4te" "bin/autom4te") + (file "/usr/local/bin/automake" "bin/automake") + (file "/usr/local/bin/aclocal" "bin/aclocal") + (file "/usr/local/bin/autoreconf" "bin/autoreconf") + (file "/usr/local/bin/libtoolize" "bin/libtoolize") + (file "/usr/local/bin/pkg-config" "bin/pkg-config") + (file "/usr/local/bin/gm4" "bin/m4") + (directory "/usr/local/share/autoconf2.72" "share/autoconf2.72") + (directory "/usr/local/share/automake-1.18" "share/automake-1.18") + (directory "/usr/local/share/aclocal" "share/aclocal") + (directory "/usr/local/share/libtool" "share/libtool")))) + +(define freebsd-openssl + (freebsd-package + #:name "freebsd-openssl" + #:version freebsd-release + #:build-system 'copy-build-system + #:inputs (list freebsd-libc) + #:home-page "https://www.openssl.org/" + #:synopsis "Prototype package for OpenSSL libraries from FreeBSD base" + #:description + "Prototype package definition that stages the OpenSSL shared libraries +shipped with FreeBSD base." + #:license 'openssl + #:install-plan + '((file "/usr/lib/libcrypto.so" "lib/libcrypto.so") + (file "/usr/lib/libssl.so" "lib/libssl.so")))) + +(define freebsd-zlib + (freebsd-package + #:name "freebsd-zlib" + #:version freebsd-release + #:build-system 'copy-build-system + #:inputs (list freebsd-libc) + #:home-page "https://zlib.net/" + #:synopsis "Prototype package for zlib from FreeBSD base" + #:description + "Prototype package definition that stages the base FreeBSD zlib shared +library for profile experiments." + #:license 'zlib + #:install-plan + '((file "/lib/libz.so.6" "lib/libz.so.6")))) + +(define %freebsd-core-packages + (list freebsd-kernel + freebsd-kernel-headers + freebsd-libc + freebsd-userland + freebsd-clang-toolchain + freebsd-gmake + freebsd-autotools + freebsd-openssl + freebsd-zlib + freebsd-sh + freebsd-bash)) + +(define %freebsd-development-profile-packages + (list freebsd-kernel + freebsd-kernel-headers + freebsd-libc + freebsd-userland + freebsd-clang-toolchain + freebsd-gmake + freebsd-autotools + freebsd-openssl + freebsd-zlib + freebsd-sh + freebsd-bash)) diff --git a/tests/packages/freebsd-package-profile-prototype.scm b/tests/packages/freebsd-package-profile-prototype.scm new file mode 100644 index 0000000..271f9d7 --- /dev/null +++ b/tests/packages/freebsd-package-profile-prototype.scm @@ -0,0 +1,258 @@ +(use-modules (fruix packages freebsd) + (guix build utils) + (ice-9 format) + (ice-9 ftw) + (ice-9 match) + (ice-9 popen) + (srfi srfi-1) + (srfi srfi-13) + (rnrs io ports)) + +(define (getenv* name default) + (or (getenv name) default)) + +(define (trim-trailing-newlines str) + (let loop ((len (string-length str))) + (if (and (> len 0) + (char=? (string-ref str (- len 1)) #\newline)) + (loop (- len 1)) + (substring str 0 len)))) + +(define (command-output program . args) + (let* ((port (apply open-pipe* OPEN_READ program args)) + (output (get-string-all port)) + (status (close-pipe port))) + (unless (zero? status) + (error (format #f "command failed: ~a ~s => ~a" + program args status))) + (trim-trailing-newlines output))) + +(define workdir + (or (getenv "WORKDIR") + (error "WORKDIR environment variable is required"))) +(define store-dir + (string-append workdir "/store")) +(define profile-dir + (string-append workdir "/profiles/freebsd-development")) +(define build-dir + (string-append workdir "/profile-build")) +(define metadata-file + (string-append workdir "/freebsd-package-profile-prototype-metadata.txt")) +(define built-package-paths '()) + +(define (sha256-string text) + (let ((temp-file (string-append workdir "/hash-input.txt"))) + (call-with-output-file temp-file + (lambda (port) + (display text port))) + (command-output "sha256" "-q" temp-file))) + +(define (package-manifest-string package) + (string-append + "name=" (freebsd-package-name package) "\n" + "version=" (freebsd-package-version package) "\n" + "build-system=" (symbol->string (freebsd-package-build-system package)) "\n" + "inputs=" (string-join (map freebsd-package-name + (freebsd-package-inputs package)) ",") "\n" + "install-plan=" (object->string (freebsd-package-install-plan package)))) + +(define (package-output-path package) + (let ((cached (assoc-ref built-package-paths (freebsd-package-name package)))) + (if cached + cached + #f))) + +(define (remember-package-output! package path) + (set! built-package-paths + (acons (freebsd-package-name package) path built-package-paths))) + +(define (write-file path content) + (call-with-output-file path + (lambda (port) + (display content port)))) + +(define (install-wrapper source destination) + (write-file destination + (string-append "#!/bin/sh\nexec " source " \"$@\"\n")) + (chmod destination #o555)) + +(define (materialize-plan-entry output-path entry) + (match entry + (('file source target) + (let ((destination (string-append output-path "/" target))) + (mkdir-p (dirname destination)) + (if (string-prefix? "bin/" target) + (install-wrapper source destination) + (symlink source destination)))) + (('directory source target) + (let ((destination (string-append output-path "/" target))) + (mkdir-p (dirname destination)) + (copy-recursively source destination))) + (_ + (error (format #f "unsupported install plan entry: ~s" entry))))) + +(define (materialize-package package) + (or (package-output-path package) + (let* ((input-paths (map materialize-package + (freebsd-package-inputs package))) + (hash (sha256-string (package-manifest-string package))) + (output-path (string-append store-dir "/" hash "-" + (freebsd-package-name package) + "-" (freebsd-package-version package)))) + (unless (file-exists? output-path) + (mkdir-p output-path) + (for-each (lambda (entry) + (materialize-plan-entry output-path entry)) + (freebsd-package-install-plan package)) + (write-file (string-append output-path "/.references") + (string-join input-paths "\n")) + (write-file (string-append output-path "/.fruix-package") + (package-manifest-string package))) + (remember-package-output! package output-path) + output-path))) + +(define (directory-entries path) + (filter (lambda (entry) + (not (member entry '("." "..")))) + (scandir path))) + +(define (merge-output-into-profile output-path profile-root) + (define (walk relative) + (let ((source (if (string-null? relative) + output-path + (string-append output-path "/" relative)))) + (for-each + (lambda (entry) + (unless (member entry '(".references" ".fruix-package")) + (let* ((entry-relative (if (string-null? relative) + entry + (string-append relative "/" entry))) + (source-entry (string-append output-path "/" entry-relative)) + (target-entry (string-append profile-root "/" entry-relative)) + (st (lstat source-entry))) + (if (eq? 'directory (stat:type st)) + (begin + (mkdir-p target-entry) + (walk entry-relative)) + (begin + (mkdir-p (dirname target-entry)) + (if (file-exists? target-entry) + (let ((existing (false-if-exception (readlink target-entry)))) + (unless (or (and existing + (string=? existing source-entry)) + (and existing + (file-exists? existing) + (same-file-contents? existing source-entry))) + (error (format #f "profile collision for ~a" target-entry)))) + (symlink source-entry target-entry))))))) + (directory-entries source)))) + (mkdir-p profile-root) + (walk "")) + +(define (profile-file path) + (string-append profile-dir "/" path)) + +(define (assert-file-exists path) + (unless (file-exists? path) + (error (format #f "required file missing: ~a" path)))) + +(define (same-file-contents? a b) + (zero? (system* "cmp" "-s" a b))) + +(define (validate-profile) + (let* ((bash-version (command-output (profile-file "bin/bash") "--version")) + (make-version (command-output (profile-file "bin/make") "--version")) + (autoconf-version (command-output (profile-file "bin/autoconf") "--version")) + (cc-version (command-output (profile-file "bin/cc") "--version")) + (hello-source (string-append build-dir "/hello.c")) + (hello-binary (string-append build-dir "/hello")) + (hello-output + (begin + (mkdir-p build-dir) + (write-file hello-source + "#include \nint main(void){puts(\"hello-from-freebsd-profile\");return 0;}\n") + (command-output (profile-file "bin/sh") "-c" + (string-append + "PATH=" profile-dir "/bin:/usr/bin:/bin " + "CPPFLAGS=-I" profile-dir "/include " + "LDFLAGS=-L" profile-dir "/lib " + (profile-file "bin/cc") " " hello-source + " -o " hello-binary + " && " hello-binary))))) + (for-each assert-file-exists + (list (profile-file "bin/sh") + (profile-file "bin/bash") + (profile-file "bin/cc") + (profile-file "bin/make") + (profile-file "bin/autoreconf") + (profile-file "bin/pkg-config") + (profile-file "include/sys/param.h") + (profile-file "lib/libc.so.7") + (profile-file "lib/libcrypto.so") + (profile-file "lib/libz.so.6") + (profile-file "boot/kernel/kernel"))) + (unless (string-prefix? "GNU bash, version" (car (string-split bash-version #\newline))) + (error "unexpected bash version output")) + (unless (string-prefix? "GNU Make" (car (string-split make-version #\newline))) + (error "unexpected make version output")) + (unless (string-prefix? "autoconf" (car (string-split autoconf-version #\newline))) + (error "unexpected autoconf version output")) + (unless (string-contains cc-version "FreeBSD clang version") + (error "unexpected cc version output")) + (unless (string=? hello-output "hello-from-freebsd-profile") + (error (format #f "unexpected hello output: ~s" hello-output))) + `((bash-version . ,(car (string-split bash-version #\newline))) + (make-version . ,(car (string-split make-version #\newline))) + (autoconf-version . ,(car (string-split autoconf-version #\newline))) + (cc-version . ,(car (string-split cc-version #\newline))) + (hello-output . ,hello-output)))) + +(mkdir-p workdir) +(mkdir-p store-dir) +(mkdir-p profile-dir) + +(let* ((core-paths (map materialize-package %freebsd-core-packages)) + (profile-package-paths (map materialize-package + %freebsd-development-profile-packages)) + (validation (begin + (for-each (lambda (path) + (merge-output-into-profile path profile-dir)) + profile-package-paths) + (validate-profile)))) + (call-with-output-file metadata-file + (lambda (port) + (format port "workdir=~a~%" workdir) + (format port "store_dir=~a~%" store-dir) + (format port "profile_dir=~a~%" profile-dir) + (format port "core_package_count=~a~%" (length %freebsd-core-packages)) + (format port "profile_package_count=~a~%" + (length %freebsd-development-profile-packages)) + (for-each + (lambda (package) + (format port "package=~a version=~a build_system=~a inputs=~a~%" + (freebsd-package-name package) + (freebsd-package-version package) + (freebsd-package-build-system package) + (map freebsd-package-name (freebsd-package-inputs package)))) + %freebsd-core-packages) + (for-each + (lambda (path) + (format port "store_item=~a~%" path)) + core-paths) + (format port "bash_version=~a~%" (assoc-ref validation 'bash-version)) + (format port "make_version=~a~%" (assoc-ref validation 'make-version)) + (format port "autoconf_version=~a~%" (assoc-ref validation 'autoconf-version)) + (format port "cc_version=~a~%" (assoc-ref validation 'cc-version)) + (format port "hello_output=~a~%" (assoc-ref validation 'hello-output)))) + + (when (getenv "METADATA_OUT") + (mkdir-p (dirname (getenv "METADATA_OUT"))) + (copy-file metadata-file (getenv "METADATA_OUT"))) + + (format #t "PASS freebsd-package-profile-prototype~%") + (format #t "Profile directory: ~a~%" profile-dir) + (format #t "Metadata file: ~a~%" metadata-file) + (when (getenv "METADATA_OUT") + (format #t "Copied metadata to: ~a~%" (getenv "METADATA_OUT"))) + (display "--- metadata ---\n") + (display (call-with-input-file metadata-file get-string-all))) diff --git a/tests/packages/run-freebsd-package-profile-prototype.sh b/tests/packages/run-freebsd-package-profile-prototype.sh new file mode 100755 index 0000000..3245e64 --- /dev/null +++ b/tests/packages/run-freebsd-package-profile-prototype.sh @@ -0,0 +1,71 @@ +#!/bin/sh +set -eu + +project_root=${PROJECT_ROOT:-$(pwd)} +guix_source_dir=${GUIX_SOURCE_DIR:-"$HOME/repos/guix"} +script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd) +runner_scm=$script_dir/freebsd-package-profile-prototype.scm + +if [ -n "${GUILE_BIN:-}" ]; then + guile_bin=$GUILE_BIN +elif [ -x /tmp/guile-freebsd-validate-install/bin/guile ]; then + guile_bin=/tmp/guile-freebsd-validate-install/bin/guile +else + cat >&2 <<'EOF' +A fixed local Guile build is required for this harness. +Set GUILE_BIN to a locally built fixed Guile, for example: + GUILE_BIN=/tmp/guile-freebsd-validate-install/bin/guile +EOF + exit 1 +fi + +if [ ! -x "$guile_bin" ]; then + echo "Guile binary is not executable: $guile_bin" >&2 + exit 1 +fi + +guile_prefix=$(CDPATH= cd -- "$(dirname "$guile_bin")/.." && pwd) +guile_lib_dir=$guile_prefix/lib +if [ -e "$guile_lib_dir/libguile-3.0.so.1" ]; then + if [ -n "${LD_LIBRARY_PATH:-}" ]; then + export LD_LIBRARY_PATH="$guile_lib_dir:$LD_LIBRARY_PATH" + else + export LD_LIBRARY_PATH="$guile_lib_dir" + fi +fi + +cleanup=0 +if [ -n "${WORKDIR:-}" ]; then + workdir=$WORKDIR + mkdir -p "$workdir" +else + workdir=$(mktemp -d /tmp/fruix-freebsd-package-profile.XXXXXX) + cleanup=1 +fi + +if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then + cleanup=0 +fi + +cleanup_workdir() { + if [ "$cleanup" -eq 1 ]; then + rm -rf "$workdir" + fi +} +trap cleanup_workdir EXIT INT TERM + +export GUILE_BIN="$guile_bin" +export GUILE_AUTO_COMPILE=0 +export WORKDIR="$workdir" +export PROJECT_ROOT="$project_root" +if [ -n "${GUILE_LOAD_PATH:-}" ]; then + export GUILE_LOAD_PATH="$project_root/modules:$guix_source_dir:$GUILE_LOAD_PATH" +else + export GUILE_LOAD_PATH="$project_root/modules:$guix_source_dir" +fi + +printf 'Using Guile: %s\n' "$guile_bin" +printf 'Using LD_LIBRARY_PATH: %s\n' "${LD_LIBRARY_PATH:-}" +printf 'Working directory: %s\n' "$workdir" + +"$guile_bin" -s "$runner_scm"