[bug#72867,v3] gexp: Make 'local-file' follow symlinks.
Commit Message
Fixes <https://lists.gnu.org/archive/html/guix-devel/2024-08/msg00047.html>
While the issue can be easily fixed (a one line change in 'absolute-dirname')
by changing 'current-source-directory' so that it always follows symlinks,
such a change may break someone else's code. Instead, this patch keeps the
original behavior of 'current-source-directory' macro and adds optional
'follow-symlinks?' argument to it.
This patch is the result of collective work of
Florian Pelz <pelzflorian@pelzflorian.de> and
Nigko Yerden <nigko.yerden@gmail.com>
* guix/utils.scm (absolute-dirname): Add 'follow-symlinks?' mandatory
argument.
(%guix-source-root-directory): Pass #f to 'absolute-dirname'
'follow-symlinks?' argument.
(current-source-directory): Add 'follow-symlinks?' optional argument.
* guix/gexp.scm (local-file): Pass #t to 'current-source-directory'
'follow-symlinks?' argument.
* tests/gexp.scm ("local-file, load through symlink"): New test.
Change-Id: Ieb30101275deb56b7436df444f9bc21d240fba59
---
Forgot to unwrap #'follow-symlinks? syntax object with 'syntax->datum'
when calling 'source-directory' inside 'current-source-directory'.
guix/gexp.scm | 2 +-
guix/utils.scm | 53 ++++++++++++++++++++++++++++----------------------
tests/gexp.scm | 23 ++++++++++++++++++++++
3 files changed, 54 insertions(+), 24 deletions(-)
base-commit: 4c49cd171e2aa06af05cf52403050b18f100867a
Comments
Hello Nigko.
Nigko Yerden <nigko.yerden@gmail.com> writes:
> This patch is the result of collective work of
> Florian Pelz <pelzflorian@pelzflorian.de> and
> Nigko Yerden <nigko.yerden@gmail.com>
Thanks for the credit, but it would be unusual to mention me in the
commit message, where discussion does not count.
Please do not put me in the commit message; I made no code contribution.
I also would favor to simplify `current-source-directory' and not add an
optional follow-symlinks? argument. I believe processing profiles is
the only reasonable case that unconditionally following symlinks would
break, and people do not do profile processing in outside code.
> * tests/gexp.scm ("local-file, load through symlink"): New test.
This one is a good test; but it tests only half, namely the
rare-in-practice case of `local-file' when loading a Scheme file. Here,
`current-source-directory' evaluate file-name to
"/tmp/guix-directory.VxrxZT/dir/link-to-code.scm", which has a slash as
prefix, so absolute-dirname is not called.
The original issue is that the package in a channel according to
cookbook’s “The Repository as a Channel” cannot be built when the
load-path is set up in the usual way. There, absolute-dirname gets
called. I think we would need a (very similar) test that covers this.
Instead of primitive-load, we would need to invoke Guile on a file in a
channel or in the GUILE_LOAD_PATH, or set %load-path. I may be wrong
here and do not know how, but we definitely should cover when
`file-name' in is not prefixed with a slash.
Regards,
Florian
Hello Florian,
pelzflorian (Florian Pelz) wrote:
> I also would favor to simplify `current-source-directory' and not add an
> optional follow-symlinks? argument. I believe processing profiles is
> the only reasonable case that unconditionally following symlinks would
> break, and people do not do profile processing in outside code.
Why do you think that making 'current-source-directory' to always follow
symlinks would not break Guix's own code as well?
What are these people whose code would be broken supposed to do? I think
they would need to write their own 'current-source-directory' from scratch.
Why not help them by providing 'follow-symlinks?' argument?
Instead of 'current-source-directory' simplification we can also consider
changing the default for 'follow-symlinks?' to #t.
Regards,
Nigko
@@ -508,7 +508,7 @@ (define-syntax local-file
(string? (syntax->datum #'file))
;; FILE is a literal, so resolve it relative to the source directory.
#'(%local-file file
- (delay (absolute-file-name file (current-source-directory)))
+ (delay (absolute-file-name file (current-source-directory #t)))
rest ...))
((_ (assume-valid-file-name file) rest ...)
;; FILE is not a literal, so resolve it relative to the current
@@ -1110,41 +1110,48 @@ (define (canonical-newline-port port)
(define (%guix-source-root-directory)
"Return the source root directory of the Guix found in %load-path."
- (dirname (absolute-dirname "guix/packages.scm")))
+ (dirname (absolute-dirname "guix/packages.scm" #f)))
(define absolute-dirname
;; Memoize to avoid repeated 'stat' storms from 'search-path'.
- (mlambda (file)
+ (mlambda (file follow-symlinks?)
"Return the absolute name of the directory containing FILE, or #f upon
-failure."
+failure. Follow symlinks if FOLLOW-SYMLINKS? is true."
(match (search-path %load-path file)
(#f #f)
((? string? file)
- ;; If there are relative names in %LOAD-PATH, FILE can be relative and
- ;; needs to be canonicalized.
- (if (string-prefix? "/" file)
- (dirname file)
- (canonicalize-path (dirname file)))))))
+ (if follow-symlinks?
+ (dirname (canonicalize-path file))
+ ;; If there are relative names in %LOAD-PATH, FILE can be relative
+ ;; and needs to be canonicalized.
+ (if (string-prefix? "/" file)
+ (dirname file)
+ (canonicalize-path (dirname file))))))))
(define-syntax current-source-directory
(lambda (s)
"Return the absolute name of the current directory, or #f if it could not
-be determined."
+be determined. Do not follow symlinks if FOLLOW-SYMLINKS? is false (the default)."
+ (define (source-directory follow-symlinks?)
+ (match (assq 'filename (or (syntax-source s) '()))
+ (('filename . (? string? file-name))
+ ;; If %FILE-PORT-NAME-CANONICALIZATION is 'relative, then FILE-NAME
+ ;; can be relative. In that case, we try to find out at run time
+ ;; the absolute file name by looking at %LOAD-PATH; doing this at
+ ;; run time rather than expansion time is necessary to allow files
+ ;; to be moved on the file system.
+ (if (string-prefix? "/" file-name)
+ (dirname (if follow-symlinks?
+ (canonicalize-path file-name)
+ file-name))
+ #`(absolute-dirname #,file-name #,follow-symlinks?)))
+ ((or ('filename . #f) #f)
+ ;; raising an error would upset Geiser users
+ #f)))
(syntax-case s ()
- ((_)
- (match (assq 'filename (or (syntax-source s) '()))
- (('filename . (? string? file-name))
- ;; If %FILE-PORT-NAME-CANONICALIZATION is 'relative, then FILE-NAME
- ;; can be relative. In that case, we try to find out at run time
- ;; the absolute file name by looking at %LOAD-PATH; doing this at
- ;; run time rather than expansion time is necessary to allow files
- ;; to be moved on the file system.
- (if (string-prefix? "/" file-name)
- (dirname file-name)
- #`(absolute-dirname #,file-name)))
- ((or ('filename . #f) #f)
- ;; raising an error would upset Geiser users
- #f))))))
+ ((_) (source-directory #f))
+ ((_ follow-symlinks?)
+ (source-directory (syntax->datum #'follow-symlinks?))))))
;;;
@@ -292,6 +292,29 @@ (define %extension-package
(equal? (scandir (string-append dir "/tests"))
'("." ".." "gexp.scm"))))))
+(test-assert "local-file, load through symlink"
+ ;; See <https://issues.guix.gnu.org/72867>.
+ (call-with-temporary-directory
+ (lambda (tmp-dir)
+ (chdir tmp-dir)
+ ;; create content file
+ (call-with-output-file "content"
+ (lambda (port) (display "Hi!" port)))
+ ;; create code that call 'local-file'
+ ;; with the content file and returns its
+ ;; absolute file-name. An error is raised
+ ;; if the content file can't be found.
+ (call-with-output-file "code.scm"
+ (lambda (port) (display "\
+(use-modules (guix gexp))
+(define file (local-file \"content\" \"test-file\"))
+(local-file-absolute-file-name file)" port)))
+ (mkdir "dir")
+ (chdir "dir")
+ (symlink "../code.scm" "link-to-code.scm")
+ ;; call 'local-file' through symlink
+ (primitive-load (string-append tmp-dir "/dir/link-to-code.scm")))))
+
(test-assert "one plain file"
(let* ((file (plain-file "hi" "Hello, world!"))
(exp (gexp (display (ungexp file))))