summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--doc/guix.texi57
-rw-r--r--guix/scripts/shell.scm109
-rw-r--r--tests/guix-shell-export-manifest.sh84
4 files changed, 248 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am
index 8bc66978697..fecce7c6f70 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -572,6 +572,7 @@ SH_TESTS = \
572 tests/guix-environment.sh \ 572 tests/guix-environment.sh \
573 tests/guix-environment-container.sh \ 573 tests/guix-environment-container.sh \
574 tests/guix-shell.sh \ 574 tests/guix-shell.sh \
575 tests/guix-shell-export-manifest.sh \
575 tests/guix-graph.sh \ 576 tests/guix-graph.sh \
576 tests/guix-describe.sh \ 577 tests/guix-describe.sh \
577 tests/guix-repl.sh \ 578 tests/guix-repl.sh \
diff --git a/doc/guix.texi b/doc/guix.texi
index b539a786631..8f7389a1ba4 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -5848,6 +5848,55 @@ This is similar to the same-named option in @command{guix package}
5848(@pxref{profile-manifest, @option{--manifest}}) and uses the same 5848(@pxref{profile-manifest, @option{--manifest}}) and uses the same
5849manifest files. 5849manifest files.
5850 5850
5851See @option{--export-manifest} below on how to obtain a first manifest.
5852
5853@cindex manifest, exporting
5854@anchor{shell-export-manifest}
5855@item --export-manifest
5856Write to standard output a manifest suitable for @option{--manifest}
5857corresponding to given command-line options.
5858
5859This is a way to ``convert'' command-line arguments into a manifest.
5860For example, imagine you are tired of typing long lines and would like
5861to get a manifest equivalent to this command line:
5862
5863@example
5864guix shell -D guile git emacs emacs-geiser emacs-geiser-guile
5865@end example
5866
5867Just add @option{--export-manifest} to the command line above:
5868
5869@example
5870guix shell --export-manifest \
5871 -D guile git emacs emacs-geiser emacs-geiser-guile
5872@end example
5873
5874@noindent
5875... and you get a manifest along these lines:
5876
5877@lisp
5878(concatenate-manifests
5879 (list (specifications->manifest
5880 (list "git"
5881 "emacs"
5882 "emacs-geiser"
5883 "emacs-geiser-guile"))
5884 (package->development-manifest
5885 (specification->package "guile"))))
5886@end lisp
5887
5888You can store it into a file, say @file{manifest.scm}, and from there
5889pass it to @command{guix shell} or indeed pretty much any @command{guix}
5890command:
5891
5892@example
5893guix shell -m manifest.scm
5894@end example
5895
5896Voilà, you've converted a long command line into a manifest! That
5897conversion process honors package transformation options (@pxref{Package
5898Transformation Options}) so it should be lossless.
5899
5851@item --profile=@var{profile} 5900@item --profile=@var{profile}
5852@itemx -p @var{profile} 5901@itemx -p @var{profile}
5853Create an environment containing the packages installed in @var{profile}. 5902Create an environment containing the packages installed in @var{profile}.
@@ -6235,6 +6284,10 @@ This is similar to the same-named option in @command{guix package}
6235(@pxref{profile-manifest, @option{--manifest}}) and uses the same 6284(@pxref{profile-manifest, @option{--manifest}}) and uses the same
6236manifest files. 6285manifest files.
6237 6286
6287@xref{shell-export-manifest, @command{guix shell --export-manifest}},
6288for information on how to ``convert'' command-line options into a
6289manifest.
6290
6238@item --ad-hoc 6291@item --ad-hoc
6239Include all specified packages in the resulting environment, as if an 6292Include all specified packages in the resulting environment, as if an
6240@i{ad hoc} package were defined with them as inputs. This option is 6293@i{ad hoc} package were defined with them as inputs. This option is
@@ -6693,6 +6746,10 @@ for use on machines that do not have Guix installed. Note that you can
6693specify @emph{either} a manifest file @emph{or} a list of packages, 6746specify @emph{either} a manifest file @emph{or} a list of packages,
6694but not both. 6747but not both.
6695 6748
6749@xref{shell-export-manifest, @command{guix shell --export-manifest}},
6750for information on how to ``convert'' command-line options into a
6751manifest.
6752
6696@item --system=@var{system} 6753@item --system=@var{system}
6697@itemx -s @var{system} 6754@itemx -s @var{system}
6698Attempt to build for @var{system}---e.g., @code{i686-linux}---instead of 6755Attempt to build for @var{system}---e.g., @code{i686-linux}---instead of
diff --git a/guix/scripts/shell.scm b/guix/scripts/shell.scm
index 1eab05d7378..d9af2517c27 100644
--- a/guix/scripts/shell.scm
+++ b/guix/scripts/shell.scm
@@ -21,7 +21,8 @@
21 #:use-module ((guix diagnostics) #:select (location)) 21 #:use-module ((guix diagnostics) #:select (location))
22 #:use-module (guix scripts environment) 22 #:use-module (guix scripts environment)
23 #:autoload (guix scripts build) (show-build-options-help) 23 #:autoload (guix scripts build) (show-build-options-help)
24 #:autoload (guix transformations) (transformation-option-key? 24 #:autoload (guix transformations) (options->transformation
25 transformation-option-key?
25 show-transformation-options-help) 26 show-transformation-options-help)
26 #:use-module (guix scripts) 27 #:use-module (guix scripts)
27 #:use-module (guix packages) 28 #:use-module (guix packages)
@@ -41,7 +42,12 @@
41 #:use-module ((guix build utils) #:select (mkdir-p)) 42 #:use-module ((guix build utils) #:select (mkdir-p))
42 #:use-module (guix cache) 43 #:use-module (guix cache)
43 #:use-module ((ice-9 ftw) #:select (scandir)) 44 #:use-module ((ice-9 ftw) #:select (scandir))
44 #:autoload (gnu packages) (cache-is-authoritative?) 45 #:autoload (ice-9 pretty-print) (pretty-print)
46 #:autoload (gnu packages) (cache-is-authoritative?
47 package-unique-version-prefix
48 specification->package
49 specification->package+output
50 specifications->manifest)
45 #:export (guix-shell)) 51 #:export (guix-shell))
46 52
47(define (show-help) 53(define (show-help)
@@ -55,10 +61,13 @@ interactive shell in that environment.\n"))
55 -D, --development include the development inputs of the next package")) 61 -D, --development include the development inputs of the next package"))
56 (display (G_ " 62 (display (G_ "
57 -f, --file=FILE add to the environment the package FILE evaluates to")) 63 -f, --file=FILE add to the environment the package FILE evaluates to"))
64
58 (display (G_ " 65 (display (G_ "
59 -q inhibit loading of 'guix.scm' and 'manifest.scm'")) 66 -q inhibit loading of 'guix.scm' and 'manifest.scm'"))
60 (display (G_ " 67 (display (G_ "
61 --rebuild-cache rebuild cached environment, if any")) 68 --rebuild-cache rebuild cached environment, if any"))
69 (display (G_ "
70 --export-manifest print a manifest for the given options"))
62 71
63 (show-environment-options-help) 72 (show-environment-options-help)
64 (newline) 73 (newline)
@@ -112,6 +121,10 @@ interactive shell in that environment.\n"))
112 ;; 'wrapped-option'. 121 ;; 'wrapped-option'.
113 (alist-delete 'ad-hoc? result))) 122 (alist-delete 'ad-hoc? result)))
114 123
124 (option '("export-manifest") #f #f
125 (lambda (opt name arg result)
126 (alist-cons 'export-manifest? #t result)))
127
115 ;; For consistency with 'guix package', support '-f' rather than 128 ;; For consistency with 'guix package', support '-f' rather than
116 ;; '-l' like 'guix environment' does. 129 ;; '-l' like 'guix environment' does.
117 (option '(#\f "file") #t #f 130 (option '(#\f "file") #t #f
@@ -382,6 +395,94 @@ return #f and #f."
382 395
383 396
384;;; 397;;;
398;;; Exporting a manifest.
399;;;
400
401(define (manifest-entry-version-prefix entry)
402 "Search among all the versions of ENTRY's package that are available, and
403return the shortest unambiguous version prefix for this package."
404 (package-unique-version-prefix (manifest-entry-name entry)
405 (manifest-entry-version entry)))
406
407(define (manifest->code* manifest extra-manifests)
408 "Like 'manifest->code', but insert a 'concatenate-manifests' call that
409concatenates MANIFESTS, a list of expressions."
410 (if (null? (manifest-entries manifest))
411 (match extra-manifests
412 ((one) one)
413 (lst `(concatenate-manifests ,@extra-manifests)))
414 (match (manifest->code manifest
415 #:entry-package-version
416 manifest-entry-version-prefix)
417 (('begin exp ... last)
418 `(begin
419 ,@exp
420 ,(match extra-manifests
421 (() last)
422 (_ `(concatenate-manifests
423 (list ,last ,@extra-manifests)))))))))
424
425(define (export-manifest opts port)
426 "Write to PORT a manifest corresponding to OPTS."
427 (define (manifest-lift proc)
428 (lambda (entry)
429 (match (manifest-entry-item entry)
430 ((? package? p)
431 (manifest-entry
432 (inherit (package->manifest-entry (proc p)))
433 (output (manifest-entry-output entry))))
434 (_
435 entry))))
436
437 (define (validated-spec spec)
438 ;; Return SPEC if it's a valid package spec.
439 (specification->package+output spec)
440 spec)
441
442 (let* ((transform (options->transformation opts))
443 (specs (reverse
444 (filter-map (match-lambda
445 (('package 'ad-hoc-package spec)
446 (validated-spec spec))
447 (_ #f))
448 opts)))
449 (extras (reverse
450 (filter-map (match-lambda
451 (('package 'package spec)
452 ;; Make sure SPEC is valid.
453 (specification->package spec)
454
455 ;; XXX: This is an approximation:
456 ;; transformation options are not applied.
457 `(package->development-manifest
458 (specification->package ,spec)))
459 (_ #f))
460 opts)))
461 (manifest (concatenate-manifests
462 (cons (map-manifest-entries
463 (manifest-lift transform)
464 (specifications->manifest specs))
465 (filter-map (match-lambda
466 (('manifest . file)
467 (load-manifest file))
468 (_ #f))
469 opts)))))
470 (display (G_ "\
471;; What follows is a \"manifest\" equivalent to the command line you gave.
472;; You can store it in a file that you may then pass to any 'guix' command
473;; that accepts a '--manifest' (or '-m') option.\n")
474 port)
475 (match (manifest->code* manifest extras)
476 (('begin exp ...)
477 (for-each (lambda (exp)
478 (newline port)
479 (pretty-print exp port))
480 exp))
481 (exp
482 (pretty-print exp port)))))
483
484
485;;;
385;;; One-time hints. 486;;; One-time hints.
386;;; 487;;;
387 488
@@ -445,4 +546,6 @@ to make sure your shell does not clobber environment variables."))) )
445 cache-entries 546 cache-entries
446 #:entry-expiration entry-expiration))) 547 #:entry-expiration entry-expiration)))
447 548
448 (guix-environment* opts)) 549 (if (assoc-ref opts 'export-manifest?)
550 (export-manifest opts (current-output-port))
551 (guix-environment* opts)))
diff --git a/tests/guix-shell-export-manifest.sh b/tests/guix-shell-export-manifest.sh
new file mode 100644
index 00000000000..cbb90f04bfc
--- /dev/null
+++ b/tests/guix-shell-export-manifest.sh
@@ -0,0 +1,84 @@
1# GNU Guix --- Functional package management for GNU
2# Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
3#
4# This file is part of GNU Guix.
5#
6# GNU Guix is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 3 of the License, or (at
9# your option) any later version.
10#
11# GNU Guix is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19#
20# Test 'guix shell --export-manifest'.
21#
22
23guix shell --version
24
25tmpdir="t-guix-manifest-$$"
26trap 'rm -r "$tmpdir"' EXIT
27mkdir "$tmpdir"
28
29manifest="$tmpdir/manifest.scm"
30
31# Basics.
32guix shell --export-manifest guile-bootstrap > "$manifest"
33test "$(guix build -m "$manifest")" = "$(guix build guile-bootstrap)"
34
35guix shell -m "$manifest" --bootstrap -- \
36 "$SHELL" -c 'guix package --export-manifest -p "$GUIX_ENVIRONMENT"' > \
37 "$manifest.second"
38for m in "$manifest" "$manifest.second"
39do
40 grep -v '^;' < "$m" > "$m.new" # filter out comments
41 mv "$m.new" "$m"
42done
43
44cat "$manifest"
45cat "$manifest.second"
46
47cmp "$manifest" "$manifest.second"
48
49# Combining manifests.
50guix shell --export-manifest -m "$manifest" gash gash-utils \
51 > "$manifest.second"
52guix build -m "$manifest.second" -d | \
53 grep "$(guix build guile-bootstrap -d)"
54guix build -m "$manifest.second" -d | \
55 grep "$(guix build gash -d)"
56
57# Package transformation option.
58guix shell --export-manifest guile guix --with-latest=guile-json > "$manifest"
59grep 'options->transformation' "$manifest"
60grep '(with-latest . "guile-json")' "$manifest"
61
62# Development manifest.
63guix shell --export-manifest -D guile git > "$manifest"
64grep 'package->development-manifest' "$manifest"
65grep '"guile"' "$manifest"
66guix build -m "$manifest" -d | \
67 grep "$(guix build -e '(@@ (gnu packages commencement) gcc-final)' -d)"
68guix build -m "$manifest" -d | \
69 grep "$(guix build git -d)"
70
71# Test various combinations to make sure generated code uses interfaces
72# correctly.
73for options in \
74 "coreutils grep sed" \
75 "gsl openblas gcc-toolchain --tune" \
76 "guile -m $manifest.previous" \
77 "git:send-email gdb guile:debug" \
78 "git -D coreutils"
79do
80 guix shell --export-manifest $options > "$manifest"
81 cat "$manifest"
82 guix shell -m "$manifest" -n
83 mv "$manifest" "$manifest.previous"
84done