Prefer local builds for "small" derivations.
[jackhill/guix/guix.git] / tests / derivations.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
3 ;;;
4 ;;; This file is part of GNU Guix.
5 ;;;
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.
10 ;;;
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.
15 ;;;
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/>.
18
19
20 (define-module (test-derivations)
21 #:use-module (guix derivations)
22 #:use-module (guix store)
23 #:use-module (guix utils)
24 #:use-module (guix hash)
25 #:use-module (guix base32)
26 #:use-module ((guix packages) #:select (package-derivation))
27 #:use-module ((gnu packages) #:select (search-bootstrap-binary))
28 #:use-module (gnu packages bootstrap)
29 #:use-module ((gnu packages guile) #:select (guile-1.8))
30 #:use-module (srfi srfi-1)
31 #:use-module (srfi srfi-11)
32 #:use-module (srfi srfi-26)
33 #:use-module (srfi srfi-34)
34 #:use-module (srfi srfi-64)
35 #:use-module (rnrs io ports)
36 #:use-module (rnrs bytevectors)
37 #:use-module (web uri)
38 #:use-module (ice-9 rdelim)
39 #:use-module (ice-9 regex)
40 #:use-module (ice-9 ftw)
41 #:use-module (ice-9 match))
42
43 (define %store
44 (false-if-exception (open-connection)))
45
46 (when %store
47 ;; Make sure we build everything by ourselves.
48 (set-build-options %store #:use-substitutes? #f)
49
50 ;; By default, use %BOOTSTRAP-GUILE for the current system.
51 (let ((drv (package-derivation %store %bootstrap-guile)))
52 (%guile-for-build drv)))
53
54 (define (bootstrap-binary name)
55 (let ((bin (search-bootstrap-binary name (%current-system))))
56 (and %store
57 (add-to-store %store name #t "sha256" bin))))
58
59 (define %bash
60 (bootstrap-binary "bash"))
61 (define %mkdir
62 (bootstrap-binary "mkdir"))
63
64 (define* (directory-contents dir #:optional (slurp get-bytevector-all))
65 "Return an alist representing the contents of DIR."
66 (define prefix-len (string-length dir))
67 (sort (file-system-fold (const #t) ; enter?
68 (lambda (path stat result) ; leaf
69 (alist-cons (string-drop path prefix-len)
70 (call-with-input-file path slurp)
71 result))
72 (lambda (path stat result) result) ; down
73 (lambda (path stat result) result) ; up
74 (lambda (path stat result) result) ; skip
75 (lambda (path stat errno result) result) ; error
76 '()
77 dir)
78 (lambda (e1 e2)
79 (string<? (car e1) (car e2)))))
80
81 (test-begin "derivations")
82
83 (test-assert "parse & export"
84 (let* ((f (search-path %load-path "tests/test.drv"))
85 (b1 (call-with-input-file f get-bytevector-all))
86 (d1 (read-derivation (open-bytevector-input-port b1)))
87 (b2 (call-with-bytevector-output-port (cut write-derivation d1 <>)))
88 (d2 (read-derivation (open-bytevector-input-port b2))))
89 (and (equal? b1 b2)
90 (equal? d1 d2))))
91
92 (test-skip (if %store 0 12))
93
94 (test-assert "add-to-store, flat"
95 (let* ((file (search-path %load-path "language/tree-il/spec.scm"))
96 (drv (add-to-store %store "flat-test" #f "sha256" file)))
97 (and (eq? 'regular (stat:type (stat drv)))
98 (valid-path? %store drv)
99 (equal? (call-with-input-file file get-bytevector-all)
100 (call-with-input-file drv get-bytevector-all)))))
101
102 (test-assert "add-to-store, recursive"
103 (let* ((dir (dirname (search-path %load-path "language/tree-il/spec.scm")))
104 (drv (add-to-store %store "dir-tree-test" #t "sha256" dir)))
105 (and (eq? 'directory (stat:type (stat drv)))
106 (valid-path? %store drv)
107 (equal? (directory-contents dir)
108 (directory-contents drv)))))
109
110 (test-assert "derivation with no inputs"
111 (let* ((builder (add-text-to-store %store "my-builder.sh"
112 "echo hello, world\n"
113 '()))
114 (drv (derivation %store "foo"
115 %bash `("-e" ,builder)
116 #:env-vars '(("HOME" . "/homeless")))))
117 (and (store-path? (derivation-file-name drv))
118 (valid-path? %store (derivation-file-name drv)))))
119
120 (test-assert "build derivation with 1 source"
121 (let* ((builder (add-text-to-store %store "my-builder.sh"
122 "echo hello, world > \"$out\"\n"
123 '()))
124 (drv (derivation %store "foo"
125 %bash `(,builder)
126 #:env-vars '(("HOME" . "/homeless")
127 ("zzz" . "Z!")
128 ("AAA" . "A!"))
129 #:inputs `((,%bash) (,builder))))
130 (succeeded?
131 (build-derivations %store (list drv))))
132 (and succeeded?
133 (let ((path (derivation->output-path drv)))
134 (and (valid-path? %store path)
135 (string=? (call-with-input-file path read-line)
136 "hello, world"))))))
137
138 (test-assert "derivation with local file as input"
139 (let* ((builder (add-text-to-store
140 %store "my-builder.sh"
141 "(while read line ; do echo \"$line\" ; done) < $in > $out"
142 '()))
143 (input (search-path %load-path "ice-9/boot-9.scm"))
144 (input* (add-to-store %store (basename input)
145 #t "sha256" input))
146 (drv (derivation %store "derivation-with-input-file"
147 %bash `(,builder)
148
149 ;; Cheat to pass the actual file name to the
150 ;; builder.
151 #:env-vars `(("in" . ,input*))
152
153 #:inputs `((,%bash)
154 (,builder)
155 (,input))))) ; ← local file name
156 (and (build-derivations %store (list drv))
157 ;; Note: we can't compare the files because the above trick alters
158 ;; the contents.
159 (valid-path? %store (derivation->output-path drv)))))
160
161 (test-assert "fixed-output derivation"
162 (let* ((builder (add-text-to-store %store "my-fixed-builder.sh"
163 "echo -n hello > $out" '()))
164 (hash (sha256 (string->utf8 "hello")))
165 (drv (derivation %store "fixed"
166 %bash `(,builder)
167 #:inputs `((,builder)) ; optional
168 #:hash hash #:hash-algo 'sha256))
169 (succeeded? (build-derivations %store (list drv))))
170 (and succeeded?
171 (let ((p (derivation->output-path drv)))
172 (and (equal? (string->utf8 "hello")
173 (call-with-input-file p get-bytevector-all))
174 (bytevector? (query-path-hash %store p)))))))
175
176 (test-assert "fixed-output derivation: output paths are equal"
177 (let* ((builder1 (add-text-to-store %store "fixed-builder1.sh"
178 "echo -n hello > $out" '()))
179 (builder2 (add-text-to-store %store "fixed-builder2.sh"
180 "echo hey; echo -n hello > $out" '()))
181 (hash (sha256 (string->utf8 "hello")))
182 (drv1 (derivation %store "fixed"
183 %bash `(,builder1)
184 #:hash hash #:hash-algo 'sha256))
185 (drv2 (derivation %store "fixed"
186 %bash `(,builder2)
187 #:hash hash #:hash-algo 'sha256))
188 (succeeded? (build-derivations %store (list drv1 drv2))))
189 (and succeeded?
190 (equal? (derivation->output-path drv1)
191 (derivation->output-path drv2)))))
192
193 (test-assert "derivation with a fixed-output input"
194 ;; A derivation D using a fixed-output derivation F doesn't has the same
195 ;; output path when passed F or F', as long as F and F' have the same output
196 ;; path.
197 (let* ((builder1 (add-text-to-store %store "fixed-builder1.sh"
198 "echo -n hello > $out" '()))
199 (builder2 (add-text-to-store %store "fixed-builder2.sh"
200 "echo hey; echo -n hello > $out" '()))
201 (hash (sha256 (string->utf8 "hello")))
202 (fixed1 (derivation %store "fixed"
203 %bash `(,builder1)
204 #:hash hash #:hash-algo 'sha256))
205 (fixed2 (derivation %store "fixed"
206 %bash `(,builder2)
207 #:hash hash #:hash-algo 'sha256))
208 (fixed-out (derivation->output-path fixed1))
209 (builder3 (add-text-to-store
210 %store "final-builder.sh"
211 ;; Use Bash hackery to avoid Coreutils.
212 "echo $in ; (read -u 3 c; echo $c) 3< $in > $out" '()))
213 (final1 (derivation %store "final"
214 %bash `(,builder3)
215 #:env-vars `(("in" . ,fixed-out))
216 #:inputs `((,%bash) (,builder3) (,fixed1))))
217 (final2 (derivation %store "final"
218 %bash `(,builder3)
219 #:env-vars `(("in" . ,fixed-out))
220 #:inputs `((,%bash) (,builder3) (,fixed2))))
221 (succeeded? (build-derivations %store
222 (list final1 final2))))
223 (and succeeded?
224 (equal? (derivation->output-path final1)
225 (derivation->output-path final2)))))
226
227 (test-assert "multiple-output derivation"
228 (let* ((builder (add-text-to-store %store "my-fixed-builder.sh"
229 "echo one > $out ; echo two > $second"
230 '()))
231 (drv (derivation %store "fixed"
232 %bash `(,builder)
233 #:env-vars '(("HOME" . "/homeless")
234 ("zzz" . "Z!")
235 ("AAA" . "A!"))
236 #:inputs `((,%bash) (,builder))
237 #:outputs '("out" "second")))
238 (succeeded? (build-derivations %store (list drv))))
239 (and succeeded?
240 (let ((one (derivation->output-path drv "out"))
241 (two (derivation->output-path drv "second")))
242 (and (lset= equal?
243 (derivation->output-paths drv)
244 `(("out" . ,one) ("second" . ,two)))
245 (eq? 'one (call-with-input-file one read))
246 (eq? 'two (call-with-input-file two read)))))))
247
248 (test-assert "multiple-output derivation, non-alphabetic order"
249 ;; Here, the outputs are not listed in alphabetic order. Yet, the store
250 ;; path computation must reorder them first.
251 (let* ((builder (add-text-to-store %store "my-fixed-builder.sh"
252 "echo one > $out ; echo two > $AAA"
253 '()))
254 (drv (derivation %store "fixed"
255 %bash `(,builder)
256 #:inputs `((,%bash) (,builder))
257 #:outputs '("out" "AAA")))
258 (succeeded? (build-derivations %store (list drv))))
259 (and succeeded?
260 (let ((one (derivation->output-path drv "out"))
261 (two (derivation->output-path drv "AAA")))
262 (and (eq? 'one (call-with-input-file one read))
263 (eq? 'two (call-with-input-file two read)))))))
264
265 (test-assert "multiple-output derivation, derivation-path->output-path"
266 (let* ((builder (add-text-to-store %store "builder.sh"
267 "echo one > $out ; echo two > $second"
268 '()))
269 (drv (derivation %store "multiple"
270 %bash `(,builder)
271 #:outputs '("out" "second")))
272 (drv-file (derivation-file-name drv))
273 (one (derivation->output-path drv "out"))
274 (two (derivation->output-path drv "second"))
275 (first (derivation-path->output-path drv-file "out"))
276 (second (derivation-path->output-path drv-file "second")))
277 (and (not (string=? one two))
278 (string-suffix? "-second" two)
279 (string=? first one)
280 (string=? second two))))
281
282 (test-assert "user of multiple-output derivation"
283 ;; Check whether specifying several inputs coming from the same
284 ;; multiple-output derivation works.
285 (let* ((builder1 (add-text-to-store %store "my-mo-builder.sh"
286 "echo one > $out ; echo two > $two"
287 '()))
288 (mdrv (derivation %store "multiple-output"
289 %bash `(,builder1)
290 #:inputs `((,%bash) (,builder1))
291 #:outputs '("out" "two")))
292 (builder2 (add-text-to-store %store "my-mo-user-builder.sh"
293 "read x < $one;
294 read y < $two;
295 echo \"($x $y)\" > $out"
296 '()))
297 (udrv (derivation %store "multiple-output-user"
298 %bash `(,builder2)
299 #:env-vars `(("one"
300 . ,(derivation->output-path
301 mdrv "out"))
302 ("two"
303 . ,(derivation->output-path
304 mdrv "two")))
305 #:inputs `((,%bash)
306 (,builder2)
307 ;; two occurrences of MDRV:
308 (,mdrv)
309 (,mdrv "two")))))
310 (and (build-derivations %store (list (pk 'udrv udrv)))
311 (let ((p (derivation->output-path udrv)))
312 (and (valid-path? %store p)
313 (equal? '(one two) (call-with-input-file p read)))))))
314
315 (test-assert "derivation with #:references-graphs"
316 (let* ((input1 (add-text-to-store %store "foo" "hello"
317 (list %bash)))
318 (input2 (add-text-to-store %store "bar"
319 (number->string (random 7777))
320 (list input1)))
321 (builder (add-text-to-store %store "build-graph"
322 (format #f "
323 ~a $out
324 (while read l ; do echo $l ; done) < bash > $out/bash
325 (while read l ; do echo $l ; done) < input1 > $out/input1
326 (while read l ; do echo $l ; done) < input2 > $out/input2"
327 %mkdir)
328 (list %mkdir)))
329 (drv (derivation %store "closure-graphs"
330 %bash `(,builder)
331 #:references-graphs
332 `(("bash" . ,%bash)
333 ("input1" . ,input1)
334 ("input2" . ,input2))
335 #:inputs `((,%bash) (,builder))))
336 (out (derivation->output-path drv)))
337 (define (deps path . deps)
338 (let ((count (length deps)))
339 (string-append path "\n\n" (number->string count) "\n"
340 (string-join (sort deps string<?) "\n")
341 (if (zero? count) "" "\n"))))
342
343 (and (build-derivations %store (list drv))
344 (equal? (directory-contents out get-string-all)
345 `(("/bash" . ,(string-append %bash "\n\n0\n"))
346 ("/input1" . ,(if (string>? input1 %bash)
347 (string-append (deps %bash)
348 (deps input1 %bash))
349 (string-append (deps input1 %bash)
350 (deps %bash))))
351 ("/input2" . ,(string-concatenate
352 (map cdr
353 (sort
354 (map (lambda (p d)
355 (cons p (apply deps p d)))
356 (list %bash input1 input2)
357 (list '() (list %bash) (list input1)))
358 (lambda (x y)
359 (match x
360 ((p1 . _)
361 (match y
362 ((p2 . _)
363 (string<? p1 p2)))))))))))))))
364
365 \f
366 (define %coreutils
367 (false-if-exception
368 (and (getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)
369 (or (package-derivation %store %bootstrap-coreutils&co)
370 (nixpkgs-derivation "coreutils")))))
371
372 (test-skip (if %coreutils 0 1))
373
374 (test-assert "build derivation with coreutils"
375 (let* ((builder
376 (add-text-to-store %store "build-with-coreutils.sh"
377 "echo $PATH ; mkdir --version ; mkdir $out ; touch $out/good"
378 '()))
379 (drv
380 (derivation %store "foo"
381 %bash `(,builder)
382 #:env-vars `(("PATH" .
383 ,(string-append
384 (derivation->output-path %coreutils)
385 "/bin")))
386 #:inputs `((,builder)
387 (,%coreutils))))
388 (succeeded?
389 (build-derivations %store (list drv))))
390 (and succeeded?
391 (let ((p (derivation->output-path drv)))
392 (and (valid-path? %store p)
393 (file-exists? (string-append p "/good")))))))
394
395 (test-skip (if (%guile-for-build) 0 8))
396
397 (test-assert "build-expression->derivation and derivation-prerequisites"
398 (let ((drv (build-expression->derivation %store "fail" #f)))
399 (any (match-lambda
400 (($ <derivation-input> path)
401 (string=? path (derivation-file-name (%guile-for-build)))))
402 (derivation-prerequisites drv))))
403
404 (test-assert "build-expression->derivation without inputs"
405 (let* ((builder '(begin
406 (mkdir %output)
407 (call-with-output-file (string-append %output "/test")
408 (lambda (p)
409 (display '(hello guix) p)))))
410 (drv (build-expression->derivation %store "goo" builder))
411 (succeeded? (build-derivations %store (list drv))))
412 (and succeeded?
413 (let ((p (derivation->output-path drv)))
414 (equal? '(hello guix)
415 (call-with-input-file (string-append p "/test") read))))))
416
417 (test-assert "build-expression->derivation and max-silent-time"
418 (let* ((store (let ((s (open-connection)))
419 (set-build-options s #:max-silent-time 1)
420 s))
421 (builder '(begin (sleep 100) (mkdir %output) #t))
422 (drv (build-expression->derivation store "silent" builder))
423 (out-path (derivation->output-path drv)))
424 (guard (c ((nix-protocol-error? c)
425 (and (string-contains (nix-protocol-error-message c)
426 "failed")
427 (not (valid-path? store out-path)))))
428 (build-derivations store (list drv))
429 #f)))
430
431 (test-assert "build-expression->derivation and derivation-prerequisites-to-build"
432 (let ((drv (build-expression->derivation %store "fail" #f)))
433 ;; The only direct dependency is (%guile-for-build) and it's already
434 ;; built.
435 (null? (derivation-prerequisites-to-build %store drv))))
436
437 (test-assert "derivation-prerequisites-to-build when outputs already present"
438 (let* ((builder '(begin (mkdir %output) #t))
439 (input-drv (build-expression->derivation %store "input" builder))
440 (input-path (derivation-output-path
441 (assoc-ref (derivation-outputs input-drv)
442 "out")))
443 (drv (build-expression->derivation %store "something" builder
444 #:inputs
445 `(("i" ,input-drv))))
446 (output (derivation->output-path drv)))
447 ;; Make sure these things are not already built.
448 (when (valid-path? %store input-path)
449 (delete-paths %store (list input-path)))
450 (when (valid-path? %store output)
451 (delete-paths %store (list output)))
452
453 (and (equal? (map derivation-input-path
454 (derivation-prerequisites-to-build %store drv))
455 (list (derivation-file-name input-drv)))
456
457 ;; Build DRV and delete its input.
458 (build-derivations %store (list drv))
459 (delete-paths %store (list input-path))
460 (not (valid-path? %store input-path))
461
462 ;; Now INPUT-PATH is missing, yet it shouldn't be listed as a
463 ;; prerequisite to build because DRV itself is already built.
464 (null? (derivation-prerequisites-to-build %store drv)))))
465
466 (test-skip (if (getenv "GUIX_BINARY_SUBSTITUTE_URL") 0 1))
467 (test-assert "derivation-prerequisites-to-build and substitutes"
468 (let* ((store (open-connection))
469 (drv (build-expression->derivation store "prereq-subst"
470 (random 1000)))
471 (output (derivation->output-path drv))
472 (dir (and=> (getenv "GUIX_BINARY_SUBSTITUTE_URL")
473 (compose uri-path string->uri))))
474 ;; Create fake substituter data, to be read by `substitute-binary'.
475 (call-with-output-file (string-append dir "/nix-cache-info")
476 (lambda (p)
477 (format p "StoreDir: ~a\nWantMassQuery: 0\n"
478 (%store-prefix))))
479 (call-with-output-file (string-append dir "/" (store-path-hash-part output)
480 ".narinfo")
481 (lambda (p)
482 (format p "StorePath: ~a
483 URL: ~a
484 Compression: none
485 NarSize: 1234
486 References:
487 System: ~a
488 Deriver: ~a~%"
489 output ; StorePath
490 (string-append dir "/example.nar") ; URL
491 (%current-system) ; System
492 (basename
493 (derivation-file-name drv))))) ; Deriver
494
495 (let-values (((build download)
496 (derivation-prerequisites-to-build store drv))
497 ((build* download*)
498 (derivation-prerequisites-to-build store drv
499 #:use-substitutes? #f)))
500 (pk build download build* download*)
501 (and (null? build)
502 (equal? download (list output))
503 (null? download*)
504 (null? build*)))))
505
506 (test-assert "build-expression->derivation with expression returning #f"
507 (let* ((builder '(begin
508 (mkdir %output)
509 #f)) ; fail!
510 (drv (build-expression->derivation %store "fail" builder))
511 (out-path (derivation->output-path drv)))
512 (guard (c ((nix-protocol-error? c)
513 ;; Note that the output path may exist at this point, but it
514 ;; is invalid.
515 (and (string-match "build .* failed"
516 (nix-protocol-error-message c))
517 (not (valid-path? %store out-path)))))
518 (build-derivations %store (list drv))
519 #f)))
520
521 (test-assert "build-expression->derivation with two outputs"
522 (let* ((builder '(begin
523 (call-with-output-file (assoc-ref %outputs "out")
524 (lambda (p)
525 (display '(hello) p)))
526 (call-with-output-file (assoc-ref %outputs "second")
527 (lambda (p)
528 (display '(world) p)))))
529 (drv (build-expression->derivation %store "double" builder
530 #:outputs '("out"
531 "second")))
532 (succeeded? (build-derivations %store (list drv))))
533 (and succeeded?
534 (let ((one (derivation->output-path drv))
535 (two (derivation->output-path drv "second")))
536 (and (equal? '(hello) (call-with-input-file one read))
537 (equal? '(world) (call-with-input-file two read)))))))
538
539 (test-skip (if %coreutils 0 1))
540 (test-assert "build-expression->derivation with one input"
541 (let* ((builder '(call-with-output-file %output
542 (lambda (p)
543 (let ((cu (assoc-ref %build-inputs "cu")))
544 (close 1)
545 (dup2 (port->fdes p) 1)
546 (execl (string-append cu "/bin/uname")
547 "uname" "-a")))))
548 (drv (build-expression->derivation %store "uname" builder
549 #:inputs
550 `(("cu" ,%coreutils))))
551 (succeeded? (build-derivations %store (list drv))))
552 (and succeeded?
553 (let ((p (derivation->output-path drv)))
554 (string-contains (call-with-input-file p read-line) "GNU")))))
555
556 (test-assert "imported-files"
557 (let* ((files `(("x" . ,(search-path %load-path "ice-9/q.scm"))
558 ("a/b/c" . ,(search-path %load-path
559 "guix/derivations.scm"))
560 ("p/q" . ,(search-path %load-path "guix.scm"))
561 ("p/z" . ,(search-path %load-path "guix/store.scm"))))
562 (drv (imported-files %store files)))
563 (and (build-derivations %store (list drv))
564 (let ((dir (derivation->output-path drv)))
565 (every (match-lambda
566 ((path . source)
567 (equal? (call-with-input-file (string-append dir "/" path)
568 get-bytevector-all)
569 (call-with-input-file source
570 get-bytevector-all))))
571 files)))))
572
573 (test-assert "build-expression->derivation with modules"
574 (let* ((builder `(begin
575 (use-modules (guix build utils))
576 (let ((out (assoc-ref %outputs "out")))
577 (mkdir-p (string-append out "/guile/guix/nix"))
578 #t)))
579 (drv (build-expression->derivation %store "test-with-modules"
580 builder
581 #:modules
582 '((guix build utils)))))
583 (and (build-derivations %store (list drv))
584 (let* ((p (derivation->output-path drv))
585 (s (stat (string-append p "/guile/guix/nix"))))
586 (eq? (stat:type s) 'directory)))))
587
588 (test-assert "build-expression->derivation: same fixed-output path"
589 (let* ((builder1 '(call-with-output-file %output
590 (lambda (p)
591 (write "hello" p))))
592 (builder2 '(call-with-output-file (pk 'difference-here! %output)
593 (lambda (p)
594 (write "hello" p))))
595 (hash (sha256 (string->utf8 "hello")))
596 (input1 (build-expression->derivation %store "fixed" builder1
597 #:hash hash
598 #:hash-algo 'sha256))
599 (input2 (build-expression->derivation %store "fixed" builder2
600 #:hash hash
601 #:hash-algo 'sha256))
602 (succeeded? (build-derivations %store (list input1 input2))))
603 (and succeeded?
604 (not (string=? (derivation-file-name input1)
605 (derivation-file-name input2)))
606 (string=? (derivation->output-path input1)
607 (derivation->output-path input2)))))
608
609 (test-assert "build-expression->derivation with a fixed-output input"
610 (let* ((builder1 '(call-with-output-file %output
611 (lambda (p)
612 (write "hello" p))))
613 (builder2 '(call-with-output-file (pk 'difference-here! %output)
614 (lambda (p)
615 (write "hello" p))))
616 (hash (sha256 (string->utf8 "hello")))
617 (input1 (build-expression->derivation %store "fixed" builder1
618 #:hash hash
619 #:hash-algo 'sha256))
620 (input2 (build-expression->derivation %store "fixed" builder2
621 #:hash hash
622 #:hash-algo 'sha256))
623 (builder3 '(let ((input (assoc-ref %build-inputs "input")))
624 (call-with-output-file %output
625 (lambda (out)
626 (format #f "My input is ~a.~%" input)))))
627 (final1 (build-expression->derivation %store "final" builder3
628 #:inputs
629 `(("input" ,input1))))
630 (final2 (build-expression->derivation %store "final" builder3
631 #:inputs
632 `(("input" ,input2)))))
633 (and (string=? (derivation->output-path final1)
634 (derivation->output-path final2))
635 (string=? (derivation->output-path final1)
636 (derivation-path->output-path
637 (derivation-file-name final1)))
638 (build-derivations %store (list final1 final2)))))
639
640 (test-assert "build-expression->derivation with #:references-graphs"
641 (let* ((input (add-text-to-store %store "foo" "hello"
642 (list %bash %mkdir)))
643 (builder '(copy-file "input" %output))
644 (drv (build-expression->derivation %store "references-graphs"
645 builder
646 #:references-graphs
647 `(("input" . ,input))))
648 (out (derivation->output-path drv)))
649 (define (deps path . deps)
650 (let ((count (length deps)))
651 (string-append path "\n\n" (number->string count) "\n"
652 (string-join (sort deps string<?) "\n")
653 (if (zero? count) "" "\n"))))
654
655 (and (build-derivations %store (list drv))
656 (equal? (call-with-input-file out get-string-all)
657 (string-concatenate
658 (map cdr
659 (sort (map (lambda (p d)
660 (cons p (apply deps p d)))
661 (list input %bash %mkdir)
662 (list (list %bash %mkdir)
663 '() '()))
664 (lambda (x y)
665 (match x
666 ((p1 . _)
667 (match y
668 ((p2 . _)
669 (string<? p1 p2)))))))))))))
670
671
672 (test-equal "map-derivation"
673 "hello"
674 (let* ((joke (package-derivation %store guile-1.8))
675 (good (package-derivation %store %bootstrap-guile))
676 (drv1 (build-expression->derivation %store "original-drv1"
677 #f ; systematically fail
678 #:guile-for-build joke))
679 (drv2 (build-expression->derivation %store "original-drv2"
680 '(call-with-output-file %output
681 (lambda (p)
682 (display "hello" p)))))
683 (drv3 (build-expression->derivation %store "drv-to-remap"
684 '(let ((in (assoc-ref
685 %build-inputs "in")))
686 (copy-file in %output))
687 #:inputs `(("in" ,drv1))
688 #:guile-for-build joke))
689 (drv4 (map-derivation %store drv3 `((,drv1 . ,drv2)
690 (,joke . ,good))))
691 (out (derivation->output-path drv4)))
692 (and (build-derivations %store (list (pk 'remapped drv4)))
693 (call-with-input-file out get-string-all))))
694
695 (test-equal "map-derivation, sources"
696 "hello"
697 (let* ((script1 (add-text-to-store %store "fail.sh" "exit 1"))
698 (script2 (add-text-to-store %store "hi.sh" "echo -n hello > $out"))
699 (bash-full (package-derivation %store (@ (gnu packages bash) bash)))
700 (drv1 (derivation %store "drv-to-remap"
701
702 ;; XXX: This wouldn't work in practice, but if
703 ;; we append "/bin/bash" then we can't replace
704 ;; it with the bootstrap bash, which is a
705 ;; single file.
706 (derivation->output-path bash-full)
707
708 `("-e" ,script1)
709 #:inputs `((,bash-full) (,script1))))
710 (drv2 (map-derivation %store drv1
711 `((,bash-full . ,%bash)
712 (,script1 . ,script2))))
713 (out (derivation->output-path drv2)))
714 (and (build-derivations %store (list (pk 'remapped* drv2)))
715 (call-with-input-file out get-string-all))))
716
717 (test-end)
718
719 \f
720 (exit (= (test-runner-fail-count (test-runner-current)) 0))