1(define-module (nmeum bootloader grub)2 #:use-module (gnu)3 #:use-module (gnu bootloader)4 #:use-module (gnu bootloader grub)5 #:use-module (guix gexp)6 #:use-module (guix import utils)7 #:use-module ((srfi srfi-1) #:select (break))8 #:use-module ((srfi srfi-11) #:select (let-values)))910;; wraps an existing Grub 'bootloader-installer' in a procedure which copies11;; all files referenced in Grub's configuration file to the install directory.12;;13;; TODO: make use of the new in-vicinity procedure (unreleased in GNU Guile).14;; See https://cgit.git.savannah.gnu.org/cgit/guile.git/commit/?id=755f703dcb3110e1920e42078edc6d9c88cc8b2815(define-public (grub-copy installer)16 #~(lambda (bootloader device mount-point)17 (use-modules ((srfi srfi-1) #:select (fold lset-adjoin lset-difference))18 ((guix import utils) #:select (read-lines))19 ((guix store) #:select (direct-store-path))20 (ice-9 regex))2122 ;; regex for finding a path to the Store in the Grub configuration file.23 ;; Obviously a heurstic, ideally we would get this information from Grub.24 (define store-regexp (make-regexp "/gnu/store/[A-Za-z0-9@/._-]+"))2526 ;; regex for finding the linux command in a Grub configuration file.27 ;; See https://www.gnu.org/software/grub/manual/grub/grub.html#linux28 (define linux-regexp (make-regexp "^[[:space:]]*linux[[:space:]]"))2930 ;; Takes a list of /gnu/store paths and returns a list of unique directory31 ;; entries in the /gnu/store directory (usually: hash + package + version).32 (define (store-entries paths)33 (fold34 (lambda (path acc)35 (lset-adjoin36 equal?37 acc38 (substring (direct-store-path path) (string-length "/gnu/store"))))39 '() paths))4041 (define (required-paths lines)42 (fold43 (lambda (line acc)44 (let ((paths (map match:substring (list-matches store-regexp line))))45 (if (null? paths)46 acc47 (if (regexp-exec linux-regexp line) ; ignore kernel cmdline paths48 (lset-adjoin equal? acc (car paths))49 (apply lset-adjoin (cons* equal? acc paths))))))50 '() lines))5152 (define (existing-paths store)53 (map54 (lambda (path)55 (substring path (string-length "/boot")))56 (find-files store)))5758 (let* ((install-dir (canonicalize-path (string-append mount-point "/boot")))59 (grub-cfg (string-append install-dir "/grub/grub.cfg"))60 (grub-lines (call-with-input-file grub-cfg read-lines))61 (required (required-paths grub-lines))62 (existing (existing-paths (string-append install-dir "/gnu/store"))))63 (for-each ; remove leftovers from old generations64 (lambda (store-entry)65 (delete-file-recursively66 (string-append install-dir "/gnu/store/" store-entry)))67 (lset-difference equal? (store-entries existing) (store-entries required)))68 (for-each ; copy required files69 (lambda (store-file)70 (let ((dest-file (string-append install-dir store-file)))71 (mkdir-p (dirname dest-file))72 (copy-recursively store-file dest-file)))73 required))7475 ;; Invoke the 'bootloader-installer' that we are wrapping.76 (#$installer bootloader device mount-point)))7778;; wraps an existing 'bootloader-configuration-file-generator' and removes79;; any '#:store-crypto-devices' from the arguments passed to the generator.80(define-public (configuration-file-generator-without-crypto-devices generator)81 (define (filter-keyword keyword args)82 (let-values (((head tail) (break83 (lambda (arg)84 (equal? arg keyword))85 args)))86 (if (null? tail)87 args88 (append head (cdr (cdr tail))))))8990 (lambda args91 (apply92 generator93 (filter-keyword #:store-crypto-devices args))))