distro: Update bootstrap glibc package.
[jackhill/guix/guix.git] / guix / derivations.scm
CommitLineData
77d3cf08
LC
1;;; Guix --- Nix package management from Guile. -*- coding: utf-8 -*-
2;;; Copyright (C) 2012 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of Guix.
5;;;
6;;; 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;;; 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 Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix derivations)
20 #:use-module (srfi srfi-1)
21 #:use-module (srfi srfi-9)
22 #:use-module (srfi srfi-26)
23 #:use-module (rnrs io ports)
24 #:use-module (rnrs bytevectors)
25 #:use-module (ice-9 match)
26 #:use-module (ice-9 rdelim)
69f90f5c 27 #:use-module (guix store)
26bbbb95 28 #:use-module (guix utils)
9a20830e
LC
29 #:export (<derivation>
30 derivation?
77d3cf08
LC
31 derivation-outputs
32 derivation-inputs
33 derivation-sources
34 derivation-system
35 derivation-builder-arguments
36 derivation-builder-environment-vars
9a20830e
LC
37 derivation-prerequisites
38 derivation-prerequisites-to-build
77d3cf08 39
9a20830e 40 <derivation-output>
77d3cf08
LC
41 derivation-output?
42 derivation-output-path
43 derivation-output-hash-algo
44 derivation-output-hash
45
9a20830e 46 <derivation-input>
77d3cf08
LC
47 derivation-input?
48 derivation-input-path
49 derivation-input-sub-derivations
50
51 fixed-output-derivation?
341c6fdd
LC
52 derivation-hash
53
54 read-derivation
26bbbb95 55 write-derivation
de4c3f26 56 derivation-path->output-path
d9085c23
LC
57 derivation
58
59 %guile-for-build
99634e3f
LC
60 build-expression->derivation
61 imported-files))
77d3cf08
LC
62
63;;;
64;;; Nix derivations, as implemented in Nix's `derivations.cc'.
65;;;
66
67(define-record-type <derivation>
68 (make-derivation outputs inputs sources system builder args env-vars)
69 derivation?
70 (outputs derivation-outputs) ; list of name/<derivation-output> pairs
71 (inputs derivation-inputs) ; list of <derivation-input>
72 (sources derivation-sources) ; list of store paths
73 (system derivation-system) ; string
74 (builder derivation-builder) ; store path
75 (args derivation-builder-arguments) ; list of strings
76 (env-vars derivation-builder-environment-vars)) ; list of name/value pairs
77
78(define-record-type <derivation-output>
79 (make-derivation-output path hash-algo hash)
80 derivation-output?
81 (path derivation-output-path) ; store path
82 (hash-algo derivation-output-hash-algo) ; symbol | #f
749c6567 83 (hash derivation-output-hash)) ; bytevector | #f
77d3cf08
LC
84
85(define-record-type <derivation-input>
86 (make-derivation-input path sub-derivations)
87 derivation-input?
88 (path derivation-input-path) ; store path
89 (sub-derivations derivation-input-sub-derivations)) ; list of strings
90
91(define (fixed-output-derivation? drv)
92 "Return #t if DRV is a fixed-output derivation, such as the result of a
93download with a fixed hash (aka. `fetchurl')."
94 (match drv
95 (($ <derivation>
96 (($ <derivation-output> _ (? symbol?) (? string?))))
97 #t)
98 (_ #f)))
99
9a20830e
LC
100(define (derivation-prerequisites drv)
101 "Return the list of derivation-inputs required to build DRV, recursively."
102 (let loop ((drv drv)
103 (result '()))
104 (let ((inputs (remove (cut member <> result) ; XXX: quadratic
105 (derivation-inputs drv))))
106 (fold loop
107 (append inputs result)
108 (map (lambda (i)
109 (call-with-input-file (derivation-input-path i)
110 read-derivation))
111 inputs)))))
112
113(define (derivation-prerequisites-to-build store drv)
114 "Return the list of derivation-inputs required to build DRV and not already
115available in STORE, recursively."
116 (define input-built?
117 (match-lambda
118 (($ <derivation-input> path sub-drvs)
119 (let ((out (map (cut derivation-path->output-path path <>)
120 sub-drvs)))
121 (any (cut valid-path? store <>) out)))))
122
123 (let loop ((drv drv)
124 (result '()))
125 (let ((inputs (remove (lambda (i)
126 (or (member i result) ; XXX: quadratic
127 (input-built? i)))
128 (derivation-inputs drv))))
129 (fold loop
130 (append inputs result)
131 (map (lambda (i)
132 (call-with-input-file (derivation-input-path i)
133 read-derivation))
134 inputs)))))
135
77d3cf08
LC
136(define (read-derivation drv-port)
137 "Read the derivation from DRV-PORT and return the corresponding
138<derivation> object."
139
140 (define comma (string->symbol ","))
141
142 (define (ununquote x)
143 (match x
144 (('unquote x) (ununquote x))
145 ((x ...) (map ununquote x))
146 (_ x)))
147
148 (define (outputs->alist x)
149 (fold-right (lambda (output result)
150 (match output
151 ((name path "" "")
152 (alist-cons name
153 (make-derivation-output path #f #f)
154 result))
155 ((name path hash-algo hash)
156 ;; fixed-output
749c6567
LC
157 (let ((algo (string->symbol hash-algo))
158 (hash (base16-string->bytevector hash)))
77d3cf08
LC
159 (alist-cons name
160 (make-derivation-output path algo hash)
161 result)))))
162 '()
163 x))
164
165 (define (make-input-drvs x)
166 (fold-right (lambda (input result)
167 (match input
168 ((path (sub-drvs ...))
169 (cons (make-derivation-input path sub-drvs)
170 result))))
171 '()
172 x))
173
df7bbd38
LC
174 ;; The contents of a derivation are typically ASCII, but choosing
175 ;; UTF-8 allows us to take the fast path for Guile's `scm_getc'.
176 (set-port-encoding! drv-port "UTF-8")
177
77d3cf08
LC
178 (let loop ((exp (read drv-port))
179 (result '()))
180 (match exp
181 ((? eof-object?)
182 (let ((result (reverse result)))
183 (match result
184 (('Derive ((outputs ...) (input-drvs ...)
185 (input-srcs ...)
186 (? string? system)
187 (? string? builder)
188 ((? string? args) ...)
189 ((var value) ...)))
190 (make-derivation (outputs->alist outputs)
191 (make-input-drvs input-drvs)
192 input-srcs
193 system builder args
194 (fold-right alist-cons '() var value)))
195 (_
196 (error "failed to parse derivation" drv-port result)))))
197 ((? (cut eq? <> comma))
198 (loop (read drv-port) result))
199 (_
200 (loop (read drv-port)
201 (cons (ununquote exp) result))))))
202
203(define (write-derivation drv port)
204 "Write the ATerm-like serialization of DRV to PORT. See Section 2.4 of
205Eelco Dolstra's PhD dissertation for an overview of a previous version of
206that form."
aaa848f3
LC
207
208 ;; Make sure we're using the faster implementation.
209 (define format simple-format)
210
77d3cf08
LC
211 (define (list->string lst)
212 (string-append "[" (string-join lst ",") "]"))
213
214 (define (write-list lst)
215 (display (list->string lst) port))
216
d66ac374
LC
217 (define (coalesce-duplicate-inputs inputs)
218 ;; Return a list of inputs, such that when INPUTS contains the same DRV
219 ;; twice, they are coalesced, with their sub-derivations merged. This is
220 ;; needed because Nix itself keeps only one of them.
221 (fold (lambda (input result)
222 (match input
223 (($ <derivation-input> path sub-drvs)
224 ;; XXX: quadratic
225 (match (find (match-lambda
226 (($ <derivation-input> p s)
227 (string=? p path)))
228 result)
229 (#f
230 (cons input result))
231 ((and dup ($ <derivation-input> _ sub-drvs2))
232 ;; Merge DUP with INPUT.
233 (let ((sub-drvs (delete-duplicates
234 (append sub-drvs sub-drvs2))))
235 (cons (make-derivation-input path sub-drvs)
236 (delq dup result))))))))
237 '()
238 inputs))
239
561eaf71
LC
240 ;; Note: lists are sorted alphabetically, to conform with the behavior of
241 ;; C++ `std::map' in Nix itself.
242
77d3cf08
LC
243 (match drv
244 (($ <derivation> outputs inputs sources
245 system builder args env-vars)
246 (display "Derive(" port)
247 (write-list (map (match-lambda
248 ((name . ($ <derivation-output> path hash-algo hash))
249 (format #f "(~s,~s,~s,~s)"
749c6567
LC
250 name path
251 (or (and=> hash-algo symbol->string) "")
252 (or (and=> hash bytevector->base16-string)
253 ""))))
561eaf71
LC
254 (sort outputs
255 (lambda (o1 o2)
256 (string<? (car o1) (car o2))))))
77d3cf08
LC
257 (display "," port)
258 (write-list (map (match-lambda
259 (($ <derivation-input> path sub-drvs)
260 (format #f "(~s,~a)" path
561eaf71
LC
261 (list->string (map object->string
262 (sort sub-drvs string<?))))))
d66ac374 263 (sort (coalesce-duplicate-inputs inputs)
561eaf71
LC
264 (lambda (i1 i2)
265 (string<? (derivation-input-path i1)
266 (derivation-input-path i2))))))
77d3cf08 267 (display "," port)
561eaf71 268 (write-list (map object->string (sort sources string<?)))
77d3cf08
LC
269 (format port ",~s,~s," system builder)
270 (write-list (map object->string args))
271 (display "," port)
272 (write-list (map (match-lambda
273 ((name . value)
274 (format #f "(~s,~s)" name value)))
561eaf71
LC
275 (sort env-vars
276 (lambda (e1 e2)
277 (string<? (car e1) (car e2))))))
77d3cf08
LC
278 (display ")" port))))
279
aaa848f3
LC
280(define derivation-path->output-path
281 ;; This procedure is called frequently, so memoize it.
282 (memoize
283 (lambda* (path #:optional (output "out"))
284 "Read the derivation from PATH (`/nix/store/xxx.drv'), and return the store
de4c3f26 285path of its output OUTPUT."
aaa848f3
LC
286 (let* ((drv (call-with-input-file path read-derivation))
287 (outputs (derivation-outputs drv)))
288 (and=> (assoc-ref outputs output) derivation-output-path)))))
de4c3f26
LC
289
290\f
291;;;
292;;; Derivation primitive.
293;;;
294
26bbbb95
LC
295(define (compressed-hash bv size) ; `compressHash'
296 "Given the hash stored in BV, return a compressed version thereof that fits
297in SIZE bytes."
298 (define new (make-bytevector size 0))
299 (define old-size (bytevector-length bv))
300 (let loop ((i 0))
301 (if (= i old-size)
302 new
303 (let* ((j (modulo i size))
304 (o (bytevector-u8-ref new j)))
305 (bytevector-u8-set! new j
306 (logxor o (bytevector-u8-ref bv i)))
307 (loop (+ 1 i))))))
77d3cf08 308
de4c3f26
LC
309(define derivation-hash ; `hashDerivationModulo' in derivations.cc
310 (memoize
311 (lambda (drv)
312 "Return the hash of DRV, modulo its fixed-output inputs, as a bytevector."
313 (match drv
314 (($ <derivation> ((_ . ($ <derivation-output> path
749c6567 315 (? symbol? hash-algo) (? bytevector? hash)))))
de4c3f26 316 ;; A fixed-output derivation.
77d3cf08 317 (sha256
de4c3f26
LC
318 (string->utf8
319 (string-append "fixed:out:" (symbol->string hash-algo)
749c6567
LC
320 ":" (bytevector->base16-string hash)
321 ":" path))))
de4c3f26
LC
322 (($ <derivation> outputs inputs sources
323 system builder args env-vars)
324 ;; A regular derivation: replace the path of each input with that
325 ;; input's hash; return the hash of serialization of the resulting
561eaf71
LC
326 ;; derivation.
327 (let* ((inputs (map (match-lambda
328 (($ <derivation-input> path sub-drvs)
329 (let ((hash (call-with-input-file path
330 (compose bytevector->base16-string
331 derivation-hash
332 read-derivation))))
333 (make-derivation-input hash sub-drvs))))
334 inputs))
335 (drv (make-derivation outputs inputs sources
336 system builder args env-vars)))
de4c3f26
LC
337 (sha256
338 (string->utf8 (call-with-output-string
339 (cut write-derivation drv <>))))))))))
77d3cf08 340
26bbbb95
LC
341(define (store-path type hash name) ; makeStorePath
342 "Return the store path for NAME/HASH/TYPE."
343 (let* ((s (string-append type ":sha256:"
344 (bytevector->base16-string hash) ":"
345 (%store-prefix) ":" name))
346 (h (sha256 (string->utf8 s)))
347 (c (compressed-hash h 20)))
348 (string-append (%store-prefix) "/"
349 (bytevector->nix-base32-string c) "-"
350 name)))
351
352(define (output-path output hash name) ; makeOutputPath
353 "Return an output path for OUTPUT (the name of the output as a string) of
354the derivation called NAME with hash HASH."
355 (store-path (string-append "output:" output) hash
356 (if (string=? output "out")
357 name
358 (string-append name "-" output))))
359
360(define* (derivation store name system builder args env-vars inputs
361 #:key (outputs '("out")) hash hash-algo hash-mode)
362 "Build a derivation with the given arguments. Return the resulting
fb3eec83 363store path and <derivation> object. When HASH, HASH-ALGO, and HASH-MODE
26bbbb95
LC
364are given, a fixed-output derivation is created---i.e., one whose result is
365known in advance, such as a file download."
366 (define (add-output-paths drv)
367 ;; Return DRV with an actual store path for each of its output and the
368 ;; corresponding environment variable.
369 (match drv
370 (($ <derivation> outputs inputs sources
371 system builder args env-vars)
372 (let* ((drv-hash (derivation-hash drv))
373 (outputs (map (match-lambda
d9085c23
LC
374 ((output-name . ($ <derivation-output>
375 _ algo hash))
376 (let ((path (output-path output-name
377 drv-hash name)))
378 (cons output-name
379 (make-derivation-output path algo
380 hash)))))
381 outputs)))
26bbbb95
LC
382 (make-derivation outputs inputs sources system builder args
383 (map (match-lambda
384 ((name . value)
385 (cons name
386 (or (and=> (assoc-ref outputs name)
387 derivation-output-path)
388 value))))
389 env-vars))))))
390
391 (define (env-vars-with-empty-outputs)
392 ;; Return a variant of ENV-VARS where each OUTPUTS is associated with an
561eaf71 393 ;; empty string, even outputs that do not appear in ENV-VARS.
26bbbb95
LC
394 (let ((e (map (match-lambda
395 ((name . val)
396 (if (member name outputs)
397 (cons name "")
398 (cons name val))))
399 env-vars)))
561eaf71
LC
400 (fold (lambda (output-name env-vars)
401 (if (assoc output-name env-vars)
402 env-vars
403 (append env-vars `((,output-name . "")))))
404 e
405 outputs)))
26bbbb95
LC
406
407 (let* ((outputs (map (lambda (name)
408 ;; Return outputs with an empty path.
409 (cons name
410 (make-derivation-output "" hash-algo hash)))
411 outputs))
412 (inputs (map (match-lambda
de4c3f26
LC
413 (((? store-path? input))
414 (make-derivation-input input '("out")))
415 (((? store-path? input) sub-drvs ...)
26bbbb95
LC
416 (make-derivation-input input sub-drvs))
417 ((input . _)
418 (let ((path (add-to-store store
419 (basename input)
420 (hash-algo sha256) #t #t
421 input)))
422 (make-derivation-input path '()))))
d26ad5e4 423 (delete-duplicates inputs)))
26bbbb95
LC
424 (env-vars (env-vars-with-empty-outputs))
425 (drv-masked (make-derivation outputs
426 (filter (compose derivation-path?
427 derivation-input-path)
428 inputs)
429 (filter-map (lambda (i)
430 (let ((p (derivation-input-path i)))
431 (and (not (derivation-path? p))
432 p)))
433 inputs)
434 system builder args env-vars))
435 (drv (add-output-paths drv-masked)))
de4c3f26 436
d66ac374
LC
437 ;; (write-derivation drv-masked (current-error-port))
438 ;; (newline (current-error-port))
fb3eec83
LC
439 (values (add-text-to-store store (string-append name ".drv")
440 (call-with-output-string
441 (cut write-derivation drv <>))
442 (map derivation-input-path
443 inputs))
444 drv)))
d9085c23
LC
445
446\f
447;;;
448;;; Guile-based builders.
449;;;
450
451(define %guile-for-build
452 ;; The derivation of the Guile to be used within the build environment,
453 ;; when using `build-expression->derivation'.
6dd7787c 454 (make-parameter (false-if-exception (nixpkgs-derivation* "guile"))))
d9085c23 455
99634e3f
LC
456(define* (imported-files store files
457 #:key (name "file-import") (system (%current-system)))
458 "Return a derivation that imports FILES into STORE. FILES must be a list
459of (FINAL-PATH . FILE-NAME) pairs; each FILE-NAME is read from the file
460system, imported, and appears under FINAL-PATH in the resulting store path."
461 (define (parent-dirs file-name)
462 ;; Return the list of parent dirs of FILE-NAME, in the order in which an
463 ;; `mkdir -p' implementation would make them.
464 (let ((not-slash (char-set-complement (char-set #\/))))
465 (reverse
466 (fold (lambda (dir result)
467 (match result
468 (()
469 (list dir))
470 ((prev _ ...)
471 (cons (string-append prev "/" dir)
472 result))))
473 '()
474 (remove (cut string=? <> ".")
475 (string-tokenize (dirname file-name) not-slash))))))
476
477 (let* ((files (map (match-lambda
478 ((final-path . file-name)
2acb2cb6 479 (list final-path
99634e3f
LC
480 (add-to-store store (basename final-path) #t #f
481 "sha256" file-name))))
482 files))
483 (builder
484 `(begin
485 (mkdir %output) (chdir %output)
486 ,@(append-map (match-lambda
2acb2cb6 487 ((final-path store-path)
99634e3f
LC
488 (append (match (parent-dirs final-path)
489 (() '())
490 ((head ... tail)
491 (append (map (lambda (d)
492 `(false-if-exception
493 (mkdir ,d)))
494 head)
224f7ad6
LC
495 `((or (file-exists? ,tail)
496 (mkdir ,tail))))))
99634e3f
LC
497 `((symlink ,store-path ,final-path)))))
498 files))))
499 (build-expression->derivation store name (%current-system)
500 builder files)))
501
3eb98237
LC
502(define* (imported-modules store modules
503 #:key (name "module-import")
504 (system (%current-system)))
505 "Return a derivation that contains the source files of MODULES, a list of
506module names such as `(ice-9 q)'. All of MODULES must be in the current
507search path."
508 ;; TODO: Determine the closure of MODULES, build the `.go' files,
509 ;; canonicalize the source files through read/write, etc.
510 (let ((files (map (lambda (m)
511 (let ((f (string-append
512 (string-join (map symbol->string m) "/")
513 ".scm")))
514 (cons f (search-path %load-path f))))
515 modules)))
516 (imported-files store files #:name name #:system system)))
517
518
d9085c23 519(define* (build-expression->derivation store name system exp inputs
9bc07f4d 520 #:key (outputs '("out"))
3eb98237 521 hash hash-algo
4c1eddf7 522 (env-vars '())
6dd7787c
LC
523 (modules '())
524 guile-for-build)
d9085c23 525 "Return a derivation that executes Scheme expression EXP as a builder for
2acb2cb6
LC
526derivation NAME. INPUTS must be a list of (NAME DRV-PATH SUB-DRV) tuples;
527when SUB-DRV is omitted, \"out\" is assumed. EXP is evaluated in an
528environment where %OUTPUT is bound to the main output path, %OUTPUTS is bound
529to a list of output/path pairs, and where %BUILD-INPUTS is bound to an alist
4c1eddf7
LC
530of string/output-path pairs made from INPUTS. Optionally, ENV-VARS is a list
531of string pairs specifying the name and value of environment variables
532visible to the builder. The builder terminates by passing the result of EXP
533to `exit'; thus, when EXP returns #f, the build is considered to have
6dd7787c
LC
534failed.
535
536EXP is built using GUILE-FOR-BUILD (a derivation). When GUILE-FOR-BUILD is
537omitted or is #f, the value of the `%guile-for-build' fluid is used instead."
d9085c23 538 (define guile
6dd7787c
LC
539 (string-append (derivation-path->output-path (or guile-for-build
540 (%guile-for-build)))
d9085c23
LC
541 "/bin/guile"))
542
0d56a551
LC
543 (define module-form?
544 (match-lambda
545 (((or 'define-module 'use-modules) _ ...) #t)
546 (_ #f)))
547
d9085c23 548 (let* ((prologue `(begin
0d56a551
LC
549 ,@(match exp
550 ((_ ...)
551 ;; Module forms must appear at the top-level so
552 ;; that any macros they export can be expanded.
553 (filter module-form? exp))
554 (_ `(,exp)))
555
d9085c23 556 (define %output (getenv "out"))
9bc07f4d
LC
557 (define %outputs
558 (map (lambda (o)
559 (cons o (getenv o)))
560 ',outputs))
d9085c23
LC
561 (define %build-inputs
562 ',(map (match-lambda
2acb2cb6
LC
563 ((name drv . rest)
564 (let ((sub (match rest
565 (() "out")
566 ((x) x))))
567 (cons name
568 (if (derivation-path? drv)
569 (derivation-path->output-path drv
570 sub)
571 drv)))))
d44bc84b
LC
572 inputs))
573
574 ;; Guile sets it, but remove it to avoid conflicts when
575 ;; building Guile-using packages.
576 (unsetenv "LD_LIBRARY_PATH")))
d9085c23
LC
577 (builder (add-text-to-store store
578 (string-append name "-guile-builder")
0d56a551
LC
579 (string-append
580 (object->string prologue)
581 (object->string
582 `(exit
583 ,(match exp
584 ((_ ...)
585 (remove module-form? exp))
586 (_ `(,exp))))))
2acb2cb6 587 (map second inputs)))
3eb98237
LC
588 (mod-drv (if (null? modules)
589 #f
590 (imported-modules store modules)))
591 (mod-dir (and mod-drv
592 (derivation-path->output-path mod-drv))))
593 (derivation store name system guile
594 `("--no-auto-compile"
595 ,@(if mod-dir `("-L" ,mod-dir) '())
596 ,builder)
4c1eddf7 597 env-vars
6dd7787c 598 `((,(or guile-for-build (%guile-for-build)))
3eb98237 599 (,builder)
2acb2cb6 600 ,@(map cdr inputs)
3eb98237 601 ,@(if mod-drv `((,mod-drv)) '()))
9bc07f4d
LC
602 #:hash hash #:hash-algo hash-algo
603 #:outputs outputs)))