diff options
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rw-r--r-- | doc/guix.texi | 57 | ||||
| -rw-r--r-- | guix/scripts/shell.scm | 109 | ||||
| -rw-r--r-- | tests/guix-shell-export-manifest.sh | 84 |
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 |
| 5849 | manifest files. | 5849 | manifest files. |
| 5850 | 5850 | ||
| 5851 | See @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 | ||
| 5856 | Write to standard output a manifest suitable for @option{--manifest} | ||
| 5857 | corresponding to given command-line options. | ||
| 5858 | |||
| 5859 | This is a way to ``convert'' command-line arguments into a manifest. | ||
| 5860 | For example, imagine you are tired of typing long lines and would like | ||
| 5861 | to get a manifest equivalent to this command line: | ||
| 5862 | |||
| 5863 | @example | ||
| 5864 | guix shell -D guile git emacs emacs-geiser emacs-geiser-guile | ||
| 5865 | @end example | ||
| 5866 | |||
| 5867 | Just add @option{--export-manifest} to the command line above: | ||
| 5868 | |||
| 5869 | @example | ||
| 5870 | guix 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 | |||
| 5888 | You can store it into a file, say @file{manifest.scm}, and from there | ||
| 5889 | pass it to @command{guix shell} or indeed pretty much any @command{guix} | ||
| 5890 | command: | ||
| 5891 | |||
| 5892 | @example | ||
| 5893 | guix shell -m manifest.scm | ||
| 5894 | @end example | ||
| 5895 | |||
| 5896 | Voilà, you've converted a long command line into a manifest! That | ||
| 5897 | conversion process honors package transformation options (@pxref{Package | ||
| 5898 | Transformation 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} |
| 5853 | Create an environment containing the packages installed in @var{profile}. | 5902 | Create 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 |
| 6236 | manifest files. | 6285 | manifest files. |
| 6237 | 6286 | ||
| 6287 | @xref{shell-export-manifest, @command{guix shell --export-manifest}}, | ||
| 6288 | for information on how to ``convert'' command-line options into a | ||
| 6289 | manifest. | ||
| 6290 | |||
| 6238 | @item --ad-hoc | 6291 | @item --ad-hoc |
| 6239 | Include all specified packages in the resulting environment, as if an | 6292 | Include 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 | |||
| 6693 | specify @emph{either} a manifest file @emph{or} a list of packages, | 6746 | specify @emph{either} a manifest file @emph{or} a list of packages, |
| 6694 | but not both. | 6747 | but not both. |
| 6695 | 6748 | ||
| 6749 | @xref{shell-export-manifest, @command{guix shell --export-manifest}}, | ||
| 6750 | for information on how to ``convert'' command-line options into a | ||
| 6751 | manifest. | ||
| 6752 | |||
| 6696 | @item --system=@var{system} | 6753 | @item --system=@var{system} |
| 6697 | @itemx -s @var{system} | 6754 | @itemx -s @var{system} |
| 6698 | Attempt to build for @var{system}---e.g., @code{i686-linux}---instead of | 6755 | Attempt 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 | ||
| 403 | return 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 | ||
| 409 | concatenates 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 | |||
| 23 | guix shell --version | ||
| 24 | |||
| 25 | tmpdir="t-guix-manifest-$$" | ||
| 26 | trap 'rm -r "$tmpdir"' EXIT | ||
| 27 | mkdir "$tmpdir" | ||
| 28 | |||
| 29 | manifest="$tmpdir/manifest.scm" | ||
| 30 | |||
| 31 | # Basics. | ||
| 32 | guix shell --export-manifest guile-bootstrap > "$manifest" | ||
| 33 | test "$(guix build -m "$manifest")" = "$(guix build guile-bootstrap)" | ||
| 34 | |||
| 35 | guix shell -m "$manifest" --bootstrap -- \ | ||
| 36 | "$SHELL" -c 'guix package --export-manifest -p "$GUIX_ENVIRONMENT"' > \ | ||
| 37 | "$manifest.second" | ||
| 38 | for m in "$manifest" "$manifest.second" | ||
| 39 | do | ||
| 40 | grep -v '^;' < "$m" > "$m.new" # filter out comments | ||
| 41 | mv "$m.new" "$m" | ||
| 42 | done | ||
| 43 | |||
| 44 | cat "$manifest" | ||
| 45 | cat "$manifest.second" | ||
| 46 | |||
| 47 | cmp "$manifest" "$manifest.second" | ||
| 48 | |||
| 49 | # Combining manifests. | ||
| 50 | guix shell --export-manifest -m "$manifest" gash gash-utils \ | ||
| 51 | > "$manifest.second" | ||
| 52 | guix build -m "$manifest.second" -d | \ | ||
| 53 | grep "$(guix build guile-bootstrap -d)" | ||
| 54 | guix build -m "$manifest.second" -d | \ | ||
| 55 | grep "$(guix build gash -d)" | ||
| 56 | |||
| 57 | # Package transformation option. | ||
| 58 | guix shell --export-manifest guile guix --with-latest=guile-json > "$manifest" | ||
| 59 | grep 'options->transformation' "$manifest" | ||
| 60 | grep '(with-latest . "guile-json")' "$manifest" | ||
| 61 | |||
| 62 | # Development manifest. | ||
| 63 | guix shell --export-manifest -D guile git > "$manifest" | ||
| 64 | grep 'package->development-manifest' "$manifest" | ||
| 65 | grep '"guile"' "$manifest" | ||
| 66 | guix build -m "$manifest" -d | \ | ||
| 67 | grep "$(guix build -e '(@@ (gnu packages commencement) gcc-final)' -d)" | ||
| 68 | guix build -m "$manifest" -d | \ | ||
| 69 | grep "$(guix build git -d)" | ||
| 70 | |||
| 71 | # Test various combinations to make sure generated code uses interfaces | ||
| 72 | # correctly. | ||
| 73 | for 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" | ||
| 79 | do | ||
| 80 | guix shell --export-manifest $options > "$manifest" | ||
| 81 | cat "$manifest" | ||
| 82 | guix shell -m "$manifest" -n | ||
| 83 | mv "$manifest" "$manifest.previous" | ||
| 84 | done | ||
