projects/guix·blob java-team · refs

258 lines · 9.6 KiB

1# GNU Guix --- Functional package management for GNU
2# Copyright © 2018-2020, 2023, 2025 Ludovic Courtès <[email protected]>
3# Copyright © 2020 Eric Bavier <[email protected]>
4#
5# This file is part of GNU Guix.
6#
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.
11#
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.
16#
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/>.
19
20#
21# Test the 'guix pack --relocatable' using the external store, if any.
22#
23
24guix pack --version
25
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.
30
31storedir="`guile -c '(use-modules (guix config))(display %storedir)'`"
32localstatedir="`guile -c '(use-modules (guix config))(display %localstatedir)'`"
33NIX_STORE_DIR="$storedir"
34GUIX_DAEMON_SOCKET="$localstatedir/guix/daemon-socket/socket"
35GUIX_BUILD_OPTIONS="--timeout=`guile -c '(use-modules (guix tests))(display %tests-build-timeout)'`"
36export NIX_STORE_DIR GUIX_DAEMON_SOCKET GUIX_BUILD_OPTIONS
37
38if ! guile -c '(use-modules (guix)) (exit (false-if-exception (open-connection)))'
39then
40 exit 77
41fi
42
43# Attempt to run the given command in a namespace where the store is
44# invisible. This makes sure the presence of the store does not hide
45# problems.
46run_without_store ()
47{
48 if unshare -r true # Are user namespaces supported?
49 then
50 # Run that relocatable executable in a user namespace where we "erase"
51 # the store by mounting an empty file system on top of it. That way,
52 # we exercise the wrapper code that creates the user namespace and
53 # bind-mounts the store.
54 unshare -mrf sh -c 'mount -t tmpfs -o ro none "$NIX_STORE_DIR"; '"$*"
55 else
56 # Run the relocatable program in the current namespaces. This is a
57 # weak test because we're going to access store items from the host
58 # store.
59 sh -c "$*"
60 fi
61}
62
63# Wait for the given file to show up. Error out if it doesn't show up in a
64# timely fashion.
65wait_for_file ()
66{
67 i=0
68 while ! test -f "$1" && test $i -lt 20
69 do
70 sleep 0.3
71 i=`expr $i + 1`
72 done
73 test -f "$1"
74}
75
76test_directory="`mktemp -d`"
77export test_directory
78trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
79
80if unshare -r true
81then
82 # Test the 'userns' execution engine.
83 tarball="`guix pack -R -S /Bin=bin sed`"
84 (cd "$test_directory"; tar xvf "$tarball")
85
86 chmod +w "$test_directory"
87 run_without_store "$test_directory/Bin/sed" --version > "$test_directory/output"
88 grep 'GNU sed' "$test_directory/output"
89
90 # Same with an explicit engine.
91 run_without_store GUIX_EXECUTION_ENGINE="userns" \
92 "$test_directory/Bin/sed" --version > "$test_directory/output"
93 grep 'GNU sed' "$test_directory/output"
94
95 # Check whether the exit code is preserved.
96 run_without_store "$test_directory/Bin/sed" --does-not-exist && false
97
98 chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
99else
100 echo "'userns' execution tests skipped" >&2
101fi
102
103case "`uname -m`" in
104 x86_64|i?86)
105 # Try '-RR' and PRoot.
106 tarball="`guix pack -RR -S /Bin=bin sed`"
107 tar tvf "$tarball" | grep /bin/proot
108 (cd "$test_directory"; tar xf "$tarball")
109 chmod +w "$test_directory"
110 run_without_store GUIX_EXECUTION_ENGINE="proot" \
111 "$test_directory/Bin/sed" --version > "$test_directory/output"
112 grep 'GNU sed' "$test_directory/output"
113
114 # Now with fakechroot.
115 run_without_store GUIX_EXECUTION_ENGINE="fakechroot" \
116 "$test_directory/Bin/sed" --version > "$test_directory/output"
117 grep 'GNU sed' "$test_directory/output"
118 unset GUIX_EXECUTION_ENGINE
119
120 chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
121
122 if unshare -r true
123 then
124 # Check whether the store contains everything it should. Check
125 # once when erasing $STORE_PARENT ("/gnu") and once when erasing
126 # $NIX_STORE_DIR ("/gnu/store").
127 tarball="`guix pack -RR -S /bin=bin bash-minimal`"
128 (cd "$test_directory"; tar xf "$tarball")
129
130 STORE_PARENT="`dirname $NIX_STORE_DIR`"
131 export STORE_PARENT
132
133 for engine in userns proot fakechroot
134 do
135 for i in $(guix gc -R $(guix build bash-minimal | grep -v -e '-doc$'))
136 do
137 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)
138 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 done
140 done
141
142 chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
143 fi
144 ;;
145 *)
146 echo "skipping PRoot and Fakechroot tests" >&2
147 ;;
148esac
149
150if unshare -r true
151then
152 # Check what happens if the wrapped binary forks and leaves child
153 # processes behind, like a daemon. The root file system should remain
154 # available to those child processes. See <https://bugs.gnu.org/44261>.
155 cat > "$test_directory/manifest.scm" <<EOF
156(use-modules (guix))
157
158(define daemon
159 (program-file "daemon"
160 #~(begin
161 (use-modules (ice-9 match)
162 (ice-9 ftw))
163
164 (call-with-output-file "parent-store"
165 (lambda (port)
166 (write (scandir (ungexp (%store-prefix)))
167 port)))
168
169 (match (primitive-fork)
170 (0 (sigaction SIGHUP (const #t))
171 (call-with-output-file "pid"
172 (lambda (port)
173 (display (getpid) port)))
174 (pause)
175 (call-with-output-file "child-store"
176 (lambda (port)
177 (write (scandir (ungexp (%store-prefix)))
178 port))))
179 (_ #t)))))
180
181(define package
182 (computed-file "package"
183 #~(let ((out (ungexp output)))
184 (mkdir out)
185 (mkdir (string-append out "/bin"))
186 (symlink (ungexp daemon)
187 (string-append out "/bin/daemon")))))
188
189(manifest (list (manifest-entry
190 (name "daemon")
191 (version "0")
192 (item package))))
193EOF
194
195 tarball="$(guix pack -S /bin=bin -R -m "$test_directory/manifest.scm")"
196 (cd "$test_directory"; tar xf "$tarball")
197
198 # Run '/bin/daemon', which forks, then wait for the child, send it SIGHUP
199 # so that it dumps its view of the store, and make sure the child and
200 # parent both see the same store contents.
201 chmod +w "$test_directory"
202 (cd "$test_directory"; run_without_store ./bin/daemon)
203 wait_for_file "$test_directory/pid"
204 kill -HUP $(cat "$test_directory/pid")
205 wait_for_file "$test_directory/child-store"
206 diff -u "$test_directory/parent-store" "$test_directory/child-store"
207
208 chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
209fi
210
211# Ensure '-R' works with outputs other than "out".
212tarball="`guix pack -R -S /share=share groff:doc`"
213(cd "$test_directory"; tar xf "$tarball")
214test -d "$test_directory/share/doc/groff/html"
215chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
216
217# Ensure '-R' applies to propagated inputs. Failing to do that, it would fail
218# with a profile collision error in this case because 'python-scipy'
219# propagates 'python-numpy'. See <https://bugs.gnu.org/42510>.
220#
221# 'python-scipy' depends on the package defined with the Guile variable
222# 'python-numpy' but the package specification 'python-numpy' doesn't always
223# refer to the same package (e.g. when multiple versions are defined) so the
224# Guile variable is directly referred to.
225guix pack -RR -e '(@ (gnu packages python-xyz) python-numpy)' python-scipy --no-grafts -n
226
227# Check that packages that mix executable and support files (e.g. git) in the
228# "binary" directories still work after wrapped.
229cat >"$test_directory/manifest.scm" <<'EOF'
230(use-modules (guix) (guix profiles) (guix search-paths)
231 (gnu packages bootstrap))
232(manifest
233 (list (manifest-entry
234 (name "test") (version "0")
235 (item (file-union "test"
236 `(("bin/hello"
237 ,(program-file
238 "hello"
239 #~(begin
240 (add-to-load-path (getenv "HELLO_EXEC_PATH"))
241 (display (load-from-path "msg"))(newline))
242 #:guile %bootstrap-guile))
243 ("libexec/hello/msg"
244 ,(plain-file "msg" "42")))))
245 (search-paths
246 (list (search-path-specification
247 (variable "HELLO_EXEC_PATH")
248 (files '("libexec/hello"))
249 (separator #f)))))))
250EOF
251tarball="`guix pack -RR -S /opt= -m $test_directory/manifest.scm`"
252(cd "$test_directory"; tar xvf "$tarball")
253chmod +w "$test_directory"
254( export GUIX_PROFILE=$test_directory/opt
255 . $GUIX_PROFILE/etc/profile
256 run_without_store "$test_directory/opt/bin/hello" > "$test_directory/output" )
257cat "$test_directory/output"
258test "`cat $test_directory/output`" = "42"