Toggle diff (495 lines)
diff --git a/Makefile.am b/Makefile.am
index 7402c89b62..40b6c75e23 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -318,6 +318,7 @@ MODULES = \
guix/scripts/import/pypi.scm \
guix/scripts/import/stackage.scm \
guix/scripts/import/texlive.scm \
+ guix/scripts/manifest.scm \
guix/scripts/environment.scm \
guix/scripts/shell.scm \
guix/scripts/publish.scm \
@@ -568,6 +569,7 @@ SH_TESTS = \
tests/guix-environment.sh \
tests/guix-environment-container.sh \
tests/guix-shell.sh \
+ tests/guix-manifest.sh \
tests/guix-graph.sh \
tests/guix-describe.sh \
tests/guix-repl.sh \
diff --git a/doc/guix.texi b/doc/guix.texi
index dbe281ead7..4dc3c8b1fc 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -233,6 +233,7 @@ Package Management
* Invoking guix package:: Package installation, removal, etc.
* Substitutes:: Downloading pre-built binaries.
* Packages with Multiple Outputs:: Single source package, multiple outputs.
+* Invoking guix manifest:: Producing environment declarations.
* Invoking guix gc:: Running the garbage collector.
* Invoking guix pull:: Fetching the latest Guix and distribution.
* Invoking guix time-machine:: Running an older revision of Guix.
@@ -3042,6 +3043,7 @@ guix install emacs-guix
* Invoking guix package:: Package installation, removal, etc.
* Substitutes:: Downloading pre-built binaries.
* Packages with Multiple Outputs:: Single source package, multiple outputs.
+* Invoking guix manifest:: Producing environment declarations.
* Invoking guix gc:: Running the garbage collector.
* Invoking guix pull:: Fetching the latest Guix and distribution.
* Invoking guix time-machine:: Running an older revision of Guix.
@@ -3412,7 +3414,9 @@ The example above gives you all the software required to develop Emacs,
similar to what @command{guix environment emacs} provides.
@xref{export-manifest, @option{--export-manifest}}, to learn how to
-obtain a manifest file from an existing profile.
+obtain a manifest file from an existing profile, and @pxref{Invoking
+guix manifest} on how to generate a manifest file from a list of package
+specs and command-line options.
@item --roll-back
@cindex rolling back
@@ -4159,6 +4163,137 @@ Files}). The outputs of a packages are listed in the third column of
the output of @command{guix package --list-available} (@pxref{Invoking
guix package}).
+@node Invoking guix manifest
+@section Invoking @command{guix manifest}
+
+@cindex manifest, generating
+The @command{guix manifest} command outputs a @dfn{manifest}
+corresponding to the packages and options specified on the command line.
+Manifests are code snippets that @emph{declare} the set of packages you
+want to deploy---you can view them as a more expressive form of what you
+pass on the command line to @command{guix install}, @command{guix
+shell}, etc. All these commands accept a @option{--manifest} (or
+@option{-m}) option that allows you to pass them a manifest. For
+non-trivial package sets and customizations, you'll find that using a
+manifest rather than a long command line is often more convenient.
+
+But how do you go from that long command line you're familiar with to
+that appealing but possibly intimidating ``manifest'' thing? The
+@command{guix manifest} command is your companion on this journey: it
+essentially ``translates'' command-line arguments into manifests.
+
+Let's look at a few examples. What's a manifest corresponding to a
+basic list of package specifications? We can figure out by running,
+say:
+
+@example
+guix manifest coreutils grep sed
+@end example
+
+@noindent
+... which outputs this manifest:
+
+@lisp
+(specifications->manifest
+ (list "coreutils" "grep" "sed"))
+@end lisp
+
+The manifest constructs a list containing the three @dfn{package specs}
+and passes it to the @code{specifications->manifest} procedure, which
+returns a manifest corresponding of the three designated packages.
+
+@quotation Note
+Manifests are @emph{symbolic}: they refer to packages by their
+specification (name and optionally version), which denote different
+packages over time---@code{coreutils} above might refer to version 8.32
+today and to 9.0 six months from now.
+
+To ``pin'' a package set to a specific revision, you will additionally
+need a @dfn{channel file} as produced by @command{guix describe -f
+channels} (@pxref{Invoking guix describe}).
+@end quotation
+
+That one was easy, but @command{guix manifest} can also handle more
+complex cases. For example, consider the manifest for the development
+environment of Guile, with the addition of Git:
+
+@example
+guix manifest -D guile git
+@end example
+
+@noindent
+This gives us:
+
+@example
+(concatenate-manifests
+ (list (specifications->manifest (list "git"))
+ (package->development-manifest
+ (specification->package "guile"))))
+@end example
+
+The @command{guix manifest} command also takes care package
+transformation options, producing a manifest that faithfully reapplies
+them (@pxref{Package Transformation Options}):
+
+@example
+guix manifest intel-mpi-benchmarks --with-input=openmpi=mpich
+@end example
+
+@noindent
+... yields:
+
+@lisp
+(use-modules (guix transformations))
+
+(define transform1
+ (options->transformation
+ '((with-input . "openmpi=mpich"))))
+
+(packages->manifest
+ (list (transform1
+ (specification->package "intel-mpi-benchmarks"))))
+@end lisp
+
+Convenient, no? You can take the output of @command{guix manifest}
+as-is, store it in a file, and enjoy it. But you can also view it as
+raw material that you can modify to refine the expression of the
+environment you want to deploy.
+
+The general syntax is:
+
+@example
+guix manifest [@var{options}] @var{spec}@dots{}
+@end example
+
+@noindent
+... where each @var{spec} denotes a package and (optionally) its output,
+such as @code{emacs}, @code{gcc-toolchain@@8}, or
+@code{git:send-email}. The available options are:
+
+@table @option
+@item --development
+@itemx -D
+Consider the environment needed to @emph{develop} the following package
+rather than the package itself.
+
+For instance, to obtain a manifest representing the environment to
+develop Elixir (and not Elixir itself), with the addition of Nano, run:
+
+@example
+guix manifest -D elixir nano
+@end example
+
+In this example, @option{-D} affects @code{elixir}, not @code{nano}.
+
+@item --manifest=@var{file}
+@itemx -m @var{file}
+Read the manifest in @var{file} and combine it with other options to
+produce the resulting manifest.
+@end table
+
+Additionally, @command{guix manifest} understands all the package
+transformation options (@pxref{Package Transformation Options}).
+
@node Invoking guix gc
@section Invoking @command{guix gc}
@@ -5847,6 +5982,9 @@ This is similar to the same-named option in @command{guix package}
(@pxref{profile-manifest, @option{--manifest}}) and uses the same
manifest files.
+@xref{Invoking guix manifest}, for information on how to ``convert''
+command-line options into a manifest.
+
@item --profile=@var{profile}
@itemx -p @var{profile}
Create an environment containing the packages installed in @var{profile}.
@@ -6234,6 +6372,9 @@ This is similar to the same-named option in @command{guix package}
(@pxref{profile-manifest, @option{--manifest}}) and uses the same
manifest files.
+@xref{Invoking guix manifest}, for information on how to ``convert''
+command-line options into a manifest.
+
@item --ad-hoc
Include all specified packages in the resulting environment, as if an
@i{ad hoc} package were defined with them as inputs. This option is
@@ -6692,6 +6833,9 @@ for use on machines that do not have Guix installed. Note that you can
specify @emph{either} a manifest file @emph{or} a list of packages,
but not both.
+@xref{Invoking guix manifest}, for information on how to ``convert''
+command-line options into a manifest.
+
@item --system=@var{system}
@itemx -s @var{system}
Attempt to build for @var{system}---e.g., @code{i686-linux}---instead of
diff --git a/guix/scripts/manifest.scm b/guix/scripts/manifest.scm
new file mode 100644
index 0000000000..fea5d130c3
--- /dev/null
+++ b/guix/scripts/manifest.scm
@@ -0,0 +1,174 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts manifest)
+ #:use-module (guix ui)
+ #:use-module ((guix diagnostics) #:select (location))
+ #:use-module (guix scripts environment)
+ #:use-module (guix transformations)
+ #:use-module (guix scripts)
+ #:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:autoload (gnu packages) (specifications->manifest
+ specification->package
+ package-unique-version-prefix)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:use-module (srfi srfi-37)
+ #:use-module (srfi srfi-71)
+ #:use-module (ice-9 match)
+ #:autoload (ice-9 pretty-print) (pretty-print)
+ #:export (guix-manifest))
+
+(define (show-help)
+ (display (G_ "Usage: guix manifest [OPTION] SPECS...
+Print a manifest corresponding to the given package SPECS.\n"))
+ (newline)
+
+ (display (G_ "
+ -D, --development include the development inputs of the next package"))
+ (display (G_ "
+ -m, --manifest=FILE create environment with the manifest from FILE"))
+
+ (newline)
+ (show-transformation-options-help)
+ (newline)
+ (display (G_ "
+ -h, --help display this help and exit"))
+ (display (G_ "
+ -V, --version display version information and exit"))
+ (newline)
+ (show-bug-report-information))
+
+(define %options
+ ;; Specification of the command-line options.
+ (cons* (option '(#\h "help") #f #f
+ (lambda args
+ (show-help)
+ (exit 0)))
+ (option '(#\V "version") #f #f
+ (lambda args
+ (show-version-and-exit "guix manifest")))
+
+ (option '(#\D "development") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'development? #t result)))
+ (option '(#\m "manifest") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'manifest arg result)))
+
+ %transformation-options))
+
+(define %default-options
+ ;; Default option alist.
+ '())
+
+(define (load-manifest file) ;TODO: factorize
+ "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 (manifest-entry-version-prefix entry)
+ "Search among all the versions of ENTRY's package that are available, and
+return the shortest unambiguous version prefix for this package."
+ (package-unique-version-prefix (manifest-entry-name entry)
+ (manifest-entry-version entry)))
+
+(define (manifest->code* manifest extra-manifests)
+ "Like 'manifest->code', but insert a 'concatenate-manifests' call that
+concatenates MANIFESTS, a list of expressions."
+ (if (null? (manifest-entries manifest))
+ (match extra-manifests
+ ((one) one)
+ (lst `(concatenate-manifests ,@extra-manifests)))
+ (match (manifest->code manifest
+ #:entry-package-version
+ manifest-entry-version-prefix)
+ (('begin exp ... last)
+ `(begin
+ ,@exp
+ ,(match extra-manifests
+ (() last)
+ (_ `(concatenate-manifests
+ (list ,last ,@extra-manifests)))))))))
+
+
+(define-command (guix-manifest . args)
+ (category development)
+ (synopsis "turn command-line arguments into a manifest")
+
+ (define (manifest-lift proc)
+ (lambda (entry)
+ (match (manifest-entry-item entry)
+ ((? package? p)
+ (manifest-entry
+ (inherit (package->manifest-entry (proc p)))
+ (output (manifest-entry-output entry))))
+ (_
+ entry))))
+
+ (define (handle-argument arg result)
+ (if (assoc-ref result 'development?)
+ (alist-cons 'development-inputs arg
+ (alist-delete 'development? result))
+ (alist-cons 'argument arg result)))
+
+ (with-error-handling
+ (let* ((opts (parse-command-line args %options (list %default-options)
+ #:build-options? #f
+ #:argument-handler handle-argument))
+ (transform (options->transformation opts))
+ (specs (reverse
+ (filter-map (match-lambda
+ (('argument . spec) spec)
+ (_ #f))
+ opts)))
+ (extras (reverse
+ (filter-map (match-lambda
+ (('development-inputs . spec)
+ ;; Make sure SPEC is valid.
+ (specification->package spec)
+
+ ;; XXX: This is an approximation:
+ ;; transformation options are not
+ ;; applied.
+ `(package->development-manifest
+ (specification->package ,spec)))
+ (_ #f))
+ opts)))
+ (manifest (concatenate-manifests
+ (cons (map-manifest-entries
+ (manifest-lift transform)
+ (specifications->manifest specs))
+ (filter-map (match-lambda
+ (('manifest . file)
+ (load-manifest file))
+ (_ #f))
+ opts)))))
+ (display (G_ "\
+;; What follows is a \"manifest\" equivalent to the command line you gave.
+;; You can store it in a file that you may then pass to any 'guix' command
+;; that accepts a '--manifest' (or '-m') option.\n"))
+ (match (manifest->code* manifest extras)
+ (('begin exp ...)
+ (for-each (lambda (exp)
+ (newline)
+ (pretty-print exp))
+ exp))
+ (exp
+ (pretty-print exp))))))
diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in
index d97ba8c209..5b8eadf884 100644
--- a/po/guix/POTFILES.in
+++ b/po/guix/POTFILES.in
@@ -99,6 +99,7 @@ guix/scripts/weather.scm
guix/scripts/describe.scm
guix/scripts/processes.scm
guix/scripts/deploy.scm
+guix/scripts/manifest.scm
guix/gexp.scm
guix/gnu-maintenance.scm
guix/scripts/container.scm
diff --git a/tests/guix-manifest.sh b/tests/guix-manifest.sh
new file mode 100644
index 0000000000..de82815ba0
--- /dev/null
+++ b/tests/guix-manifest.sh
@@ -0,0 +1,76 @@
+# GNU Guix --- Functional package management for GNU
+# Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
+#
+# This file is part of GNU Guix.
+#
+# GNU Guix is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GNU Guix is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+#
+# Test 'guix manifest'.
+#
+
+guix manifest --version
+
+tmpdir="t-guix-manifest-$$"
+trap 'rm -r "$tmpdir"' EXIT
+mkdir "$tmpdir"
+
+manifest="$tmpdir/manifest.scm"
+
+# Basics.
+guix manifest guile-bootstrap > "$manifest"
+test "$(guix build -m "$manifest")" = "$(guix build guile-bootstrap)"
+
+guix shell -m "$manifest" --bootstrap -- \
+ "$SHELL" -c 'guix package --export-manifest -p "$GUIX_ENVIRONMENT"' > \
+ "$manifest.second"
+for m in "$manifest" "$manifest.second"
+do
+ grep -v '^;' < "$m" > "$m.new" # filter out comments
+ mv "$m.new" "$m"
+done
+
+cat "$manifest"
+cat "$manifest.second"
+
+cmp "$manifest" "$manifest.second"
+
+# Package transformation option.
+guix manifest guile guix --with-latest=guile-json > "$manifest"
+grep 'options->transformation' "$manifest"
+grep '(with-latest . "guile-json")' "$manifest"
+
+# Development manifest.
+guix manifest -D guile git > "$manifest"
+grep 'package->development-manifest' "$manifest"
+grep '"guile"' "$manifest"
+guix build -m "$manifest" -d | \
+ grep "$(guix build -e '(@@ (gnu packages commencement) gcc-final)' -d)"
+guix build -m "$manifest" -d | \
+ grep "$(guix build git -d)"
+
+# Test various combinations to make sure generated code uses interfaces
+# correctly.
+for options in \
+ "coreutils grep sed" \
+ "gsl openblas gcc-toolchain --tune" \
+ "guile -m $manifest.previous" \
+ "git:send-email gdb guile:debug" \
+ "git -D coreutils"
+do
+ guix manifest $options > "$manifest"
+ cat "$manifest"
+ guix shell -m "$manifest" -n
+ mv "$manifest" "$manifest.previous"
+done
--
2.34.0