gexp: ‘local-file’ expands its argument only once.

Fixes a bug whereby (local-file (in-vicinity (getcwd) "xyz")) would
point to different files depending on the current working directory at
the time it is lowered.

* guix/gexp.scm (local-file): Expand FILE only once.
* tests/gexp.scm ("local-file, capture at the right time"): New test.

Change-Id: I2cc23296de3799e68f7d8b7be6061be3043e1176
This commit is contained in:
Ludovic Courtès 2025-04-05 23:22:47 +02:00
parent 649b52b0d4
commit 21221710f2
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
2 changed files with 30 additions and 11 deletions

View file

@ -520,24 +520,27 @@ appears."
((_ (assume-source-relative-file-name file) rest ...)
;; FILE is not a literal, but the user requested we look it up
;; relative to the current source directory.
#'(%local-file file
(delay (absolute-file-name file (current-source-directory)))
rest ...))
#'(let ((f file))
(%local-file f
(delay (absolute-file-name f (current-source-directory)))
rest ...)))
((_ (assume-valid-file-name file) rest ...)
;; FILE is not a literal, so resolve it relative to the current
;; directory. Since the user declared FILE is valid, do not pass
;; #:literal? #f so that we do not warn about it later on.
#'(%local-file file
(delay (absolute-file-name file (getcwd)))
rest ...))
#'(let ((f file))
(%local-file f
(delay (absolute-file-name f (getcwd)))
rest ...)))
((_ file rest ...)
;; Resolve FILE relative to the current directory.
(with-syntax ((location (datum->syntax s (syntax-source s))))
#`(%local-file file
(delay (absolute-file-name file (getcwd)))
rest ...
#:location 'location
#:literal? #f))) ;warn if FILE is relative
#`(let ((f file))
(%local-file f
(delay (absolute-file-name f (getcwd)))
rest ...
#:location 'location
#:literal? #f)))) ;warn if FILE is relative
((_)
#'(syntax-error "missing file name"))
(id

View file

@ -298,6 +298,22 @@
(equal? (scandir (string-append dir "/tests"))
'("." ".." "gexp.scm"))))))
(test-assert "local-file, capture file at the right time"
(call-with-temporary-directory
(lambda (directory)
(call-with-output-file (in-vicinity directory "the-unique-file.txt")
(lambda (port)
(display "Hi!" port)))
(let ((file (with-directory-excursion directory
;; If the argument to 'local-file' were resolved when
;; 'local-file-absolute-file-name' is called, we'd get the
;; wrong result.
(local-file (in-vicinity (getcwd)
"the-unique-file.txt")))))
(string=? (local-file-absolute-file-name file)
(in-vicinity directory "the-unique-file.txt"))))))
(test-assert "one plain file"
(let* ((file (plain-file "hi" "Hello, world!"))
(exp (gexp (display (ungexp file))))