1 # GNU Guix --- Functional package management for GNU
2 # Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
3 # Copyright © 2020 Eric Bavier <bavier@posteo.net>
5 # This file is part of GNU Guix.
7 # GNU Guix is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or (at
10 # your option) any later version.
12 # GNU Guix is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21 # Test the 'guix pack --relocatable' using the external store, if any.
26 # 'guix pack --relocatable' requires a C compiler and libc.a, which our
27 # bootstrap binaries don't provide. To make the test relatively inexpensive,
28 # run it on the user's global store if possible, on the grounds that binaries
29 # may already be there or can be built or downloaded inexpensively.
31 storedir
="`guile -c '(use-modules (guix config))(display %storedir)'`"
32 localstatedir
="`guile -c '(use-modules (guix config))(display %localstatedir)'`"
33 NIX_STORE_DIR
="$storedir"
34 GUIX_DAEMON_SOCKET
="$localstatedir/guix/daemon-socket/socket"
35 export NIX_STORE_DIR GUIX_DAEMON_SOCKET
37 if ! guile
-c '(use-modules (guix)) (exit (false-if-exception (open-connection)))'
42 # Attempt to run the given command in a namespace where the store is
43 # invisible. This makes sure the presence of the store does not hide
47 if unshare
-r true
# Are user namespaces supported?
49 # Run that relocatable executable in a user namespace where we "erase"
50 # the store by mounting an empty file system on top of it. That way,
51 # we exercise the wrapper code that creates the user namespace and
52 # bind-mounts the store.
53 unshare
-mrf sh
-c 'mount -t tmpfs -o ro none "$NIX_STORE_DIR"; '"$*"
55 # Run the relocatable program in the current namespaces. This is a
56 # weak test because we're going to access store items from the host
62 # Wait for the given file to show up. Error out if it doesn't show up in a
67 while ! test -f "$1" && test $i -lt 20
75 test_directory
="`mktemp -d`"
77 trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
81 # Test the 'userns' execution engine.
82 tarball
="`guix pack -R -S /Bin=bin sed`"
83 (cd "$test_directory"; tar xvf
"$tarball")
85 run_without_store
"$test_directory/Bin/sed" --version > "$test_directory/output"
86 grep 'GNU sed' "$test_directory/output"
88 # Same with an explicit engine.
89 run_without_store GUIX_EXECUTION_ENGINE
="userns" \
90 "$test_directory/Bin/sed" --version > "$test_directory/output"
91 grep 'GNU sed' "$test_directory/output"
93 # Check whether the exit code is preserved.
94 ! run_without_store
"$test_directory/Bin/sed" --does-not-exist
96 chmod -Rf +w
"$test_directory"; rm -rf "$test_directory"/*
98 echo "'userns' execution tests skipped" >&2
103 # Try '-RR' and PRoot.
104 tarball
="`guix pack -RR -S /Bin=bin sed`"
105 tar tvf
"$tarball" |
grep /bin
/proot
106 (cd "$test_directory"; tar xf
"$tarball")
107 run_without_store GUIX_EXECUTION_ENGINE
="proot" \
108 "$test_directory/Bin/sed" --version > "$test_directory/output"
109 grep 'GNU sed' "$test_directory/output"
111 # Now with fakechroot.
112 run_without_store GUIX_EXECUTION_ENGINE
="fakechroot" \
113 "$test_directory/Bin/sed" --version > "$test_directory/output"
114 grep 'GNU sed' "$test_directory/output"
115 unset GUIX_EXECUTION_ENGINE
117 chmod -Rf +w
"$test_directory"; rm -rf "$test_directory"/*
121 # Check whether the store contains everything it should. Check
122 # once when erasing $STORE_PARENT ("/gnu") and once when erasing
123 # $NIX_STORE_DIR ("/gnu/store").
124 tarball
="`guix pack -RR -S /bin=bin bash-minimal`"
125 (cd "$test_directory"; tar xf
"$tarball")
127 STORE_PARENT
="`dirname $NIX_STORE_DIR`"
130 for engine
in userns proot fakechroot
132 for i
in $
(guix gc
-R $
(guix build bash-minimal |
grep -v -e '-doc$'))
134 unshare
-mrf sh
-c "mount -t tmpfs none \"$NIX_STORE_DIR\"; GUIX_EXECUTION_ENGINE=$engine $test_directory/bin/sh -c 'echo $NIX_STORE_DIR/*'" |
grep $
(basename $i)
135 unshare
-mrf sh
-c "mount -t tmpfs none \"$STORE_PARENT\"; GUIX_EXECUTION_ENGINE=$engine $test_directory/bin/sh -c 'echo $NIX_STORE_DIR/*'" |
grep $
(basename $i)
139 chmod -Rf +w
"$test_directory"; rm -rf "$test_directory"/*
143 echo "skipping PRoot and Fakechroot tests" >&2
149 # Check what happens if the wrapped binary forks and leaves child
150 # processes behind, like a daemon. The root file system should remain
151 # available to those child processes. See <https://bugs.gnu.org/44261>.
152 cat > "$test_directory/manifest.scm" <<EOF
156 (program-file "daemon"
158 (use-modules (ice-9 match)
161 (call-with-output-file "parent-store"
163 (write (scandir (ungexp (%store-prefix)))
166 (match (primitive-fork)
167 (0 (sigaction SIGHUP (const #t))
168 (call-with-output-file "pid"
170 (display (getpid) port)))
172 (call-with-output-file "child-store"
174 (write (scandir (ungexp (%store-prefix)))
179 (computed-file "package"
180 #~(let ((out (ungexp output)))
182 (mkdir (string-append out "/bin"))
183 (symlink (ungexp daemon)
184 (string-append out "/bin/daemon")))))
186 (manifest (list (manifest-entry
192 tarball
="$(guix pack -S /bin=bin -R -m "$test_directory/manifest.scm
")"
193 (cd "$test_directory"; tar xf
"$tarball")
195 # Run '/bin/daemon', which forks, then wait for the child, send it SIGHUP
196 # so that it dumps its view of the store, and make sure the child and
197 # parent both see the same store contents.
198 (cd "$test_directory"; run_without_store .
/bin
/daemon
)
199 wait_for_file
"$test_directory/pid"
200 kill -HUP $
(cat "$test_directory/pid")
201 wait_for_file
"$test_directory/child-store"
202 diff -u "$test_directory/parent-store" "$test_directory/child-store"
204 chmod -Rf +w
"$test_directory"; rm -rf "$test_directory"/*
207 # Ensure '-R' works with outputs other than "out".
208 tarball
="`guix pack -R -S /share=share groff:doc`"
209 (cd "$test_directory"; tar xf
"$tarball")
210 test -d "$test_directory/share/doc/groff/html"
211 chmod -Rf +w
"$test_directory"; rm -rf "$test_directory"/*
213 # Ensure '-R' applies to propagated inputs. Failing to do that, it would fail
214 # with a profile collision error in this case because 'python-scipy'
215 # propagates 'python-numpy'. See <https://bugs.gnu.org/42510>.
216 guix pack
-RR python-numpy python-scipy
--no-grafts -n
218 # Check that packages that mix executable and support files (e.g. git) in the
219 # "binary" directories still work after wrapped.
220 cat >"$test_directory/manifest.scm" <<'EOF'
221 (use-modules (guix) (guix profiles) (guix search-paths)
222 (gnu packages bootstrap))
224 (list (manifest-entry
225 (name "test") (version "0")
226 (item (file-union "test"
231 (add-to-load-path (getenv "HELLO_EXEC_PATH"))
232 (display (load-from-path "msg"))(newline))
233 #:guile %bootstrap-guile))
235 ,(plain-file "msg" "42")))))
237 (list (search-path-specification
238 (variable "HELLO_EXEC_PATH")
239 (files '("libexec/hello"))
242 tarball
="`guix pack -RR -S /opt= -m $test_directory/manifest.scm`"
243 (cd "$test_directory"; tar xvf
"$tarball")
244 ( export GUIX_PROFILE
=$test_directory/opt
245 .
$GUIX_PROFILE/etc
/profile
246 run_without_store
"$test_directory/opt/bin/hello" > "$test_directory/output" )
247 cat "$test_directory/output"
248 test "`cat $test_directory/output`" = "42"