gnu: Add BioPerl.
[jackhill/guix/guix.git] / guix / gexp.scm
CommitLineData
21b679f6 1;;; GNU Guix --- Functional package management for GNU
462a3fa3 2;;; Copyright © 2014, 2015 Ludovic Courtès <ludo@gnu.org>
21b679f6
LC
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix gexp)
e87f0591 20 #:use-module (guix store)
21b679f6 21 #:use-module (guix monads)
e87f0591 22 #:use-module (guix derivations)
aa72d9af 23 #:use-module (guix utils)
21b679f6
LC
24 #:use-module (srfi srfi-1)
25 #:use-module (srfi srfi-9)
7560b00b 26 #:use-module (srfi srfi-9 gnu)
21b679f6
LC
27 #:use-module (srfi srfi-26)
28 #:use-module (ice-9 match)
29 #:export (gexp
30 gexp?
0dbea56b
LC
31
32 gexp-input
33 gexp-input?
558e8b11 34
d9ae938f
LC
35 local-file
36 local-file?
74d441ab
LC
37 local-file-file
38 local-file-name
39 local-file-recursive?
0dbea56b 40
558e8b11
LC
41 plain-file
42 plain-file?
43 plain-file-name
44 plain-file-content
45
21b679f6
LC
46 gexp->derivation
47 gexp->file
462a3fa3 48 gexp->script
aa72d9af
LC
49 text-file*
50 imported-files
51 imported-modules
ff40e9b7
LC
52 compiled-modules
53
54 define-gexp-compiler
6b6298ae
LC
55 gexp-compiler?
56
57 lower-inputs))
21b679f6
LC
58
59;;; Commentary:
60;;;
61;;; This module implements "G-expressions", or "gexps". Gexps are like
62;;; S-expressions (sexps), with two differences:
63;;;
64;;; 1. References (un-quotations) to derivations or packages in a gexp are
667b2508
LC
65;;; replaced by the corresponding output file name; in addition, the
66;;; 'ungexp-native' unquote-like form allows code to explicitly refer to
67;;; the native code of a given package, in case of cross-compilation;
21b679f6
LC
68;;;
69;;; 2. Gexps embed information about the derivations they refer to.
70;;;
71;;; Gexps make it easy to write to files Scheme code that refers to store
72;;; items, or to write Scheme code to build derivations.
73;;;
74;;; Code:
75
76;; "G expressions".
77(define-record-type <gexp>
667b2508 78 (make-gexp references natives proc)
21b679f6
LC
79 gexp?
80 (references gexp-references) ; ((DRV-OR-PKG OUTPUT) ...)
667b2508 81 (natives gexp-native-references) ; ((DRV-OR-PKG OUTPUT) ...)
21b679f6
LC
82 (proc gexp-proc)) ; procedure
83
7560b00b
LC
84(define (write-gexp gexp port)
85 "Write GEXP on PORT."
86 (display "#<gexp " port)
2cf0ea0d
LC
87
88 ;; Try to write the underlying sexp. Now, this trick doesn't work when
89 ;; doing things like (ungexp-splicing (gexp ())) because GEXP's procedure
90 ;; tries to use 'append' on that, which fails with wrong-type-arg.
91 (false-if-exception
667b2508
LC
92 (write (apply (gexp-proc gexp)
93 (append (gexp-references gexp)
94 (gexp-native-references gexp)))
95 port))
7560b00b
LC
96 (format port " ~a>"
97 (number->string (object-address gexp) 16)))
98
99(set-record-type-printer! <gexp> write-gexp)
100
bcb13287
LC
101\f
102;;;
103;;; Methods.
104;;;
105
106;; Compiler for a type of objects that may be introduced in a gexp.
107(define-record-type <gexp-compiler>
108 (gexp-compiler predicate lower)
109 gexp-compiler?
110 (predicate gexp-compiler-predicate)
111 (lower gexp-compiler-lower))
112
113(define %gexp-compilers
114 ;; List of <gexp-compiler>.
115 '())
116
117(define (register-compiler! compiler)
118 "Register COMPILER as a gexp compiler."
119 (set! %gexp-compilers (cons compiler %gexp-compilers)))
120
121(define (lookup-compiler object)
122 "Search a compiler for OBJECT. Upon success, return the three argument
123procedure to lower it; otherwise return #f."
124 (any (match-lambda
125 (($ <gexp-compiler> predicate lower)
126 (and (predicate object) lower)))
127 %gexp-compilers))
128
129(define-syntax-rule (define-gexp-compiler (name (param predicate)
130 system target)
131 body ...)
132 "Define NAME as a compiler for objects matching PREDICATE encountered in
133gexps. BODY must return a derivation for PARAM, an object that matches
134PREDICATE, for SYSTEM and TARGET (the latter of which is #f except when
135cross-compiling.)"
136 (begin
137 (define name
138 (gexp-compiler predicate
139 (lambda (param system target)
140 body ...)))
141 (register-compiler! name)))
142
2924f0d6
LC
143(define-gexp-compiler (derivation-compiler (drv derivation?) system target)
144 ;; Derivations are the lowest-level representation, so this is the identity
145 ;; compiler.
146 (with-monad %store-monad
147 (return drv)))
148
bcb13287 149\f
d9ae938f 150;;;
558e8b11 151;;; File declarations.
d9ae938f
LC
152;;;
153
154(define-record-type <local-file>
155 (%local-file file name recursive?)
156 local-file?
157 (file local-file-file) ;string
158 (name local-file-name) ;string
159 (recursive? local-file-recursive?)) ;Boolean
160
161(define* (local-file file #:optional (name (basename file))
020f3e41 162 #:key recursive?)
d9ae938f
LC
163 "Return an object representing local file FILE to add to the store; this
164object can be used in a gexp. FILE will be added to the store under NAME--by
165default the base name of FILE.
166
167When RECURSIVE? is true, the contents of FILE are added recursively; if FILE
168designates a flat file and RECURSIVE? is true, its contents are added, and its
169permission bits are kept.
170
171This is the declarative counterpart of the 'interned-file' monadic procedure."
7833db1f
LC
172 ;; Canonicalize FILE so that if it's a symlink, it is resolved. Failing to
173 ;; do that, when RECURSIVE? is #t, we could end up creating a dangling
174 ;; symlink in the store, and when RECURSIVE? is #f 'add-to-store' would just
175 ;; throw an error, both of which are inconvenient.
176 (%local-file (canonicalize-path file) name recursive?))
d9ae938f
LC
177
178(define-gexp-compiler (local-file-compiler (file local-file?) system target)
179 ;; "Compile" FILE by adding it to the store.
180 (match file
181 (($ <local-file> file name recursive?)
182 (interned-file file name #:recursive? recursive?))))
183
558e8b11
LC
184(define-record-type <plain-file>
185 (%plain-file name content references)
186 plain-file?
187 (name plain-file-name) ;string
188 (content plain-file-content) ;string
189 (references plain-file-references)) ;list (currently unused)
190
191(define (plain-file name content)
192 "Return an object representing a text file called NAME with the given
193CONTENT (a string) to be added to the store.
194
195This is the declarative counterpart of 'text-file'."
196 ;; XXX: For now just ignore 'references' because it's not clear how to use
197 ;; them in a declarative context.
198 (%plain-file name content '()))
199
200(define-gexp-compiler (plain-file-compiler (file plain-file?) system target)
201 ;; "Compile" FILE by adding it to the store.
202 (match file
203 (($ <plain-file> name content references)
204 (text-file name content references))))
205
d9ae938f 206\f
bcb13287
LC
207;;;
208;;; Inputs & outputs.
209;;;
210
e39d1461
LC
211;; The input of a gexp.
212(define-record-type <gexp-input>
0dbea56b 213 (%gexp-input thing output native?)
e39d1461
LC
214 gexp-input?
215 (thing gexp-input-thing) ;<package> | <origin> | <derivation> | ...
216 (output gexp-input-output) ;string
217 (native? gexp-input-native?)) ;Boolean
218
f7328634
LC
219(define (write-gexp-input input port)
220 (match input
221 (($ <gexp-input> thing output #f)
222 (format port "#<gexp-input ~s:~a>" thing output))
223 (($ <gexp-input> thing output #t)
224 (format port "#<gexp-input native ~s:~a>" thing output))))
225
226(set-record-type-printer! <gexp-input> write-gexp-input)
227
0dbea56b
LC
228(define* (gexp-input thing ;convenience procedure
229 #:optional (output "out")
230 #:key native?)
231 "Return a new <gexp-input> for the OUTPUT of THING; NATIVE? determines
232whether this should be considered a \"native\" input or not."
233 (%gexp-input thing output native?))
234
21b679f6
LC
235;; Reference to one of the derivation's outputs, for gexps used in
236;; derivations.
1e87da58
LC
237(define-record-type <gexp-output>
238 (gexp-output name)
239 gexp-output?
240 (name gexp-output-name))
21b679f6 241
f7328634
LC
242(define (write-gexp-output output port)
243 (match output
244 (($ <gexp-output> name)
245 (format port "#<gexp-output ~a>" name))))
246
247(set-record-type-printer! <gexp-output> write-gexp-output)
248
21b679f6
LC
249(define raw-derivation
250 (store-lift derivation))
251
68a61e9f
LC
252(define* (lower-inputs inputs
253 #:key system target)
254 "Turn any package from INPUTS into a derivation for SYSTEM; return the
255corresponding input list as a monadic value. When TARGET is true, use it as
256the cross-compilation target triplet."
21b679f6
LC
257 (with-monad %store-monad
258 (sequence %store-monad
259 (map (match-lambda
2242ff45
LC
260 (((? struct? thing) sub-drv ...)
261 (mlet* %store-monad ((lower -> (lookup-compiler thing))
262 (drv (lower thing system target)))
263 (return `(,drv ,@sub-drv))))
264 (input
265 (return input)))
21b679f6
LC
266 inputs))))
267
b53833b2
LC
268(define* (lower-reference-graphs graphs #:key system target)
269 "Given GRAPHS, a list of (FILE-NAME INPUT ...) lists for use as a
270#:reference-graphs argument, lower it such that each INPUT is replaced by the
271corresponding derivation."
272 (match graphs
273 (((file-names . inputs) ...)
274 (mlet %store-monad ((inputs (lower-inputs inputs
275 #:system system
276 #:target target)))
277 (return (map cons file-names inputs))))))
278
c8351d9a
LC
279(define* (lower-references lst #:key system target)
280 "Based on LST, a list of output names and packages, return a list of output
281names and file names suitable for the #:allowed-references argument to
282'derivation'."
283 ;; XXX: Currently outputs other than "out" are not supported, and things
284 ;; other than packages aren't either.
285 (with-monad %store-monad
286 (define lower
287 (match-lambda
288 ((? string? output)
289 (return output))
accb682c
LC
290 (($ <gexp-input> thing output native?)
291 (mlet* %store-monad ((lower -> (lookup-compiler thing))
292 (drv (lower thing system
293 (if native? #f target))))
294 (return (derivation->output-path drv output))))
bcb13287
LC
295 (thing
296 (mlet* %store-monad ((lower -> (lookup-compiler thing))
297 (drv (lower thing system target)))
c8351d9a
LC
298 (return (derivation->output-path drv))))))
299
300 (sequence %store-monad (map lower lst))))
301
ff40e9b7
LC
302(define default-guile-derivation
303 ;; Here we break the abstraction by talking to the higher-level layer.
304 ;; Thus, do the resolution lazily to hide the circular dependency.
305 (let ((proc (delay
306 (let ((iface (resolve-interface '(guix packages))))
307 (module-ref iface 'default-guile-derivation)))))
308 (lambda (system)
309 ((force proc) system))))
310
21b679f6
LC
311(define* (gexp->derivation name exp
312 #:key
68a61e9f 313 system (target 'current)
21b679f6
LC
314 hash hash-algo recursive?
315 (env-vars '())
316 (modules '())
4684f301 317 (module-path %load-path)
21b679f6 318 (guile-for-build (%guile-for-build))
ce45eb4c 319 (graft? (%graft?))
21b679f6 320 references-graphs
c8351d9a 321 allowed-references
c0468155 322 leaked-env-vars
4a6aeb67 323 local-build? (substitutable? #t))
21b679f6 324 "Return a derivation NAME that runs EXP (a gexp) with GUILE-FOR-BUILD (a
68a61e9f
LC
325derivation) on SYSTEM. When TARGET is true, it is used as the
326cross-compilation target triplet for packages referred to by EXP.
21b679f6
LC
327
328Make MODULES available in the evaluation context of EXP; MODULES is a list of
4684f301 329names of Guile modules searched in MODULE-PATH to be copied in the store,
21b679f6
LC
330compiled, and made available in the load path during the execution of
331EXP---e.g., '((guix build utils) (guix build gnu-build-system)).
332
ce45eb4c
LC
333GRAFT? determines whether packages referred to by EXP should be grafted when
334applicable.
335
b53833b2
LC
336When REFERENCES-GRAPHS is true, it must be a list of tuples of one of the
337following forms:
338
339 (FILE-NAME PACKAGE)
340 (FILE-NAME PACKAGE OUTPUT)
341 (FILE-NAME DERIVATION)
342 (FILE-NAME DERIVATION OUTPUT)
343 (FILE-NAME STORE-ITEM)
344
345The right-hand-side of each element of REFERENCES-GRAPHS is automatically made
346an input of the build process of EXP. In the build environment, each
347FILE-NAME contains the reference graph of the corresponding item, in a simple
348text format.
349
c8351d9a
LC
350ALLOWED-REFERENCES must be either #f or a list of output names and packages.
351In the latter case, the list denotes store items that the result is allowed to
352refer to. Any reference to another store item will lead to a build error.
b53833b2 353
21b679f6
LC
354The other arguments are as for 'derivation'."
355 (define %modules modules)
356 (define outputs (gexp-outputs exp))
357
b53833b2
LC
358 (define (graphs-file-names graphs)
359 ;; Return a list of (FILE-NAME . STORE-PATH) pairs made from GRAPHS.
360 (map (match-lambda
2924f0d6 361 ;; TODO: Remove 'derivation?' special cases.
b53833b2
LC
362 ((file-name (? derivation? drv))
363 (cons file-name (derivation->output-path drv)))
364 ((file-name (? derivation? drv) sub-drv)
365 (cons file-name (derivation->output-path drv sub-drv)))
366 ((file-name thing)
367 (cons file-name thing)))
368 graphs))
369
ce45eb4c
LC
370 (mlet* %store-monad (;; The following binding forces '%current-system' and
371 ;; '%current-target-system' to be looked up at >>=
372 ;; time.
373 (graft? (set-grafting graft?))
68a61e9f 374
5d098459 375 (system -> (or system (%current-system)))
68a61e9f
LC
376 (target -> (if (eq? target 'current)
377 (%current-target-system)
378 target))
667b2508 379 (normals (lower-inputs (gexp-inputs exp)
68a61e9f
LC
380 #:system system
381 #:target target))
667b2508
LC
382 (natives (lower-inputs (gexp-native-inputs exp)
383 #:system system
384 #:target #f))
385 (inputs -> (append normals natives))
68a61e9f
LC
386 (sexp (gexp->sexp exp
387 #:system system
388 #:target target))
21b679f6
LC
389 (builder (text-file (string-append name "-builder")
390 (object->string sexp)))
391 (modules (if (pair? %modules)
392 (imported-modules %modules
393 #:system system
4684f301 394 #:module-path module-path
21b679f6
LC
395 #:guile guile-for-build)
396 (return #f)))
397 (compiled (if (pair? %modules)
398 (compiled-modules %modules
399 #:system system
4684f301 400 #:module-path module-path
21b679f6
LC
401 #:guile guile-for-build)
402 (return #f)))
b53833b2
LC
403 (graphs (if references-graphs
404 (lower-reference-graphs references-graphs
405 #:system system
406 #:target target)
407 (return #f)))
c8351d9a
LC
408 (allowed (if allowed-references
409 (lower-references allowed-references
410 #:system system
411 #:target target)
412 (return #f)))
21b679f6
LC
413 (guile (if guile-for-build
414 (return guile-for-build)
ff40e9b7 415 (default-guile-derivation system))))
ce45eb4c
LC
416 (mbegin %store-monad
417 (set-grafting graft?) ;restore the initial setting
418 (raw-derivation name
419 (string-append (derivation->output-path guile)
420 "/bin/guile")
421 `("--no-auto-compile"
422 ,@(if (pair? %modules)
423 `("-L" ,(derivation->output-path modules)
424 "-C" ,(derivation->output-path compiled))
425 '())
426 ,builder)
427 #:outputs outputs
428 #:env-vars env-vars
429 #:system system
430 #:inputs `((,guile)
431 (,builder)
432 ,@(if modules
433 `((,modules) (,compiled) ,@inputs)
434 inputs)
435 ,@(match graphs
436 (((_ . inputs) ...) inputs)
437 (_ '())))
438 #:hash hash #:hash-algo hash-algo #:recursive? recursive?
439 #:references-graphs (and=> graphs graphs-file-names)
440 #:allowed-references allowed
c0468155 441 #:leaked-env-vars leaked-env-vars
4a6aeb67
LC
442 #:local-build? local-build?
443 #:substitutable? substitutable?))))
21b679f6 444
1123759b
LC
445(define* (gexp-inputs exp #:key native?)
446 "Return the input list for EXP. When NATIVE? is true, return only native
447references; otherwise, return only non-native references."
21b679f6
LC
448 (define (add-reference-inputs ref result)
449 (match ref
1123759b
LC
450 (($ <gexp-input> (? gexp? exp) _ #t)
451 (if native?
452 (append (gexp-inputs exp)
453 (gexp-inputs exp #:native? #t)
454 result)
455 result))
456 (($ <gexp-input> (? gexp? exp) _ #f)
457 (if native?
458 (append (gexp-inputs exp #:native? #t)
459 result)
460 (append (gexp-inputs exp)
461 result)))
e39d1461
LC
462 (($ <gexp-input> (? string? str))
463 (if (direct-store-path? str)
464 (cons `(,str) result)
21b679f6 465 result))
bcb13287
LC
466 (($ <gexp-input> (? struct? thing) output)
467 (if (lookup-compiler thing)
468 ;; THING is a derivation, or a package, or an origin, etc.
469 (cons `(,thing ,output) result)
470 result))
1123759b 471 (($ <gexp-input> (lst ...) output n?)
e39d1461
LC
472 (fold-right add-reference-inputs result
473 ;; XXX: For now, automatically convert LST to a list of
474 ;; gexp-inputs.
0dbea56b
LC
475 (map (match-lambda
476 ((? gexp-input? x) x)
1123759b 477 (x (%gexp-input x "out" (or n? native?))))
0dbea56b 478 lst)))
21b679f6
LC
479 (_
480 ;; Ignore references to other kinds of objects.
481 result)))
482
483 (fold-right add-reference-inputs
484 '()
1123759b
LC
485 (if native?
486 (gexp-native-references exp)
487 (gexp-references exp))))
667b2508
LC
488
489(define gexp-native-inputs
1123759b 490 (cut gexp-inputs <> #:native? #t))
21b679f6
LC
491
492(define (gexp-outputs exp)
493 "Return the outputs referred to by EXP as a list of strings."
494 (define (add-reference-output ref result)
495 (match ref
1e87da58 496 (($ <gexp-output> name)
21b679f6 497 (cons name result))
e39d1461 498 (($ <gexp-input> (? gexp? exp))
21b679f6 499 (append (gexp-outputs exp) result))
e39d1461
LC
500 (($ <gexp-input> (lst ...) output native?)
501 ;; XXX: Automatically convert LST.
0dbea56b
LC
502 (add-reference-output (map (match-lambda
503 ((? gexp-input? x) x)
504 (x (%gexp-input x "out" native?)))
505 lst)
e39d1461 506 result))
f9efe568
LC
507 ((lst ...)
508 (fold-right add-reference-output result lst))
21b679f6
LC
509 (_
510 result)))
511
7e75a673
LC
512 (delete-duplicates
513 (add-reference-output (gexp-references exp) '())))
21b679f6 514
68a61e9f
LC
515(define* (gexp->sexp exp #:key
516 (system (%current-system))
517 (target (%current-target-system)))
21b679f6
LC
518 "Return (monadically) the sexp corresponding to EXP for the given OUTPUT,
519and in the current monad setting (system type, etc.)"
667b2508 520 (define* (reference->sexp ref #:optional native?)
21b679f6
LC
521 (with-monad %store-monad
522 (match ref
1e87da58 523 (($ <gexp-output> output)
bfd9eed9
LC
524 ;; Output file names are not known in advance but the daemon defines
525 ;; an environment variable for each of them at build time, so use
526 ;; that trick.
527 (return `((@ (guile) getenv) ,output)))
e39d1461 528 (($ <gexp-input> (? gexp? exp) output n?)
667b2508
LC
529 (gexp->sexp exp
530 #:system system
e39d1461
LC
531 #:target (if (or n? native?) #f target)))
532 (($ <gexp-input> (refs ...) output n?)
667b2508 533 (sequence %store-monad
e39d1461
LC
534 (map (lambda (ref)
535 ;; XXX: Automatically convert REF to an gexp-input.
0dbea56b
LC
536 (reference->sexp
537 (if (gexp-input? ref)
538 ref
539 (%gexp-input ref "out" n?))
540 native?))
e39d1461 541 refs)))
bcb13287
LC
542 (($ <gexp-input> (? struct? thing) output n?)
543 (let ((lower (lookup-compiler thing))
544 (target (if (or n? native?) #f target)))
d9ae938f
LC
545 (mlet %store-monad ((obj (lower thing system target)))
546 ;; OBJ must be either a derivation or a store file name.
547 (return (match obj
548 ((? derivation? drv)
549 (derivation->output-path drv output))
550 ((? string? file)
551 file))))))
e39d1461
LC
552 (($ <gexp-input> x)
553 (return x))
21b679f6
LC
554 (x
555 (return x)))))
556
557 (mlet %store-monad
558 ((args (sequence %store-monad
667b2508
LC
559 (append (map reference->sexp (gexp-references exp))
560 (map (cut reference->sexp <> #t)
561 (gexp-native-references exp))))))
21b679f6
LC
562 (return (apply (gexp-proc exp) args))))
563
21b679f6
LC
564(define (syntax-location-string s)
565 "Return a string representing the source code location of S."
566 (let ((props (syntax-source s)))
567 (if props
568 (let ((file (assoc-ref props 'filename))
569 (line (and=> (assoc-ref props 'line) 1+))
570 (column (assoc-ref props 'column)))
571 (if file
572 (simple-format #f "~a:~a:~a"
573 file line column)
574 (simple-format #f "~a:~a" line column)))
575 "<unknown location>")))
576
577(define-syntax gexp
578 (lambda (s)
579 (define (collect-escapes exp)
580 ;; Return all the 'ungexp' present in EXP.
581 (let loop ((exp exp)
582 (result '()))
607e1b51
LC
583 (syntax-case exp (ungexp
584 ungexp-splicing
585 ungexp-native
586 ungexp-native-splicing)
21b679f6
LC
587 ((ungexp _)
588 (cons exp result))
589 ((ungexp _ _)
590 (cons exp result))
591 ((ungexp-splicing _ ...)
592 (cons exp result))
607e1b51
LC
593 ((ungexp-native _ ...)
594 result)
595 ((ungexp-native-splicing _ ...)
596 result)
21b679f6
LC
597 ((exp0 exp ...)
598 (let ((result (loop #'exp0 result)))
599 (fold loop result #'(exp ...))))
600 (_
601 result))))
602
667b2508
LC
603 (define (collect-native-escapes exp)
604 ;; Return all the 'ungexp-native' forms present in EXP.
605 (let loop ((exp exp)
606 (result '()))
607e1b51
LC
607 (syntax-case exp (ungexp
608 ungexp-splicing
609 ungexp-native
610 ungexp-native-splicing)
667b2508
LC
611 ((ungexp-native _)
612 (cons exp result))
613 ((ungexp-native _ _)
614 (cons exp result))
615 ((ungexp-native-splicing _ ...)
616 (cons exp result))
607e1b51
LC
617 ((ungexp _ ...)
618 result)
619 ((ungexp-splicing _ ...)
620 result)
667b2508
LC
621 ((exp0 exp ...)
622 (let ((result (loop #'exp0 result)))
623 (fold loop result #'(exp ...))))
624 (_
625 result))))
626
21b679f6
LC
627 (define (escape->ref exp)
628 ;; Turn 'ungexp' form EXP into a "reference".
667b2508
LC
629 (syntax-case exp (ungexp ungexp-splicing
630 ungexp-native ungexp-native-splicing
631 output)
21b679f6 632 ((ungexp output)
1e87da58 633 #'(gexp-output "out"))
21b679f6 634 ((ungexp output name)
1e87da58 635 #'(gexp-output name))
21b679f6 636 ((ungexp thing)
0dbea56b 637 #'(%gexp-input thing "out" #f))
21b679f6 638 ((ungexp drv-or-pkg out)
0dbea56b 639 #'(%gexp-input drv-or-pkg out #f))
21b679f6 640 ((ungexp-splicing lst)
0dbea56b 641 #'(%gexp-input lst "out" #f))
667b2508 642 ((ungexp-native thing)
0dbea56b 643 #'(%gexp-input thing "out" #t))
667b2508 644 ((ungexp-native drv-or-pkg out)
0dbea56b 645 #'(%gexp-input drv-or-pkg out #t))
667b2508 646 ((ungexp-native-splicing lst)
0dbea56b 647 #'(%gexp-input lst "out" #t))))
21b679f6 648
667b2508
LC
649 (define (substitute-ungexp exp substs)
650 ;; Given EXP, an 'ungexp' or 'ungexp-native' form, substitute it with
651 ;; the corresponding form in SUBSTS.
652 (match (assoc exp substs)
653 ((_ id)
654 id)
655 (_
656 #'(syntax-error "error: no 'ungexp' substitution"
657 #'ref))))
658
659 (define (substitute-ungexp-splicing exp substs)
660 (syntax-case exp ()
661 ((exp rest ...)
662 (match (assoc #'exp substs)
663 ((_ id)
664 (with-syntax ((id id))
665 #`(append id
666 #,(substitute-references #'(rest ...) substs))))
667 (_
668 #'(syntax-error "error: no 'ungexp-splicing' substitution"
669 #'ref))))))
670
21b679f6
LC
671 (define (substitute-references exp substs)
672 ;; Return a variant of EXP where all the cars of SUBSTS have been
673 ;; replaced by the corresponding cdr.
667b2508
LC
674 (syntax-case exp (ungexp ungexp-native
675 ungexp-splicing ungexp-native-splicing)
21b679f6 676 ((ungexp _ ...)
667b2508
LC
677 (substitute-ungexp exp substs))
678 ((ungexp-native _ ...)
679 (substitute-ungexp exp substs))
21b679f6 680 (((ungexp-splicing _ ...) rest ...)
667b2508
LC
681 (substitute-ungexp-splicing exp substs))
682 (((ungexp-native-splicing _ ...) rest ...)
683 (substitute-ungexp-splicing exp substs))
21b679f6
LC
684 ((exp0 exp ...)
685 #`(cons #,(substitute-references #'exp0 substs)
686 #,(substitute-references #'(exp ...) substs)))
687 (x #''x)))
688
689 (syntax-case s (ungexp output)
690 ((_ exp)
667b2508
LC
691 (let* ((normals (delete-duplicates (collect-escapes #'exp)))
692 (natives (delete-duplicates (collect-native-escapes #'exp)))
693 (escapes (append normals natives))
21b679f6
LC
694 (formals (generate-temporaries escapes))
695 (sexp (substitute-references #'exp (zip escapes formals)))
667b2508
LC
696 (refs (map escape->ref normals))
697 (nrefs (map escape->ref natives)))
e39d1461 698 #`(make-gexp (list #,@refs) (list #,@nrefs)
21b679f6
LC
699 (lambda #,formals
700 #,sexp)))))))
701
702\f
aa72d9af
LC
703;;;
704;;; Module handling.
705;;;
706
707(define %mkdir-p-definition
708 ;; The code for 'mkdir-p' is copied from (guix build utils). We use it in
709 ;; derivations that cannot use the #:modules argument of 'gexp->derivation'
710 ;; precisely because they implement that functionality.
711 (gexp
712 (define (mkdir-p dir)
713 (define absolute?
714 (string-prefix? "/" dir))
715
716 (define not-slash
717 (char-set-complement (char-set #\/)))
718
719 (let loop ((components (string-tokenize dir not-slash))
720 (root (if absolute? "" ".")))
721 (match components
722 ((head tail ...)
723 (let ((path (string-append root "/" head)))
724 (catch 'system-error
725 (lambda ()
726 (mkdir path)
727 (loop tail path))
728 (lambda args
729 (if (= EEXIST (system-error-errno args))
730 (loop tail path)
731 (apply throw args))))))
732 (() #t))))))
733
734(define* (imported-files files
735 #:key (name "file-import")
736 (system (%current-system))
737 (guile (%guile-for-build)))
738 "Return a derivation that imports FILES into STORE. FILES must be a list
739of (FINAL-PATH . FILE-NAME) pairs; each FILE-NAME is read from the file
740system, imported, and appears under FINAL-PATH in the resulting store path."
741 (define file-pair
742 (match-lambda
743 ((final-path . file-name)
744 (mlet %store-monad ((file (interned-file file-name
745 (basename final-path))))
746 (return (list final-path file))))))
747
748 (mlet %store-monad ((files (sequence %store-monad
749 (map file-pair files))))
750 (define build
751 (gexp
752 (begin
753 (use-modules (ice-9 match))
754
755 (ungexp %mkdir-p-definition)
756
757 (mkdir (ungexp output)) (chdir (ungexp output))
758 (for-each (match-lambda
759 ((final-path store-path)
760 (mkdir-p (dirname final-path))
761 (symlink store-path final-path)))
762 '(ungexp files)))))
763
764 ;; TODO: Pass FILES as an environment variable so that BUILD remains
765 ;; exactly the same regardless of FILES: less disk space, and fewer
766 ;; 'add-to-store' RPCs.
767 (gexp->derivation name build
768 #:system system
769 #:guile-for-build guile
770 #:local-build? #t)))
771
772(define search-path*
773 ;; A memoizing version of 'search-path' so 'imported-modules' does not end
774 ;; up looking for the same files over and over again.
775 (memoize search-path))
776
777(define* (imported-modules modules
778 #:key (name "module-import")
779 (system (%current-system))
780 (guile (%guile-for-build))
781 (module-path %load-path))
782 "Return a derivation that contains the source files of MODULES, a list of
783module names such as `(ice-9 q)'. All of MODULES must be in the MODULE-PATH
784search path."
785 ;; TODO: Determine the closure of MODULES, build the `.go' files,
786 ;; canonicalize the source files through read/write, etc.
787 (let ((files (map (lambda (m)
788 (let ((f (string-append
789 (string-join (map symbol->string m) "/")
790 ".scm")))
791 (cons f (search-path* module-path f))))
792 modules)))
793 (imported-files files #:name name #:system system
794 #:guile guile)))
795
796(define* (compiled-modules modules
797 #:key (name "module-import-compiled")
798 (system (%current-system))
799 (guile (%guile-for-build))
800 (module-path %load-path))
801 "Return a derivation that builds a tree containing the `.go' files
802corresponding to MODULES. All the MODULES are built in a context where
803they can refer to each other."
804 (mlet %store-monad ((modules (imported-modules modules
805 #:system system
806 #:guile guile
807 #:module-path
808 module-path)))
809 (define build
810 (gexp
811 (begin
812 (use-modules (ice-9 ftw)
813 (ice-9 match)
814 (srfi srfi-26)
815 (system base compile))
816
817 (ungexp %mkdir-p-definition)
818
819 (define (regular? file)
820 (not (member file '("." ".."))))
821
822 (define (process-directory directory output)
823 (let ((entries (map (cut string-append directory "/" <>)
824 (scandir directory regular?))))
825 (for-each (lambda (entry)
826 (if (file-is-directory? entry)
827 (let ((output (string-append output "/"
828 (basename entry))))
829 (mkdir-p output)
830 (process-directory entry output))
831 (let* ((base (string-drop-right
832 (basename entry)
833 4)) ;.scm
834 (output (string-append output "/" base
835 ".go")))
836 (compile-file entry
837 #:output-file output
838 #:opts
839 %auto-compilation-options))))
840 entries)))
841
842 (set! %load-path (cons (ungexp modules) %load-path))
843 (mkdir (ungexp output))
844 (chdir (ungexp modules))
845 (process-directory "." (ungexp output)))))
846
847 ;; TODO: Pass MODULES as an environment variable.
848 (gexp->derivation name build
849 #:system system
850 #:guile-for-build guile
851 #:local-build? #t)))
852
853\f
21b679f6
LC
854;;;
855;;; Convenience procedures.
856;;;
857
53e89b17
LC
858(define (default-guile)
859 ;; Lazily resolve 'guile-final'. This module must not refer to (gnu …)
860 ;; modules directly, to avoid circular dependencies, hence this hack.
bdb36958 861 (module-ref (resolve-interface '(gnu packages commencement))
53e89b17
LC
862 'guile-final))
863
21b679f6 864(define* (gexp->script name exp
53e89b17 865 #:key (modules '()) (guile (default-guile)))
21b679f6
LC
866 "Return an executable script NAME that runs EXP using GUILE with MODULES in
867its search path."
868 (mlet %store-monad ((modules (imported-modules modules))
869 (compiled (compiled-modules modules)))
870 (gexp->derivation name
871 (gexp
872 (call-with-output-file (ungexp output)
873 (lambda (port)
c17b5ab4
LC
874 ;; Note: that makes a long shebang. When the store
875 ;; is /gnu/store, that fits within the 128-byte
876 ;; limit imposed by Linux, but that may go beyond
877 ;; when running tests.
21b679f6
LC
878 (format port
879 "#!~a/bin/guile --no-auto-compile~%!#~%"
880 (ungexp guile))
4a4cbd0b
LC
881
882 ;; Write the 'eval-when' form so that it can be
883 ;; compiled.
21b679f6 884 (write
4a4cbd0b
LC
885 '(eval-when (expand load eval)
886 (set! %load-path
887 (cons (ungexp modules) %load-path))
888 (set! %load-compiled-path
889 (cons (ungexp compiled)
890 %load-compiled-path)))
21b679f6
LC
891 port)
892 (write '(ungexp exp) port)
893 (chmod port #o555)))))))
894
895(define (gexp->file name exp)
896 "Return a derivation that builds a file NAME containing EXP."
897 (gexp->derivation name
898 (gexp
899 (call-with-output-file (ungexp output)
900 (lambda (port)
dc254e05
LC
901 (write '(ungexp exp) port))))
902 #:local-build? #t))
21b679f6 903
462a3fa3
LC
904(define* (text-file* name #:rest text)
905 "Return as a monadic value a derivation that builds a text file containing
d9ae938f
LC
906all of TEXT. TEXT may list, in addition to strings, objects of any type that
907can be used in a gexp: packages, derivations, local file objects, etc. The
908resulting store file holds references to all these."
462a3fa3
LC
909 (define builder
910 (gexp (call-with-output-file (ungexp output "out")
911 (lambda (port)
912 (display (string-append (ungexp-splicing text)) port)))))
913
914 (gexp->derivation name builder))
915
21b679f6
LC
916\f
917;;;
918;;; Syntactic sugar.
919;;;
920
921(eval-when (expand load eval)
667b2508
LC
922 (define* (read-ungexp chr port #:optional native?)
923 "Read an 'ungexp' or 'ungexp-splicing' form from PORT. When NATIVE? is
924true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
21b679f6
LC
925 (define unquote-symbol
926 (match (peek-char port)
927 (#\@
928 (read-char port)
667b2508
LC
929 (if native?
930 'ungexp-native-splicing
931 'ungexp-splicing))
21b679f6 932 (_
667b2508
LC
933 (if native?
934 'ungexp-native
935 'ungexp))))
21b679f6
LC
936
937 (match (read port)
938 ((? symbol? symbol)
939 (let ((str (symbol->string symbol)))
940 (match (string-index-right str #\:)
941 (#f
942 `(,unquote-symbol ,symbol))
943 (colon
944 (let ((name (string->symbol (substring str 0 colon)))
945 (output (substring str (+ colon 1))))
946 `(,unquote-symbol ,name ,output))))))
947 (x
948 `(,unquote-symbol ,x))))
949
950 (define (read-gexp chr port)
951 "Read a 'gexp' form from PORT."
952 `(gexp ,(read port)))
953
954 ;; Extend the reader
955 (read-hash-extend #\~ read-gexp)
667b2508
LC
956 (read-hash-extend #\$ read-ungexp)
957 (read-hash-extend #\+ (cut read-ungexp <> <> #t)))
21b679f6
LC
958
959;;; gexp.scm ends here