diff options
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rw-r--r-- | doc/guix.texi | 51 | ||||
| -rw-r--r-- | guix/scripts/repl.scm | 77 | ||||
| -rw-r--r-- | tests/guix-repl.sh | 84 |
4 files changed, 179 insertions, 34 deletions
diff --git a/Makefile.am b/Makefile.am index 9cf9318e8a9..8988cdfa127 100644 --- a/Makefile.am +++ b/Makefile.am | |||
| @@ -477,6 +477,7 @@ SH_TESTS = \ | |||
| 477 | tests/guix-environment-container.sh \ | 477 | tests/guix-environment-container.sh \ |
| 478 | tests/guix-graph.sh \ | 478 | tests/guix-graph.sh \ |
| 479 | tests/guix-describe.sh \ | 479 | tests/guix-describe.sh \ |
| 480 | tests/guix-repl.sh \ | ||
| 480 | tests/guix-lint.sh | 481 | tests/guix-lint.sh |
| 481 | 482 | ||
| 482 | TESTS = $(SCM_TESTS) $(SH_TESTS) | 483 | TESTS = $(SCM_TESTS) $(SH_TESTS) |
diff --git a/doc/guix.texi b/doc/guix.texi index 510347b2222..d55eaff02c5 100644 --- a/doc/guix.texi +++ b/doc/guix.texi | |||
| @@ -239,7 +239,7 @@ Programming Interface | |||
| 239 | * Derivations:: Low-level interface to package derivations. | 239 | * Derivations:: Low-level interface to package derivations. |
| 240 | * The Store Monad:: Purely functional interface to the store. | 240 | * The Store Monad:: Purely functional interface to the store. |
| 241 | * G-Expressions:: Manipulating build expressions. | 241 | * G-Expressions:: Manipulating build expressions. |
| 242 | * Invoking guix repl:: Fiddling with Guix interactively. | 242 | * Invoking guix repl:: Programming Guix in Guile |
| 243 | 243 | ||
| 244 | Defining Packages | 244 | Defining Packages |
| 245 | 245 | ||
| @@ -5474,7 +5474,7 @@ package definitions. | |||
| 5474 | * Derivations:: Low-level interface to package derivations. | 5474 | * Derivations:: Low-level interface to package derivations. |
| 5475 | * The Store Monad:: Purely functional interface to the store. | 5475 | * The Store Monad:: Purely functional interface to the store. |
| 5476 | * G-Expressions:: Manipulating build expressions. | 5476 | * G-Expressions:: Manipulating build expressions. |
| 5477 | * Invoking guix repl:: Fiddling with Guix interactively. | 5477 | * Invoking guix repl:: Programming Guix in Guile |
| 5478 | @end menu | 5478 | @end menu |
| 5479 | 5479 | ||
| 5480 | @node Package Modules | 5480 | @node Package Modules |
| @@ -8248,12 +8248,47 @@ has an associated gexp compiler, such as a @code{<package>}. | |||
| 8248 | @node Invoking guix repl | 8248 | @node Invoking guix repl |
| 8249 | @section Invoking @command{guix repl} | 8249 | @section Invoking @command{guix repl} |
| 8250 | 8250 | ||
| 8251 | @cindex REPL, read-eval-print loop | 8251 | @cindex REPL, read-eval-print loop, script |
| 8252 | The @command{guix repl} command spawns a Guile @dfn{read-eval-print loop} | 8252 | The @command{guix repl} command makes it easier to program Guix in Guile |
| 8253 | (REPL) for interactive programming (@pxref{Using Guile Interactively,,, guile, | 8253 | by launching a Guile @dfn{read-eval-print loop} (REPL) for interactive |
| 8254 | GNU Guile Reference Manual}). Compared to just launching the @command{guile} | 8254 | programming (@pxref{Using Guile Interactively,,, guile, |
| 8255 | GNU Guile Reference Manual}), or by running Guile scripts | ||
| 8256 | (@pxref{Running Guile Scripts,,, guile, | ||
| 8257 | GNU Guile Reference Manual}). | ||
| 8258 | Compared to just launching the @command{guile} | ||
| 8255 | command, @command{guix repl} guarantees that all the Guix modules and all its | 8259 | command, @command{guix repl} guarantees that all the Guix modules and all its |
| 8256 | dependencies are available in the search path. You can use it this way: | 8260 | dependencies are available in the search path. |
| 8261 | |||
| 8262 | The general syntax is: | ||
| 8263 | |||
| 8264 | @example | ||
| 8265 | guix repl @var{options} [@var{file} @var{args}] | ||
| 8266 | @end example | ||
| 8267 | |||
| 8268 | When a @var{file} argument is provided, @var{file} is | ||
| 8269 | executed as a Guile scripts: | ||
| 8270 | |||
| 8271 | @example | ||
| 8272 | guix repl my-script.scm | ||
| 8273 | @end example | ||
| 8274 | |||
| 8275 | To pass arguments to the script, use @code{--} to prevent them from | ||
| 8276 | being interpreted as arguments to @command{guix repl} itself: | ||
| 8277 | |||
| 8278 | @example | ||
| 8279 | guix repl -- my-script.scm --input=foo.txt | ||
| 8280 | @end example | ||
| 8281 | |||
| 8282 | To make a script executable directly from the shell, using the guix | ||
| 8283 | executable that is on the user's search path, add the following two | ||
| 8284 | lines at the top of the script: | ||
| 8285 | |||
| 8286 | @example | ||
| 8287 | @code{#!/usr/bin/env -S guix repl --} | ||
| 8288 | @code{!#} | ||
| 8289 | @end example | ||
| 8290 | |||
| 8291 | Without a file name argument, a Guile REPL is started: | ||
| 8257 | 8292 | ||
| 8258 | @example | 8293 | @example |
| 8259 | $ guix repl | 8294 | $ guix repl |
| @@ -8302,7 +8337,7 @@ Add @var{directory} to the front of the package module search path | |||
| 8302 | (@pxref{Package Modules}). | 8337 | (@pxref{Package Modules}). |
| 8303 | 8338 | ||
| 8304 | This allows users to define their own packages and make them visible to | 8339 | This allows users to define their own packages and make them visible to |
| 8305 | the command-line tool. | 8340 | the script or REPL. |
| 8306 | 8341 | ||
| 8307 | @item -q | 8342 | @item -q |
| 8308 | Inhibit loading of the @file{~/.guile} file. By default, that | 8343 | Inhibit loading of the @file{~/.guile} file. By default, that |
diff --git a/guix/scripts/repl.scm b/guix/scripts/repl.scm index ff1f2088940..e2679f4301d 100644 --- a/guix/scripts/repl.scm +++ b/guix/scripts/repl.scm | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com> | 3 | ;;; Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com> |
| 4 | ;;; Copyright © 2020 Konrad Hinsen <konrad.hinsen@fastmail.net> | ||
| 4 | ;;; | 5 | ;;; |
| 5 | ;;; This file is part of GNU Guix. | 6 | ;;; This file is part of GNU Guix. |
| 6 | ;;; | 7 | ;;; |
| @@ -22,6 +23,7 @@ | |||
| 22 | #:use-module (guix scripts) | 23 | #:use-module (guix scripts) |
| 23 | #:use-module (guix repl) | 24 | #:use-module (guix repl) |
| 24 | #:use-module (srfi srfi-1) | 25 | #:use-module (srfi srfi-1) |
| 26 | #:use-module (srfi srfi-26) | ||
| 25 | #:use-module (srfi srfi-37) | 27 | #:use-module (srfi srfi-37) |
| 26 | #:use-module (ice-9 match) | 28 | #:use-module (ice-9 match) |
| 27 | #:use-module (rnrs bytevectors) | 29 | #:use-module (rnrs bytevectors) |
| @@ -32,7 +34,8 @@ | |||
| 32 | 34 | ||
| 33 | ;;; Commentary: | 35 | ;;; Commentary: |
| 34 | ;;; | 36 | ;;; |
| 35 | ;;; This command provides a Guile REPL | 37 | ;;; This command provides a Guile script runner and REPL in an environment |
| 38 | ;;; that contains all the modules comprising Guix. | ||
| 36 | 39 | ||
| 37 | (define %default-options | 40 | (define %default-options |
| 38 | `((type . guile))) | 41 | `((type . guile))) |
| @@ -63,8 +66,9 @@ | |||
| 63 | 66 | ||
| 64 | 67 | ||
| 65 | (define (show-help) | 68 | (define (show-help) |
| 66 | (display (G_ "Usage: guix repl [OPTIONS...] | 69 | (display (G_ "Usage: guix repl [OPTIONS...] [-- FILE ARGS...] |
| 67 | Start a Guile REPL in the Guix execution environment.\n")) | 70 | In the Guix execution environment, run FILE as a Guile script with |
| 71 | command-line arguments ARGS. If no FILE is given, start a Guile REPL.\n")) | ||
| 68 | (display (G_ " | 72 | (display (G_ " |
| 69 | -t, --type=TYPE start a REPL of the given TYPE")) | 73 | -t, --type=TYPE start a REPL of the given TYPE")) |
| 70 | (display (G_ " | 74 | (display (G_ " |
| @@ -135,12 +139,13 @@ call THUNK." | |||
| 135 | 139 | ||
| 136 | (define (guix-repl . args) | 140 | (define (guix-repl . args) |
| 137 | (define opts | 141 | (define opts |
| 138 | ;; Return the list of package names. | ||
| 139 | (args-fold* args %options | 142 | (args-fold* args %options |
| 140 | (lambda (opt name arg result) | 143 | (lambda (opt name arg result) |
| 141 | (leave (G_ "~A: unrecognized option~%") name)) | 144 | (leave (G_ "~A: unrecognized option~%") name)) |
| 142 | (lambda (arg result) | 145 | (lambda (arg result) |
| 143 | (leave (G_ "~A: extraneous argument~%") arg)) | 146 | (append `((script . ,arg) |
| 147 | (ignore-dot-guile . #t)) | ||
| 148 | result)) | ||
| 144 | %default-options)) | 149 | %default-options)) |
| 145 | 150 | ||
| 146 | (define user-config | 151 | (define user-config |
| @@ -148,28 +153,48 @@ call THUNK." | |||
| 148 | (lambda (home) | 153 | (lambda (home) |
| 149 | (string-append home "/.guile")))) | 154 | (string-append home "/.guile")))) |
| 150 | 155 | ||
| 156 | (define (set-user-module) | ||
| 157 | (set-current-module user-module) | ||
| 158 | (when (and (not (assoc-ref opts 'ignore-dot-guile?)) | ||
| 159 | user-config | ||
| 160 | (file-exists? user-config)) | ||
| 161 | (load user-config))) | ||
| 162 | |||
| 163 | (define script | ||
| 164 | (reverse | ||
| 165 | (filter-map (match-lambda | ||
| 166 | (('script . script) script) | ||
| 167 | (_ #f)) | ||
| 168 | opts))) | ||
| 169 | |||
| 151 | (with-error-handling | 170 | (with-error-handling |
| 152 | (let ((type (assoc-ref opts 'type))) | 171 | |
| 153 | (call-with-connection (assoc-ref opts 'listen) | 172 | (unless (null? script) |
| 154 | (lambda () | 173 | ;; Run script |
| 155 | (case type | 174 | (save-module-excursion |
| 156 | ((guile) | 175 | (lambda () |
| 157 | (save-module-excursion | 176 | (set-program-arguments script) |
| 158 | (lambda () | 177 | (set-user-module) |
| 159 | (set-current-module user-module) | 178 | (load-in-vicinity "." (car script))))) |
| 160 | (when (and (not (assoc-ref opts 'ignore-dot-guile?)) | 179 | |
| 161 | user-config | 180 | (when (null? script) |
| 162 | (file-exists? user-config)) | 181 | ;; Start REPL |
| 163 | (load user-config)) | 182 | (let ((type (assoc-ref opts 'type))) |
| 164 | 183 | (call-with-connection (assoc-ref opts 'listen) | |
| 165 | ;; Do not exit repl on SIGINT. | 184 | (lambda () |
| 166 | ((@@ (ice-9 top-repl) call-with-sigint) | 185 | (case type |
| 167 | (lambda () | 186 | ((guile) |
| 168 | (start-repl)))))) | 187 | (save-module-excursion |
| 169 | ((machine) | 188 | (lambda () |
| 170 | (machine-repl)) | 189 | (set-user-module) |
| 171 | (else | 190 | ;; Do not exit repl on SIGINT. |
| 172 | (leave (G_ "~a: unknown type of REPL~%") type)))))))) | 191 | ((@@ (ice-9 top-repl) call-with-sigint) |
| 192 | (lambda () | ||
| 193 | (start-repl)))))) | ||
| 194 | ((machine) | ||
| 195 | (machine-repl)) | ||
| 196 | (else | ||
| 197 | (leave (G_ "~a: unknown type of REPL~%") type))))))))) | ||
| 173 | 198 | ||
| 174 | ;; Local Variables: | 199 | ;; Local Variables: |
| 175 | ;; eval: (put 'call-with-connection 'scheme-indent-function 1) | 200 | ;; eval: (put 'call-with-connection 'scheme-indent-function 1) |
diff --git a/tests/guix-repl.sh b/tests/guix-repl.sh new file mode 100644 index 00000000000..e1c2b8241fe --- /dev/null +++ b/tests/guix-repl.sh | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | # GNU Guix --- Functional package management for GNU | ||
| 2 | # Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com> | ||
| 3 | # Copyright © 2020 Konrad Hinsen <konrad.hinsen@fastmail.net> | ||
| 4 | # | ||
| 5 | # This file is part of GNU Guix. | ||
| 6 | # | ||
| 7 | # GNU Guix is free software; you can redistribute it and/or modify it | ||
| 8 | # under the terms of the GNU General Public License as published by | ||
| 9 | # the Free Software Foundation; either version 3 of the License, or (at | ||
| 10 | # your option) any later version. | ||
| 11 | # | ||
| 12 | # GNU Guix is distributed in the hope that it will be useful, but | ||
| 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | # GNU General Public License for more details. | ||
| 16 | # | ||
| 17 | # You should have received a copy of the GNU General Public License | ||
| 18 | # along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | |||
| 20 | # | ||
| 21 | # Test the `guix repl' command-line utility. | ||
| 22 | # | ||
| 23 | |||
| 24 | guix repl --version | ||
| 25 | |||
| 26 | test_directory="`mktemp -d`" | ||
| 27 | export test_directory | ||
| 28 | trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT | ||
| 29 | |||
| 30 | tmpfile="$test_directory/foo.scm" | ||
| 31 | rm -f "$tmpfile" | ||
| 32 | trap 'rm -f "$tmpfile"' EXIT | ||
| 33 | |||
| 34 | module_dir="t-guix-repl-$$" | ||
| 35 | mkdir "$module_dir" | ||
| 36 | trap 'rm -rf "$module_dir"' EXIT | ||
| 37 | |||
| 38 | |||
| 39 | cat > "$tmpfile"<<EOF | ||
| 40 | (use-modules (guix packages) | ||
| 41 | (gnu packages base)) | ||
| 42 | |||
| 43 | (format #t "~a\n" (package-name coreutils)) | ||
| 44 | EOF | ||
| 45 | |||
| 46 | test "`guix repl "$tmpfile"`" = "coreutils" | ||
| 47 | |||
| 48 | |||
| 49 | cat > "$module_dir/foo.scm"<<EOF | ||
| 50 | (define-module (foo) | ||
| 51 | #:use-module (guix packages) | ||
| 52 | #:use-module (gnu packages base)) | ||
| 53 | |||
| 54 | (define-public dummy | ||
| 55 | (package (inherit hello) | ||
| 56 | (name "dummy") | ||
| 57 | (version "42") | ||
| 58 | (synopsis "dummy package") | ||
| 59 | (description "dummy package. Only used for testing purposes."))) | ||
| 60 | EOF | ||
| 61 | |||
| 62 | cat > "$tmpfile"<<EOF | ||
| 63 | (use-modules (guix packages) | ||
| 64 | (foo)) | ||
| 65 | |||
| 66 | (format #t "~a\n" (package-version dummy)) | ||
| 67 | EOF | ||
| 68 | |||
| 69 | test "`guix repl "$tmpfile" -L "$module_dir"`" = "42" | ||
| 70 | |||
| 71 | cat > "$tmpfile"<<EOF | ||
| 72 | (format #t "~a\n" (cdr (command-line))) | ||
| 73 | EOF | ||
| 74 | |||
| 75 | test "`guix repl -- "$tmpfile" -a b --input=foo.txt`" = "(-a b --input=foo.txt)" | ||
| 76 | |||
| 77 | cat > "$tmpfile"<<EOF | ||
| 78 | #!$(type -P env) -S guix repl -- | ||
| 79 | !# | ||
| 80 | (format #t "~a\n" (cdr (command-line))) | ||
| 81 | EOF | ||
| 82 | chmod 755 $tmpfile | ||
| 83 | |||
| 84 | test "`"$tmpfile" -a b --input=foo.txt`" = "(-a b --input=foo.txt)" | ||
