diff mbox series

[bug#41066] gnu: grub: Support for chain loading.

Message ID 7A4ABEA8-4500-4D55-BCCE-BFB37FB06B2C@vodafonemail.de
State Accepted
Headers show
Series [bug#41066] gnu: grub: Support for chain loading. | expand

Checks

Context Check Description
cbaines/applying patch fail View Laminar job

Commit Message

Stefan May 3, 2020, 11:34 p.m. UTC
* gnu/bootloaders/grub.scm (grub-efi-net-bootloader-chain): New efi bootloader
for chaining with other bootloaders.
* guix/packages.scm (package-collection): New function to build a union of
packages with a collection of certain files.

This allows to chain grub-efi mainly for single-board-computers with e.g.
U-Boot, device-tree files, plain configuration files, etc. like this:

(operating-system
 (bootloader
   (grub-efi-net-bootloader-chain
     (list u-boot
           firmware)
     '("libexec/u-boot.bin"
       "firmware/")
     (list (plain-file "config.txt"
                       "kernel=u-boot.bin"))
     #:target "/boot-tftp"
     #:efi-subdir "efi/boot")
   (target "/boot-tftp"))
  ...)
---
gnu/bootloader/grub.scm |  36 +++++++++++++
guix/packages.scm       | 114 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 150 insertions(+)

Comments

Danny Milosavljevic May 24, 2020, 11:13 a.m. UTC | #1
I guess it is possible to do it like that--and maybe we even should.

But a collection of packages and accompanying setup is called a profile.

Maybe you'd rather want a bootloader profile instead of a bootloader
package-of-packages.

We do the same for kernel modules--it just creates a profile of all the
kernel module packages using the procedure "profile-derivation" and then
uses a profile hook to configure the whole thing.

See also operating-system-directory-base-entries in gnu/system.scm for
how this is done with kernel modules (the profile-derivation call).

You could do something similar with multiple bootloaders that are chained
together that make some kind of useful whole.

A profile hook could then make sure that this collection of bootloaders
actually makes sense and then chain them together in the right order,
if any.

What do you think?
Stefan May 24, 2020, 1:21 p.m. UTC | #2
Hi Danny!

> Am 24.05.2020 um 13:13 schrieb Danny Milosavljevic <dannym@scratchpost.org>:
> 
> I guess it is possible to do it like that--and maybe we even should.
> 
> But a collection of packages and accompanying setup is called a profile.

Good point.

> Maybe you'd rather want a bootloader profile instead of a bootloader
> package-of-packages.
> 
> We do the same for kernel modules--it just creates a profile of all the
> kernel module packages using the procedure "profile-derivation" and then
> uses a profile hook to configure the whole thing.
> 
> See also operating-system-directory-base-entries in gnu/system.scm for
> how this is done with kernel modules (the profile-derivation call).
> 
> You could do something similar with multiple bootloaders that are chained
> together that make some kind of useful whole.
> 
> A profile hook could then make sure that this collection of bootloaders
> actually makes sense and then chain them together in the right order,
> if any.
> 
> What do you think?

I’m still a bloody newbie. This sounds like a huge rework, probably too huge for me.

The biggest trouble from my point of view is that the bootloader installer functions only get a <bootloader> argument, which internally only has a <package> field. Then this <package> would need to be replaced by some kind of <profile>.

My current solution provides a different package with the proper collection of files to copy for the installer. That was quite easy – well, beside the problem to tear in a plain-file, for which I needed the trick with the source field.


Bye

Stefan
diff mbox series

Patch

diff --git a/gnu/bootloader/grub.scm b/gnu/bootloader/grub.scm
index 9ca4f016f6..67736724a7 100644
--- a/gnu/bootloader/grub.scm
+++ b/gnu/bootloader/grub.scm
@@ -22,6 +22,7 @@ 
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

(define-module (gnu bootloader grub)
+  #:use-module (guix packages)
  #:use-module (guix records)
  #:use-module ((guix utils) #:select (%current-system %current-target-system))
  #:use-module (guix gexp)
@@ -54,6 +55,7 @@ 
            grub-bootloader
            grub-efi-bootloader
            grub-efi-net-bootloader
+            grub-efi-net-bootloader-chain
            grub-mkrescue-bootloader

            grub-configuration))
@@ -525,6 +527,40 @@  TARGET for the system whose root is mounted at MOUNT-POINT."
     (installer (install-grub-efi-net efi-subdir))
     (configuration-file (string-append target "/" efi-subdir "/grub.cfg")))))

