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