gexp: Preserve source location for #~ and #$ read extensions.
Read hash extensions preserve source location info as source properties on their result. However, in Guile 3.0.8, that location would be dismissed, leading 'local-file' to fail to resolve file names relative to the source directory. Fixes <https://issues.guix.gnu.org/54003>. Reported by Aleksandr Vityazev <avityazev@posteo.org>. * guix/gexp.scm <eval-when> [read-syntax-redefined?, read-procedure] [read-syntax*]: New variables. [read-ungexp]: Adjust to expect either sexps or syntax objects. [read-gexp]: Call 'read-procedure'. * tests/gexp.scm ("local-file, relative file name, within gexp") ("local-file, relative file name, within gexp, compiled"): New tests.
This commit is contained in:
parent
176354c2f8
commit
ca155a20ae
2 changed files with 72 additions and 5 deletions
|
|
@ -2176,6 +2176,29 @@ is true, the derivation will not print anything."
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
(eval-when (expand load eval)
|
(eval-when (expand load eval)
|
||||||
|
(define-once read-syntax-redefined?
|
||||||
|
;; Have we already redefined 'read-syntax'? This needs to be done on
|
||||||
|
;; 3.0.8 only to work around <https://issues.guix.gnu.org/54003>.
|
||||||
|
(or (not (module-variable the-scm-module 'read-syntax))
|
||||||
|
(not (guile-version>? "3.0.7"))))
|
||||||
|
|
||||||
|
(define read-procedure
|
||||||
|
;; The current read procedure being called: either 'read' or
|
||||||
|
;; 'read-syntax'.
|
||||||
|
(make-parameter read))
|
||||||
|
|
||||||
|
(define read-syntax*
|
||||||
|
;; Replacement for 'read-syntax'.
|
||||||
|
(let ((read-syntax (and=> (module-variable the-scm-module 'read-syntax)
|
||||||
|
variable-ref)))
|
||||||
|
(lambda (port . rest)
|
||||||
|
(parameterize ((read-procedure read-syntax))
|
||||||
|
(apply read-syntax port rest)))))
|
||||||
|
|
||||||
|
(unless read-syntax-redefined?
|
||||||
|
(set! (@ (guile) read-syntax) read-syntax*)
|
||||||
|
(set! read-syntax-redefined? #t))
|
||||||
|
|
||||||
(define* (read-ungexp chr port #:optional native?)
|
(define* (read-ungexp chr port #:optional native?)
|
||||||
"Read an 'ungexp' or 'ungexp-splicing' form from PORT. When NATIVE? is
|
"Read an 'ungexp' or 'ungexp-splicing' form from PORT. When NATIVE? is
|
||||||
true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
|
true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
|
||||||
|
|
@ -2191,22 +2214,39 @@ true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
|
||||||
'ungexp-native
|
'ungexp-native
|
||||||
'ungexp))))
|
'ungexp))))
|
||||||
|
|
||||||
(match (read port)
|
(define symbolic?
|
||||||
((? symbol? symbol)
|
;; Depending on whether (read-procedure) is 'read' or 'read-syntax', we
|
||||||
(let ((str (symbol->string symbol)))
|
;; might get either sexps or syntax objects. Adjust accordingly.
|
||||||
|
(if (eq? (read-procedure) read)
|
||||||
|
symbol?
|
||||||
|
(compose symbol? syntax->datum)))
|
||||||
|
|
||||||
|
(define symbolic->string
|
||||||
|
(if (eq? (read-procedure) read)
|
||||||
|
symbol->string
|
||||||
|
(compose symbol->string syntax->datum)))
|
||||||
|
|
||||||
|
(define wrapped-symbol
|
||||||
|
(if (eq? (read-procedure) read)
|
||||||
|
(lambda (_ symbol) symbol)
|
||||||
|
datum->syntax))
|
||||||
|
|
||||||
|
(match ((read-procedure) port)
|
||||||
|
((? symbolic? symbol)
|
||||||
|
(let ((str (symbolic->string symbol)))
|
||||||
(match (string-index-right str #\:)
|
(match (string-index-right str #\:)
|
||||||
(#f
|
(#f
|
||||||
`(,unquote-symbol ,symbol))
|
`(,unquote-symbol ,symbol))
|
||||||
(colon
|
(colon
|
||||||
(let ((name (string->symbol (substring str 0 colon)))
|
(let ((name (string->symbol (substring str 0 colon)))
|
||||||
(output (substring str (+ colon 1))))
|
(output (substring str (+ colon 1))))
|
||||||
`(,unquote-symbol ,name ,output))))))
|
`(,unquote-symbol ,(wrapped-symbol symbol name) ,output))))))
|
||||||
(x
|
(x
|
||||||
`(,unquote-symbol ,x))))
|
`(,unquote-symbol ,x))))
|
||||||
|
|
||||||
(define (read-gexp chr port)
|
(define (read-gexp chr port)
|
||||||
"Read a 'gexp' form from PORT."
|
"Read a 'gexp' form from PORT."
|
||||||
`(gexp ,(read port)))
|
`(gexp ,((read-procedure) port)))
|
||||||
|
|
||||||
;; Extend the reader
|
;; Extend the reader
|
||||||
(read-hash-extend #\~ read-gexp)
|
(read-hash-extend #\~ read-gexp)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#:use-module (guix tests)
|
#:use-module (guix tests)
|
||||||
#:use-module ((guix build utils) #:select (with-directory-excursion))
|
#:use-module ((guix build utils) #:select (with-directory-excursion))
|
||||||
#:use-module ((guix utils) #:select (call-with-temporary-directory))
|
#:use-module ((guix utils) #:select (call-with-temporary-directory))
|
||||||
|
#:use-module ((guix ui) #:select (load*))
|
||||||
#:use-module (gnu packages)
|
#:use-module (gnu packages)
|
||||||
#:use-module (gnu packages base)
|
#:use-module (gnu packages base)
|
||||||
#:use-module (gnu packages bootstrap)
|
#:use-module (gnu packages bootstrap)
|
||||||
|
|
@ -222,6 +223,32 @@
|
||||||
(let ((file (local-file (string-copy "../base32.scm"))))
|
(let ((file (local-file (string-copy "../base32.scm"))))
|
||||||
(local-file-absolute-file-name file)))))
|
(local-file-absolute-file-name file)))))
|
||||||
|
|
||||||
|
(test-assert "local-file, relative file name, within gexp"
|
||||||
|
(let* ((file (search-path %load-path "guix/base32.scm"))
|
||||||
|
(interned (add-to-store %store "base32.scm" #f "sha256" file)))
|
||||||
|
(equal? `(the file is ,interned)
|
||||||
|
(gexp->sexp*
|
||||||
|
#~(the file is #$(local-file "../guix/base32.scm"))))))
|
||||||
|
|
||||||
|
(test-assert "local-file, relative file name, within gexp, compiled"
|
||||||
|
;; In Guile 3.0.8, everything read by the #~ and #$ read hash extensions
|
||||||
|
;; would lack source location info, which in turn would lead
|
||||||
|
;; (current-source-directory), called by 'local-file', to return #f, thereby
|
||||||
|
;; breaking 'local-file' resolution. See
|
||||||
|
;; <https://issues.guix.gnu.org/54003>.
|
||||||
|
(let ((file (tmpnam)))
|
||||||
|
(call-with-output-file file
|
||||||
|
(lambda (port)
|
||||||
|
(display (string-append "#~(this file is #$(local-file \""
|
||||||
|
(basename file) "\" \"t.scm\"))")
|
||||||
|
port)))
|
||||||
|
|
||||||
|
(let* ((interned (add-to-store %store "t.scm" #f "sha256" file))
|
||||||
|
(module (make-fresh-user-module)))
|
||||||
|
(module-use! module (resolve-interface '(guix gexp)))
|
||||||
|
(equal? `(this file is ,interned)
|
||||||
|
(gexp->sexp* (load* file module))))))
|
||||||
|
|
||||||
(test-assertm "local-file, #:select?"
|
(test-assertm "local-file, #:select?"
|
||||||
(mlet* %store-monad ((select? -> (lambda (file stat)
|
(mlet* %store-monad ((select? -> (lambda (file stat)
|
||||||
(member (basename file)
|
(member (basename file)
|
||||||
|
|
|
||||||
Reference in a new issue