+(define* (grub-efi-net-bootloader-chain bootloader-packages
+                                        bootloader-package-contents
+                                        #:optional (files '())
+                                        #:key
+                                        (target #f)
+                                        (efi-subdir #f))
+  "Defines a (grub-efi-net-bootloader) with ADDITIONAL-BOOTLOADER-FILES from
+ADDITIONAL-BOOTLOADER-PACKAGES and ADDITIONAL-FILES, all collected as a
+(package-collection), whose files inside the \"collection\" folder get
+copied into TARGET along with the the bootloader installation in EFI-SUBDIR."
+  (let* ((base-bootloader (grub-efi-net-bootloader #:target target
+                                                   #:efi-subdir efi-subdir))
+         (base-installer (bootloader-installer base-bootloader))
+         (packages (package-collection
+                    (cons (bootloader-package base-bootloader)
+                          bootloader-packages)
+                    bootloader-package-contents
+                    files)))
+    (bootloader
+     (inherit base-bootloader)
+     (package packages)
+     (installer
+      #~(lambda (bootloader target mount-point)
+          (#$base-installer bootloader target mount-point)
+          (copy-recursively
+           (string-append bootloader "/collection")
+           (string-join (delete ""
+                                (string-split
+                                 (string-append mount-point "/" target)
+                                 #\/))
+                        "/"
+                        'prefix)
+           #:follow-symlinks? #t))))))
+
(define* grub-mkrescue-bootloader
  (bootloader
   (inherit grub-efi-bootloader)
diff --git a/guix/packages.scm b/guix/packages.scm
index 2fa4fd05d7..987c3b80ac 100644
--- a/guix/packages.scm
+++ b/guix/packages.scm
@@ -32,6 +32,7 @@ 
  #:use-module (guix derivations)
  #:use-module (guix memoization)
  #:use-module (guix build-system)
+  #:use-module (guix build-system trivial)
  #:use-module (guix search-paths)
  #:use-module (guix sets)
  #:use-module (ice-9 match)
@@ -114,6 +115,7 @@ 
            package-with-patches
            package-with-extra-patches
            package/inherit
+            package-collection

            transitive-input-references

@@ -944,6 +946,118 @@  OVERRIDES."
      overrides ...
      (replacement (and=> (package-replacement p) loop)))))

+(define* (package-collection packages package-contents #:optional (files '()))
+  "Defines a package union from PACKAGES and additional FILES.  Its output
+\":out\" has a \"collection\" directory with links to selected PACKAGE-CONTENTS
+and FILES. The output \":collection\" of the package links to that directory."
+  (let ((package-names (map (lambda (package)
+                              (package-name package))
+                            packages))
+        (link-machine '(lambda (file directory targetname)
+                         (symlink file
+                                  (string-append directory
+                                                 "/"
+                                                 (targetname file))))))
+    (package
+     (name (string-join (append '("package-collection") package-names) "-"))
+     ;; We copy the version of the first package.
+     (version (package-version (first packages)))
+     ;; FILES are expected to be a list of gexps like 'plain-file'. As gexps
+     ;; can't (yet) be used in the arguments of a package we convert FILES into
+     ;; the source of this package.
+     (source (computed-file
+              "computed-files"
+              (with-imported-modules
+               '((guix build utils))
+               #~(begin
+                   (use-modules (guix build utils))
+                   (define (targetname file)
+                     ;; A plain-file inside the store has a name like
+                     ;; gnu/store/9x6y7j75qy9z6iv21byrbyj4yy8hb490-config.txt.
+                     ;; We take its basename and drop the hash from it.
+                     ;; Therefore it expects the first '-' at index 32.
+                     ;; Otherwise the basename of file is returned
+                     (let ((name (basename file)))
+                       (if (and (> (string-length name) 33)
+                                (= (string-index name #\- 0 33) 32))
+                           (substring name 33)
+                           (name))))
+                   (mkdir-p #$output)
+                   (for-each (lambda (file)
+                               (#$link-machine file #$output targetname))
+                             '#$files)))))
+     (build-system trivial-build-system)
+     (arguments
+      `(#:modules
+        ((guix build union)
+         (guix build utils))
+        #:builder
+        (begin
+          (use-modules (guix build union)
+                       (guix build utils)
+                       (ice-9 ftw)
+                       (ice-9 match)
+                       (srfi srfi-1))
+          ;; Make a union of all packages as :out.
+          (match %build-inputs
+            (((names . directories) ...)
+             (union-build %output directories)))
+          (let* ((directory-content
+                  ;; Creates a list of absolute path names inside DIR.
+                  (lambda (dir)
+                    (map (lambda (name)
+                           (string-append dir name))
+                         (scandir dir (lambda (name)
+                                        (not (member name '("." ".."))))))))
+                 (select-names
+                  ;; Select names ending with (filter) or without "/" (remove)
+                  (lambda (select names)
+                    (select (lambda (name)
+                              (string=? (string-take-right name 1) "/"))
+                      names)))
+                 (content
+                  ;; The selected package content as a list of absolute paths.
+                  (map (lambda (name)
+                         (string-append %output "/" name))
+                       ',package-contents))
+                 (directory-names
+                  (append (select-names filter content)
+                          (list (string-append
+                                 (assoc-ref %build-inputs "source")
+                                 "/"))))
+                 (names-from-directories
+                  (fold (lambda (directory previous)
+                          (append (directory-content directory) previous))
+                        '()
+                        directory-names))
+                 (names-from-content (select-names remove content))
+                 (names (append names-from-directories names-from-content))
+                 (collection-directory (string-append %output "/collection"))
+                 (collection (assoc-ref %outputs "collection")))
+            ;; Collect links to package-contents and file.
+            (mkdir-p collection-directory)
+            (for-each (lambda (name)
+                        (,link-machine name collection-directory basename))
+                      names)
+            (symlink collection-directory collection)))))
+     (inputs (fold-right
+              (lambda (package previous)
+                (cons (list (package-name package) package) previous))
+              '()
+              packages))
+     (outputs '("out" "collection"))
+     (synopsis "Package union with a collection of package contents and files")
+     (description
+      (string-append "A package collection is useful when bootloaders need to "
+                     "be chained and the bootloader-installer needs to install "
+                     "selected parts of them.  This collection includes: "
+                     (string-join package-names ", ") "."))
+     (license
+      (append (map (lambda (package)
+                     (package-license package))
+                   packages)))
+     (home-page ""))))
+
^L
;;;
;;; Package derivations.