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