[PATCH] guix: add script execution to "guix repl"

  • Done
  • quality assurance status badge
Details
3 participants
  • Konrad Hinsen
  • Ludovic Courtès
  • zimoun
Owner
unassigned
Submitted by
Konrad Hinsen
Severity
normal
K
K
Konrad Hinsen wrote on 14 May 2020 11:18
(address . guix-patches@gnu.org)
m1k11eg9ho.fsf@khs-macbook.home
* guix/scripts/repl.scm: Add filename options for script execution.
* doc/guix.texi: Document script execution by "guix repl"
---
doc/guix.texi | 38 +++++++++++++++-----
guix/scripts/repl.scm | 80 ++++++++++++++++++++++++++-----------------
2 files changed, 78 insertions(+), 40 deletions(-)

Toggle diff (199 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index d6fbd85fde..68f1a8bba3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -238,7 +238,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
@@ -5415,7 +5415,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
@@ -8134,12 +8134,31 @@ 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{files}
+@end example
+
+When at least one @var{files} argument is provided, @var{files} are
+executed as Guile scripts in the given order:
+
+@example
+$ guix repl my-script.scm
+@end example
+
+Otherwise a Guile REPL is started:
@example
$ guix repl
@@ -8188,11 +8207,12 @@ 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 scripts or REPL.
@item -q
Inhibit loading of the @file{~/.guile} file. By default, that
-configuration file is loaded when spawning a @code{guile} REPL.
+configuration file is loaded when executing scripts or spawning
+a @code{guile} REPL.
@end table
@c *********************************************************************
diff --git a/guix/scripts/repl.scm b/guix/scripts/repl.scm
index ff1f208894..f4cb744bbd 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.
;;;
@@ -32,10 +33,12 @@
;;; 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)))
+ `((scripts . ())
+ (type . guile)))
(define %options
(list (option '(#\h "help") #f #f
@@ -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...] [FILES...]
+In the Guix execution environment, run FILES as Guile scripts,
+or start a Guile REPL if no FILES are given,\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))
+ (alist-cons 'scripts
+ (cons arg (cdr (assq 'scripts result)))
+ result))
%default-options))
(define user-config
@@ -148,29 +153,42 @@ 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)))
+
(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))))))))
-
-;; Local Variables:
-;; eval: (put 'call-with-connection 'scheme-indent-function 1)
-;; End:
+ (let ((scripts (reverse (assoc-ref opts 'scripts))))
+
+ (for-each (lambda (script)
+ (save-module-excursion
+ (lambda ()
+ (set-user-module)
+ (load script))))
+ scripts)
+
+ (when (null? scripts)
+ (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)
+ ;; End:
+ )
--
2.26.2
Z
Z
zimoun wrote on 14 May 2020 18:25
[PATCH v2] guix repl: Add script execution.
(address . 41253@debbugs.gnu.org)(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)
20200514162537.29071-1-zimon.toutoune@gmail.com
From: Konrad Hinsen <konrad.hinsen@fastmail.net>

* 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 | 1 +
doc/guix.texi | 38 +++++++++++++++-----
guix/scripts/repl.scm | 80 ++++++++++++++++++++++++++-----------------
tests/guix-repl.sh | 70 +++++++++++++++++++++++++++++++++++++
4 files changed, 149 insertions(+), 40 deletions(-)
create mode 100644 tests/guix-repl.sh

Toggle diff (287 lines)
diff --git a/Makefile.am b/Makefile.am
index 752445afcb..eb4e360c6d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -471,6 +471,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 d6fbd85fde..68f1a8bba3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -238,7 +238,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
@@ -5415,7 +5415,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
@@ -8134,12 +8134,31 @@ 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{files}
+@end example
+
+When at least one @var{files} argument is provided, @var{files} are
+executed as Guile scripts in the given order:
+
+@example
+$ guix repl my-script.scm
+@end example
+
+Otherwise a Guile REPL is started:
@example
$ guix repl
@@ -8188,11 +8207,12 @@ 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 scripts or REPL.
@item -q
Inhibit loading of the @file{~/.guile} file. By default, that
-configuration file is loaded when spawning a @code{guile} REPL.
+configuration file is loaded when executing scripts or spawning
+a @code{guile} REPL.
@end table
@c *********************************************************************
diff --git a/guix/scripts/repl.scm b/guix/scripts/repl.scm
index ff1f208894..f4cb744bbd 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.
;;;
@@ -32,10 +33,12 @@
;;; 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)))
+ `((scripts . ())
+ (type . guile)))
(define %options
(list (option '(#\h "help") #f #f
@@ -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...] [FILES...]
+In the Guix execution environment, run FILES as Guile scripts,
+or start a Guile REPL if no FILES are given,\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))
+ (alist-cons 'scripts
+ (cons arg (cdr (assq 'scripts result)))
+ result))
%default-options))
(define user-config
@@ -148,29 +153,42 @@ 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)))
+
(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))))))))
-
-;; Local Variables:
-;; eval: (put 'call-with-connection 'scheme-indent-function 1)
-;; End:
+ (let ((scripts (reverse (assoc-ref opts 'scripts))))
+
+ (for-each (lambda (script)
+ (save-module-excursion
+ (lambda ()
+ (set-user-module)
+ (load script))))
+ scripts)
+
+ (when (null? scripts)
+ (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)
+ ;; End:
+ )
diff --git a/tests/guix-repl.sh b/tests/guix-repl.sh
new file mode 100644
index 0000000000..b93d48248d
--- /dev/null
+++ b/tests/guix-repl.sh
@@ -0,0 +1,70 @@
+# GNU Guix --- Functional package management for GNU
+# Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com>
+#
+# 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
+
+# Inhibit loading of ~/.guile to avoid conflict
+test "`guix repl -q "$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
+
+# Inhibit loading of ~/.guile to avoid conflict
+test "`guix repl -q "$tmpfile" -L "$module_dir"`" = "42"
--
2.26.1
Z
Z
zimoun wrote on 14 May 2020 18:31
Re: [bug#41253] [PATCH] guix: add script execution to "guix repl"
(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)(address . 41253@debbugs.gnu.org)
CAJ3okZ3ffUGaw5R6jhfYpkQE1j8g_yYwGPLxfkBfkoioCLNZ+g@mail.gmail.com
Dear Konrad,

Nice!
Thank you!

On Thu, 14 May 2020 at 11:19, Konrad Hinsen <konrad.hinsen@fastmail.net> wrote:
Toggle quote (4 lines)
>
> * guix/scripts/repl.scm: Add filename options for script execution.
> * doc/guix.texi: Document script execution by "guix repl"

I have sent a v2 adding tests and rewording the commit message -- to
be what I understand the compliance; sorry if I did a mistake.
Well, I am not sure by the tests.

Last, I am not convinced by the @cindex term and I think "@cindex
repl, guix repl" or "@cindex REPL, guix repl" is better. But that's
only matter of taste. :-)


All the best,
simon
K
K
Konrad Hinsen wrote on 29 May 2020 12:11
[PATCH v3] guix repl: Add script execution.
(address . 41253@debbugs.gnu.org)(name . zimoun)(address . zimon.toutoune@gmail.com)
m1a71ryrsh.fsf@fastmail.net
* 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 | 1 +
doc/guix.texi | 45 ++++++++++++++++++-----
guix/scripts/repl.scm | 84 ++++++++++++++++++++++++++-----------------
tests/guix-repl.sh | 78 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 167 insertions(+), 41 deletions(-)
create mode 100644 tests/guix-repl.sh

Toggle diff (305 lines)
diff --git a/Makefile.am b/Makefile.am
index 5b64386b53..859b6a4bc2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -473,6 +473,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 5b9942d420..aeb88a95b6 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
@@ -5466,7 +5466,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
@@ -8240,12 +8240,38 @@ 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{files}
+@end example
+
+When at least one @var{files} argument is provided, @var{files} are
+executed as Guile scripts in the given order:
+
+@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
+
+Without any filename argument, a Guile REPL is started:
@example
$ guix repl
@@ -8294,11 +8320,12 @@ 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
-configuration file is loaded when spawning a @code{guile} REPL.
+configuration file is loaded when executing scripts or spawning
+a @code{guile} REPL.
@end table
@c *********************************************************************
diff --git a/guix/scripts/repl.scm b/guix/scripts/repl.scm
index ff1f208894..f8d0483ad5 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.
;;;
@@ -32,10 +33,12 @@
;;; 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)))
+ `((script-args . ())
+ (type . guile)))
(define %options
(list (option '(#\h "help") #f #f
@@ -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,42 +139,58 @@ 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))
+ (alist-cons 'script-args
+ (cons arg (cdr (assq 'script-args result)))
+ result))
%default-options))
-
+
(define user-config
(and=> (getenv "HOME")
(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)))
+
(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))))))))
-
-;; Local Variables:
-;; eval: (put 'call-with-connection 'scheme-indent-function 1)
-;; End:
+ (let ((script-args (reverse (assoc-ref opts 'script-args))))
+
+ (unless (null? script-args)
+ ;; Run script
+ (save-module-excursion
+ (lambda ()
+ (set-program-arguments script-args)
+ (set-user-module)
+ (load (car script-args)))))
+
+ (when (null? script-args)
+ ;; 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)
+ ;; End:
+ )
diff --git a/tests/guix-repl.sh b/tests/guix-repl.sh
new file mode 100644
index 0000000000..78b775baf3
--- /dev/null
+++ b/tests/guix-repl.sh
@@ -0,0 +1,78 @@
+# 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
+
+# Inhibit loading of ~/.guile to avoid conflict
+test "`guix repl -q "$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
+
+# Inhibit loading of ~/.guile to avoid conflict
+test "`guix repl -q "$tmpfile" -L "$module_dir"`" = "42"
+
+cat > "$tmpfile"<<EOF
+(format #t "~a\n" (cdr (command-line)))
+EOF
+
+# Inhibit loading of ~/.guile to avoid conflict
+test "`guix repl -q -- "$tmpfile" -a --input=foo.txt`" = "(-a --input=foo.txt)"
--
2.26.2
K
K
Konrad Hinsen wrote on 29 May 2020 12:16
Re: [bug#41253] [PATCH] guix: add script execution to "guix repl"
(name . zimoun)(address . zimon.toutoune@gmail.com)(address . 41253@debbugs.gnu.org)
m17dwvyrke.fsf@fastmail.net
Hi Simon,

Toggle quote (4 lines)
> I have sent a v2 adding tests and rewording the commit message -- to
> be what I understand the compliance; sorry if I did a mistake.
> Well, I am not sure by the tests.

Way better than no tests!

I just submitted a v3, following a thread on help-guix
The v3 patch allows

guix repl script.scm

but also

guix repl – script.scm -a -b –-input=foo.txt

In exchange, only a single script can be run. This is perfectly
in line with how Guile works, but it breaks your nice interpretation
of "REPL" as applying to script execution ;-)

Cheers,
Konrad
Z
Z
zimoun wrote on 29 May 2020 15:53
(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)(address . 41253@debbugs.gnu.org)
CAJ3okZ1Gx9DO485qRJ19Rb1VEE4RAxBCxEffqux6pNLO4TWG-g@mail.gmail.com
Hi Konrad,

On Fri, 29 May 2020 at 12:16, Konrad Hinsen <konrad.hinsen@fastmail.net> wrote:

Toggle quote (3 lines)
> I just submitted a v3, following a thread on help-guix
> (see https://www.mail-archive.com/help-guix@gnu.org/msg09328.html).

May I ask why this mai-archive.com instead of
lists.gnu.org/archive/html/help-guix?


Toggle quote (8 lines)
> The v3 patch allows
>
> guix repl script.scm
>
> but also
>
> guix repl – script.scm -a -b –-input=foo.txt

Nice!

Toggle quote (2 lines)
> In exchange, only a single script can be run. This is perfectly

Do you mean that

guix repl script_1.scm script_2.scm

does not work?


Toggle quote (3 lines)
> in line with how Guile works, but it breaks your nice interpretation
> of "REPL" as applying to script execution ;-)

I can live with that. :-)


Thank you for the patch!!
Hope it will be merged soon. :-)


Cheers,
simon


ps:
Do you know why "M-x debbugs-gnu" is doing a mess with this thread?
K
K
Konrad Hinsen wrote on 29 May 2020 19:21
(name . zimoun)(address . zimon.toutoune@gmail.com)(address . 41253@debbugs.gnu.org)
m1y2pay7v0.fsf@khs-macbook.home
Hi Simon,

Toggle quote (3 lines)
> May I ask why this mai-archive.com instead of
> lists.gnu.org/archive/html/help-guix?

Because that's what DuckDuckGo found for me. I can't remember where
all those list archives are, I just search!

Toggle quote (8 lines)
>> In exchange, only a single script can be run. This is perfectly
>
> Do you mean that
>
> guix repl script_1.scm script_2.scm
>
> does not work?

Well, it does in a way, but differently from before: it runs script?.scm
and passes it script?.scm as its (only) argument.

Toggle quote (2 lines)
> Do you know why "M-x debbugs-gnu" is doing a mess with this thread?

No, I have never heard about "M-x debbugs-gnu". Sounds interesting
though...

Cheers,
Konrad
L
L
Ludovic Courtès wrote on 4 Jun 2020 17:06
Re: [bug#41253] [PATCH v3] guix repl: Add script execution.
(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)
87img698gn.fsf@gnu.org
Hello!

Konrad Hinsen <konrad.hinsen@fastmail.net> skribis:

Toggle quote (4 lines)
> * guix/scripts/repl.scm: Add filename options for script execution.
> * doc/guix.texi (Invoking guix repl): Document it.
> * tests/guix-repl.sh: Test it.

I like it!

It cannot be used as a shebang, can it?

There’s also the Makefile.am change that could be mentioned in the
commit log.

Some comments:

Toggle quote (14 lines)
> +When at least one @var{files} argument is provided, @var{files} are
> +executed as Guile scripts in the given order:
> +
> +@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

I’d remove “$” from the examples.

Toggle quote (2 lines)
> +Without any filename argument, a Guile REPL is started:

s/filename/file name/

Toggle quote (6 lines)
> @item -q
> Inhibit loading of the @file{~/.guile} file. By default, that
> -configuration file is loaded when spawning a @code{guile} REPL.
> +configuration file is loaded when executing scripts or spawning
> +a @code{guile} REPL.

I think this change is unnecessary (see below).

Toggle quote (3 lines)
> + (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"))
^ ^
Please add a space and change comma to period.

Toggle quote (6 lines)
> (lambda (arg result)
> - (leave (G_ "~A: extraneous argument~%") arg))
> + (alist-cons 'script-args
> + (cons arg (cdr (assq 'script-args result)))
> + result))

I’d change that to just:

(append `((script . ,arg) (ignore-dot-guile . #t)) result)

and later we can collect all the values associated with 'script.

I think “script” is clearer than “script-args” (and more in-line with
the naming conventions.)

Last but not least: I think -q should always be implied when running a
script. ~/.guile is really meant for the REPL, nothing else.

Toggle quote (5 lines)
> + ;; Local Variables:
> + ;; eval: (put 'call-with-connection 'scheme-indent-function 1)
> + ;; End:
> + )

Please move the comment to the bottom, outside the parens, as before,
otherwise this paren will feel lonely. :-)

Thoughts?

Looks like we’re almost done. Thank you!

Ludo’.
K
K
Konrad Hinsen wrote on 5 Jun 2020 10:48
(name . Ludovic Courtès)(address . ludo@gnu.org)
m1wo4lkidm.fsf@khs-macbook.home
Hi Ludo,

Thanks for your feedback!

Toggle quote (2 lines)
> It cannot be used as a shebang, can it?

It can. And that might be worth documenting. Here's an example:

===== File foo.scm =========================================
#!/usr/bin/env -S guix repl
!#
(use-modules (ice-9 format))

(format #t "foo called with arguments: ~s\n"(command-line))
===== End of file foo.scm ==================================

hinsen@guix ~$ ~/foo.scm a b c
;;; note: source file /home/hinsen/temp/foo.scm
;;; newer than compiled /home/hinsen/.cache/guile/ccache/3.0-LE-8-4.2/home/hinsen/temp/foo.scm.go
foo called with arguments: ("/home/hinsen/temp/foo.scm" "a" "b" "c")

The ugly part is that the script needs to be called with '–' as its
first argument if any of the following arguments start with dashes:

hinsen@guix ~$ ~/foo.scm -- --help
;;; note: source file /home/hinsen/temp/foo.scm
;;; newer than compiled /home/hinsen/.cache/guile/ccache/3.0-LE-8-4.2/home/hinsen/temp/foo.scm.go
foo called with arguments: ("/home/hinsen/temp/foo.scm" "--help")

That could be fixed at the price of a command line interface for "guix
repl" that deviates a bit from Guix conventions. For example,
is there's a file name argument, pass all arguments following it to the
script, eliminating the need for –.

I'll try that in a v4, and also take into account all your remaining
remarks. Just one note on the examples:

Toggle quote (6 lines)
>> +@example
>> +$ guix repl -- my-script.scm --input=foo.txt
>> +@end example
>
> I’d remove “$” from the examples.

There are many examples in guix.texi with $, and also many without. Plus
some with # as the command line prompt.

It makes sense to add the command line prompt for examples that also
show output, so I am not sure what the best convention is - but it would
be nice to apply a uniform style everywhere (but NOT as part of
this patch, of course).

Cheers,
Konrad
K
K
Konrad Hinsen wrote on 5 Jun 2020 12:18
(name . Ludovic Courtès)(address . ludo@gnu.org)
m1r1utke8y.fsf@khs-macbook.home
A technical question: is there any way to suppress the error message
about the source being newer than the cached version? This is really
annoying when running scripts.

Toggle quote (5 lines)
> hinsen@guix ~$ ~/foo.scm a b c
> ;;; note: source file /home/hinsen/temp/foo.scm
> ;;; newer than compiled /home/hinsen/.cache/guile/ccache/3.0-LE-8-4.2/home/hinsen/temp/foo.scm.go
> foo called with arguments: ("/home/hinsen/temp/foo.scm" "a" "b" "c")

Cheers,
Konrad
L
L
Ludovic Courtès wrote on 5 Jun 2020 18:36
(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)
87d06d1ncq.fsf@gnu.org
Hello Konrad,

Konrad Hinsen <konrad.hinsen@fastmail.net> skribis:

Toggle quote (17 lines)
>> It cannot be used as a shebang, can it?
>
> It can. And that might be worth documenting. Here's an example:
>
> ===== File foo.scm =========================================
> #!/usr/bin/env -S guix repl
> !#
> (use-modules (ice-9 format))
>
> (format #t "foo called with arguments: ~s\n"(command-line))
> ===== End of file foo.scm ==================================
>
> hinsen@guix ~$ ~/foo.scm a b c
> ;;; note: source file /home/hinsen/temp/foo.scm
> ;;; newer than compiled /home/hinsen/.cache/guile/ccache/3.0-LE-8-4.2/home/hinsen/temp/foo.scm.go
> foo called with arguments: ("/home/hinsen/temp/foo.scm" "a" "b" "c")

Nice!

Toggle quote (13 lines)
> The ugly part is that the script needs to be called with '–' as its
> first argument if any of the following arguments start with dashes:
>
> hinsen@guix ~$ ~/foo.scm -- --help
> ;;; note: source file /home/hinsen/temp/foo.scm
> ;;; newer than compiled /home/hinsen/.cache/guile/ccache/3.0-LE-8-4.2/home/hinsen/temp/foo.scm.go
> foo called with arguments: ("/home/hinsen/temp/foo.scm" "--help")
>
> That could be fixed at the price of a command line interface for "guix
> repl" that deviates a bit from Guix conventions. For example,
> is there's a file name argument, pass all arguments following it to the
> script, eliminating the need for –.

Yes, that makes sense to me.

Toggle quote (12 lines)
> I'll try that in a v4, and also take into account all your remaining
> remarks. Just one note on the examples:
>
>>> +@example
>>> +$ guix repl -- my-script.scm --input=foo.txt
>>> +@end example
>>
>> I’d remove “$” from the examples.
>
> There are many examples in guix.texi with $, and also many without. Plus
> some with # as the command line prompt.

Yeah, the manual is kinda inconsistent, and I’m self-inconsistent to
tell the truth. :-)

I’ve come to the conclusion that snippets that contain only input should
be written without a prompt, for easier copy/pasting.

(I’ve seen Python documentation where JS magic allows people to toggle
prompt display, I find it nice.)

Toggle quote (9 lines)
> A technical question: is there any way to suppress the error message
> about the source being newer than the cached version? This is really
> annoying when running scripts.
>
>> hinsen@guix ~$ ~/foo.scm a b c
>> ;;; note: source file /home/hinsen/temp/foo.scm
>> ;;; newer than compiled /home/hinsen/.cache/guile/ccache/3.0-LE-8-4.2/home/hinsen/temp/foo.scm.go
>> foo called with arguments: ("/home/hinsen/temp/foo.scm" "a" "b" "c")

I guess ~/.cache/…/*.go exists because you first ran “guile foo.scm”,
no? In that case, you can simply remove that .go file.

The ‘guix’ command turns off auto-compilation so ‘guix repl’ scripts
would always be interpreted, for better or worse.

Thanks,
Ludo’.
K
K
Konrad Hinsen wrote on 6 Jun 2020 07:18
[PATCH v4] guix repl: Add script execution.
(address . 41253@debbugs.gnu.org)
m1a71gkbzx.fsf@khs-macbook.home
* 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.
---
Makefile.am | 1 +
doc/guix.texi | 51 +++++++++++++++++++++-----
guix/scripts/repl.scm | 82 ++++++++++++++++++++++++++++--------------
tests/guix-repl.sh | 84 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 184 insertions(+), 34 deletions(-)
create mode 100644 tests/guix-repl.sh

Toggle diff (321 lines)
diff --git a/Makefile.am b/Makefile.am
index 5b64386b53..859b6a4bc2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -473,6 +473,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 056bf011f6..b95709d0c6 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
@@ -5472,7 +5472,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
@@ -8246,12 +8246,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
@@ -8300,7 +8335,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..4c2537b55d 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,53 @@ 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
+ (map cdr
+ (filter (lambda (opt)
+ (eq? (car opt) 'script))
+ opts))))
+ (define script-file
+ (let ((file (car script))
+ (directory (getcwd)))
+ (canonicalize-path
+ (cond ((string-prefix? "/" file) file)
+ (else (string-append directory "/" file))))))
+
(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 (cons script-file (cdr script)))
+ (set-user-module)
+ (load script-file))))
+
+ (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..2750188468
--- /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
+#!/usr/bin/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)"
--
2.26.2
K
K
Konrad Hinsen wrote on 6 Jun 2020 07:22
Re: [bug#41253] [PATCH v3] guix repl: Add script execution.
(name . Ludovic Courtès)(address . ludo@gnu.org)
m17dwkkbu6.fsf@khs-macbook.home
Hi Ludo,

Patch v4 is on its way to the world impatiently waiting for it ;-)

Toggle quote (7 lines)
>> That could be fixed at the price of a command line interface for "guix
>> repl" that deviates a bit from Guix conventions. For example,
>> is there's a file name argument, pass all arguments following it to the
>> script, eliminating the need for –.
>
> Yes, that makes sense to me.

I found a much simpler solution: putting "guix repl –" on the shebang
line makes it all work. This is documented in the manual and by a
test case.

Toggle quote (3 lines)
> I guess ~/.cache/…/*.go exists because you first ran “guile foo.scm”,
> no? In that case, you can simply remove that .go file.

That did the job, thanks!

Cheers,
Konrad.
L
L
Ludovic Courtès wrote on 12 Jun 2020 17:58
Re: [bug#41253] [PATCH v4] guix repl: Add script execution.
(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)(address . 41253@debbugs.gnu.org)
87mu585l8y.fsf@gnu.org
Hi, and sorry for the delay!

Konrad Hinsen <konrad.hinsen@fastmail.net> skribis:

Toggle quote (5 lines)
> * 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.

I have some comments regarding style and how to deal with multiple
scripts, but I think we’re pretty much there.

Toggle quote (5 lines)
> +The general syntax is:
> +
> +@example
> +guix repl @var{options} @var{file} @var{args}

Should be: [@var{file} @var{args}@dots{}]
The square brackets show it’s optional.

Toggle quote (3 lines)
> +When a @var{file} argument is provided, @var{file} is
> +executed as a Guile scripts:

“When one or more @var{file} argument is provided, each @var{file} is
executed as a Guile program:”

Toggle quote (6 lines)
> + (define script (reverse
> + (map cdr
> + (filter (lambda (opt)
> + (eq? (car opt) 'script))
> + opts))))

To avoid car/cdr (info "(guix) Data Types and Pattern Matching"), I
suggest something along these lines:

(define scripts ;plural, no?
(reverse
(filter-map (match-lambda
(('script . script) script)
(_ #f))
opts)))

Toggle quote (7 lines)
> + (define script-file
> + (let ((file (car script))
> + (directory (getcwd)))
> + (canonicalize-path
> + (cond ((string-prefix? "/" file) file)
> + (else (string-append directory "/" file))))))

I think we can just use file names as they arrive, without attempting to
canonicalize them or anything.

Toggle quote (8 lines)
> + (unless (null? script)
> + ;; Run script
> + (save-module-excursion
> + (lambda ()
> + (set-program-arguments (cons script-file (cdr script)))
> + (set-user-module)
> + (load script-file))))

=> (for-each load scripts)
Toggle quote (3 lines)
> +cat > "$tmpfile"<<EOF
> +#!/usr/bin/env -S guix repl --

Rather:

#!$(type -P env)

This will ensure it works even in Guix build environments.

Could you send an updated patch?

Thank you!

Ludo’.
K
K
Konrad Hinsen wrote on 13 Jun 2020 18:34
[PATCH v5] guix repl: Add script execution.
(address . 41253@debbugs.gnu.org)
m1a716j55t.fsf@fastmail.net
* 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.
---
Makefile.am | 1 +
doc/guix.texi | 51 +++++++++++++++++++++-----
guix/scripts/repl.scm | 84 +++++++++++++++++++++++++++++--------------
tests/guix-repl.sh | 84 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 186 insertions(+), 34 deletions(-)
create mode 100644 tests/guix-repl.sh

Toggle diff (323 lines)
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 15e077a41c..8ad3a833c6 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..51fffa278a 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,55 @@ 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)))
+
+ (define script-file
+ (let ((file (car script))
+ (directory (getcwd)))
+ (canonicalize-path
+ (cond ((string-prefix? "/" file) file)
+ (else (string-append directory "/" file))))))
+
(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 (cons script-file (cdr script)))
+ (set-user-module)
+ (load script-file))))
+
+ (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)"
--
2.26.2
K
K
Konrad Hinsen wrote on 13 Jun 2020 18:39
Re: [bug#41253] [PATCH v4] guix repl: Add script execution.
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 41253@debbugs.gnu.org)
m17dwaj4xd.fsf@fastmail.net
Hi Ludo,

Patch v5 is on its way! A few comments:

Toggle quote (3 lines)
> Should be: [@var{file} @var{args}@dots{}]
> The square brackets show it’s optional.

OK.

Toggle quote (6 lines)
>> +When a @var{file} argument is provided, @var{file} is
>> +executed as a Guile scripts:
>
> “When one or more @var{file} argument is provided, each @var{file} is
> executed as a Guile program:”

No, that's no longer true. Only one script can be run at a time, because

guix repl script1.scm script2.scm

now means "run script1.scm with script2.scm as its argument". And therefore...

Toggle quote (2 lines)
> (define scripts ;plural, no?

This is not a plural. But filter-map is indeed nicer!

Toggle quote (10 lines)
>> + (define script-file
>> + (let ((file (car script))
>> + (directory (getcwd)))
>> + (canonicalize-path
>> + (cond ((string-prefix? "/" file) file)
>> + (else (string-append directory "/" file))))))
>
> I think we can just use file names as they arrive, without attempting to
> canonicalize them or anything.

That's what I thought (and tried) as well, at first. Problems:

- It doesn't work when run via pre-inst-env with a non-absolute
filename for the script. The script is looked up relative
to the directory containing repl.scm.

- The script filename is also the first item of (command-line)
when called inside the script, and that's useful only it it's
an absolute filename.

Toggle quote (7 lines)
>> +cat > "$tmpfile"<<EOF
>> +#!/usr/bin/env -S guix repl --
>
> Rather:
>
> #!$(type -P env)

I didn't know that was possible on a shebang line!

Toggle quote (2 lines)
> Could you send an updated patch?

Done!

Cheers,
Konrad
L
L
Ludovic Courtès wrote on 13 Jun 2020 21:44
(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)(address . 41253@debbugs.gnu.org)
87v9juyclo.fsf@gnu.org
Hi,

Konrad Hinsen <konrad.hinsen@fastmail.net> skribis:

Toggle quote (9 lines)
>> “When one or more @var{file} argument is provided, each @var{file} is
>> executed as a Guile program:”
>
> No, that's no longer true. Only one script can be run at a time, because
>
> guix repl script1.scm script2.scm
>
> now means "run script1.scm with script2.scm as its argument". And therefore...

Ah OK, sorry for the confusion.

Toggle quote (16 lines)
>>> + (define script-file
>>> + (let ((file (car script))
>>> + (directory (getcwd)))
>>> + (canonicalize-path
>>> + (cond ((string-prefix? "/" file) file)
>>> + (else (string-append directory "/" file))))))
>>
>> I think we can just use file names as they arrive, without attempting to
>> canonicalize them or anything.
>
> That's what I thought (and tried) as well, at first. Problems:
>
> - It doesn't work when run via pre-inst-env with a non-absolute
> filename for the script. The script is looked up relative
> to the directory containing repl.scm.

Oh right, that’s because we’re using ‘load’. We should instead do:

(load-in-vicinity "." file)

Alternatively, (primitive-load file), but in that case the script would
be systematically interpreted.

Toggle quote (4 lines)
> - The script filename is also the first item of (command-line)
> when called inside the script, and that's useful only it it's
> an absolute filename.

In what way is it useful?

Toggle quote (9 lines)
>>> +cat > "$tmpfile"<<EOF
>>> +#!/usr/bin/env -S guix repl --
>>
>> Rather:
>>
>> #!$(type -P env)
>
> I didn't know that was possible on a shebang line!

It’s not, but here it’s evaluated as part of the here-document
expansion.

Thanks!

Ludo’.
K
K
Konrad Hinsen wrote on 14 Jun 2020 09:00
[PATCH v6] guix repl: Add script execution.
(address . 41253@debbugs.gnu.org)
m14krei137.fsf@fastmail.net
* 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.
---
Makefile.am | 1 +
doc/guix.texi | 51 +++++++++++++++++++++-----
guix/scripts/repl.scm | 77 +++++++++++++++++++++++++--------------
tests/guix-repl.sh | 84 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 179 insertions(+), 34 deletions(-)
create mode 100644 tests/guix-repl.sh

Toggle diff (316 lines)
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 15e077a41c..8ad3a833c6 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)"
--
2.26.2
K
K
Konrad Hinsen wrote on 14 Jun 2020 09:02
Re: [bug#41253] [PATCH v4] guix repl: Add script execution.
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 41253@debbugs.gnu.org)
m11rmii0yv.fsf@fastmail.net
Hi Ludo,

Toggle quote (4 lines)
> Oh right, that’s because we’re using ‘load’. We should instead do:
>
> (load-in-vicinity "." file)

Nice! That makes it cleaner indeed, since...

Toggle quote (6 lines)
>> - The script filename is also the first item of (command-line)
>> when called inside the script, and that's useful only it it's
>> an absolute filename.
>
> In what way is it useful?

I though it was a POSIX requirement, but (1) I didn't find anything on
that topic and (2) plaing Guile just passes on what I type on the
command line, so I won't try to do "better".

v6 is on the way!

Cheers,
Konrad
L
L
Ludovic Courtès wrote on 14 Jun 2020 22:50
Re: [bug#41253] [PATCH v6] guix repl: Add script execution.
(name . Konrad Hinsen)(address . konrad.hinsen@fastmail.net)(address . 41253-done@debbugs.gnu.org)
87v9jts77o.fsf@gnu.org
Hi,

Konrad Hinsen <konrad.hinsen@fastmail.net> skribis:

Toggle quote (5 lines)
> * 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.

Perfect, applied!

If you want you can followup with an update of ‘etc/news.scm’ to that
people can learn about this change.

Thank you for your work & for your patience!

Ludo’.
Closed
?
Your comment

This issue is archived.

To comment on this conversation send an email to 41253@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 41253
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch