ui: Allow evaluating multi-expressions strings with read/eval.

This can be useful when evaluating a scheme-file store output for example,
which has multiple top level expressions.

* guix/ui.scm (read/eval): Also accept a port object as argument.  Read and
evaluate all expressions from input port or string.

Change-Id: I0213706fa4824c3a8ffe5d93f44f263048cb62c2
This commit is contained in:
Maxim Cournoyer 2025-05-05 13:30:29 +09:00
parent bc2e923c19
commit 18ed22536d
No known key found for this signature in database
GPG key ID: 1260E46482E63562
2 changed files with 43 additions and 9 deletions

View file

@ -15,7 +15,7 @@
;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr> ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
;;; Copyright © 2019, 2021 Simon Tournier <zimon.toutoune@gmail.com> ;;; Copyright © 2019, 2021 Simon Tournier <zimon.toutoune@gmail.com>
;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net> ;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net>
;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com> ;;; Copyright © 2020, 2025 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2018 Steve Sprang <scs@stevesprang.com> ;;; Copyright © 2018 Steve Sprang <scs@stevesprang.com>
;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info> ;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info>
;;; Copyright © 2022 Liliana Marie Prikler <liliana.prikler@gmail.com> ;;; Copyright © 2022 Liliana Marie Prikler <liliana.prikler@gmail.com>
@ -926,13 +926,18 @@ similar."
module))) module)))
(define (read/eval str) (define (read/eval str)
"Read and evaluate STR, raising an error if something goes wrong." "Read and evaluate STR, which can also be a port, raising an error if
(let ((exp (catch #t something goes wrong. STR may contain one or more expressions; the return
(lambda () value is that of the last evaluated expression."
(call-with-input-string str read)) (define (read/safe port)
(lambda args (catch #t
(leave (G_ "failed to read expression ~s: ~s~%") (lambda ()
str args))))) (read port))
(lambda args
(leave (G_ "failed to read expression ~s: ~s~%")
str args))))
(define (eval/safe exp)
(catch #t (catch #t
(lambda () (lambda ()
(eval exp (force %guix-user-module))) (eval exp (force %guix-user-module)))
@ -956,7 +961,19 @@ similar."
((error args ...) ((error args ...)
(apply display-error #f (current-error-port) args)) (apply display-error #f (current-error-port) args))
(what? #f)) (what? #f))
(exit 1))))) (exit 1))))
(let ((call-with-port-or-string (if (port? str)
call-with-port
call-with-input-string)))
(call-with-port-or-string
str
(lambda (port)
(let loop ((exp (read/safe port))
(result #f))
(if (eof-object? exp)
result
(loop (read/safe port) (eval/safe exp))))))))
(define (read/eval-package-expression str) (define (read/eval-package-expression str)
"Read and evaluate STR and return the package it refers to, or exit an "Read and evaluate STR and return the package it refers to, or exit an

View file

@ -2,6 +2,7 @@
# Copyright © 2012-2014, 2016-2025 Ludovic Courtès <ludo@gnu.org> # Copyright © 2012-2014, 2016-2025 Ludovic Courtès <ludo@gnu.org>
# Copyright © 2020 Marius Bakke <mbakke@fastmail.com> # Copyright © 2020 Marius Bakke <mbakke@fastmail.com>
# Copyright © 2021 Chris Marusich <cmmarusich@gmail.com> # Copyright © 2021 Chris Marusich <cmmarusich@gmail.com>
# Copyright © 2025 Maxim Cournoyer <maxim.cournoyer@gmail.com>
# #
# This file is part of GNU Guix. # This file is part of GNU Guix.
# #
@ -420,6 +421,22 @@ then
guix build -m <(echo '(specifications->manifest (list "guile"))') -n guix build -m <(echo '(specifications->manifest (list "guile"))') -n
fi fi
# Build a scheme->file object via multiple expressions, and validate it
# produces the correct result when evaluated.
scheme_file=$(guix build -e \
"(use-modules (guix gexp)) \
(scheme-file \"mathematics\" \
'(begin \
(define add +) \
(define multiply *) \
(add 5 (multiply 2 10)))
#:guile (@@ (gnu packages bootstrap) %bootstrap-guile))")
guile -c \
"(begin \
(use-modules (guix ui) (rnrs base) (srfi srfi-26)) \
(assert (= 25 (call-with-input-file \"$scheme_file\" \
(cut read/eval <>)))))"
# Using 'GUIX_BUILD_OPTIONS'. # Using 'GUIX_BUILD_OPTIONS'.
GUIX_BUILD_OPTIONS="--dry-run --no-grafts" GUIX_BUILD_OPTIONS="--dry-run --no-grafts"
export GUIX_BUILD_OPTIONS export GUIX_BUILD_OPTIONS