derivations: Memoize 'derivation-path->base16-hash'.
[jackhill/guix/guix.git] / guix / derivations.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
1909431c 2;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
77d3cf08 3;;;
233e7676 4;;; This file is part of GNU Guix.
77d3cf08 5;;;
233e7676 6;;; GNU Guix is free software; you can redistribute it and/or modify it
77d3cf08
LC
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;;;
233e7676 11;;; GNU Guix is distributed in the hope that it will be useful, but
77d3cf08
LC
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
233e7676 17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
77d3cf08
LC
18
19(define-module (guix derivations)
20 #:use-module (srfi srfi-1)
21 #:use-module (srfi srfi-9)
07c86312 22 #:use-module (srfi srfi-9 gnu)
77d3cf08
LC
23 #:use-module (srfi srfi-26)
24 #:use-module (rnrs io ports)
25 #:use-module (rnrs bytevectors)
26 #:use-module (ice-9 match)
27 #:use-module (ice-9 rdelim)
e387ab7c 28 #:use-module (ice-9 vlist)
69f90f5c 29 #:use-module (guix store)
26bbbb95 30 #:use-module (guix utils)
72626a71 31 #:use-module (guix hash)
ddc29a78 32 #:use-module (guix base32)
9a20830e
LC
33 #:export (<derivation>
34 derivation?
77d3cf08
LC
35 derivation-outputs
36 derivation-inputs
37 derivation-sources
38 derivation-system
39 derivation-builder-arguments
40 derivation-builder-environment-vars
6a446d56 41 derivation-file-name
9a20830e
LC
42 derivation-prerequisites
43 derivation-prerequisites-to-build
77d3cf08 44
9a20830e 45 <derivation-output>
77d3cf08
LC
46 derivation-output?
47 derivation-output-path
48 derivation-output-hash-algo
49 derivation-output-hash
36bbbbd1 50 derivation-output-recursive?
77d3cf08 51
9a20830e 52 <derivation-input>
77d3cf08
LC
53 derivation-input?
54 derivation-input-path
55 derivation-input-sub-derivations
dd36b51b 56 derivation-input-output-paths
77d3cf08
LC
57
58 fixed-output-derivation?
341c6fdd
LC
59 derivation-hash
60
61 read-derivation
26bbbb95 62 write-derivation
59688fc4
LC
63 derivation->output-path
64 derivation->output-paths
de4c3f26 65 derivation-path->output-path
7244a5f7 66 derivation-path->output-paths
d9085c23 67 derivation
fb59e275 68 graft-derivation
e387ab7c 69 map-derivation
d9085c23
LC
70
71 %guile-for-build
f989fa39
LC
72 imported-modules
73 compiled-modules
99634e3f 74 build-expression->derivation
59688fc4
LC
75 imported-files)
76 #:replace (build-derivations))
77d3cf08
LC
77
78;;;
79;;; Nix derivations, as implemented in Nix's `derivations.cc'.
80;;;
81
82(define-record-type <derivation>
6a446d56
LC
83 (make-derivation outputs inputs sources system builder args env-vars
84 file-name)
77d3cf08
LC
85 derivation?
86 (outputs derivation-outputs) ; list of name/<derivation-output> pairs
87 (inputs derivation-inputs) ; list of <derivation-input>
88 (sources derivation-sources) ; list of store paths
89 (system derivation-system) ; string
90 (builder derivation-builder) ; store path
91 (args derivation-builder-arguments) ; list of strings
6a446d56
LC
92 (env-vars derivation-builder-environment-vars) ; list of name/value pairs
93 (file-name derivation-file-name)) ; the .drv file name
77d3cf08
LC
94
95(define-record-type <derivation-output>
36bbbbd1 96 (make-derivation-output path hash-algo hash recursive?)
77d3cf08
LC
97 derivation-output?
98 (path derivation-output-path) ; store path
99 (hash-algo derivation-output-hash-algo) ; symbol | #f
36bbbbd1
LC
100 (hash derivation-output-hash) ; bytevector | #f
101 (recursive? derivation-output-recursive?)) ; Boolean
77d3cf08
LC
102
103(define-record-type <derivation-input>
104 (make-derivation-input path sub-derivations)
105 derivation-input?
106 (path derivation-input-path) ; store path
107 (sub-derivations derivation-input-sub-derivations)) ; list of strings
108
07c86312
LC
109(set-record-type-printer! <derivation>
110 (lambda (drv port)
111 (format port "#<derivation ~a => ~a ~a>"
112 (derivation-file-name drv)
113 (string-join
114 (map (match-lambda
115 ((_ . output)
116 (derivation-output-path output)))
117 (derivation-outputs drv)))
118 (number->string (object-address drv) 16))))
119
77d3cf08
LC
120(define (fixed-output-derivation? drv)
121 "Return #t if DRV is a fixed-output derivation, such as the result of a
122download with a fixed hash (aka. `fetchurl')."
123 (match drv
124 (($ <derivation>
99e17dc9 125 (("out" . ($ <derivation-output> _ (? symbol?) (? bytevector?)))))
77d3cf08
LC
126 #t)
127 (_ #f)))
128
dd36b51b
LC
129(define (derivation-input-output-paths input)
130 "Return the list of output paths corresponding to INPUT, a
131<derivation-input>."
132 (match input
133 (($ <derivation-input> path sub-drvs)
134 (map (cut derivation-path->output-path path <>)
135 sub-drvs))))
136
9a20830e
LC
137(define (derivation-prerequisites drv)
138 "Return the list of derivation-inputs required to build DRV, recursively."
139 (let loop ((drv drv)
140 (result '()))
141 (let ((inputs (remove (cut member <> result) ; XXX: quadratic
142 (derivation-inputs drv))))
143 (fold loop
144 (append inputs result)
145 (map (lambda (i)
146 (call-with-input-file (derivation-input-path i)
147 read-derivation))
148 inputs)))))
149
784bb1f3 150(define* (derivation-prerequisites-to-build store drv
dd36b51b
LC
151 #:key
152 (outputs
153 (map
154 car
155 (derivation-outputs drv)))
156 (use-substitutes? #t))
157 "Return two values: the list of derivation-inputs required to build the
158OUTPUTS of DRV and not already available in STORE, recursively, and the list
159of required store paths that can be substituted. When USE-SUBSTITUTES? is #f,
160that second value is the empty list."
161 (define (derivation-output-paths drv sub-drvs)
162 (match drv
163 (($ <derivation> outputs)
164 (map (lambda (sub-drv)
165 (derivation-output-path (assoc-ref outputs sub-drv)))
166 sub-drvs))))
167
784bb1f3
LC
168 (define built?
169 (cut valid-path? store <>))
170
dd36b51b
LC
171 (define substitutable?
172 ;; Return true if the given path is substitutable. Call
173 ;; `substitutable-paths' upfront, to benefit from parallelism in the
174 ;; substituter.
175 (if use-substitutes?
176 (let ((s (substitutable-paths store
177 (append
178 (derivation-output-paths drv outputs)
179 (append-map
180 derivation-input-output-paths
181 (derivation-prerequisites drv))))))
182 (cut member <> s))
183 (const #f)))
184
9a20830e 185 (define input-built?
dd36b51b
LC
186 (compose (cut any built? <>) derivation-input-output-paths))
187
188 (define input-substitutable?
189 ;; Return true if and only if all of SUB-DRVS are subsitutable. If at
190 ;; least one is missing, then everything must be rebuilt.
191 (compose (cut every substitutable? <>) derivation-input-output-paths))
784bb1f3
LC
192
193 (define (derivation-built? drv sub-drvs)
dd36b51b
LC
194 (every built? (derivation-output-paths drv sub-drvs)))
195
196 (define (derivation-substitutable? drv sub-drvs)
197 (every substitutable? (derivation-output-paths drv sub-drvs)))
198
199 (let loop ((drv drv)
200 (sub-drvs outputs)
201 (build '())
202 (substitute '()))
203 (cond ((derivation-built? drv sub-drvs)
204 (values build substitute))
205 ((derivation-substitutable? drv sub-drvs)
206 (values build
207 (append (derivation-output-paths drv sub-drvs)
208 substitute)))
209 (else
210 (let ((inputs (remove (lambda (i)
211 (or (member i build) ; XXX: quadratic
212 (input-built? i)
213 (input-substitutable? i)))
214 (derivation-inputs drv))))
215 (fold2 loop
216 (append inputs build)
217 (append (append-map (lambda (input)
218 (if (and (not (input-built? input))
219 (input-substitutable? input))
220 (derivation-input-output-paths
221 input)
222 '()))
223 (derivation-inputs drv))
224 substitute)
225 (map (lambda (i)
226 (call-with-input-file (derivation-input-path i)
227 read-derivation))
228 inputs)
229 (map derivation-input-sub-derivations inputs)))))))
9a20830e 230
d0840e4a
LC
231(define (%read-derivation drv-port)
232 ;; Actually read derivation from DRV-PORT.
77d3cf08
LC
233
234 (define comma (string->symbol ","))
235
236 (define (ununquote x)
237 (match x
238 (('unquote x) (ununquote x))
239 ((x ...) (map ununquote x))
240 (_ x)))
241
242 (define (outputs->alist x)
243 (fold-right (lambda (output result)
244 (match output
245 ((name path "" "")
246 (alist-cons name
36bbbbd1 247 (make-derivation-output path #f #f #f)
77d3cf08
LC
248 result))
249 ((name path hash-algo hash)
250 ;; fixed-output
36bbbbd1
LC
251 (let* ((rec? (string-prefix? "r:" hash-algo))
252 (algo (string->symbol
253 (if rec?
254 (string-drop hash-algo 2)
255 hash-algo)))
256 (hash (base16-string->bytevector hash)))
77d3cf08 257 (alist-cons name
36bbbbd1
LC
258 (make-derivation-output path algo
259 hash rec?)
77d3cf08
LC
260 result)))))
261 '()
262 x))
263
264 (define (make-input-drvs x)
265 (fold-right (lambda (input result)
266 (match input
267 ((path (sub-drvs ...))
268 (cons (make-derivation-input path sub-drvs)
269 result))))
270 '()
271 x))
272
df7bbd38
LC
273 ;; The contents of a derivation are typically ASCII, but choosing
274 ;; UTF-8 allows us to take the fast path for Guile's `scm_getc'.
275 (set-port-encoding! drv-port "UTF-8")
276
77d3cf08
LC
277 (let loop ((exp (read drv-port))
278 (result '()))
279 (match exp
280 ((? eof-object?)
281 (let ((result (reverse result)))
282 (match result
283 (('Derive ((outputs ...) (input-drvs ...)
284 (input-srcs ...)
285 (? string? system)
286 (? string? builder)
287 ((? string? args) ...)
288 ((var value) ...)))
289 (make-derivation (outputs->alist outputs)
290 (make-input-drvs input-drvs)
291 input-srcs
292 system builder args
6a446d56
LC
293 (fold-right alist-cons '() var value)
294 (port-filename drv-port)))
77d3cf08
LC
295 (_
296 (error "failed to parse derivation" drv-port result)))))
297 ((? (cut eq? <> comma))
298 (loop (read drv-port) result))
299 (_
300 (loop (read drv-port)
301 (cons (ununquote exp) result))))))
302
d0840e4a
LC
303(define read-derivation
304 (let ((cache (make-weak-value-hash-table 200)))
305 (lambda (drv-port)
306 "Read the derivation from DRV-PORT and return the corresponding
307<derivation> object."
308 ;; Memoize that operation because `%read-derivation' is quite expensive,
309 ;; and because the same argument is read more than 15 times on average
310 ;; during something like (package-derivation s gdb).
311 (let ((file (and=> (port-filename drv-port) basename)))
312 (or (and file (hash-ref cache file))
313 (let ((drv (%read-derivation drv-port)))
314 (hash-set! cache file drv)
315 drv))))))
316
d8085599
LC
317(define-inlinable (write-sequence lst write-item port)
318 ;; Write each element of LST with WRITE-ITEM to PORT, separating them with a
319 ;; comma.
320 (match lst
321 (()
322 #t)
323 ((prefix (... ...) last)
324 (for-each (lambda (item)
325 (write-item item port)
326 (display "," port))
327 prefix)
328 (write-item last port))))
329
330(define-inlinable (write-list lst write-item port)
331 ;; Write LST as a derivation list to PORT, using WRITE-ITEM to write each
332 ;; element.
333 (display "[" port)
334 (write-sequence lst write-item port)
335 (display "]" port))
336
337(define-inlinable (write-tuple lst write-item port)
338 ;; Same, but write LST as a tuple.
339 (display "(" port)
340 (write-sequence lst write-item port)
341 (display ")" port))
342
77d3cf08
LC
343(define (write-derivation drv port)
344 "Write the ATerm-like serialization of DRV to PORT. See Section 2.4 of
345Eelco Dolstra's PhD dissertation for an overview of a previous version of
346that form."
aaa848f3
LC
347
348 ;; Make sure we're using the faster implementation.
349 (define format simple-format)
350
d8085599
LC
351 (define (write-string-list lst)
352 (write-list lst write port))
77d3cf08 353
d66ac374
LC
354 (define (coalesce-duplicate-inputs inputs)
355 ;; Return a list of inputs, such that when INPUTS contains the same DRV
356 ;; twice, they are coalesced, with their sub-derivations merged. This is
357 ;; needed because Nix itself keeps only one of them.
358 (fold (lambda (input result)
359 (match input
360 (($ <derivation-input> path sub-drvs)
361 ;; XXX: quadratic
362 (match (find (match-lambda
363 (($ <derivation-input> p s)
364 (string=? p path)))
365 result)
366 (#f
367 (cons input result))
368 ((and dup ($ <derivation-input> _ sub-drvs2))
369 ;; Merge DUP with INPUT.
370 (let ((sub-drvs (delete-duplicates
371 (append sub-drvs sub-drvs2))))
372 (cons (make-derivation-input path sub-drvs)
373 (delq dup result))))))))
374 '()
375 inputs))
376
d8085599
LC
377 (define (write-output output port)
378 (match output
36bbbbd1 379 ((name . ($ <derivation-output> path hash-algo hash recursive?))
d8085599 380 (write-tuple (list name path
36bbbbd1
LC
381 (if hash-algo
382 (string-append (if recursive? "r:" "")
383 (symbol->string hash-algo))
384 "")
d8085599
LC
385 (or (and=> hash bytevector->base16-string)
386 ""))
387 write
388 port))))
389
390 (define (write-input input port)
391 (match input
392 (($ <derivation-input> path sub-drvs)
393 (display "(" port)
394 (write path port)
395 (display "," port)
396 (write-string-list (sort sub-drvs string<?))
397 (display ")" port))))
398
399 (define (write-env-var env-var port)
400 (match env-var
401 ((name . value)
402 (display "(" port)
403 (write name port)
404 (display "," port)
405 (write value port)
406 (display ")" port))))
407
561eaf71
LC
408 ;; Note: lists are sorted alphabetically, to conform with the behavior of
409 ;; C++ `std::map' in Nix itself.
410
77d3cf08
LC
411 (match drv
412 (($ <derivation> outputs inputs sources
413 system builder args env-vars)
414 (display "Derive(" port)
d8085599
LC
415 (write-list (sort outputs
416 (lambda (o1 o2)
417 (string<? (car o1) (car o2))))
418 write-output
419 port)
77d3cf08 420 (display "," port)
d8085599
LC
421 (write-list (sort (coalesce-duplicate-inputs inputs)
422 (lambda (i1 i2)
423 (string<? (derivation-input-path i1)
424 (derivation-input-path i2))))
425 write-input
426 port)
77d3cf08 427 (display "," port)
d8085599 428 (write-string-list (sort sources string<?))
77d3cf08 429 (format port ",~s,~s," system builder)
d8085599 430 (write-string-list args)
77d3cf08 431 (display "," port)
d8085599
LC
432 (write-list (sort env-vars
433 (lambda (e1 e2)
434 (string<? (car e1) (car e2))))
435 write-env-var
436 port)
77d3cf08
LC
437 (display ")" port))))
438
be4e38fb
LC
439(define derivation->string
440 (memoize
441 (lambda (drv)
442 "Return the external representation of DRV as a string."
443 (with-fluids ((%default-port-encoding "UTF-8"))
444 (call-with-output-string
445 (cut write-derivation drv <>))))))
446
59688fc4
LC
447(define* (derivation->output-path drv #:optional (output "out"))
448 "Return the store path of its output OUTPUT."
449 (let ((outputs (derivation-outputs drv)))
450 (and=> (assoc-ref outputs output) derivation-output-path)))
451
452(define (derivation->output-paths drv)
453 "Return the list of name/path pairs of the outputs of DRV."
454 (map (match-lambda
455 ((name . output)
456 (cons name (derivation-output-path output))))
457 (derivation-outputs drv)))
458
aaa848f3
LC
459(define derivation-path->output-path
460 ;; This procedure is called frequently, so memoize it.
461 (memoize
462 (lambda* (path #:optional (output "out"))
6f58d582 463 "Read the derivation from PATH (`/gnu/store/xxx.drv'), and return the store
de4c3f26 464path of its output OUTPUT."
d0dc4907
LC
465 (derivation->output-path (call-with-input-file path read-derivation)
466 output))))
de4c3f26 467
7244a5f7 468(define (derivation-path->output-paths path)
6f58d582 469 "Read the derivation from PATH (`/gnu/store/xxx.drv'), and return the
7244a5f7 470list of name/path pairs of its outputs."
59688fc4 471 (derivation->output-paths (call-with-input-file path read-derivation)))
7244a5f7 472
de4c3f26
LC
473\f
474;;;
475;;; Derivation primitive.
476;;;
477
26bbbb95
LC
478(define (compressed-hash bv size) ; `compressHash'
479 "Given the hash stored in BV, return a compressed version thereof that fits
480in SIZE bytes."
481 (define new (make-bytevector size 0))
482 (define old-size (bytevector-length bv))
483 (let loop ((i 0))
484 (if (= i old-size)
485 new
486 (let* ((j (modulo i size))
487 (o (bytevector-u8-ref new j)))
488 (bytevector-u8-set! new j
489 (logxor o (bytevector-u8-ref bv i)))
490 (loop (+ 1 i))))))
77d3cf08 491
1391dcb0
LC
492(define derivation-path->base16-hash
493 (memoize
494 (lambda (file)
495 "Return a string containing the base16 representation of the hash of the
496derivation at FILE."
497 (call-with-input-file file
498 (compose bytevector->base16-string
499 derivation-hash
500 read-derivation)))))
501
de4c3f26
LC
502(define derivation-hash ; `hashDerivationModulo' in derivations.cc
503 (memoize
504 (lambda (drv)
505 "Return the hash of DRV, modulo its fixed-output inputs, as a bytevector."
506 (match drv
507 (($ <derivation> ((_ . ($ <derivation-output> path
36bbbbd1
LC
508 (? symbol? hash-algo) (? bytevector? hash)
509 (? boolean? recursive?)))))
de4c3f26 510 ;; A fixed-output derivation.
77d3cf08 511 (sha256
de4c3f26 512 (string->utf8
36bbbbd1
LC
513 (string-append "fixed:out:"
514 (if recursive? "r:" "")
515 (symbol->string hash-algo)
749c6567
LC
516 ":" (bytevector->base16-string hash)
517 ":" path))))
de4c3f26
LC
518 (($ <derivation> outputs inputs sources
519 system builder args env-vars)
520 ;; A regular derivation: replace the path of each input with that
521 ;; input's hash; return the hash of serialization of the resulting
561eaf71
LC
522 ;; derivation.
523 (let* ((inputs (map (match-lambda
524 (($ <derivation-input> path sub-drvs)
1391dcb0 525 (let ((hash (derivation-path->base16-hash path)))
561eaf71
LC
526 (make-derivation-input hash sub-drvs))))
527 inputs))
528 (drv (make-derivation outputs inputs sources
6a446d56
LC
529 system builder args env-vars
530 #f)))
b0fad8a2
LC
531
532 ;; XXX: At this point this remains faster than `port-sha256', because
533 ;; the SHA256 port's `write' method gets called for every single
534 ;; character.
de4c3f26 535 (sha256
be4e38fb 536 (string->utf8 (derivation->string drv)))))))))
77d3cf08 537
26bbbb95
LC
538(define (store-path type hash name) ; makeStorePath
539 "Return the store path for NAME/HASH/TYPE."
540 (let* ((s (string-append type ":sha256:"
541 (bytevector->base16-string hash) ":"
542 (%store-prefix) ":" name))
543 (h (sha256 (string->utf8 s)))
544 (c (compressed-hash h 20)))
545 (string-append (%store-prefix) "/"
546 (bytevector->nix-base32-string c) "-"
547 name)))
548
549(define (output-path output hash name) ; makeOutputPath
550 "Return an output path for OUTPUT (the name of the output as a string) of
551the derivation called NAME with hash HASH."
552 (store-path (string-append "output:" output) hash
553 (if (string=? output "out")
554 name
555 (string-append name "-" output))))
556
36bbbbd1
LC
557(define (fixed-output-path output hash-algo hash recursive? name)
558 "Return an output path for the fixed output OUTPUT defined by HASH of type
559HASH-ALGO, of the derivation NAME. RECURSIVE? has the same meaning as for
560'add-to-store'."
561 (if (and recursive? (eq? hash-algo 'sha256))
562 (store-path "source" hash name)
563 (let ((tag (string-append "fixed:" output ":"
564 (if recursive? "r:" "")
565 (symbol->string hash-algo) ":"
566 (bytevector->base16-string hash) ":")))
567 (store-path (string-append "output:" output)
568 (sha256 (string->utf8 tag))
569 name))))
570
a987d2c0
LC
571(define* (derivation store name builder args
572 #:key
573 (system (%current-system)) (env-vars '())
574 (inputs '()) (outputs '("out"))
2096ef47 575 hash hash-algo recursive?
b53be755 576 references-graphs allowed-references
1909431c 577 local-build?)
59688fc4 578 "Build a derivation with the given arguments, and return the resulting
2096ef47 579<derivation> object. When HASH and HASH-ALGO are given, a
59688fc4 580fixed-output derivation is created---i.e., one whose result is known in
36bbbbd1
LC
581advance, such as a file download. If, in addition, RECURSIVE? is true, then
582that fixed output may be an executable file or a directory and HASH must be
583the hash of an archive containing this output.
5b0c9d16 584
858e9282 585When REFERENCES-GRAPHS is true, it must be a list of file name/store path
5b0c9d16 586pairs. In that case, the reference graph of each store path is exported in
1909431c
LC
587the build environment in the corresponding file, in a simple text format.
588
b53be755
LC
589When ALLOWED-REFERENCES is true, it must be a list of store items or outputs
590that the derivation's output may refer to.
591
1909431c
LC
592When LOCAL-BUILD? is true, declare that the derivation is not a good candidate
593for offloading and should rather be built locally. This is the case for small
594derivations where the costs of data transfers would outweigh the benefits."
26bbbb95
LC
595 (define (add-output-paths drv)
596 ;; Return DRV with an actual store path for each of its output and the
597 ;; corresponding environment variable.
598 (match drv
599 (($ <derivation> outputs inputs sources
600 system builder args env-vars)
601 (let* ((drv-hash (derivation-hash drv))
602 (outputs (map (match-lambda
d9085c23 603 ((output-name . ($ <derivation-output>
36bbbbd1
LC
604 _ algo hash rec?))
605 (let ((path (if hash
606 (fixed-output-path output-name
607 algo hash
608 rec? name)
609 (output-path output-name
610 drv-hash name))))
d9085c23
LC
611 (cons output-name
612 (make-derivation-output path algo
36bbbbd1 613 hash rec?)))))
d9085c23 614 outputs)))
26bbbb95
LC
615 (make-derivation outputs inputs sources system builder args
616 (map (match-lambda
617 ((name . value)
618 (cons name
619 (or (and=> (assoc-ref outputs name)
620 derivation-output-path)
621 value))))
6a446d56
LC
622 env-vars)
623 #f)))))
26bbbb95 624
5b0c9d16
LC
625 (define (user+system-env-vars)
626 ;; Some options are passed to the build daemon via the env. vars of
627 ;; derivations (urgh!). We hide that from our API, but here is the place
628 ;; where we kludgify those options.
b53be755
LC
629 (let ((env-vars `(,@(if local-build?
630 `(("preferLocalBuild" . "1"))
631 '())
632 ,@(if allowed-references
633 `(("allowedReferences"
634 . ,(string-join allowed-references)))
635 '())
636 ,@env-vars)))
1909431c
LC
637 (match references-graphs
638 (((file . path) ...)
639 (let ((value (map (cut string-append <> " " <>)
640 file path)))
641 ;; XXX: This all breaks down if an element of FILE or PATH contains
642 ;; white space.
643 `(("exportReferencesGraph" . ,(string-join value " "))
644 ,@env-vars)))
645 (#f
646 env-vars))))
5b0c9d16
LC
647
648 (define (env-vars-with-empty-outputs env-vars)
26bbbb95 649 ;; Return a variant of ENV-VARS where each OUTPUTS is associated with an
561eaf71 650 ;; empty string, even outputs that do not appear in ENV-VARS.
26bbbb95
LC
651 (let ((e (map (match-lambda
652 ((name . val)
653 (if (member name outputs)
654 (cons name "")
655 (cons name val))))
656 env-vars)))
561eaf71
LC
657 (fold (lambda (output-name env-vars)
658 (if (assoc output-name env-vars)
659 env-vars
660 (append env-vars `((,output-name . "")))))
661 e
662 outputs)))
26bbbb95 663
6a446d56
LC
664 (define (set-file-name drv file)
665 ;; Set FILE as the 'file-name' field of DRV.
666 (match drv
667 (($ <derivation> outputs inputs sources system builder
668 args env-vars)
669 (make-derivation outputs inputs sources system builder
670 args env-vars file))))
671
26bbbb95
LC
672 (let* ((outputs (map (lambda (name)
673 ;; Return outputs with an empty path.
674 (cons name
36bbbbd1
LC
675 (make-derivation-output "" hash-algo
676 hash recursive?)))
26bbbb95
LC
677 outputs))
678 (inputs (map (match-lambda
59688fc4
LC
679 (((? derivation? drv))
680 (make-derivation-input (derivation-file-name drv)
681 '("out")))
682 (((? derivation? drv) sub-drvs ...)
683 (make-derivation-input (derivation-file-name drv)
684 sub-drvs))
200dc937 685 (((? direct-store-path? input))
de4c3f26 686 (make-derivation-input input '("out")))
200dc937 687 (((? direct-store-path? input) sub-drvs ...)
26bbbb95
LC
688 (make-derivation-input input sub-drvs))
689 ((input . _)
690 (let ((path (add-to-store store
691 (basename input)
a9ebd9ef 692 #t "sha256" input)))
26bbbb95 693 (make-derivation-input path '()))))
d26ad5e4 694 (delete-duplicates inputs)))
5b0c9d16 695 (env-vars (env-vars-with-empty-outputs (user+system-env-vars)))
26bbbb95
LC
696 (drv-masked (make-derivation outputs
697 (filter (compose derivation-path?
698 derivation-input-path)
699 inputs)
700 (filter-map (lambda (i)
701 (let ((p (derivation-input-path i)))
702 (and (not (derivation-path? p))
703 p)))
704 inputs)
6a446d56 705 system builder args env-vars #f))
26bbbb95 706 (drv (add-output-paths drv-masked)))
de4c3f26 707
6a446d56 708 (let ((file (add-text-to-store store (string-append name ".drv")
be4e38fb 709 (derivation->string drv)
6a446d56
LC
710 (map derivation-input-path
711 inputs))))
59688fc4
LC
712 (set-file-name drv file))))
713
e387ab7c
LC
714(define* (map-derivation store drv mapping
715 #:key (system (%current-system)))
716 "Given MAPPING, a list of pairs of derivations, return a derivation based on
717DRV where all the 'car's of MAPPING have been replaced by its 'cdr's,
718recursively."
719 (define (substitute str initial replacements)
720 (fold (lambda (path replacement result)
721 (string-replace-substring result path
722 replacement))
723 str
724 initial replacements))
725
726 (define (substitute-file file initial replacements)
727 (define contents
728 (with-fluids ((%default-port-encoding #f))
729 (call-with-input-file file get-string-all)))
730
731 (let ((updated (substitute contents initial replacements)))
732 (if (string=? updated contents)
733 file
734 ;; XXX: permissions aren't preserved.
735 (add-text-to-store store (store-path-package-name file)
736 updated))))
737
738 (define input->output-paths
739 (match-lambda
a716e36d 740 (((? derivation? drv))
e387ab7c 741 (list (derivation->output-path drv)))
a716e36d 742 (((? derivation? drv) sub-drvs ...)
e387ab7c 743 (map (cut derivation->output-path drv <>)
a716e36d
LC
744 sub-drvs))
745 ((file)
746 (list file))))
e387ab7c
LC
747
748 (let ((mapping (fold (lambda (pair result)
749 (match pair
a716e36d 750 (((? derivation? orig) . replacement)
e387ab7c 751 (vhash-cons (derivation-file-name orig)
a716e36d
LC
752 replacement result))
753 ((file . replacement)
754 (vhash-cons file replacement result))))
e387ab7c
LC
755 vlist-null
756 mapping)))
757 (define rewritten-input
758 ;; Rewrite the given input according to MAPPING, and return an input
759 ;; in the format used in 'derivation' calls.
760 (memoize
761 (lambda (input loop)
762 (match input
763 (($ <derivation-input> path (sub-drvs ...))
764 (match (vhash-assoc path mapping)
a716e36d 765 ((_ . (? derivation? replacement))
e387ab7c 766 (cons replacement sub-drvs))
a716e36d
LC
767 ((_ . replacement)
768 (list replacement))
e387ab7c
LC
769 (#f
770 (let* ((drv (loop (call-with-input-file path read-derivation))))
771 (cons drv sub-drvs)))))))))
772
773 (let loop ((drv drv))
774 (let* ((inputs (map (cut rewritten-input <> loop)
775 (derivation-inputs drv)))
776 (initial (append-map derivation-input-output-paths
777 (derivation-inputs drv)))
778 (replacements (append-map input->output-paths inputs))
779
780 ;; Sources typically refer to the output directories of the
781 ;; original inputs, INITIAL. Rewrite them by substituting
782 ;; REPLACEMENTS.
a716e36d
LC
783 (sources (map (lambda (source)
784 (match (vhash-assoc source mapping)
785 ((_ . replacement)
786 replacement)
787 (#f
788 (substitute-file source
789 initial replacements))))
e387ab7c
LC
790 (derivation-sources drv)))
791
792 ;; Now augment the lists of initials and replacements.
793 (initial (append (derivation-sources drv) initial))
794 (replacements (append sources replacements))
795 (name (store-path-package-name
796 (string-drop-right (derivation-file-name drv)
797 4))))
798 (derivation store name
799 (substitute (derivation-builder drv)
800 initial replacements)
801 (map (cut substitute <> initial replacements)
802 (derivation-builder-arguments drv))
803 #:system system
804 #:env-vars (map (match-lambda
805 ((var . value)
806 `(,var
807 . ,(substitute value initial
808 replacements))))
809 (derivation-builder-environment-vars drv))
810 #:inputs (append (map list sources) inputs)
811 #:outputs (map car (derivation-outputs drv))
812 #:hash (match (derivation-outputs drv)
813 ((($ <derivation-output> _ algo hash))
814 hash)
815 (_ #f))
816 #:hash-algo (match (derivation-outputs drv)
817 ((($ <derivation-output> _ algo hash))
818 algo)
819 (_ #f)))))))
820
59688fc4
LC
821\f
822;;;
823;;; Store compatibility layer.
824;;;
825
826(define (build-derivations store derivations)
827 "Build DERIVATIONS, a list of <derivation> objects or .drv file names."
828 (let ((build (@ (guix store) build-derivations)))
829 (build store (map (match-lambda
830 ((? string? file) file)
831 ((and drv ($ <derivation>))
832 (derivation-file-name drv)))
833 derivations))))
d9085c23
LC
834
835\f
836;;;
837;;; Guile-based builders.
838;;;
839
840(define %guile-for-build
841 ;; The derivation of the Guile to be used within the build environment,
842 ;; when using `build-expression->derivation'.
b272c474 843 (make-parameter #f))
d9085c23 844
d9024884
LC
845(define (parent-directories file-name)
846 "Return the list of parent dirs of FILE-NAME, in the order in which an
847`mkdir -p' implementation would make them."
848 (let ((not-slash (char-set-complement (char-set #\/))))
849 (reverse
850 (fold (lambda (dir result)
851 (match result
852 (()
853 (list dir))
854 ((prev _ ...)
855 (cons (string-append prev "/" dir)
856 result))))
857 '()
858 (remove (cut string=? <> ".")
859 (string-tokenize (dirname file-name) not-slash))))))
860
99634e3f 861(define* (imported-files store files
b272c474
LC
862 #:key (name "file-import")
863 (system (%current-system))
864 (guile (%guile-for-build)))
99634e3f
LC
865 "Return a derivation that imports FILES into STORE. FILES must be a list
866of (FINAL-PATH . FILE-NAME) pairs; each FILE-NAME is read from the file
867system, imported, and appears under FINAL-PATH in the resulting store path."
99634e3f
LC
868 (let* ((files (map (match-lambda
869 ((final-path . file-name)
2acb2cb6 870 (list final-path
a9ebd9ef 871 (add-to-store store (basename final-path) #f
99634e3f
LC
872 "sha256" file-name))))
873 files))
874 (builder
875 `(begin
876 (mkdir %output) (chdir %output)
877 ,@(append-map (match-lambda
2acb2cb6 878 ((final-path store-path)
d9024884 879 (append (match (parent-directories final-path)
99634e3f
LC
880 (() '())
881 ((head ... tail)
882 (append (map (lambda (d)
883 `(false-if-exception
884 (mkdir ,d)))
885 head)
224f7ad6
LC
886 `((or (file-exists? ,tail)
887 (mkdir ,tail))))))
99634e3f
LC
888 `((symlink ,store-path ,final-path)))))
889 files))))
dd1a5a15
LC
890 (build-expression->derivation store name builder
891 #:system system
892 #:inputs files
6ce206cb
LC
893 #:guile-for-build guile
894 #:local-build? #t)))
99634e3f 895
3eb98237
LC
896(define* (imported-modules store modules
897 #:key (name "module-import")
b272c474 898 (system (%current-system))
8dcb0c55
LC
899 (guile (%guile-for-build))
900 (module-path %load-path))
3eb98237 901 "Return a derivation that contains the source files of MODULES, a list of
8dcb0c55 902module names such as `(ice-9 q)'. All of MODULES must be in the MODULE-PATH
3eb98237
LC
903search path."
904 ;; TODO: Determine the closure of MODULES, build the `.go' files,
905 ;; canonicalize the source files through read/write, etc.
906 (let ((files (map (lambda (m)
907 (let ((f (string-append
908 (string-join (map symbol->string m) "/")
909 ".scm")))
8dcb0c55 910 (cons f (search-path module-path f))))
3eb98237 911 modules)))
b272c474
LC
912 (imported-files store files #:name name #:system system
913 #:guile guile)))
3eb98237 914
d9024884
LC
915(define* (compiled-modules store modules
916 #:key (name "module-import-compiled")
b272c474 917 (system (%current-system))
8dcb0c55
LC
918 (guile (%guile-for-build))
919 (module-path %load-path))
d9024884
LC
920 "Return a derivation that builds a tree containing the `.go' files
921corresponding to MODULES. All the MODULES are built in a context where
922they can refer to each other."
923 (let* ((module-drv (imported-modules store modules
b272c474 924 #:system system
8dcb0c55
LC
925 #:guile guile
926 #:module-path module-path))
59688fc4 927 (module-dir (derivation->output-path module-drv))
d9024884
LC
928 (files (map (lambda (m)
929 (let ((f (string-join (map symbol->string m)
930 "/")))
931 (cons (string-append f ".go")
932 (string-append module-dir "/" f ".scm"))))
933 modules)))
934 (define builder
935 `(begin
936 (use-modules (system base compile))
937 (let ((out (assoc-ref %outputs "out")))
938 (mkdir out)
939 (chdir out))
940
941 (set! %load-path
942 (cons ,module-dir %load-path))
943
944 ,@(map (match-lambda
945 ((output . input)
946 (let ((make-parent-dirs (map (lambda (dir)
947 `(unless (file-exists? ,dir)
948 (mkdir ,dir)))
949 (parent-directories output))))
950 `(begin
951 ,@make-parent-dirs
952 (compile-file ,input
953 #:output-file ,output
954 #:opts %auto-compilation-options)))))
955 files)))
956
dd1a5a15
LC
957 (build-expression->derivation store name builder
958 #:inputs `(("modules" ,module-drv))
959 #:system system
6ce206cb
LC
960 #:guile-for-build guile
961 #:local-build? #t)))
3eb98237 962
78a90c7f
LC
963(define* (graft-derivation store name drv replacements
964 #:key (guile (%guile-for-build)))
fb59e275
LC
965 "Return a derivation called NAME, based on DRV but with all the first
966elements of REPLACEMENTS replaced by the corresponding second element.
967REPLACEMENTS must be a list of ((DRV OUTPUT) . (DRV2 OUTPUT)) pairs."
968 ;; XXX: Someday rewrite using gexps.
969 (define mapping
970 ;; List of store item pairs.
971 (map (match-lambda
972 (((source source-outputs ...) . (target target-outputs ...))
973 (cons (if (derivation? source)
974 (apply derivation->output-path source source-outputs)
975 source)
976 (if (derivation? target)
977 (apply derivation->output-path target target-outputs)
978 target))))
979 replacements))
980
981 (define outputs
982 (match (derivation-outputs drv)
983 (((names . outputs) ...)
984 (map derivation-output-path outputs))))
985
986 (define output-names
987 (match (derivation-outputs drv)
988 (((names . outputs) ...)
989 names)))
990
991 (define build
992 `(begin
993 (use-modules (guix build graft)
994 (guix build utils)
995 (ice-9 match))
996
997 (let ((mapping ',mapping))
998 (for-each (lambda (input output)
999 (format #t "rewriting '~a' to '~a'...~%" input output)
1000 (rewrite-directory input output
1001 `((,input . ,output)
1002 ,@mapping)))
1003 ',outputs
1004 (match %outputs
1005 (((names . files) ...)
1006 files))))))
1007
1008 (define add-label
1009 (cut cons "x" <>))
1010
1011 (match replacements
1012 (((sources . targets) ...)
1013 (build-expression->derivation store name build
78a90c7f 1014 #:guile-for-build guile
fb59e275
LC
1015 #:modules '((guix build graft)
1016 (guix build utils))
1017 #:inputs `(("original" ,drv)
1018 ,@(append (map add-label sources)
1019 (map add-label targets)))
1020 #:outputs output-names
1021 #:local-build? #t))))
1022
dd1a5a15
LC
1023(define* (build-expression->derivation store name exp
1024 #:key
1025 (system (%current-system))
1026 (inputs '())
1027 (outputs '("out"))
36bbbbd1 1028 hash hash-algo recursive?
4c1eddf7 1029 (env-vars '())
6dd7787c 1030 (modules '())
9c629a27 1031 guile-for-build
1909431c 1032 references-graphs
63a42824 1033 allowed-references
1909431c 1034 local-build?)
874e6874
LC
1035 "Return a derivation that executes Scheme expression EXP as a builder
1036for derivation NAME. INPUTS must be a list of (NAME DRV-PATH SUB-DRV)
1037tuples; when SUB-DRV is omitted, \"out\" is assumed. MODULES is a list
1038of names of Guile modules from the current search path to be copied in
1039the store, compiled, and made available in the load path during the
1040execution of EXP.
1041
1042EXP is evaluated in an environment where %OUTPUT is bound to the main
1043output path, %OUTPUTS is bound to a list of output/path pairs, and where
1044%BUILD-INPUTS is bound to an alist of string/output-path pairs made from
1045INPUTS. Optionally, ENV-VARS is a list of string pairs specifying the
1046name and value of environment variables visible to the builder. The
1047builder terminates by passing the result of EXP to `exit'; thus, when
1048EXP returns #f, the build is considered to have failed.
6dd7787c
LC
1049
1050EXP is built using GUILE-FOR-BUILD (a derivation). When GUILE-FOR-BUILD is
9c629a27
LC
1051omitted or is #f, the value of the `%guile-for-build' fluid is used instead.
1052
63a42824
LC
1053See the `derivation' procedure for the meaning of REFERENCES-GRAPHS,
1054ALLOWED-REFERENCES, and LOCAL-BUILD?."
b272c474
LC
1055 (define guile-drv
1056 (or guile-for-build (%guile-for-build)))
1057
d9085c23 1058 (define guile
59688fc4 1059 (string-append (derivation->output-path guile-drv)
d9085c23
LC
1060 "/bin/guile"))
1061
0d56a551
LC
1062 (define module-form?
1063 (match-lambda
a987d2c0
LC
1064 (((or 'define-module 'use-modules) _ ...) #t)
1065 (_ #f)))
0d56a551 1066
7bdd1f0e
LC
1067 (define source-path
1068 ;; When passed an input that is a source, return its path; otherwise
1069 ;; return #f.
1070 (match-lambda
59688fc4
LC
1071 ((_ (? derivation?) _ ...)
1072 #f)
7bdd1f0e
LC
1073 ((_ path _ ...)
1074 (and (not (derivation-path? path))
1075 path))))
1076
d9085c23 1077 (let* ((prologue `(begin
0d56a551
LC
1078 ,@(match exp
1079 ((_ ...)
1080 ;; Module forms must appear at the top-level so
1081 ;; that any macros they export can be expanded.
1082 (filter module-form? exp))
1083 (_ `(,exp)))
1084
d9085c23 1085 (define %output (getenv "out"))
9bc07f4d
LC
1086 (define %outputs
1087 (map (lambda (o)
1088 (cons o (getenv o)))
1089 ',outputs))
d9085c23
LC
1090 (define %build-inputs
1091 ',(map (match-lambda
2acb2cb6
LC
1092 ((name drv . rest)
1093 (let ((sub (match rest
1094 (() "out")
1095 ((x) x))))
1096 (cons name
59688fc4
LC
1097 (cond
1098 ((derivation? drv)
1099 (derivation->output-path drv sub))
1100 ((derivation-path? drv)
1101 (derivation-path->output-path drv
1102 sub))
1103 (else drv))))))
d44bc84b
LC
1104 inputs))
1105
d9024884
LC
1106 ,@(if (null? modules)
1107 '()
1108 ;; Remove our own settings.
1109 '((unsetenv "GUILE_LOAD_COMPILED_PATH")))
1110
d44bc84b
LC
1111 ;; Guile sets it, but remove it to avoid conflicts when
1112 ;; building Guile-using packages.
1113 (unsetenv "LD_LIBRARY_PATH")))
d9085c23
LC
1114 (builder (add-text-to-store store
1115 (string-append name "-guile-builder")
0bb1aa9e
LC
1116
1117 ;; Explicitly use UTF-8 for determinism,
1118 ;; and also because UTF-8 output is faster.
1119 (with-fluids ((%default-port-encoding
1120 "UTF-8"))
1121 (call-with-output-string
1122 (lambda (port)
1123 (write prologue port)
1124 (write
1125 `(exit
1126 ,(match exp
1127 ((_ ...)
1128 (remove module-form? exp))
1129 (_ `(,exp))))
1130 port))))
7bdd1f0e
LC
1131
1132 ;; The references don't really matter
1133 ;; since the builder is always used in
1134 ;; conjunction with the drv that needs
1135 ;; it. For clarity, we add references
1136 ;; to the subset of INPUTS that are
1137 ;; sources, avoiding references to other
1138 ;; .drv; otherwise, BUILDER's hash would
1139 ;; depend on those, even if they are
1140 ;; fixed-output.
1141 (filter-map source-path inputs)))
1142
d9024884 1143 (mod-drv (and (pair? modules)
ae39d1b2
LC
1144 (imported-modules store modules
1145 #:guile guile-drv
1146 #:system system)))
3eb98237 1147 (mod-dir (and mod-drv
59688fc4 1148 (derivation->output-path mod-drv)))
d9024884 1149 (go-drv (and (pair? modules)
ae39d1b2
LC
1150 (compiled-modules store modules
1151 #:guile guile-drv
1152 #:system system)))
d9024884 1153 (go-dir (and go-drv
59688fc4 1154 (derivation->output-path go-drv))))
a987d2c0 1155 (derivation store name guile
3eb98237
LC
1156 `("--no-auto-compile"
1157 ,@(if mod-dir `("-L" ,mod-dir) '())
1158 ,builder)
d9024884 1159
a987d2c0
LC
1160 #:system system
1161
1162 #:inputs `((,(or guile-for-build (%guile-for-build)))
1163 (,builder)
1164 ,@(map cdr inputs)
1165 ,@(if mod-drv `((,mod-drv) (,go-drv)) '()))
1166
d9024884
LC
1167 ;; When MODULES is non-empty, shamelessly clobber
1168 ;; $GUILE_LOAD_COMPILED_PATH.
a987d2c0
LC
1169 #:env-vars (if go-dir
1170 `(("GUILE_LOAD_COMPILED_PATH" . ,go-dir)
1171 ,@(alist-delete "GUILE_LOAD_COMPILED_PATH"
1172 env-vars))
1173 env-vars)
1174
9bc07f4d 1175 #:hash hash #:hash-algo hash-algo
36bbbbd1 1176 #:recursive? recursive?
9c629a27 1177 #:outputs outputs
1909431c 1178 #:references-graphs references-graphs
63a42824 1179 #:allowed-references allowed-references
1909431c 1180 #:local-build? local-build?)))