derivations: Optimize `write-derivation'.
[jackhill/guix/guix.git] / guix / derivations.scm
CommitLineData
233e7676
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013 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)
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)
ddc29a78 29 #:use-module (guix base32)
9a20830e
LC
30 #:export (<derivation>
31 derivation?
77d3cf08
LC
32 derivation-outputs
33 derivation-inputs
34 derivation-sources
35 derivation-system
36 derivation-builder-arguments
37 derivation-builder-environment-vars
9a20830e
LC
38 derivation-prerequisites
39 derivation-prerequisites-to-build
77d3cf08 40
9a20830e 41 <derivation-output>
77d3cf08
LC
42 derivation-output?
43 derivation-output-path
44 derivation-output-hash-algo
45 derivation-output-hash
46
9a20830e 47 <derivation-input>
77d3cf08
LC
48 derivation-input?
49 derivation-input-path
50 derivation-input-sub-derivations
51
52 fixed-output-derivation?
341c6fdd
LC
53 derivation-hash
54
55 read-derivation
26bbbb95 56 write-derivation
de4c3f26 57 derivation-path->output-path
7244a5f7 58 derivation-path->output-paths
d9085c23
LC
59 derivation
60
61 %guile-for-build
99634e3f
LC
62 build-expression->derivation
63 imported-files))
77d3cf08
LC
64
65;;;
66;;; Nix derivations, as implemented in Nix's `derivations.cc'.
67;;;
68
69(define-record-type <derivation>
70 (make-derivation outputs inputs sources system builder args env-vars)
71 derivation?
72 (outputs derivation-outputs) ; list of name/<derivation-output> pairs
73 (inputs derivation-inputs) ; list of <derivation-input>
74 (sources derivation-sources) ; list of store paths
75 (system derivation-system) ; string
76 (builder derivation-builder) ; store path
77 (args derivation-builder-arguments) ; list of strings
78 (env-vars derivation-builder-environment-vars)) ; list of name/value pairs
79
80(define-record-type <derivation-output>
81 (make-derivation-output path hash-algo hash)
82 derivation-output?
83 (path derivation-output-path) ; store path
84 (hash-algo derivation-output-hash-algo) ; symbol | #f
749c6567 85 (hash derivation-output-hash)) ; bytevector | #f
77d3cf08
LC
86
87(define-record-type <derivation-input>
88 (make-derivation-input path sub-derivations)
89 derivation-input?
90 (path derivation-input-path) ; store path
91 (sub-derivations derivation-input-sub-derivations)) ; list of strings
92
93(define (fixed-output-derivation? drv)
94 "Return #t if DRV is a fixed-output derivation, such as the result of a
95download with a fixed hash (aka. `fetchurl')."
96 (match drv
97 (($ <derivation>
98 (($ <derivation-output> _ (? symbol?) (? string?))))
99 #t)
100 (_ #f)))
101
9a20830e
LC
102(define (derivation-prerequisites drv)
103 "Return the list of derivation-inputs required to build DRV, recursively."
104 (let loop ((drv drv)
105 (result '()))
106 (let ((inputs (remove (cut member <> result) ; XXX: quadratic
107 (derivation-inputs drv))))
108 (fold loop
109 (append inputs result)
110 (map (lambda (i)
111 (call-with-input-file (derivation-input-path i)
112 read-derivation))
113 inputs)))))
114
784bb1f3
LC
115(define* (derivation-prerequisites-to-build store drv
116 #:key (outputs
117 (map
118 car
119 (derivation-outputs drv))))
120 "Return the list of derivation-inputs required to build the OUTPUTS of
121DRV and not already available in STORE, recursively."
122 (define built?
123 (cut valid-path? store <>))
124
9a20830e
LC
125 (define input-built?
126 (match-lambda
127 (($ <derivation-input> path sub-drvs)
128 (let ((out (map (cut derivation-path->output-path path <>)
129 sub-drvs)))
784bb1f3
LC
130 (any built? out)))))
131
132 (define (derivation-built? drv sub-drvs)
133 (match drv
134 (($ <derivation> outputs)
135 (let ((paths (map (lambda (sub-drv)
136 (derivation-output-path
137 (assoc-ref outputs sub-drv)))
138 sub-drvs)))
139 (every built? paths)))))
140
141 (let loop ((drv drv)
142 (sub-drvs outputs)
143 (result '()))
144 (if (derivation-built? drv sub-drvs)
145 result
146 (let ((inputs (remove (lambda (i)
147 (or (member i result) ; XXX: quadratic
148 (input-built? i)))
149 (derivation-inputs drv))))
150 (fold loop
151 (append inputs result)
152 (map (lambda (i)
153 (call-with-input-file (derivation-input-path i)
154 read-derivation))
155 inputs)
156 (map derivation-input-sub-derivations inputs))))))
9a20830e 157
d0840e4a
LC
158(define (%read-derivation drv-port)
159 ;; Actually read derivation from DRV-PORT.
77d3cf08
LC
160
161 (define comma (string->symbol ","))
162
163 (define (ununquote x)
164 (match x
165 (('unquote x) (ununquote x))
166 ((x ...) (map ununquote x))
167 (_ x)))
168
169 (define (outputs->alist x)
170 (fold-right (lambda (output result)
171 (match output
172 ((name path "" "")
173 (alist-cons name
174 (make-derivation-output path #f #f)
175 result))
176 ((name path hash-algo hash)
177 ;; fixed-output
749c6567
LC
178 (let ((algo (string->symbol hash-algo))
179 (hash (base16-string->bytevector hash)))
77d3cf08
LC
180 (alist-cons name
181 (make-derivation-output path algo hash)
182 result)))))
183 '()
184 x))
185
186 (define (make-input-drvs x)
187 (fold-right (lambda (input result)
188 (match input
189 ((path (sub-drvs ...))
190 (cons (make-derivation-input path sub-drvs)
191 result))))
192 '()
193 x))
194
df7bbd38
LC
195 ;; The contents of a derivation are typically ASCII, but choosing
196 ;; UTF-8 allows us to take the fast path for Guile's `scm_getc'.
197 (set-port-encoding! drv-port "UTF-8")
198
77d3cf08
LC
199 (let loop ((exp (read drv-port))
200 (result '()))
201 (match exp
202 ((? eof-object?)
203 (let ((result (reverse result)))
204 (match result
205 (('Derive ((outputs ...) (input-drvs ...)
206 (input-srcs ...)
207 (? string? system)
208 (? string? builder)
209 ((? string? args) ...)
210 ((var value) ...)))
211 (make-derivation (outputs->alist outputs)
212 (make-input-drvs input-drvs)
213 input-srcs
214 system builder args
215 (fold-right alist-cons '() var value)))
216 (_
217 (error "failed to parse derivation" drv-port result)))))
218 ((? (cut eq? <> comma))
219 (loop (read drv-port) result))
220 (_
221 (loop (read drv-port)
222 (cons (ununquote exp) result))))))
223
d0840e4a
LC
224(define read-derivation
225 (let ((cache (make-weak-value-hash-table 200)))
226 (lambda (drv-port)
227 "Read the derivation from DRV-PORT and return the corresponding
228<derivation> object."
229 ;; Memoize that operation because `%read-derivation' is quite expensive,
230 ;; and because the same argument is read more than 15 times on average
231 ;; during something like (package-derivation s gdb).
232 (let ((file (and=> (port-filename drv-port) basename)))
233 (or (and file (hash-ref cache file))
234 (let ((drv (%read-derivation drv-port)))
235 (hash-set! cache file drv)
236 drv))))))
237
d8085599
LC
238(define-inlinable (write-sequence lst write-item port)
239 ;; Write each element of LST with WRITE-ITEM to PORT, separating them with a
240 ;; comma.
241 (match lst
242 (()
243 #t)
244 ((prefix (... ...) last)
245 (for-each (lambda (item)
246 (write-item item port)
247 (display "," port))
248 prefix)
249 (write-item last port))))
250
251(define-inlinable (write-list lst write-item port)
252 ;; Write LST as a derivation list to PORT, using WRITE-ITEM to write each
253 ;; element.
254 (display "[" port)
255 (write-sequence lst write-item port)
256 (display "]" port))
257
258(define-inlinable (write-tuple lst write-item port)
259 ;; Same, but write LST as a tuple.
260 (display "(" port)
261 (write-sequence lst write-item port)
262 (display ")" port))
263
77d3cf08
LC
264(define (write-derivation drv port)
265 "Write the ATerm-like serialization of DRV to PORT. See Section 2.4 of
266Eelco Dolstra's PhD dissertation for an overview of a previous version of
267that form."
aaa848f3
LC
268
269 ;; Make sure we're using the faster implementation.
270 (define format simple-format)
271
d8085599
LC
272 (define (write-string-list lst)
273 (write-list lst write port))
77d3cf08 274
d66ac374
LC
275 (define (coalesce-duplicate-inputs inputs)
276 ;; Return a list of inputs, such that when INPUTS contains the same DRV
277 ;; twice, they are coalesced, with their sub-derivations merged. This is
278 ;; needed because Nix itself keeps only one of them.
279 (fold (lambda (input result)
280 (match input
281 (($ <derivation-input> path sub-drvs)
282 ;; XXX: quadratic
283 (match (find (match-lambda
284 (($ <derivation-input> p s)
285 (string=? p path)))
286 result)
287 (#f
288 (cons input result))
289 ((and dup ($ <derivation-input> _ sub-drvs2))
290 ;; Merge DUP with INPUT.
291 (let ((sub-drvs (delete-duplicates
292 (append sub-drvs sub-drvs2))))
293 (cons (make-derivation-input path sub-drvs)
294 (delq dup result))))))))
295 '()
296 inputs))
297
d8085599
LC
298 (define (write-output output port)
299 (match output
300 ((name . ($ <derivation-output> path hash-algo hash))
301 (write-tuple (list name path
302 (or (and=> hash-algo symbol->string) "")
303 (or (and=> hash bytevector->base16-string)
304 ""))
305 write
306 port))))
307
308 (define (write-input input port)
309 (match input
310 (($ <derivation-input> path sub-drvs)
311 (display "(" port)
312 (write path port)
313 (display "," port)
314 (write-string-list (sort sub-drvs string<?))
315 (display ")" port))))
316
317 (define (write-env-var env-var port)
318 (match env-var
319 ((name . value)
320 (display "(" port)
321 (write name port)
322 (display "," port)
323 (write value port)
324 (display ")" port))))
325
561eaf71
LC
326 ;; Note: lists are sorted alphabetically, to conform with the behavior of
327 ;; C++ `std::map' in Nix itself.
328
77d3cf08
LC
329 (match drv
330 (($ <derivation> outputs inputs sources
331 system builder args env-vars)
332 (display "Derive(" port)
d8085599
LC
333 (write-list (sort outputs
334 (lambda (o1 o2)
335 (string<? (car o1) (car o2))))
336 write-output
337 port)
77d3cf08 338 (display "," port)
d8085599
LC
339 (write-list (sort (coalesce-duplicate-inputs inputs)
340 (lambda (i1 i2)
341 (string<? (derivation-input-path i1)
342 (derivation-input-path i2))))
343 write-input
344 port)
77d3cf08 345 (display "," port)
d8085599 346 (write-string-list (sort sources string<?))
77d3cf08 347 (format port ",~s,~s," system builder)
d8085599 348 (write-string-list args)
77d3cf08 349 (display "," port)
d8085599
LC
350 (write-list (sort env-vars
351 (lambda (e1 e2)
352 (string<? (car e1) (car e2))))
353 write-env-var
354 port)
77d3cf08
LC
355 (display ")" port))))
356
aaa848f3
LC
357(define derivation-path->output-path
358 ;; This procedure is called frequently, so memoize it.
359 (memoize
360 (lambda* (path #:optional (output "out"))
361 "Read the derivation from PATH (`/nix/store/xxx.drv'), and return the store
de4c3f26 362path of its output OUTPUT."
aaa848f3
LC
363 (let* ((drv (call-with-input-file path read-derivation))
364 (outputs (derivation-outputs drv)))
365 (and=> (assoc-ref outputs output) derivation-output-path)))))
de4c3f26 366
7244a5f7
LC
367(define (derivation-path->output-paths path)
368 "Read the derivation from PATH (`/nix/store/xxx.drv'), and return the
369list of name/path pairs of its outputs."
370 (let* ((drv (call-with-input-file path read-derivation))
371 (outputs (derivation-outputs drv)))
372 (map (match-lambda
373 ((name . output)
374 (cons name (derivation-output-path output))))
375 outputs)))
376
de4c3f26
LC
377\f
378;;;
379;;; Derivation primitive.
380;;;
381
26bbbb95
LC
382(define (compressed-hash bv size) ; `compressHash'
383 "Given the hash stored in BV, return a compressed version thereof that fits
384in SIZE bytes."
385 (define new (make-bytevector size 0))
386 (define old-size (bytevector-length bv))
387 (let loop ((i 0))
388 (if (= i old-size)
389 new
390 (let* ((j (modulo i size))
391 (o (bytevector-u8-ref new j)))
392 (bytevector-u8-set! new j
393 (logxor o (bytevector-u8-ref bv i)))
394 (loop (+ 1 i))))))
77d3cf08 395
de4c3f26
LC
396(define derivation-hash ; `hashDerivationModulo' in derivations.cc
397 (memoize
398 (lambda (drv)
399 "Return the hash of DRV, modulo its fixed-output inputs, as a bytevector."
400 (match drv
401 (($ <derivation> ((_ . ($ <derivation-output> path
749c6567 402 (? symbol? hash-algo) (? bytevector? hash)))))
de4c3f26 403 ;; A fixed-output derivation.
77d3cf08 404 (sha256
de4c3f26
LC
405 (string->utf8
406 (string-append "fixed:out:" (symbol->string hash-algo)
749c6567
LC
407 ":" (bytevector->base16-string hash)
408 ":" path))))
de4c3f26
LC
409 (($ <derivation> outputs inputs sources
410 system builder args env-vars)
411 ;; A regular derivation: replace the path of each input with that
412 ;; input's hash; return the hash of serialization of the resulting
561eaf71
LC
413 ;; derivation.
414 (let* ((inputs (map (match-lambda
415 (($ <derivation-input> path sub-drvs)
416 (let ((hash (call-with-input-file path
417 (compose bytevector->base16-string
418 derivation-hash
419 read-derivation))))
420 (make-derivation-input hash sub-drvs))))
421 inputs))
422 (drv (make-derivation outputs inputs sources
423 system builder args env-vars)))
de4c3f26
LC
424 (sha256
425 (string->utf8 (call-with-output-string
426 (cut write-derivation drv <>))))))))))
77d3cf08 427
26bbbb95
LC
428(define (store-path type hash name) ; makeStorePath
429 "Return the store path for NAME/HASH/TYPE."
430 (let* ((s (string-append type ":sha256:"
431 (bytevector->base16-string hash) ":"
432 (%store-prefix) ":" name))
433 (h (sha256 (string->utf8 s)))
434 (c (compressed-hash h 20)))
435 (string-append (%store-prefix) "/"
436 (bytevector->nix-base32-string c) "-"
437 name)))
438
439(define (output-path output hash name) ; makeOutputPath
440 "Return an output path for OUTPUT (the name of the output as a string) of
441the derivation called NAME with hash HASH."
442 (store-path (string-append "output:" output) hash
443 (if (string=? output "out")
444 name
445 (string-append name "-" output))))
446
447(define* (derivation store name system builder args env-vars inputs
448 #:key (outputs '("out")) hash hash-algo hash-mode)
449 "Build a derivation with the given arguments. Return the resulting
fb3eec83 450store path and <derivation> object. When HASH, HASH-ALGO, and HASH-MODE
26bbbb95
LC
451are given, a fixed-output derivation is created---i.e., one whose result is
452known in advance, such as a file download."
200dc937
LC
453 (define direct-store-path?
454 (let ((len (+ 1 (string-length (%store-prefix)))))
455 (lambda (p)
456 ;; Return #t if P is a store path, and not a sub-directory of a
457 ;; store path. This predicate is needed because files *under* a
458 ;; store path are not valid inputs.
459 (and (store-path? p)
460 (not (string-index (substring p len) #\/))))))
461
26bbbb95
LC
462 (define (add-output-paths drv)
463 ;; Return DRV with an actual store path for each of its output and the
464 ;; corresponding environment variable.
465 (match drv
466 (($ <derivation> outputs inputs sources
467 system builder args env-vars)
468 (let* ((drv-hash (derivation-hash drv))
469 (outputs (map (match-lambda
d9085c23
LC
470 ((output-name . ($ <derivation-output>
471 _ algo hash))
472 (let ((path (output-path output-name
473 drv-hash name)))
474 (cons output-name
475 (make-derivation-output path algo
476 hash)))))
477 outputs)))
26bbbb95
LC
478 (make-derivation outputs inputs sources system builder args
479 (map (match-lambda
480 ((name . value)
481 (cons name
482 (or (and=> (assoc-ref outputs name)
483 derivation-output-path)
484 value))))
485 env-vars))))))
486
487 (define (env-vars-with-empty-outputs)
488 ;; Return a variant of ENV-VARS where each OUTPUTS is associated with an
561eaf71 489 ;; empty string, even outputs that do not appear in ENV-VARS.
26bbbb95
LC
490 (let ((e (map (match-lambda
491 ((name . val)
492 (if (member name outputs)
493 (cons name "")
494 (cons name val))))
495 env-vars)))
561eaf71
LC
496 (fold (lambda (output-name env-vars)
497 (if (assoc output-name env-vars)
498 env-vars
499 (append env-vars `((,output-name . "")))))
500 e
501 outputs)))
26bbbb95
LC
502
503 (let* ((outputs (map (lambda (name)
504 ;; Return outputs with an empty path.
505 (cons name
506 (make-derivation-output "" hash-algo hash)))
507 outputs))
508 (inputs (map (match-lambda
200dc937 509 (((? direct-store-path? input))
de4c3f26 510 (make-derivation-input input '("out")))
200dc937 511 (((? direct-store-path? input) sub-drvs ...)
26bbbb95
LC
512 (make-derivation-input input sub-drvs))
513 ((input . _)
514 (let ((path (add-to-store store
515 (basename input)
a9ebd9ef 516 #t "sha256" input)))
26bbbb95 517 (make-derivation-input path '()))))
d26ad5e4 518 (delete-duplicates inputs)))
26bbbb95
LC
519 (env-vars (env-vars-with-empty-outputs))
520 (drv-masked (make-derivation outputs
521 (filter (compose derivation-path?
522 derivation-input-path)
523 inputs)
524 (filter-map (lambda (i)
525 (let ((p (derivation-input-path i)))
526 (and (not (derivation-path? p))
527 p)))
528 inputs)
529 system builder args env-vars))
530 (drv (add-output-paths drv-masked)))
de4c3f26 531
d66ac374
LC
532 ;; (write-derivation drv-masked (current-error-port))
533 ;; (newline (current-error-port))
fb3eec83
LC
534 (values (add-text-to-store store (string-append name ".drv")
535 (call-with-output-string
536 (cut write-derivation drv <>))
537 (map derivation-input-path
538 inputs))
539 drv)))
d9085c23
LC
540
541\f
542;;;
543;;; Guile-based builders.
544;;;
545
546(define %guile-for-build
547 ;; The derivation of the Guile to be used within the build environment,
548 ;; when using `build-expression->derivation'.
b272c474 549 (make-parameter #f))
d9085c23 550
d9024884
LC
551(define (parent-directories file-name)
552 "Return the list of parent dirs of FILE-NAME, in the order in which an
553`mkdir -p' implementation would make them."
554 (let ((not-slash (char-set-complement (char-set #\/))))
555 (reverse
556 (fold (lambda (dir result)
557 (match result
558 (()
559 (list dir))
560 ((prev _ ...)
561 (cons (string-append prev "/" dir)
562 result))))
563 '()
564 (remove (cut string=? <> ".")
565 (string-tokenize (dirname file-name) not-slash))))))
566
99634e3f 567(define* (imported-files store files
b272c474
LC
568 #:key (name "file-import")
569 (system (%current-system))
570 (guile (%guile-for-build)))
99634e3f
LC
571 "Return a derivation that imports FILES into STORE. FILES must be a list
572of (FINAL-PATH . FILE-NAME) pairs; each FILE-NAME is read from the file
573system, imported, and appears under FINAL-PATH in the resulting store path."
99634e3f
LC
574 (let* ((files (map (match-lambda
575 ((final-path . file-name)
2acb2cb6 576 (list final-path
a9ebd9ef 577 (add-to-store store (basename final-path) #f
99634e3f
LC
578 "sha256" file-name))))
579 files))
580 (builder
581 `(begin
582 (mkdir %output) (chdir %output)
583 ,@(append-map (match-lambda
2acb2cb6 584 ((final-path store-path)
d9024884 585 (append (match (parent-directories final-path)
99634e3f
LC
586 (() '())
587 ((head ... tail)
588 (append (map (lambda (d)
589 `(false-if-exception
590 (mkdir ,d)))
591 head)
224f7ad6
LC
592 `((or (file-exists? ,tail)
593 (mkdir ,tail))))))
99634e3f
LC
594 `((symlink ,store-path ,final-path)))))
595 files))))
ae39d1b2 596 (build-expression->derivation store name system
b272c474
LC
597 builder files
598 #:guile-for-build guile)))
99634e3f 599
3eb98237
LC
600(define* (imported-modules store modules
601 #:key (name "module-import")
b272c474 602 (system (%current-system))
8dcb0c55
LC
603 (guile (%guile-for-build))
604 (module-path %load-path))
3eb98237 605 "Return a derivation that contains the source files of MODULES, a list of
8dcb0c55 606module names such as `(ice-9 q)'. All of MODULES must be in the MODULE-PATH
3eb98237
LC
607search path."
608 ;; TODO: Determine the closure of MODULES, build the `.go' files,
609 ;; canonicalize the source files through read/write, etc.
610 (let ((files (map (lambda (m)
611 (let ((f (string-append
612 (string-join (map symbol->string m) "/")
613 ".scm")))
8dcb0c55 614 (cons f (search-path module-path f))))
3eb98237 615 modules)))
b272c474
LC
616 (imported-files store files #:name name #:system system
617 #:guile guile)))
3eb98237 618
d9024884
LC
619(define* (compiled-modules store modules
620 #:key (name "module-import-compiled")
b272c474 621 (system (%current-system))
8dcb0c55
LC
622 (guile (%guile-for-build))
623 (module-path %load-path))
d9024884
LC
624 "Return a derivation that builds a tree containing the `.go' files
625corresponding to MODULES. All the MODULES are built in a context where
626they can refer to each other."
627 (let* ((module-drv (imported-modules store modules
b272c474 628 #:system system
8dcb0c55
LC
629 #:guile guile
630 #:module-path module-path))
d9024884
LC
631 (module-dir (derivation-path->output-path module-drv))
632 (files (map (lambda (m)
633 (let ((f (string-join (map symbol->string m)
634 "/")))
635 (cons (string-append f ".go")
636 (string-append module-dir "/" f ".scm"))))
637 modules)))
638 (define builder
639 `(begin
640 (use-modules (system base compile))
641 (let ((out (assoc-ref %outputs "out")))
642 (mkdir out)
643 (chdir out))
644
645 (set! %load-path
646 (cons ,module-dir %load-path))
647
648 ,@(map (match-lambda
649 ((output . input)
650 (let ((make-parent-dirs (map (lambda (dir)
651 `(unless (file-exists? ,dir)
652 (mkdir ,dir)))
653 (parent-directories output))))
654 `(begin
655 ,@make-parent-dirs
656 (compile-file ,input
657 #:output-file ,output
658 #:opts %auto-compilation-options)))))
659 files)))
660
661 (build-expression->derivation store name system builder
b272c474
LC
662 `(("modules" ,module-drv))
663 #:guile-for-build guile)))
3eb98237 664
d9085c23 665(define* (build-expression->derivation store name system exp inputs
9bc07f4d 666 #:key (outputs '("out"))
3eb98237 667 hash hash-algo
4c1eddf7 668 (env-vars '())
6dd7787c
LC
669 (modules '())
670 guile-for-build)
874e6874
LC
671 "Return a derivation that executes Scheme expression EXP as a builder
672for derivation NAME. INPUTS must be a list of (NAME DRV-PATH SUB-DRV)
673tuples; when SUB-DRV is omitted, \"out\" is assumed. MODULES is a list
674of names of Guile modules from the current search path to be copied in
675the store, compiled, and made available in the load path during the
676execution of EXP.
677
678EXP is evaluated in an environment where %OUTPUT is bound to the main
679output path, %OUTPUTS is bound to a list of output/path pairs, and where
680%BUILD-INPUTS is bound to an alist of string/output-path pairs made from
681INPUTS. Optionally, ENV-VARS is a list of string pairs specifying the
682name and value of environment variables visible to the builder. The
683builder terminates by passing the result of EXP to `exit'; thus, when
684EXP returns #f, the build is considered to have failed.
6dd7787c
LC
685
686EXP is built using GUILE-FOR-BUILD (a derivation). When GUILE-FOR-BUILD is
687omitted or is #f, the value of the `%guile-for-build' fluid is used instead."
b272c474
LC
688 (define guile-drv
689 (or guile-for-build (%guile-for-build)))
690
d9085c23 691 (define guile
b272c474 692 (string-append (derivation-path->output-path guile-drv)
d9085c23
LC
693 "/bin/guile"))
694
0d56a551
LC
695 (define module-form?
696 (match-lambda
697 (((or 'define-module 'use-modules) _ ...) #t)
698 (_ #f)))
699
7bdd1f0e
LC
700 (define source-path
701 ;; When passed an input that is a source, return its path; otherwise
702 ;; return #f.
703 (match-lambda
704 ((_ path _ ...)
705 (and (not (derivation-path? path))
706 path))))
707
d9085c23 708 (let* ((prologue `(begin
0d56a551
LC
709 ,@(match exp
710 ((_ ...)
711 ;; Module forms must appear at the top-level so
712 ;; that any macros they export can be expanded.
713 (filter module-form? exp))
714 (_ `(,exp)))
715
d9085c23 716 (define %output (getenv "out"))
9bc07f4d
LC
717 (define %outputs
718 (map (lambda (o)
719 (cons o (getenv o)))
720 ',outputs))
d9085c23
LC
721 (define %build-inputs
722 ',(map (match-lambda
2acb2cb6
LC
723 ((name drv . rest)
724 (let ((sub (match rest
725 (() "out")
726 ((x) x))))
727 (cons name
728 (if (derivation-path? drv)
729 (derivation-path->output-path drv
730 sub)
731 drv)))))
d44bc84b
LC
732 inputs))
733
d9024884
LC
734 ,@(if (null? modules)
735 '()
736 ;; Remove our own settings.
737 '((unsetenv "GUILE_LOAD_COMPILED_PATH")))
738
d44bc84b
LC
739 ;; Guile sets it, but remove it to avoid conflicts when
740 ;; building Guile-using packages.
741 (unsetenv "LD_LIBRARY_PATH")))
d9085c23
LC
742 (builder (add-text-to-store store
743 (string-append name "-guile-builder")
0d56a551
LC
744 (string-append
745 (object->string prologue)
746 (object->string
747 `(exit
748 ,(match exp
749 ((_ ...)
750 (remove module-form? exp))
751 (_ `(,exp))))))
7bdd1f0e
LC
752
753 ;; The references don't really matter
754 ;; since the builder is always used in
755 ;; conjunction with the drv that needs
756 ;; it. For clarity, we add references
757 ;; to the subset of INPUTS that are
758 ;; sources, avoiding references to other
759 ;; .drv; otherwise, BUILDER's hash would
760 ;; depend on those, even if they are
761 ;; fixed-output.
762 (filter-map source-path inputs)))
763
d9024884 764 (mod-drv (and (pair? modules)
ae39d1b2
LC
765 (imported-modules store modules
766 #:guile guile-drv
767 #:system system)))
3eb98237 768 (mod-dir (and mod-drv
d9024884
LC
769 (derivation-path->output-path mod-drv)))
770 (go-drv (and (pair? modules)
ae39d1b2
LC
771 (compiled-modules store modules
772 #:guile guile-drv
773 #:system system)))
d9024884
LC
774 (go-dir (and go-drv
775 (derivation-path->output-path go-drv))))
3eb98237
LC
776 (derivation store name system guile
777 `("--no-auto-compile"
778 ,@(if mod-dir `("-L" ,mod-dir) '())
779 ,builder)
d9024884
LC
780
781 ;; When MODULES is non-empty, shamelessly clobber
782 ;; $GUILE_LOAD_COMPILED_PATH.
783 (if go-dir
784 `(("GUILE_LOAD_COMPILED_PATH" . ,go-dir)
785 ,@(alist-delete "GUILE_LOAD_COMPILED_PATH"
786 env-vars))
787 env-vars)
788
6dd7787c 789 `((,(or guile-for-build (%guile-for-build)))
3eb98237 790 (,builder)
2acb2cb6 791 ,@(map cdr inputs)
d9024884 792 ,@(if mod-drv `((,mod-drv) (,go-drv)) '()))
9bc07f4d
LC
793 #:hash hash #:hash-algo hash-algo
794 #:outputs outputs)))