Merge branch 'nix'.
[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
21b679f6
LC
323 local-build?)
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
ce45eb4c 442 #:local-build? local-build?))))
21b679f6 443
1123759b
LC
444(define* (gexp-inputs exp #:key native?)
445 "Return the input list for EXP. When NATIVE? is true, return only native
446references; otherwise, return only non-native references."
21b679f6
LC
447 (define (add-reference-inputs ref result)
448 (match ref
1123759b
LC
449 (($ <gexp-input> (? gexp? exp) _ #t)
450 (if native?
451 (append (gexp-inputs exp)
452 (gexp-inputs exp #:native? #t)
453 result)
454 result))
455 (($ <gexp-input> (? gexp? exp) _ #f)
456 (if native?
457 (append (gexp-inputs exp #:native? #t)
458 result)
459 (append (gexp-inputs exp)
460 result)))
e39d1461
LC
461 (($ <gexp-input> (? string? str))
462 (if (direct-store-path? str)
463 (cons `(,str) result)
21b679f6 464 result))
bcb13287
LC
465 (($ <gexp-input> (? struct? thing) output)
466 (if (lookup-compiler thing)
467 ;; THING is a derivation, or a package, or an origin, etc.
468 (cons `(,thing ,output) result)
469 result))
1123759b 470 (($ <gexp-input> (lst ...) output n?)
e39d1461
LC
471 (fold-right add-reference-inputs result
472 ;; XXX: For now, automatically convert LST to a list of
473 ;; gexp-inputs.
0dbea56b
LC
474 (map (match-lambda
475 ((? gexp-input? x) x)
1123759b 476 (x (%gexp-input x "out" (or n? native?))))
0dbea56b 477 lst)))
21b679f6
LC
478 (_
479 ;; Ignore references to other kinds of objects.
480 result)))
481
482 (fold-right add-reference-inputs
483 '()
1123759b
LC
484 (if native?
485 (gexp-native-references exp)
486 (gexp-references exp))))
667b2508
LC
487
488(define gexp-native-inputs
1123759b 489 (cut gexp-inputs <> #:native? #t))
21b679f6
LC
490
491(define (gexp-outputs exp)
492 "Return the outputs referred to by EXP as a list of strings."
493 (define (add-reference-output ref result)
494 (match ref
1e87da58 495 (($ <gexp-output> name)
21b679f6 496 (cons name result))
e39d1461 497 (($ <gexp-input> (? gexp? exp))
21b679f6 498 (append (gexp-outputs exp) result))
e39d1461
LC
499 (($ <gexp-input> (lst ...) output native?)
500 ;; XXX: Automatically convert LST.
0dbea56b
LC
501 (add-reference-output (map (match-lambda
502 ((? gexp-input? x) x)
503 (x (%gexp-input x "out" native?)))
504 lst)
e39d1461 505 result))
f9efe568
LC
506 ((lst ...)
507 (fold-right add-reference-output result lst))
21b679f6
LC
508 (_
509 result)))
510
7e75a673
LC
511 (delete-duplicates
512 (add-reference-output (gexp-references exp) '())))
21b679f6 513
68a61e9f
LC
514(define* (gexp->sexp exp #:key
515 (system (%current-system))
516 (target (%current-target-system)))
21b679f6
LC
517 "Return (monadically) the sexp corresponding to EXP for the given OUTPUT,
518and in the current monad setting (system type, etc.)"
667b2508 519 (define* (reference->sexp ref #:optional native?)
21b679f6
LC
520 (with-monad %store-monad
521 (match ref
1e87da58 522 (($ <gexp-output> output)
bfd9eed9
LC
523 ;; Output file names are not known in advance but the daemon defines
524 ;; an environment variable for each of them at build time, so use
525 ;; that trick.
526 (return `((@ (guile) getenv) ,output)))
e39d1461 527 (($ <gexp-input> (? gexp? exp) output n?)
667b2508
LC
528 (gexp->sexp exp
529 #:system system
e39d1461
LC
530 #:target (if (or n? native?) #f target)))
531 (($ <gexp-input> (refs ...) output n?)
667b2508 532 (sequence %store-monad
e39d1461
LC
533 (map (lambda (ref)
534 ;; XXX: Automatically convert REF to an gexp-input.
0dbea56b
LC
535 (reference->sexp
536 (if (gexp-input? ref)
537 ref
538 (%gexp-input ref "out" n?))
539 native?))
e39d1461 540 refs)))
bcb13287
LC
541 (($ <gexp-input> (? struct? thing) output n?)
542 (let ((lower (lookup-compiler thing))
543 (target (if (or n? native?) #f target)))
d9ae938f
LC
544 (mlet %store-monad ((obj (lower thing system target)))
545 ;; OBJ must be either a derivation or a store file name.
546 (return (match obj
547 ((? derivation? drv)
548 (derivation->output-path drv output))
549 ((? string? file)
550 file))))))
e39d1461
LC
551 (($ <gexp-input> x)
552 (return x))
21b679f6
LC
553 (x
554 (return x)))))
555
556 (mlet %store-monad
557 ((args (sequence %store-monad
667b2508
LC
558 (append (map reference->sexp (gexp-references exp))
559 (map (cut reference->sexp <> #t)
560 (gexp-native-references exp))))))
21b679f6
LC
561 (return (apply (gexp-proc exp) args))))
562
21b679f6
LC
563(define (syntax-location-string s)
564 "Return a string representing the source code location of S."
565 (let ((props (syntax-source s)))
566 (if props
567 (let ((file (assoc-ref props 'filename))
568 (line (and=> (assoc-ref props 'line) 1+))
569 (column (assoc-ref props 'column)))
570 (if file
571 (simple-format #f "~a:~a:~a"
572 file line column)
573 (simple-format #f "~a:~a" line column)))
574 "<unknown location>")))
575
576(define-syntax gexp
577 (lambda (s)
578 (define (collect-escapes exp)
579 ;; Return all the 'ungexp' present in EXP.
580 (let loop ((exp exp)
581 (result '()))
607e1b51
LC
582 (syntax-case exp (ungexp
583 ungexp-splicing
584 ungexp-native
585 ungexp-native-splicing)
21b679f6
LC
586 ((ungexp _)
587 (cons exp result))
588 ((ungexp _ _)
589 (cons exp result))
590 ((ungexp-splicing _ ...)
591 (cons exp result))
607e1b51
LC
592 ((ungexp-native _ ...)
593 result)
594 ((ungexp-native-splicing _ ...)
595 result)
21b679f6
LC
596 ((exp0 exp ...)
597 (let ((result (loop #'exp0 result)))
598 (fold loop result #'(exp ...))))
599 (_
600 result))))
601
667b2508
LC
602 (define (collect-native-escapes exp)
603 ;; Return all the 'ungexp-native' forms present in EXP.
604 (let loop ((exp exp)
605 (result '()))
607e1b51
LC
606 (syntax-case exp (ungexp
607 ungexp-splicing
608 ungexp-native
609 ungexp-native-splicing)
667b2508
LC
610 ((ungexp-native _)
611 (cons exp result))
612 ((ungexp-native _ _)
613 (cons exp result))
614 ((ungexp-native-splicing _ ...)
615 (cons exp result))
607e1b51
LC
616 ((ungexp _ ...)
617 result)
618 ((ungexp-splicing _ ...)
619 result)
667b2508
LC
620 ((exp0 exp ...)
621 (let ((result (loop #'exp0 result)))
622 (fold loop result #'(exp ...))))
623 (_
624 result))))
625
21b679f6
LC
626 (define (escape->ref exp)
627 ;; Turn 'ungexp' form EXP into a "reference".
667b2508
LC
628 (syntax-case exp (ungexp ungexp-splicing
629 ungexp-native ungexp-native-splicing
630 output)
21b679f6 631 ((ungexp output)
1e87da58 632 #'(gexp-output "out"))
21b679f6 633 ((ungexp output name)
1e87da58 634 #'(gexp-output name))
21b679f6 635 ((ungexp thing)
0dbea56b 636 #'(%gexp-input thing "out" #f))
21b679f6 637 ((ungexp drv-or-pkg out)
0dbea56b 638 #'(%gexp-input drv-or-pkg out #f))
21b679f6 639 ((ungexp-splicing lst)
0dbea56b 640 #'(%gexp-input lst "out" #f))
667b2508 641 ((ungexp-native thing)
0dbea56b 642 #'(%gexp-input thing "out" #t))
667b2508 643 ((ungexp-native drv-or-pkg out)
0dbea56b 644 #'(%gexp-input drv-or-pkg out #t))
667b2508 645 ((ungexp-native-splicing lst)
0dbea56b 646 #'(%gexp-input lst "out" #t))))
21b679f6 647
667b2508
LC
648 (define (substitute-ungexp exp substs)
649 ;; Given EXP, an 'ungexp' or 'ungexp-native' form, substitute it with
650 ;; the corresponding form in SUBSTS.
651 (match (assoc exp substs)
652 ((_ id)
653 id)
654 (_
655 #'(syntax-error "error: no 'ungexp' substitution"
656 #'ref))))
657
658 (define (substitute-ungexp-splicing exp substs)
659 (syntax-case exp ()
660 ((exp rest ...)
661 (match (assoc #'exp substs)
662 ((_ id)
663 (with-syntax ((id id))
664 #`(append id
665 #,(substitute-references #'(rest ...) substs))))
666 (_
667 #'(syntax-error "error: no 'ungexp-splicing' substitution"
668 #'ref))))))
669
21b679f6
LC
670 (define (substitute-references exp substs)
671 ;; Return a variant of EXP where all the cars of SUBSTS have been
672 ;; replaced by the corresponding cdr.
667b2508
LC
673 (syntax-case exp (ungexp ungexp-native
674 ungexp-splicing ungexp-native-splicing)
21b679f6 675 ((ungexp _ ...)
667b2508
LC
676 (substitute-ungexp exp substs))
677 ((ungexp-native _ ...)
678 (substitute-ungexp exp substs))
21b679f6 679 (((ungexp-splicing _ ...) rest ...)
667b2508
LC
680 (substitute-ungexp-splicing exp substs))
681 (((ungexp-native-splicing _ ...) rest ...)
682 (substitute-ungexp-splicing exp substs))
21b679f6
LC
683 ((exp0 exp ...)
684 #`(cons #,(substitute-references #'exp0 substs)
685 #,(substitute-references #'(exp ...) substs)))
686 (x #''x)))
687
688 (syntax-case s (ungexp output)
689 ((_ exp)
667b2508
LC
690 (let* ((normals (delete-duplicates (collect-escapes #'exp)))
691 (natives (delete-duplicates (collect-native-escapes #'exp)))
692 (escapes (append normals natives))
21b679f6
LC
693 (formals (generate-temporaries escapes))
694 (sexp (substitute-references #'exp (zip escapes formals)))
667b2508
LC
695 (refs (map escape->ref normals))
696 (nrefs (map escape->ref natives)))
e39d1461 697 #`(make-gexp (list #,@refs) (list #,@nrefs)
21b679f6
LC
698 (lambda #,formals
699 #,sexp)))))))
700
701\f
aa72d9af
LC
702;;;
703;;; Module handling.
704;;;
705
706(define %mkdir-p-definition
707 ;; The code for 'mkdir-p' is copied from (guix build utils). We use it in
708 ;; derivations that cannot use the #:modules argument of 'gexp->derivation'
709 ;; precisely because they implement that functionality.
710 (gexp
711 (define (mkdir-p dir)
712 (define absolute?
713 (string-prefix? "/" dir))
714
715 (define not-slash
716 (char-set-complement (char-set #\/)))
717
718 (let loop ((components (string-tokenize dir not-slash))
719 (root (if absolute? "" ".")))
720 (match components
721 ((head tail ...)
722 (let ((path (string-append root "/" head)))
723 (catch 'system-error
724 (lambda ()
725 (mkdir path)
726 (loop tail path))
727 (lambda args
728 (if (= EEXIST (system-error-errno args))
729 (loop tail path)
730 (apply throw args))))))
731 (() #t))))))
732
733(define* (imported-files files
734 #:key (name "file-import")
735 (system (%current-system))
736 (guile (%guile-for-build)))
737 "Return a derivation that imports FILES into STORE. FILES must be a list
738of (FINAL-PATH . FILE-NAME) pairs; each FILE-NAME is read from the file
739system, imported, and appears under FINAL-PATH in the resulting store path."
740 (define file-pair
741 (match-lambda
742 ((final-path . file-name)
743 (mlet %store-monad ((file (interned-file file-name
744 (basename final-path))))
745 (return (list final-path file))))))
746
747 (mlet %store-monad ((files (sequence %store-monad
748 (map file-pair files))))
749 (define build
750 (gexp
751 (begin
752 (use-modules (ice-9 match))
753
754 (ungexp %mkdir-p-definition)
755
756 (mkdir (ungexp output)) (chdir (ungexp output))
757 (for-each (match-lambda
758 ((final-path store-path)
759 (mkdir-p (dirname final-path))
760 (symlink store-path final-path)))
761 '(ungexp files)))))
762
763 ;; TODO: Pass FILES as an environment variable so that BUILD remains
764 ;; exactly the same regardless of FILES: less disk space, and fewer
765 ;; 'add-to-store' RPCs.
766 (gexp->derivation name build
767 #:system system
768 #:guile-for-build guile
769 #:local-build? #t)))
770
771(define search-path*
772 ;; A memoizing version of 'search-path' so 'imported-modules' does not end
773 ;; up looking for the same files over and over again.
774 (memoize search-path))
775
776(define* (imported-modules modules
777 #:key (name "module-import")
778 (system (%current-system))
779 (guile (%guile-for-build))
780 (module-path %load-path))
781 "Return a derivation that contains the source files of MODULES, a list of
782module names such as `(ice-9 q)'. All of MODULES must be in the MODULE-PATH
783search path."
784 ;; TODO: Determine the closure of MODULES, build the `.go' files,
785 ;; canonicalize the source files through read/write, etc.
786 (let ((files (map (lambda (m)
787 (let ((f (string-append
788 (string-join (map symbol->string m) "/")
789 ".scm")))
790 (cons f (search-path* module-path f))))
791 modules)))
792 (imported-files files #:name name #:system system
793 #:guile guile)))
794
795(define* (compiled-modules modules
796 #:key (name "module-import-compiled")
797 (system (%current-system))
798 (guile (%guile-for-build))
799 (module-path %load-path))
800 "Return a derivation that builds a tree containing the `.go' files
801corresponding to MODULES. All the MODULES are built in a context where
802they can refer to each other."
803 (mlet %store-monad ((modules (imported-modules modules
804 #:system system
805 #:guile guile
806 #:module-path
807 module-path)))
808 (define build
809 (gexp
810 (begin
811 (use-modules (ice-9 ftw)
812 (ice-9 match)
813 (srfi srfi-26)
814 (system base compile))
815
816 (ungexp %mkdir-p-definition)
817
818 (define (regular? file)
819 (not (member file '("." ".."))))
820
821 (define (process-directory directory output)
822 (let ((entries (map (cut string-append directory "/" <>)
823 (scandir directory regular?))))
824 (for-each (lambda (entry)
825 (if (file-is-directory? entry)
826 (let ((output (string-append output "/"
827 (basename entry))))
828 (mkdir-p output)
829 (process-directory entry output))
830 (let* ((base (string-drop-right
831 (basename entry)
832 4)) ;.scm
833 (output (string-append output "/" base
834 ".go")))
835 (compile-file entry
836 #:output-file output
837 #:opts
838 %auto-compilation-options))))
839 entries)))
840
841 (set! %load-path (cons (ungexp modules) %load-path))
842 (mkdir (ungexp output))
843 (chdir (ungexp modules))
844 (process-directory "." (ungexp output)))))
845
846 ;; TODO: Pass MODULES as an environment variable.
847 (gexp->derivation name build
848 #:system system
849 #:guile-for-build guile
850 #:local-build? #t)))
851
852\f
21b679f6
LC
853;;;
854;;; Convenience procedures.
855;;;
856
53e89b17
LC
857(define (default-guile)
858 ;; Lazily resolve 'guile-final'. This module must not refer to (gnu …)
859 ;; modules directly, to avoid circular dependencies, hence this hack.
bdb36958 860 (module-ref (resolve-interface '(gnu packages commencement))
53e89b17
LC
861 'guile-final))
862
21b679f6 863(define* (gexp->script name exp
53e89b17 864 #:key (modules '()) (guile (default-guile)))
21b679f6
LC
865 "Return an executable script NAME that runs EXP using GUILE with MODULES in
866its search path."
867 (mlet %store-monad ((modules (imported-modules modules))
868 (compiled (compiled-modules modules)))
869 (gexp->derivation name
870 (gexp
871 (call-with-output-file (ungexp output)
872 (lambda (port)
c17b5ab4
LC
873 ;; Note: that makes a long shebang. When the store
874 ;; is /gnu/store, that fits within the 128-byte
875 ;; limit imposed by Linux, but that may go beyond
876 ;; when running tests.
21b679f6
LC
877 (format port
878 "#!~a/bin/guile --no-auto-compile~%!#~%"
879 (ungexp guile))
4a4cbd0b
LC
880
881 ;; Write the 'eval-when' form so that it can be
882 ;; compiled.
21b679f6 883 (write
4a4cbd0b
LC
884 '(eval-when (expand load eval)
885 (set! %load-path
886 (cons (ungexp modules) %load-path))
887 (set! %load-compiled-path
888 (cons (ungexp compiled)
889 %load-compiled-path)))
21b679f6
LC
890 port)
891 (write '(ungexp exp) port)
892 (chmod port #o555)))))))
893
894(define (gexp->file name exp)
895 "Return a derivation that builds a file NAME containing EXP."
896 (gexp->derivation name
897 (gexp
898 (call-with-output-file (ungexp output)
899 (lambda (port)
dc254e05
LC
900 (write '(ungexp exp) port))))
901 #:local-build? #t))
21b679f6 902
462a3fa3
LC
903(define* (text-file* name #:rest text)
904 "Return as a monadic value a derivation that builds a text file containing
d9ae938f
LC
905all of TEXT. TEXT may list, in addition to strings, objects of any type that
906can be used in a gexp: packages, derivations, local file objects, etc. The
907resulting store file holds references to all these."
462a3fa3
LC
908 (define builder
909 (gexp (call-with-output-file (ungexp output "out")
910 (lambda (port)
911 (display (string-append (ungexp-splicing text)) port)))))
912
913 (gexp->derivation name builder))
914
21b679f6
LC
915\f
916;;;
917;;; Syntactic sugar.
918;;;
919
920(eval-when (expand load eval)
667b2508
LC
921 (define* (read-ungexp chr port #:optional native?)
922 "Read an 'ungexp' or 'ungexp-splicing' form from PORT. When NATIVE? is
923true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
21b679f6
LC
924 (define unquote-symbol
925 (match (peek-char port)
926 (#\@
927 (read-char port)
667b2508
LC
928 (if native?
929 'ungexp-native-splicing
930 'ungexp-splicing))
21b679f6 931 (_
667b2508
LC
932 (if native?
933 'ungexp-native
934 'ungexp))))
21b679f6
LC
935
936 (match (read port)
937 ((? symbol? symbol)
938 (let ((str (symbol->string symbol)))
939 (match (string-index-right str #\:)
940 (#f
941 `(,unquote-symbol ,symbol))
942 (colon
943 (let ((name (string->symbol (substring str 0 colon)))
944 (output (substring str (+ colon 1))))
945 `(,unquote-symbol ,name ,output))))))
946 (x
947 `(,unquote-symbol ,x))))
948
949 (define (read-gexp chr port)
950 "Read a 'gexp' form from PORT."
951 `(gexp ,(read port)))
952
953 ;; Extend the reader
954 (read-hash-extend #\~ read-gexp)
667b2508
LC
955 (read-hash-extend #\$ read-ungexp)
956 (read-hash-extend #\+ (cut read-ungexp <> <> #t)))
21b679f6
LC
957
958;;; gexp.scm ends here