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