Message ID | m1a71ryrsh.fsf@fastmail.net |
---|---|
State | Accepted |
Headers | show |
Series | [bug#41253,v3] guix repl: Add script execution. | expand |
Context | Check | Description |
---|---|---|
cbaines/comparison | success | View comparision |
cbaines/git branch | success | View Git branch |
cbaines/applying patch | success | View Laminar job |
Hello! Konrad Hinsen <konrad.hinsen@fastmail.net> skribis: > * 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: > +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. > +Without any filename argument, a Guile REPL is started: s/filename/file name/ > @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). > + (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. > (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. > + ;; 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’.
Hi Ludo, Thanks for your feedback! > 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: >> +@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
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") Cheers, Konrad
Hello Konrad, Konrad Hinsen <konrad.hinsen@fastmail.net> skribis: >> 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! > 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. > 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.) > 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’.
Hi Ludo, Patch v4 is on its way to the world impatiently waiting for it ;-) >> 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. > 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.
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)"