tests: build-emacs-utils: Allow test to pass on Emacs 27 too.
[jackhill/guix/guix.git] / tests / pack.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
4 ;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
5 ;;;
6 ;;; This file is part of GNU Guix.
7 ;;;
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
12 ;;;
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
17 ;;;
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21 (define-module (test-pack)
22 #:use-module (guix scripts pack)
23 #:use-module (guix store)
24 #:use-module (guix derivations)
25 #:use-module (guix profiles)
26 #:use-module (guix packages)
27 #:use-module (guix monads)
28 #:use-module (guix grafts)
29 #:use-module (guix tests)
30 #:use-module (guix gexp)
31 #:use-module (guix modules)
32 #:use-module (gnu packages)
33 #:use-module ((gnu packages base) #:select (glibc-utf8-locales))
34 #:use-module (gnu packages bootstrap)
35 #:use-module ((gnu packages compression) #:select (squashfs-tools))
36 #:use-module ((gnu packages debian) #:select (dpkg))
37 #:use-module ((gnu packages guile) #:select (guile-sqlite3))
38 #:use-module ((gnu packages gnupg) #:select (guile-gcrypt))
39 #:use-module (srfi srfi-64))
40
41 (define %store
42 (open-connection-for-tests))
43
44 ;; Globally disable grafts because they can trigger early builds.
45 (%graft? #f)
46
47 (define-syntax-rule (test-assertm name store exp)
48 (test-assert name
49 (let ((guile (package-derivation store %bootstrap-guile)))
50 (run-with-store store exp
51 #:guile-for-build guile))))
52
53 (define %gzip-compressor
54 ;; Compressor that uses the bootstrap 'gzip'.
55 ((@ (guix scripts pack) compressor) "gzip"
56 ".gz"
57 #~(list #+(file-append %bootstrap-coreutils&co "/bin/gzip") "-6n")))
58
59 (define %tar-bootstrap %bootstrap-coreutils&co)
60
61 (define %ar-bootstrap %bootstrap-binutils)
62
63 \f
64 (test-begin "pack")
65
66 (unless (network-reachable?) (test-skip 1))
67 (test-assertm "self-contained-tarball" %store
68 (mlet* %store-monad
69 ((profile -> (profile
70 (content (packages->manifest (list %bootstrap-guile)))
71 (hooks '())
72 (locales? #f)))
73 (tarball (self-contained-tarball "pack" profile
74 #:symlinks '(("/bin/Guile"
75 -> "bin/guile"))
76 #:compressor %gzip-compressor
77 #:archiver %tar-bootstrap))
78 (check (gexp->derivation
79 "check-tarball"
80 (with-imported-modules '((guix build utils))
81 #~(begin
82 (use-modules (guix build utils)
83 (srfi srfi-1))
84
85 (define store
86 ;; The unpacked store.
87 (string-append "." (%store-directory) "/"))
88
89 (define (canonical? file)
90 ;; Return #t if FILE is read-only and its mtime is 1.
91 (let ((st (lstat file)))
92 (or (not (string-prefix? store file))
93 (eq? 'symlink (stat:type st))
94 (and (= 1 (stat:mtime st))
95 (zero? (logand #o222
96 (stat:mode st)))))))
97
98 (define bin
99 (string-append "." #$profile "/bin"))
100
101 (setenv "PATH"
102 (string-append #$%tar-bootstrap "/bin"))
103 (system* "tar" "xvf" #$tarball)
104 (mkdir #$output)
105 (exit
106 (and (file-exists? (string-append bin "/guile"))
107 (file-exists? store)
108 (every canonical?
109 (find-files "." (const #t)
110 #:directories? #t))
111 (string=? (string-append #$%bootstrap-guile "/bin")
112 (readlink bin))
113 (string=? (string-append ".." #$profile
114 "/bin/guile")
115 (readlink "bin/Guile")))))))))
116 (built-derivations (list check))))
117
118 ;; The following test needs guile-sqlite3, libgcrypt, etc. as a consequence of
119 ;; commit c45477d2a1a651485feede20fe0f3d15aec48b39 and related changes. Thus,
120 ;; run it on the user's store, if it's available, on the grounds that these
121 ;; dependencies may be already there, or we can get substitutes or build them
122 ;; quite inexpensively; see <https://bugs.gnu.org/32184>.
123
124 (with-external-store store
125 (unless store (test-skip 1))
126 (test-assertm "self-contained-tarball + localstatedir" store
127 (mlet* %store-monad
128 ((guile (set-guile-for-build (default-guile)))
129 (profile (profile-derivation (packages->manifest
130 (list %bootstrap-guile))
131 #:hooks '()
132 #:locales? #f))
133 (tarball (self-contained-tarball "tar-pack" profile
134 #:localstatedir? #t))
135 (check (gexp->derivation
136 "check-tarball"
137 #~(let ((bin (string-append "." #$profile "/bin")))
138 (setenv "PATH"
139 (string-append #$%tar-bootstrap "/bin"))
140 (system* "tar" "xvf" #$tarball)
141 (mkdir #$output)
142 (exit
143 (and (file-exists? "var/guix/db/db.sqlite")
144 (string=? (string-append #$%bootstrap-guile "/bin")
145 (readlink bin))))))))
146 (built-derivations (list check))))
147
148 (unless store (test-skip 1))
149 (test-assertm "self-contained-tarball + localstatedir, UTF-8 file names" store
150 (mlet* %store-monad
151 ((guile (set-guile-for-build (default-guile)))
152 (tree (interned-file-tree
153 `("directory-with-utf8-file-names" directory
154 ("α" regular (data "alpha"))
155 ("λ" regular (data "lambda")))))
156 (tarball (self-contained-tarball "tar-pack" tree
157 #:localstatedir? #t))
158 (check (gexp->derivation
159 "check-tarball"
160 (with-extensions (list guile-sqlite3 guile-gcrypt)
161 (with-imported-modules (source-module-closure
162 '((guix store database)))
163 #~(begin
164 (use-modules (guix store database)
165 (rnrs io ports)
166 (srfi srfi-1))
167
168 (define (valid-file? basename data)
169 (define file
170 (string-append "./" #$tree "/" basename))
171
172 (string=? (call-with-input-file (pk 'file file)
173 get-string-all)
174 data))
175
176 (setenv "PATH"
177 (string-append #$%tar-bootstrap "/bin"))
178 (system* "tar" "xvf" #$tarball)
179
180 (sql-schema
181 #$(local-file (search-path %load-path
182 "guix/store/schema.sql")))
183 (with-database "var/guix/db/db.sqlite" db
184 ;; Make sure non-ASCII file names are properly
185 ;; handled.
186 (setenv "GUIX_LOCPATH"
187 #+(file-append glibc-utf8-locales
188 "/lib/locale"))
189 (setlocale LC_ALL "en_US.utf8")
190
191 (mkdir #$output)
192 (exit
193 (and (every valid-file?
194 '("α" "λ")
195 '("alpha" "lambda"))
196 (integer? (path-id db #$tree)))))))))))
197 (built-derivations (list check))))
198
199 (unless store (test-skip 1))
200 (test-assertm "docker-image + localstatedir" store
201 (mlet* %store-monad
202 ((guile (set-guile-for-build (default-guile)))
203 (profile (profile-derivation (packages->manifest
204 (list %bootstrap-guile))
205 #:hooks '()
206 #:locales? #f))
207 (tarball (docker-image "docker-pack" profile
208 #:symlinks '(("/bin/Guile" -> "bin/guile"))
209 #:localstatedir? #t))
210 (check (gexp->derivation
211 "check-tarball"
212 (with-imported-modules '((guix build utils))
213 #~(begin
214 (use-modules (guix build utils)
215 (ice-9 match))
216
217 (define bin
218 (string-append "." #$profile "/bin"))
219
220 (setenv "PATH" (string-append #$%tar-bootstrap "/bin"))
221 (mkdir "base")
222 (with-directory-excursion "base"
223 (invoke "tar" "xvf" #$tarball))
224
225 (match (find-files "base" "layer.tar")
226 ((layer)
227 (invoke "tar" "xvf" layer)))
228
229 (when
230 (and (file-exists? (string-append bin "/guile"))
231 (file-exists? "var/guix/db/db.sqlite")
232 (file-is-directory? "tmp")
233 (string=? (string-append #$%bootstrap-guile "/bin")
234 (pk 'binlink (readlink bin)))
235 (string=? (string-append #$profile "/bin/guile")
236 (pk 'guilelink (readlink "bin/Guile"))))
237 (mkdir #$output)))))))
238 (built-derivations (list check))))
239
240 (unless store (test-skip 1))
241 (test-assertm "squashfs-image + localstatedir" store
242 (mlet* %store-monad
243 ((guile (set-guile-for-build (default-guile)))
244 (profile (profile-derivation (packages->manifest
245 (list %bootstrap-guile))
246 #:hooks '()
247 #:locales? #f))
248 (image (squashfs-image "squashfs-pack" profile
249 #:symlinks '(("/bin" -> "bin"))
250 #:localstatedir? #t))
251 (check (gexp->derivation
252 "check-tarball"
253 (with-imported-modules '((guix build utils))
254 #~(begin
255 (use-modules (guix build utils)
256 (ice-9 match))
257
258 (define bin
259 (string-append "." #$profile "/bin"))
260
261 (setenv "PATH"
262 (string-append #$squashfs-tools "/bin"))
263 (invoke "unsquashfs" #$image)
264 (with-directory-excursion "squashfs-root"
265 (when (and (file-exists? (string-append bin
266 "/guile"))
267 (file-exists? "var/guix/db/db.sqlite")
268 (string=? (string-append #$%bootstrap-guile "/bin")
269 (pk 'binlink (readlink bin)))
270
271 ;; This is a relative symlink target.
272 (string=? (string-drop
273 (string-append #$profile "/bin")
274 1)
275 (pk 'guilelink (readlink "bin"))))
276 (mkdir #$output))))))))
277 (built-derivations (list check))))
278
279 (unless store (test-skip 1))
280 (test-assertm "deb archive with symlinks and control files" store
281 (mlet* %store-monad
282 ((guile (set-guile-for-build (default-guile)))
283 (profile (profile-derivation (packages->manifest
284 (list %bootstrap-guile))
285 #:hooks '()
286 #:locales? #f))
287 (deb (debian-archive
288 "deb-pack" profile
289 #:compressor %gzip-compressor
290 #:symlinks '(("/opt/gnu/bin" -> "bin"))
291 #:archiver %tar-bootstrap
292 #:extra-options
293 (list #:triggers-file
294 (plain-file "triggers"
295 "activate-noawait /usr/share/icons/hicolor\n")
296 #:postinst-file
297 (plain-file "postinst"
298 "echo running configure script\n"))))
299 (check
300 (gexp->derivation "check-deb-pack"
301 (with-imported-modules '((guix build utils))
302 #~(begin
303 (use-modules (guix build utils)
304 (ice-9 match)
305 (ice-9 popen)
306 (ice-9 rdelim)
307 (ice-9 textual-ports)
308 (rnrs base))
309
310 (setenv "PATH" (string-join
311 (list (string-append #+%tar-bootstrap "/bin")
312 (string-append #+dpkg "/bin")
313 (string-append #+%ar-bootstrap "/bin"))
314 ":"))
315
316 ;; Validate the output of 'dpkg --info'.
317 (let* ((port (open-pipe* OPEN_READ "dpkg" "--info" #$deb))
318 (info (get-string-all port))
319 (exit-val (status:exit-val (close-pipe port))))
320 (assert (zero? exit-val))
321
322 (assert (string-contains
323 info
324 (string-append "Package: "
325 #+(package-name %bootstrap-guile))))
326
327 (assert (string-contains
328 info
329 (string-append "Version: "
330 #+(package-version %bootstrap-guile)))))
331
332 ;; Sanity check .deb contents.
333 (invoke "ar" "-xv" #$deb)
334 (assert (file-exists? "debian-binary"))
335 (assert (file-exists? "data.tar.gz"))
336 (assert (file-exists? "control.tar.gz"))
337
338 ;; Verify there are no hard links in data.tar.gz, as hard
339 ;; links would cause dpkg to fail unpacking the archive.
340 (define hard-links
341 (let ((port (open-pipe* OPEN_READ "tar" "-tvf" "data.tar.gz")))
342 (let loop ((hard-links '()))
343 (match (read-line port)
344 ((? eof-object?)
345 (assert (zero? (status:exit-val (close-pipe port))))
346 hard-links)
347 (line
348 (if (string-prefix? "u" line)
349 (loop (cons line hard-links))
350 (loop hard-links)))))))
351
352 (unless (null? hard-links)
353 (error "hard links found in data.tar.gz" hard-links))
354
355 ;; Verify the presence of the control files.
356 (invoke "tar" "-xf" "control.tar.gz")
357 (assert (file-exists? "control"))
358 (assert (and (file-exists? "postinst")
359 (= #o111 ;script is executable
360 (logand #o111 (stat:perms
361 (stat "postinst"))))))
362 (assert (file-exists? "triggers"))
363
364 (mkdir #$output))))))
365 (built-derivations (list check)))))
366
367 (test-end)
368
369 ;; Local Variables:
370 ;; eval: (put 'test-assertm 'scheme-indent-function 2)
371 ;; End: