Subject: Re: [PATCH] gnu: bootloader: Improve support for chain loading.

From: Stefan <[email-masked]>
Date: Sun, 13 Dec 2020 20:28:57 +0100

* gnu/bootloader.scm (chain-efi-bootloader-installer): New function to call its
final-installer argument before copying files from the collection directory of a
bootloader profile to the bootloader target directory, preferring a target of
/mnt/boot/efi over /boot/efi, and only copying if source and destination
directories exist.
* (efi-bootloader-chain): Using (chain-efi-bootloader-installer) if the #:installer
argument is false.

The new chain-efi-bootloader-installer allows a customization of installers like
(install-grub-efi-netboot):

(operating-system
  (bootloader
    (bootloader-configuration
      (bootloader
        (efi-bootloader-chain
          (list (file-append firmware "/boot/")
                (file-append u-boot-my-scb "/libexec/u-boot.bin")
                (plain-file "config.txt"
                            "kernel=u-boot.bin"))
          grub-efi-netboot-bootloader
          #:hooks my-special-bootloader-profile-manipulator
          #:installer
           (chain-efi-bootloader-installer (install-grub-efi-netboot "efi/boot")))
        (target "/boot"))))
---
 gnu/bootloader.scm | 54 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 43 insertions(+), 11 deletions(-)

diff --git a/gnu/bootloader.scm b/gnu/bootloader.scm
index 6d7352ddd2..ce62d315ef 100644
--- a/gnu/bootloader.scm
+++ b/gnu/bootloader.scm
@@ -70,6 +70,7 @@
             %bootloaders
             lookup-bootloader-by-name
 
+            chain-efi-bootloader-installer
             efi-bootloader-chain))
 
 ^L
@@ -311,6 +312,38 @@ HOOKS lists additional hook functions to modify the profile."
            (allow-collisions? #f)
            (relative-symlinks? #f)))
 
+(define (chain-efi-bootloader-installer final-installer)
+  "Define a new bootloader installer gexp, which will invoke FINAL-INSTALLER
+before it will copy the content from a collection/ directory of its 'bootloader'
+argument into the directory of its 'target' argument.
+
+This order is by intention to allow overwriting bootloader files like
+device-trees with own files provided in that collection/ directory.
+
+The generated bootloader function will usually be used in this way:
+
+  (efi-bootloader-chain … #:installer (chain-efi-bootloader-installer …))"
+
+  #~(lambda (bootloader target mount-point)
+      (#$final-installer bootloader target mount-point)
+      (let* ((mount-point/target (string-append mount-point target))
+             ;; When installing Guix, it is common to mount TARGET below
+             ;; MOUNT-POINT rather than the root directory.
+             (bootloader-target (if (file-exists? mount-point/target)
+                                    mount-point/target
+                                    target))
+             (collection (string-append bootloader "/collection")))
+        (if (and (eq? (and=> (stat collection #f) stat:type)
+                      'directory)
+                 (eq? (and=> (stat bootloader-target #f) stat:type)
+                      'directory))
+            ;; Now copy the content of the collection directory.
+            (copy-recursively collection bootloader-target
+                              #:follow-symlinks? #t
+                              #:log (%make-void-port "w"))
+            (error "expecting two directories for bootloader installation"
+                   collection bootloader-target)))))
+
 (define* (efi-bootloader-chain files
                                final-bootloader
                                #:key
@@ -330,10 +363,16 @@ of the directory itself will be symlinked into the collection/ directory.
 The procedures in the HOOKS list can be used to further modify the bootloader
 profile.  It is possible to pass a single function instead of a list.
 
-If the INSTALLER argument is used, then this function will be called to install
-the bootloader.  Otherwise the installer of the FINAL-BOOTLOADER will be called."
+If the INSTALLER argument is used, then this procedure will be called to install
+the bootloader and handle the files inside the collection directory of the
+profile.  Otherwise the generated procedure from
+
+  (chain-efi-bootloader-installer (bootloader-installer FINAL-BOOTLOADER))
+
+will be used to install the bootloader."
   (let* ((final-installer (or installer
-                              (bootloader-installer final-bootloader)))
+                              (chain-efi-bootloader-installer
+                               (bootloader-installer final-bootloader))))
          (profile (efi-bootloader-profile files
                                           (bootloader-package final-bootloader)
                                           (if (list? hooks)
@@ -342,11 +381,4 @@ the bootloader.  Otherwise the installer of the FINAL-BOOTLOADER will be called.
     (bootloader
      (inherit final-bootloader)
      (package profile)
-     (installer
-      #~(lambda (bootloader target mount-point)
-          (#$final-installer bootloader target mount-point)
-          (copy-recursively
-           (string-append bootloader "/collection")
-           (string-append mount-point target)
-           #:follow-symlinks? #t
-           #:log (%make-void-port "w")))))))
+     (installer final-installer))))
-- 
2.29.2