Subject: [PATCH 3/4] package: Allow multiple '--manifest' options.

From: Ludovic Courtès <[email-masked]>
Date: Wed, 20 Nov 2019 14:44:36 +0100

* guix/scripts/package.scm (manifest-action): Remove.
(%actions): Remove it.
(load-manifest): New procedure.
(process-actions): Handle 'manifest' options.  Define 'files' from
'manifest' options.  Define 'manifest' based on FILES.  Define 'trans'
to represent the final transaction.
* tests/guix-package.sh: Test it.
* doc/guix.texi (Invoking guix package): Mention
---
 doc/guix.texi            |  3 ++-
 guix/scripts/package.scm | 50 +++++++++++++++++++++-------------------
 tests/guix-package.sh    | 13 +++++++++++
 3 files changed, 41 insertions(+), 25 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 443d5bd71f..9de1dbd6d5 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -2830,7 +2830,8 @@ $ guix package --upgrade . --do-not-upgrade emacs
 @cindex profile declaration
 @cindex profile manifest
 Create a new generation of the profile from the manifest object
-returned by the Scheme code in @var{file}.
+returned by the Scheme code in @var{file}.  This option can be repeated
+several times, in which case the manifests are concatenated.
 
 This allows you to @emph{declare} the profile's contents rather than
 constructing it through a sequence of @code{--install} and similar
diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm
index bcd03a1df9..eb578f7642 100644
--- a/guix/scripts/package.scm
+++ b/guix/scripts/package.scm
@@ -832,32 +832,17 @@ processed, #f otherwise."
   (unless dry-run?
     (delete-matching-generations store profile pattern)))
 
-(define* (manifest-action store profile file opts
-                          #:key dry-run?)
-  "Change PROFILE to contain the packages specified in FILE."
-  (let* ((user-module  (make-user-module '((guix profiles) (gnu))))
-         (manifest     (load* file user-module))
-         (bootstrap?   (assoc-ref opts 'bootstrap?))
-         (substitutes? (assoc-ref opts 'substitutes?))
-         (allow-collisions? (assoc-ref opts 'allow-collisions?)))
-    (if dry-run?
-        (format #t (G_ "would install new manifest from '~a' with ~d entries~%")
-                file (length (manifest-entries manifest)))
-        (format #t (G_ "installing new manifest from '~a' with ~d entries~%")
-                file (length (manifest-entries manifest))))
-    (build-and-use-profile store profile manifest
-                           #:allow-collisions? allow-collisions?
-                           #:bootstrap? bootstrap?
-                           #:use-substitutes? substitutes?
-                           #:dry-run? dry-run?)))
+(define (load-manifest file)
+  "Load the user-profile manifest (Scheme code) from FILE and return it."
+  (let ((user-module (make-user-module '((guix profiles) (gnu)))))
+    (load* file user-module)))
 
 (define %actions
   ;; List of actions that may be processed.  The car of each pair is the
   ;; action's symbol in the option list; the cdr is the action's procedure.
   `((roll-back? . ,roll-back-action)
     (switch-generation . ,switch-generation-action)
-    (delete-generations . ,delete-generations-action)
-    (manifest . ,manifest-action)))
+    (delete-generations . ,delete-generations-action)))
 
 (define (process-actions store opts)
   "Process any install/remove/upgrade action from OPTS."
@@ -896,7 +881,13 @@ processed, #f otherwise."
               opts)
 
     ;; Then, process normal package removal/installation/upgrade.
-    (let* ((manifest (profile-manifest profile))
+    (let* ((files    (filter-map (match-lambda
+                                   (('manifest . file) file)
+                                   (_ #f))
+                                 opts))
+           (manifest (match files
+                       (() (profile-manifest profile))
+                       (_  (concatenate-manifests (map load-manifest files)))))
            (step1    (options->removable opts manifest
                                          (manifest-transaction)))
            (step2    (options->installable opts manifest step1))
@@ -904,12 +895,23 @@ processed, #f otherwise."
                       (inherit step2)
                       (install (map transform-entry
                                     (manifest-transaction-install step2)))))
-           (new      (manifest-perform-transaction manifest step3)))
+           (new      (manifest-perform-transaction manifest step3))
+           (trans    (if (null? files)
+                         step3
+                         (fold manifest-transaction-install-entry
+                               step3
+                               (manifest-entries manifest)))))
 
       (warn-about-old-distro)
 
-      (unless (manifest-transaction-null? step3)
-        (show-manifest-transaction store manifest step3
+      (unless (manifest-transaction-null? trans)
+        ;; When '--manifest' is used, display information about TRANS as if we
+        ;; were starting from an empty profile.
+        (show-manifest-transaction store
+                                   (if (null? files)
+                                       manifest
+                                       (make-manifest '()))
+                                   trans
                                    #:dry-run? dry-run?)
         (build-and-use-profile store profile new
                                #:allow-collisions? allow-collisions?
diff --git a/tests/guix-package.sh b/tests/guix-package.sh
index 7ad0699380..6d081d58be 100644
--- a/tests/guix-package.sh
+++ b/tests/guix-package.sh
@@ -394,6 +394,19 @@ guix package -I | grep guile
 test `guix package -I | wc -l` -eq 1
 guix package --rollback --bootstrap
 
+# Applying two manifests.
+cat > "$module_dir/manifest2.scm"<<EOF
+(use-modules (gnu packages bootstrap) (guix))
+(define p (package (inherit %bootstrap-guile) (name "eliug")))
+(packages->manifest (list p))
+EOF
+guix package --bootstrap \
+     -m "$module_dir/manifest.scm" -m "$module_dir/manifest2.scm"
+guix package -I | grep guile
+guix package -I | grep eliug
+test `guix package -I | wc -l` -eq 2
+guix package --rollback --bootstrap
+
 # Applying a manifest file with inferior packages.
 cat > "$module_dir/manifest.scm"<<EOF
 (use-modules (guix inferior))
-- 
2.24.0