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 9cf9318e8a..8988cdfa12 100644 --- a/Makefile.am +++ b/Makefile.am @@ -477,6 +477,7 @@ SH_TESTS = \ tests/guix-environment-container.sh \ tests/guix-graph.sh \ tests/guix-describe.sh \ + tests/guix-repl.sh \ tests/guix-lint.sh TESTS = $(SCM_TESTS) $(SH_TESTS) diff --git a/doc/guix.texi b/doc/guix.texi index 510347b222..d55eaff02c 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -239,7 +239,7 @@ Programming Interface * Derivations:: Low-level interface to package derivations. * The Store Monad:: Purely functional interface to the store. * G-Expressions:: Manipulating build expressions. -* Invoking guix repl:: Fiddling with Guix interactively. +* Invoking guix repl:: Programming Guix in Guile Defining Packages @@ -5474,7 +5474,7 @@ package definitions. * Derivations:: Low-level interface to package derivations. * The Store Monad:: Purely functional interface to the store. * G-Expressions:: Manipulating build expressions. -* Invoking guix repl:: Fiddling with Guix interactively. +* Invoking guix repl:: Programming Guix in Guile @end menu @node Package Modules @@ -8248,12 +8248,47 @@ has an associated gexp compiler, such as a @code{<package>}. @node Invoking guix repl @section Invoking @command{guix repl} -@cindex REPL, read-eval-print loop -The @command{guix repl} command spawns a Guile @dfn{read-eval-print loop} -(REPL) for interactive programming (@pxref{Using Guile Interactively,,, guile, -GNU Guile Reference Manual}). Compared to just launching the @command{guile} +@cindex REPL, read-eval-print loop, script +The @command{guix repl} command makes it easier to program Guix in Guile +by launching a Guile @dfn{read-eval-print loop} (REPL) for interactive +programming (@pxref{Using Guile Interactively,,, guile, +GNU Guile Reference Manual}), or by running Guile scripts +(@pxref{Running Guile Scripts,,, guile, +GNU Guile Reference Manual}). +Compared to just launching the @command{guile} command, @command{guix repl} guarantees that all the Guix modules and all its -dependencies are available in the search path. You can use it this way: +dependencies are available in the search path. + +The general syntax is: + +@example +guix repl @var{options} [@var{file} @var{args}] +@end example + +When a @var{file} argument is provided, @var{file} is +executed as a Guile scripts: + +@example +guix repl my-script.scm +@end example + +To pass arguments to the script, use @code{--} to prevent them from +being interpreted as arguments to @command{guix repl} itself: + +@example +guix repl -- my-script.scm --input=foo.txt +@end example + +To make a script executable directly from the shell, using the guix +executable that is on the user's search path, add the following two +lines at the top of the script: + +@example +@code{#!/usr/bin/env -S guix repl --} +@code{!#} +@end example + +Without a file name argument, a Guile REPL is started: @example $ guix repl @@ -8302,7 +8337,7 @@ Add @var{directory} to the front of the package module search path (@pxref{Package Modules}). This allows users to define their own packages and make them visible to -the command-line tool. +the script or REPL. @item -q Inhibit loading of the @file{~/.guile} file. By default, that diff --git a/guix/scripts/repl.scm b/guix/scripts/repl.scm index ff1f208894..e2679f4301 100644 --- a/guix/scripts/repl.scm +++ b/guix/scripts/repl.scm @@ -1,6 +1,7 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com> +;;; Copyright © 2020 Konrad Hinsen <konrad.hinsen@fastmail.net> ;;; ;;; This file is part of GNU Guix. ;;; @@ -22,6 +23,7 @@ #:use-module (guix scripts) #:use-module (guix repl) #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) #:use-module (srfi srfi-37) #:use-module (ice-9 match) #:use-module (rnrs bytevectors) @@ -32,7 +34,8 @@ ;;; Commentary: ;;; -;;; This command provides a Guile REPL +;;; This command provides a Guile script runner and REPL in an environment +;;; that contains all the modules comprising Guix. (define %default-options `((type . guile))) @@ -63,8 +66,9 @@ (define (show-help) - (display (G_ "Usage: guix repl [OPTIONS...] -Start a Guile REPL in the Guix execution environment.\n")) + (display (G_ "Usage: guix repl [OPTIONS...] [-- FILE ARGS...] +In the Guix execution environment, run FILE as a Guile script with +command-line arguments ARGS. If no FILE is given, start a Guile REPL.\n")) (display (G_ " -t, --type=TYPE start a REPL of the given TYPE")) (display (G_ " @@ -135,12 +139,13 @@ call THUNK." (define (guix-repl . args) (define opts - ;; Return the list of package names. (args-fold* args %options (lambda (opt name arg result) (leave (G_ "~A: unrecognized option~%") name)) (lambda (arg result) - (leave (G_ "~A: extraneous argument~%") arg)) + (append `((script . ,arg) + (ignore-dot-guile . #t)) + result)) %default-options)) (define user-config @@ -148,28 +153,48 @@ call THUNK." (lambda (home) (string-append home "/.guile")))) + (define (set-user-module) + (set-current-module user-module) + (when (and (not (assoc-ref opts 'ignore-dot-guile?)) + user-config + (file-exists? user-config)) + (load user-config))) + + (define script + (reverse + (filter-map (match-lambda + (('script . script) script) + (_ #f)) + opts))) + (with-error-handling - (let ((type (assoc-ref opts 'type))) - (call-with-connection (assoc-ref opts 'listen) - (lambda () - (case type - ((guile) - (save-module-excursion - (lambda () - (set-current-module user-module) - (when (and (not (assoc-ref opts 'ignore-dot-guile?)) - user-config - (file-exists? user-config)) - (load user-config)) - - ;; Do not exit repl on SIGINT. - ((@@ (ice-9 top-repl) call-with-sigint) - (lambda () - (start-repl)))))) - ((machine) - (machine-repl)) - (else - (leave (G_ "~a: unknown type of REPL~%") type)))))))) + + (unless (null? script) + ;; Run script + (save-module-excursion + (lambda () + (set-program-arguments script) + (set-user-module) + (load-in-vicinity "." (car script))))) + + (when (null? script) + ;; Start REPL + (let ((type (assoc-ref opts 'type))) + (call-with-connection (assoc-ref opts 'listen) + (lambda () + (case type + ((guile) + (save-module-excursion + (lambda () + (set-user-module) + ;; Do not exit repl on SIGINT. + ((@@ (ice-9 top-repl) call-with-sigint) + (lambda () + (start-repl)))))) + ((machine) + (machine-repl)) + (else + (leave (G_ "~a: unknown type of REPL~%") type))))))))) ;; Local Variables: ;; 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 0000000000..e1c2b8241f --- /dev/null +++ b/tests/guix-repl.sh @@ -0,0 +1,84 @@ +# GNU Guix --- Functional package management for GNU +# Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com> +# Copyright © 2020 Konrad Hinsen <konrad.hinsen@fastmail.net> +# +# 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 the `guix repl' command-line utility. +# + +guix repl --version + +test_directory="`mktemp -d`" +export test_directory +trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT + +tmpfile="$test_directory/foo.scm" +rm -f "$tmpfile" +trap 'rm -f "$tmpfile"' EXIT + +module_dir="t-guix-repl-$$" +mkdir "$module_dir" +trap 'rm -rf "$module_dir"' EXIT + + +cat > "$tmpfile"<<EOF +(use-modules (guix packages) + (gnu packages base)) + +(format #t "~a\n" (package-name coreutils)) +EOF + +test "`guix repl "$tmpfile"`" = "coreutils" + + +cat > "$module_dir/foo.scm"<<EOF +(define-module (foo) + #:use-module (guix packages) + #:use-module (gnu packages base)) + +(define-public dummy + (package (inherit hello) + (name "dummy") + (version "42") + (synopsis "dummy package") + (description "dummy package. Only used for testing purposes."))) +EOF + +cat > "$tmpfile"<<EOF +(use-modules (guix packages) + (foo)) + +(format #t "~a\n" (package-version dummy)) +EOF + +test "`guix repl "$tmpfile" -L "$module_dir"`" = "42" + +cat > "$tmpfile"<<EOF +(format #t "~a\n" (cdr (command-line))) +EOF + +test "`guix repl -- "$tmpfile" -a b --input=foo.txt`" = "(-a b --input=foo.txt)" + +cat > "$tmpfile"<<EOF +#!$(type -P env) -S guix repl -- +!# +(format #t "~a\n" (cdr (command-line))) +EOF +chmod 755 $tmpfile + +test "`"$tmpfile" -a b --input=foo.txt`" = "(-a b --input=foo.txt)" |