guix repl: Add script execution.

* guix/scripts/repl.scm: Add filename options for script execution.
* doc/guix.texi (Invoking guix repl): Document it.
* tests/guix-repl.sh: Test it.
* Makefile.am: (SH_TESTS): Add it.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Konrad Hinsen 2020-06-14 09:00:12 +02:00 committed by Ludovic Courtès
parent 1b917f99b5
commit c924e54139
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
4 changed files with 179 additions and 34 deletions

View File

@ -477,6 +477,7 @@ SH_TESTS = \
tests/guix-environment-container.sh \ tests/guix-environment-container.sh \
tests/guix-graph.sh \ tests/guix-graph.sh \
tests/guix-describe.sh \ tests/guix-describe.sh \
tests/guix-repl.sh \
tests/guix-lint.sh tests/guix-lint.sh
TESTS = $(SCM_TESTS) $(SH_TESTS) TESTS = $(SCM_TESTS) $(SH_TESTS)

View File

@ -239,7 +239,7 @@ Programming Interface
* Derivations:: Low-level interface to package derivations. * Derivations:: Low-level interface to package derivations.
* The Store Monad:: Purely functional interface to the store. * The Store Monad:: Purely functional interface to the store.
* G-Expressions:: Manipulating build expressions. * G-Expressions:: Manipulating build expressions.
* Invoking guix repl:: Fiddling with Guix interactively. * Invoking guix repl:: Programming Guix in Guile
Defining Packages Defining Packages
@ -5474,7 +5474,7 @@ package definitions.
* Derivations:: Low-level interface to package derivations. * Derivations:: Low-level interface to package derivations.
* The Store Monad:: Purely functional interface to the store. * The Store Monad:: Purely functional interface to the store.
* G-Expressions:: Manipulating build expressions. * G-Expressions:: Manipulating build expressions.
* Invoking guix repl:: Fiddling with Guix interactively. * Invoking guix repl:: Programming Guix in Guile
@end menu @end menu
@node Package Modules @node Package Modules
@ -8248,12 +8248,47 @@ has an associated gexp compiler, such as a @code{<package>}.
@node Invoking guix repl @node Invoking guix repl
@section Invoking @command{guix repl} @section Invoking @command{guix repl}
@cindex REPL, read-eval-print loop @cindex REPL, read-eval-print loop, script
The @command{guix repl} command spawns a Guile @dfn{read-eval-print loop} The @command{guix repl} command makes it easier to program Guix in Guile
(REPL) for interactive programming (@pxref{Using Guile Interactively,,, guile, by launching a Guile @dfn{read-eval-print loop} (REPL) for interactive
GNU Guile Reference Manual}). Compared to just launching the @command{guile} 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 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 @example
$ guix repl $ guix repl
@ -8302,7 +8337,7 @@ Add @var{directory} to the front of the package module search path
(@pxref{Package Modules}). (@pxref{Package Modules}).
This allows users to define their own packages and make them visible to This allows users to define their own packages and make them visible to
the command-line tool. the script or REPL.
@item -q @item -q
Inhibit loading of the @file{~/.guile} file. By default, that Inhibit loading of the @file{~/.guile} file. By default, that

View File

@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com> ;;; Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com>
;;; Copyright © 2020 Konrad Hinsen <konrad.hinsen@fastmail.net>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -22,6 +23,7 @@
#:use-module (guix scripts) #:use-module (guix scripts)
#:use-module (guix repl) #:use-module (guix repl)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-37) #:use-module (srfi srfi-37)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (rnrs bytevectors) #:use-module (rnrs bytevectors)
@ -32,7 +34,8 @@
;;; Commentary: ;;; 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 (define %default-options
`((type . guile))) `((type . guile)))
@ -63,8 +66,9 @@
(define (show-help) (define (show-help)
(display (G_ "Usage: guix repl [OPTIONS...] (display (G_ "Usage: guix repl [OPTIONS...] [-- FILE ARGS...]
Start a Guile REPL in the Guix execution environment.\n")) 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_ " (display (G_ "
-t, --type=TYPE start a REPL of the given TYPE")) -t, --type=TYPE start a REPL of the given TYPE"))
(display (G_ " (display (G_ "
@ -135,12 +139,13 @@ call THUNK."
(define (guix-repl . args) (define (guix-repl . args)
(define opts (define opts
;; Return the list of package names.
(args-fold* args %options (args-fold* args %options
(lambda (opt name arg result) (lambda (opt name arg result)
(leave (G_ "~A: unrecognized option~%") name)) (leave (G_ "~A: unrecognized option~%") name))
(lambda (arg result) (lambda (arg result)
(leave (G_ "~A: extraneous argument~%") arg)) (append `((script . ,arg)
(ignore-dot-guile . #t))
result))
%default-options)) %default-options))
(define user-config (define user-config
@ -148,28 +153,48 @@ call THUNK."
(lambda (home) (lambda (home)
(string-append home "/.guile")))) (string-append home "/.guile"))))
(with-error-handling (define (set-user-module)
(let ((type (assoc-ref opts 'type))) (set-current-module user-module)
(call-with-connection (assoc-ref opts 'listen) (when (and (not (assoc-ref opts 'ignore-dot-guile?))
(lambda () user-config
(case type (file-exists? user-config))
((guile) (load user-config)))
(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. (define script
((@@ (ice-9 top-repl) call-with-sigint) (reverse
(lambda () (filter-map (match-lambda
(start-repl)))))) (('script . script) script)
((machine) (_ #f))
(machine-repl)) opts)))
(else
(leave (G_ "~a: unknown type of REPL~%") type)))))))) (with-error-handling
(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: ;; Local Variables:
;; eval: (put 'call-with-connection 'scheme-indent-function 1) ;; eval: (put 'call-with-connection 'scheme-indent-function 1)

84
tests/guix-repl.sh Normal file
View File

@ -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)"