1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
4 ;;; This file is part of GNU Guix.
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19 (define-module (test-grafts)
20 #:use-module (guix gexp)
21 #:use-module (guix monads)
22 #:use-module (guix derivations)
23 #:use-module (guix store)
24 #:use-module (guix utils)
25 #:use-module (guix grafts)
26 #:use-module (guix tests)
27 #:use-module ((gnu packages) #:select (search-bootstrap-binary))
28 #:use-module (gnu packages bootstrap)
29 #:use-module (srfi srfi-1)
30 #:use-module (srfi srfi-64)
31 #:use-module (rnrs bytevectors)
32 #:use-module (rnrs io ports)
33 #:use-module (ice-9 vlist))
36 (open-connection-for-tests))
38 (define (bootstrap-binary name)
39 (let ((bin (search-bootstrap-binary name (%current-system))))
41 (add-to-store %store name #t "sha256" bin))))
44 (bootstrap-binary "bash"))
46 (bootstrap-binary "mkdir"))
51 (test-equal "graft-derivation, grafted item is a direct dependency"
52 '((type . graft) (graft (count . 2)))
56 (symlink %output "self")
57 (call-with-output-file "text"
59 (format output "foo/~a/bar" ,%mkdir)))
60 (symlink ,%bash "sh")))
61 (orig (build-expression->derivation %store "grafted" build
62 #:inputs `(("a" ,%bash)
64 (one (add-text-to-store %store "bash" "fake bash"))
65 (two (build-expression->derivation %store "mkdir"
66 '(call-with-output-file %output
68 (display "fake mkdir" port)))))
69 (grafted (graft-derivation %store orig
75 (replacement two))))))
76 (and (build-derivations %store (list grafted))
77 (let ((properties (derivation-properties grafted))
78 (two (derivation->output-path two))
79 (grafted (derivation->output-path grafted)))
80 (and (string=? (format #f "foo/~a/bar" two)
81 (call-with-input-file (string-append grafted "/text")
83 (string=? (readlink (string-append grafted "/sh")) one)
84 (string=? (readlink (string-append grafted "/self"))
88 (test-assert "graft-derivation, grafted item uses a different name"
92 (symlink %output "self")
93 (symlink ,%bash "sh")))
94 (orig (build-expression->derivation %store "grafted" build
95 #:inputs `(("a" ,%bash))))
96 (repl (add-text-to-store %store "BaSH" "fake bash"))
97 (grafted (graft-derivation %store orig
100 (replacement repl))))))
101 (and (build-derivations %store (list grafted))
102 (let ((grafted (derivation->output-path grafted)))
103 (and (string=? (readlink (string-append grafted "/sh")) repl)
104 (string=? (readlink (string-append grafted "/self"))
107 ;; Make sure 'derivation-file-name' always gets to see an absolute file name.
108 (fluid-set! %file-port-name-canonicalization 'absolute)
110 (test-assert "graft-derivation, grafted item is an indirect dependency"
111 (let* ((build `(begin
114 (symlink %output "self")
115 (call-with-output-file "text"
117 (format output "foo/~a/bar" ,%mkdir)))
118 (symlink ,%bash "sh")))
119 (dep (build-expression->derivation %store "dep" build
120 #:inputs `(("a" ,%bash)
122 (orig (build-expression->derivation %store "thing"
124 (assoc-ref %build-inputs
127 #:inputs `(("dep" ,dep))))
128 (one (add-text-to-store %store "bash" "fake bash"))
129 (two (build-expression->derivation %store "mkdir"
130 '(call-with-output-file %output
132 (display "fake mkdir" port)))))
133 (grafted (graft-derivation %store orig
139 (replacement two))))))
140 (and (build-derivations %store (list grafted))
141 (let* ((two (derivation->output-path two))
142 (grafted (derivation->output-path grafted))
143 (dep (readlink grafted)))
144 (and (string=? (format #f "foo/~a/bar" two)
145 (call-with-input-file (string-append dep "/text")
147 (string=? (readlink (string-append dep "/sh")) one)
148 (string=? (readlink (string-append dep "/self")) dep)
149 (equal? (references %store grafted) (list dep))
152 (references %store dep)))))))
154 (test-assert "graft-derivation, preserve empty directories"
155 (run-with-store %store
156 (mlet* %store-monad ((fake (text-file "bash" "Fake bash."))
160 (drv (gexp->derivation
162 (with-imported-modules '((guix build utils))
164 (use-modules (guix build utils))
165 (mkdir-p (string-append #$output
168 (string-append #$output
170 (grafted ((store-lift graft-derivation) drv
172 (_ (built-derivations (list grafted)))
173 (out -> (derivation->output-path grafted)))
174 (return (and (string=? (readlink (string-append out "/bash"))
176 (file-is-directory? (string-append out "/a/b/c/d")))))))
178 (test-assert "graft-derivation, no dependencies on grafted output"
179 (run-with-store %store
180 (mlet* %store-monad ((fake (text-file "bash" "Fake bash."))
184 (drv (gexp->derivation "foo" #~(mkdir #$output)))
185 (grafted ((store-lift graft-derivation) drv
187 (return (eq? grafted drv)))))
189 (test-assert "graft-derivation, multiple outputs"
190 (let* ((build `(begin
191 (symlink (assoc-ref %build-inputs "a")
192 (assoc-ref %outputs "one"))
193 (symlink (assoc-ref %outputs "one")
194 (assoc-ref %outputs "two"))))
195 (orig (build-expression->derivation %store "grafted" build
196 #:inputs `(("a" ,%bash))
197 #:outputs '("one" "two")))
198 (repl (add-text-to-store %store "bash" "fake bash"))
199 (grafted (graft-derivation %store orig
202 (replacement repl))))))
203 (and (build-derivations %store (list grafted))
204 (let ((one (derivation->output-path grafted "one"))
205 (two (derivation->output-path grafted "two")))
206 (and (string=? (readlink one) repl)
207 (string=? (readlink two) one))))))
209 (test-assert "graft-derivation, replaced derivation has multiple outputs"
210 ;; Here we have a replacement just for output "one" of P1 and not for the
211 ;; other output. Make sure the graft for P1:one correctly applies to the
212 ;; dependents of P1. See <http://bugs.gnu.org/24712>.
213 (let* ((p1 (build-expression->derivation
215 `(let ((one (assoc-ref %outputs "one"))
216 (two (assoc-ref %outputs "two")))
219 #:outputs '("one" "two")))
220 (p1r (build-expression->derivation
222 `(let ((other (assoc-ref %outputs "ONE")))
224 (call-with-output-file (string-append other "/replacement")
227 (p2 (build-expression->derivation
229 `(let ((out (assoc-ref %outputs "aaa")))
230 (mkdir (assoc-ref %outputs "zzz"))
231 (mkdir out) (chdir out)
232 (symlink (assoc-ref %build-inputs "p1:one") "one")
233 (symlink (assoc-ref %build-inputs "p1:two") "two"))
234 #:outputs '("aaa" "zzz")
235 #:inputs `(("p1:one" ,p1 "one")
236 ("p1:two" ,p1 "two"))))
237 (p3 (build-expression->derivation
239 `(symlink (assoc-ref %build-inputs "p2:aaa")
240 (assoc-ref %outputs "out"))
241 #:inputs `(("p2:aaa" ,p2 "aaa")
242 ("p2:zzz" ,p2 "zzz"))))
245 (origin-output "one")
247 (replacement-output "ONE")))
248 (p3d (graft-derivation %store p3 (list p1g))))
250 (and (not (find (lambda (input)
251 ;; INPUT should not be P2:zzz since the result of P3
252 ;; does not depend on it. See
253 ;; <http://bugs.gnu.org/24886>.
254 (and (string=? (derivation-input-path input)
255 (derivation-file-name p2))
257 (derivation-input-sub-derivations input))))
258 (derivation-inputs p3d)))
260 (build-derivations %store (list p3d))
261 (let ((out (derivation->output-path (pk 'p2d p3d))))
262 (and (not (string=? (readlink out)
263 (derivation->output-path p2 "aaa")))
264 (string=? (derivation->output-path p1 "two")
265 (readlink (string-append out "/two")))
266 (file-exists? (string-append out "/one/replacement")))))))
268 (test-assert "graft-derivation with #:outputs"
269 ;; Call 'graft-derivation' with a narrowed set of outputs passed as
271 (let* ((p1 (build-expression->derivation
273 `(let ((one (assoc-ref %outputs "one"))
274 (two (assoc-ref %outputs "two")))
277 #:outputs '("one" "two")))
278 (p1r (build-expression->derivation
280 `(let ((other (assoc-ref %outputs "ONE")))
282 (call-with-output-file (string-append other "/replacement")
285 (p2 (build-expression->derivation
287 `(let ((aaa (assoc-ref %outputs "aaa"))
288 (zzz (assoc-ref %outputs "zzz")))
289 (mkdir zzz) (chdir zzz)
290 (mkdir aaa) (chdir aaa)
291 (symlink (assoc-ref %build-inputs "p1:two") "two"))
292 #:outputs '("aaa" "zzz")
293 #:inputs `(("p1:one" ,p1 "one")
294 ("p1:two" ,p1 "two"))))
297 (origin-output "one")
299 (replacement-output "ONE")))
300 (p2g (graft-derivation %store p2 (list p1g)
301 #:outputs '("aaa"))))
302 ;; P2:aaa depends on P1:two, but not on P1:one, so nothing to graft.
305 (test-equal "graft-derivation, unused outputs not depended on"
308 ;; Make sure that the result of 'graft-derivation' does not pull outputs
309 ;; that are irrelevant to the grafting process. See
310 ;; <http://bugs.gnu.org/24886>.
311 (let* ((p1 (build-expression->derivation
313 `(let ((one (assoc-ref %outputs "one"))
314 (two (assoc-ref %outputs "two")))
317 #:outputs '("one" "two")))
318 (p1r (build-expression->derivation
320 `(let ((other (assoc-ref %outputs "ONE")))
322 (call-with-output-file (string-append other "/replacement")
325 (p2 (build-expression->derivation
327 `(let ((aaa (assoc-ref %outputs "aaa"))
328 (zzz (assoc-ref %outputs "zzz")))
329 (mkdir zzz) (chdir zzz)
330 (symlink (assoc-ref %build-inputs "p1:two") "two")
331 (mkdir aaa) (chdir aaa)
332 (symlink (assoc-ref %build-inputs "p1:one") "one"))
333 #:outputs '("aaa" "zzz")
334 #:inputs `(("p1:one" ,p1 "one")
335 ("p1:two" ,p1 "two"))))
338 (origin-output "one")
340 (replacement-output "ONE")))
341 (p2g (graft-derivation %store p2 (list p1g)
342 #:outputs '("aaa"))))
344 ;; Here P2G should only depend on P1:one and P1R:one; it must not depend
345 ;; on P1:two or P1R:two since these are unused in the grafting process.
346 (and (not (eq? p2g p2))
347 (let* ((inputs (derivation-inputs p2g))
348 (match-input (lambda (drv)
350 (string=? (derivation-input-path input)
351 (derivation-file-name drv)))))
352 (p1-inputs (filter (match-input p1) inputs))
353 (p1r-inputs (filter (match-input p1r) inputs))
354 (p2-inputs (filter (match-input p2) inputs)))
355 (and (equal? p1-inputs
356 (list (derivation-input p1 '("one"))))
358 (list (derivation-input p1r '("ONE"))))
360 (list (derivation-input p2 '("aaa"))))
361 (derivation-output-names p2g))))))
363 (test-assert "graft-derivation, renaming" ;<http://bugs.gnu.org/23132>
364 (let* ((build `(begin
365 (use-modules (guix build utils))
366 (mkdir-p (string-append (assoc-ref %outputs "out") "/"
367 (assoc-ref %build-inputs "in")))))
368 (orig (build-expression->derivation %store "thing-to-graft" build
369 #:modules '((guix build utils))
370 #:inputs `(("in" ,%bash))))
371 (repl (add-text-to-store %store "bash" "fake bash"))
372 (grafted (graft-derivation %store orig
375 (replacement repl))))))
376 (and (build-derivations %store (list grafted))
377 (let ((out (derivation->output-path grafted)))
378 (file-is-directory? (string-append out "/" repl))))))
380 (test-assert "graft-derivation, grafts are not shadowed"
381 ;; We build a DAG as below, where dotted arrows represent replacements and
382 ;; solid arrows represent dependencies:
384 ;; P1 ·············> P1R
385 ;; |\__________________.
387 ;; P2 ·············> P2R
392 ;; We want to make sure that the two grafts we want to apply to P3 are
393 ;; honored and not shadowed by other computed grafts.
394 (let* ((p1 (build-expression->derivation
396 '(mkdir (assoc-ref %outputs "out"))))
397 (p1r (build-expression->derivation
399 '(let ((out (assoc-ref %outputs "out")))
401 (call-with-output-file (string-append out "/replacement")
403 (p2 (build-expression->derivation
405 `(let ((out (assoc-ref %outputs "out")))
408 (symlink (assoc-ref %build-inputs "p1") "p1"))
409 #:inputs `(("p1" ,p1))))
410 (p2r (build-expression->derivation
412 `(let ((out (assoc-ref %outputs "out")))
415 (symlink (assoc-ref %build-inputs "p1") "p1")
416 (call-with-output-file (string-append out "/replacement")
418 #:inputs `(("p1" ,p1))))
419 (p3 (build-expression->derivation
421 `(let ((out (assoc-ref %outputs "out")))
424 (symlink (assoc-ref %build-inputs "p2") "p2"))
425 #:inputs `(("p2" ,p2))))
431 (replacement (graft-derivation %store p2r (list p1g)))))
432 (p3d (graft-derivation %store p3 (list p1g p2g))))
433 (and (build-derivations %store (list p3d))
434 (let ((out (derivation->output-path (pk p3d))))
435 ;; Make sure OUT refers to the replacement of P2, which in turn
436 ;; refers to the replacement of P1, as specified by P1G and P2G.
437 ;; It used to be the case that P2G would be shadowed by a simple
438 ;; P2->P2R graft, which is not what we want.
439 (and (file-exists? (string-append out "/p2/replacement"))
440 (file-exists? (string-append out "/p2/p1/replacement")))))))
443 ;; Must be equal to REQUEST-SIZE in 'replace-store-references'.
446 (test-equal "replace-store-references, <http://bugs.gnu.org/28212>"
447 (string-append (make-string (- buffer-size 47) #\a)
448 "/gnu/store/" (make-string 32 #\8)
450 (list->string (map integer->char (iota 77 33))))
452 ;; Create input data where the right-hand-size of the dash ("-something"
453 ;; here) goes beyond the end of the internal buffer of
454 ;; 'replace-store-references'.
455 (let* ((content (string-append (make-string (- buffer-size 47) #\a)
456 "/gnu/store/" (make-string 32 #\7)
459 (map integer->char (iota 77 33)))))
460 (replacement (alist->vhash
461 `((,(make-string 32 #\7)
462 . ,(string->utf8 (string-append
465 (call-with-output-string
467 ((@@ (guix build graft) replace-store-references)
468 (open-input-string content) output