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