[bug#78378,2/2] home: services: Build "files" union allowing dangling symlinks.

Message ID f959dc4db01cc2aec47affa7e9d389368217c7f8.1746979727.git.sarg@sarg.org.ru
State New
Headers
Series home: services: Build "files" union allowing dangling symlinks. |

Commit Message

Sergey Trofimov May 11, 2025, 4:08 p.m. UTC
* gnu/home/services.scm (files->files-directory): Build file union
without checking for existence of included items.

* tests/guix-home.sh: Verify symlinking files out of store works.

Change-Id: I94054003f4a6be944252ce7a397cf56f0b979554
---
 gnu/home/services.scm | 16 +++++++++++++++-
 tests/guix-home.sh    |  8 +++++++-
 2 files changed, 22 insertions(+), 2 deletions(-)
  

Comments

Ludovic Courtès May 18, 2025, 8:28 p.m. UTC | #1
Hi Sergey,

Sergey Trofimov <sarg@sarg.org.ru> writes:

> * gnu/home/services.scm (files->files-directory): Build file union
> without checking for existence of included items.
>
> * tests/guix-home.sh: Verify symlinking files out of store works.
>
> Change-Id: I94054003f4a6be944252ce7a397cf56f0b979554

[...]

> @@ -343,7 +343,21 @@ (define (files->files-directory files)
>    ;; leading to a build failure of "files.drv".
>    (assert-no-duplicates files)
>  
> -  (file-union "files" files))
> +  ;; Compute the file-union allowing dangling symlinks
> +  ;; This enables creating symlinks to locations out of store
> +  (computed-file
> +   "files"
> +   (with-imported-modules '((guix build utils))
> +     #~(begin
> +         (use-modules (guix build utils)
> +                      (ice-9 match))
> +         (mkdir #$output)
> +         (chdir #$output)
> +         (for-each (match-lambda
> +                     ((target source)
> +                      (mkdir-p (dirname target))
> +                      (symlink source target)))
> +                   '#$files)))))

Instead of more or less duplicating ‘file-union’, how about adding a
#:dangling-symlinks? argument to ‘file-union’ that would default to #f?

When set to #t, the ‘stat’ call would not be emitted.

That would reduce code duplication and make the intent clearer IMO.

Ludo’.
  
Sergey Trofimov May 19, 2025, 10 a.m. UTC | #2
Hi Ludovic
Ludovic Courtès <ludo@gnu.org> writes:

> Hi Sergey,
>
> Sergey Trofimov <sarg@sarg.org.ru> writes:
>
>> * gnu/home/services.scm (files->files-directory): Build file union
>> without checking for existence of included items.
>>
>> * tests/guix-home.sh: Verify symlinking files out of store works.
>>
>> Change-Id: I94054003f4a6be944252ce7a397cf56f0b979554
>
> [...]
>
>> @@ -343,7 +343,21 @@ (define (files->files-directory files)
>>    ;; leading to a build failure of "files.drv".
>>    (assert-no-duplicates files)
>>  
>> -  (file-union "files" files))
>> +  ;; Compute the file-union allowing dangling symlinks
>> +  ;; This enables creating symlinks to locations out of store
>> +  (computed-file
>> +   "files"
>> +   (with-imported-modules '((guix build utils))
>> +     #~(begin
>> +         (use-modules (guix build utils)
>> +                      (ice-9 match))
>> +         (mkdir #$output)
>> +         (chdir #$output)
>> +         (for-each (match-lambda
>> +                     ((target source)
>> +                      (mkdir-p (dirname target))
>> +                      (symlink source target)))
>> +                   '#$files)))))
>
> Instead of more or less duplicating ‘file-union’, how about adding a
> #:dangling-symlinks? argument to ‘file-union’ that would default to #f?
>
> When set to #t, the ‘stat’ call would not be emitted.
>
> That would reduce code duplication and make the intent clearer IMO.
>
> Ludo’.

I've applied your suggestion in v1.
  

Patch

diff --git a/gnu/home/services.scm b/gnu/home/services.scm
index 2342dc5e07..55f9365922 100644
--- a/gnu/home/services.scm
+++ b/gnu/home/services.scm
@@ -343,7 +343,21 @@  (define (files->files-directory files)
   ;; leading to a build failure of "files.drv".
   (assert-no-duplicates files)
 
-  (file-union "files" files))
+  ;; Compute the file-union allowing dangling symlinks
+  ;; This enables creating symlinks to locations out of store
+  (computed-file
+   "files"
+   (with-imported-modules '((guix build utils))
+     #~(begin
+         (use-modules (guix build utils)
+                      (ice-9 match))
+         (mkdir #$output)
+         (chdir #$output)
+         (for-each (match-lambda
+                     ((target source)
+                      (mkdir-p (dirname target))
+                      (symlink source target)))
+                   '#$files)))))
 
 ;; Used by symlink-manager
 (define home-files-directory "files")
diff --git a/tests/guix-home.sh b/tests/guix-home.sh
index dbfe7dbd48..b8d90196f1 100644
--- a/tests/guix-home.sh
+++ b/tests/guix-home.sh
@@ -73,7 +73,9 @@  trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
                    (list `(".config/test.conf"
                            ,(plain-file
                              "tmp-file.txt"
-                             "the content of ~/.config/test.conf"))))
+                             "the content of ~/.config/test.conf"))
+
+                         `("symlink" ,(symlink-to "<test_directory>"))))
 
    (service home-bash-service-type
             (home-bash-configuration
@@ -104,6 +106,7 @@  trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
                        "# the content of bashrc-test-config.sh"))))))))
 EOF
 
+    sed -i "s,<test_directory>,$test_directory," home.scm
     echo -n "# dot-bashrc test file for guix home" > "dot-bashrc"
 
     # Check whether the graph commands work as expected.
@@ -126,6 +129,7 @@  EOF
 	guix home container home.scm -- cat '~/.config/test.conf' | \
 	    grep "the content of"
 	guix home container home.scm -- test -h '~/.bashrc'
+	guix home container home.scm -- test -h '~/symlink'
 	test "$(guix home container home.scm -- id -u)" = 1000
 	guix home container home.scm -- test -f '$HOME/sample/home.scm' && false
 	guix home container home.scm --expose="$PWD=$HOME/sample" -- \
@@ -153,6 +157,8 @@  EOF
     test -d "${HOME}/.guix-home"
     test -h "${HOME}/.bash_profile"
     test -h "${HOME}/.bashrc"
+    test -h "${HOME}/symlink"
+    test "$(readlink -f $HOME/symlink)" == "$test_directory"
     grep 'alias run="guix shell"' "$HOME/.bashrc"
     grep "alias path='echo \$PATH'" "$HOME/.bashrc"
     test "$(tail -n 2 "${HOME}/.bashrc")" == "\