gexp: Add identity compiler for derivations.
[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))
bcb13287
LC
204 (thing
205 (mlet* %store-monad ((lower -> (lookup-compiler thing))
206 (drv (lower thing system target)))
c8351d9a
LC
207 (return (derivation->output-path drv))))))
208
209 (sequence %store-monad (map lower lst))))
210
ff40e9b7
LC
211(define default-guile-derivation
212 ;; Here we break the abstraction by talking to the higher-level layer.
213 ;; Thus, do the resolution lazily to hide the circular dependency.
214 (let ((proc (delay
215 (let ((iface (resolve-interface '(guix packages))))
216 (module-ref iface 'default-guile-derivation)))))
217 (lambda (system)
218 ((force proc) system))))
219
21b679f6
LC
220(define* (gexp->derivation name exp
221 #:key
68a61e9f 222 system (target 'current)
21b679f6
LC
223 hash hash-algo recursive?
224 (env-vars '())
225 (modules '())
4684f301 226 (module-path %load-path)
21b679f6 227 (guile-for-build (%guile-for-build))
ce45eb4c 228 (graft? (%graft?))
21b679f6 229 references-graphs
c8351d9a 230 allowed-references
21b679f6
LC
231 local-build?)
232 "Return a derivation NAME that runs EXP (a gexp) with GUILE-FOR-BUILD (a
68a61e9f
LC
233derivation) on SYSTEM. When TARGET is true, it is used as the
234cross-compilation target triplet for packages referred to by EXP.
21b679f6
LC
235
236Make MODULES available in the evaluation context of EXP; MODULES is a list of
4684f301 237names of Guile modules searched in MODULE-PATH to be copied in the store,
21b679f6
LC
238compiled, and made available in the load path during the execution of
239EXP---e.g., '((guix build utils) (guix build gnu-build-system)).
240
ce45eb4c
LC
241GRAFT? determines whether packages referred to by EXP should be grafted when
242applicable.
243
b53833b2
LC
244When REFERENCES-GRAPHS is true, it must be a list of tuples of one of the
245following forms:
246
247 (FILE-NAME PACKAGE)
248 (FILE-NAME PACKAGE OUTPUT)
249 (FILE-NAME DERIVATION)
250 (FILE-NAME DERIVATION OUTPUT)
251 (FILE-NAME STORE-ITEM)
252
253The right-hand-side of each element of REFERENCES-GRAPHS is automatically made
254an input of the build process of EXP. In the build environment, each
255FILE-NAME contains the reference graph of the corresponding item, in a simple
256text format.
257
c8351d9a
LC
258ALLOWED-REFERENCES must be either #f or a list of output names and packages.
259In the latter case, the list denotes store items that the result is allowed to
260refer to. Any reference to another store item will lead to a build error.
b53833b2 261
21b679f6
LC
262The other arguments are as for 'derivation'."
263 (define %modules modules)
264 (define outputs (gexp-outputs exp))
265
b53833b2
LC
266 (define (graphs-file-names graphs)
267 ;; Return a list of (FILE-NAME . STORE-PATH) pairs made from GRAPHS.
268 (map (match-lambda
2924f0d6 269 ;; TODO: Remove 'derivation?' special cases.
b53833b2
LC
270 ((file-name (? derivation? drv))
271 (cons file-name (derivation->output-path drv)))
272 ((file-name (? derivation? drv) sub-drv)
273 (cons file-name (derivation->output-path drv sub-drv)))
274 ((file-name thing)
275 (cons file-name thing)))
276 graphs))
277
ce45eb4c
LC
278 (mlet* %store-monad (;; The following binding forces '%current-system' and
279 ;; '%current-target-system' to be looked up at >>=
280 ;; time.
281 (graft? (set-grafting graft?))
68a61e9f 282
5d098459 283 (system -> (or system (%current-system)))
68a61e9f
LC
284 (target -> (if (eq? target 'current)
285 (%current-target-system)
286 target))
667b2508 287 (normals (lower-inputs (gexp-inputs exp)
68a61e9f
LC
288 #:system system
289 #:target target))
667b2508
LC
290 (natives (lower-inputs (gexp-native-inputs exp)
291 #:system system
292 #:target #f))
293 (inputs -> (append normals natives))
68a61e9f
LC
294 (sexp (gexp->sexp exp
295 #:system system
296 #:target target))
21b679f6
LC
297 (builder (text-file (string-append name "-builder")
298 (object->string sexp)))
299 (modules (if (pair? %modules)
300 (imported-modules %modules
301 #:system system
4684f301 302 #:module-path module-path
21b679f6
LC
303 #:guile guile-for-build)
304 (return #f)))
305 (compiled (if (pair? %modules)
306 (compiled-modules %modules
307 #:system system
4684f301 308 #:module-path module-path
21b679f6
LC
309 #:guile guile-for-build)
310 (return #f)))
b53833b2
LC
311 (graphs (if references-graphs
312 (lower-reference-graphs references-graphs
313 #:system system
314 #:target target)
315 (return #f)))
c8351d9a
LC
316 (allowed (if allowed-references
317 (lower-references allowed-references
318 #:system system
319 #:target target)
320 (return #f)))
21b679f6
LC
321 (guile (if guile-for-build
322 (return guile-for-build)
ff40e9b7 323 (default-guile-derivation system))))
ce45eb4c
LC
324 (mbegin %store-monad
325 (set-grafting graft?) ;restore the initial setting
326 (raw-derivation name
327 (string-append (derivation->output-path guile)
328 "/bin/guile")
329 `("--no-auto-compile"
330 ,@(if (pair? %modules)
331 `("-L" ,(derivation->output-path modules)
332 "-C" ,(derivation->output-path compiled))
333 '())
334 ,builder)
335 #:outputs outputs
336 #:env-vars env-vars
337 #:system system
338 #:inputs `((,guile)
339 (,builder)
340 ,@(if modules
341 `((,modules) (,compiled) ,@inputs)
342 inputs)
343 ,@(match graphs
344 (((_ . inputs) ...) inputs)
345 (_ '())))
346 #:hash hash #:hash-algo hash-algo #:recursive? recursive?
347 #:references-graphs (and=> graphs graphs-file-names)
348 #:allowed-references allowed
349 #:local-build? local-build?))))
21b679f6 350
667b2508
LC
351(define* (gexp-inputs exp #:optional (references gexp-references))
352 "Return the input list for EXP, using REFERENCES to get its list of
353references."
21b679f6
LC
354 (define (add-reference-inputs ref result)
355 (match ref
e39d1461 356 (($ <gexp-input> (? gexp? exp))
667b2508 357 (append (gexp-inputs exp references) result))
e39d1461
LC
358 (($ <gexp-input> (? string? str))
359 (if (direct-store-path? str)
360 (cons `(,str) result)
21b679f6 361 result))
bcb13287
LC
362 (($ <gexp-input> (? struct? thing) output)
363 (if (lookup-compiler thing)
364 ;; THING is a derivation, or a package, or an origin, etc.
365 (cons `(,thing ,output) result)
366 result))
e39d1461
LC
367 (($ <gexp-input> (lst ...) output native?)
368 (fold-right add-reference-inputs result
369 ;; XXX: For now, automatically convert LST to a list of
370 ;; gexp-inputs.
0dbea56b
LC
371 (map (match-lambda
372 ((? gexp-input? x) x)
373 (x (%gexp-input x "out" native?)))
374 lst)))
21b679f6
LC
375 (_
376 ;; Ignore references to other kinds of objects.
377 result)))
378
379 (fold-right add-reference-inputs
380 '()
667b2508
LC
381 (references exp)))
382
383(define gexp-native-inputs
384 (cut gexp-inputs <> gexp-native-references))
21b679f6
LC
385
386(define (gexp-outputs exp)
387 "Return the outputs referred to by EXP as a list of strings."
388 (define (add-reference-output ref result)
389 (match ref
1e87da58 390 (($ <gexp-output> name)
21b679f6 391 (cons name result))
e39d1461 392 (($ <gexp-input> (? gexp? exp))
21b679f6 393 (append (gexp-outputs exp) result))
e39d1461
LC
394 (($ <gexp-input> (lst ...) output native?)
395 ;; XXX: Automatically convert LST.
0dbea56b
LC
396 (add-reference-output (map (match-lambda
397 ((? gexp-input? x) x)
398 (x (%gexp-input x "out" native?)))
399 lst)
e39d1461 400 result))
f9efe568
LC
401 ((lst ...)
402 (fold-right add-reference-output result lst))
21b679f6
LC
403 (_
404 result)))
405
7e75a673
LC
406 (delete-duplicates
407 (add-reference-output (gexp-references exp) '())))
21b679f6 408
68a61e9f
LC
409(define* (gexp->sexp exp #:key
410 (system (%current-system))
411 (target (%current-target-system)))
21b679f6
LC
412 "Return (monadically) the sexp corresponding to EXP for the given OUTPUT,
413and in the current monad setting (system type, etc.)"
667b2508 414 (define* (reference->sexp ref #:optional native?)
21b679f6
LC
415 (with-monad %store-monad
416 (match ref
1e87da58 417 (($ <gexp-output> output)
bfd9eed9
LC
418 ;; Output file names are not known in advance but the daemon defines
419 ;; an environment variable for each of them at build time, so use
420 ;; that trick.
421 (return `((@ (guile) getenv) ,output)))
e39d1461 422 (($ <gexp-input> (? gexp? exp) output n?)
667b2508
LC
423 (gexp->sexp exp
424 #:system system
e39d1461
LC
425 #:target (if (or n? native?) #f target)))
426 (($ <gexp-input> (refs ...) output n?)
667b2508 427 (sequence %store-monad
e39d1461
LC
428 (map (lambda (ref)
429 ;; XXX: Automatically convert REF to an gexp-input.
0dbea56b
LC
430 (reference->sexp
431 (if (gexp-input? ref)
432 ref
433 (%gexp-input ref "out" n?))
434 native?))
e39d1461 435 refs)))
bcb13287
LC
436 (($ <gexp-input> (? struct? thing) output n?)
437 (let ((lower (lookup-compiler thing))
438 (target (if (or n? native?) #f target)))
439 (mlet %store-monad ((drv (lower thing system target)))
440 (return (derivation->output-path drv output)))))
e39d1461
LC
441 (($ <gexp-input> x)
442 (return x))
21b679f6
LC
443 (x
444 (return x)))))
445
446 (mlet %store-monad
447 ((args (sequence %store-monad
667b2508
LC
448 (append (map reference->sexp (gexp-references exp))
449 (map (cut reference->sexp <> #t)
450 (gexp-native-references exp))))))
21b679f6
LC
451 (return (apply (gexp-proc exp) args))))
452
21b679f6
LC
453(define (syntax-location-string s)
454 "Return a string representing the source code location of S."
455 (let ((props (syntax-source s)))
456 (if props
457 (let ((file (assoc-ref props 'filename))
458 (line (and=> (assoc-ref props 'line) 1+))
459 (column (assoc-ref props 'column)))
460 (if file
461 (simple-format #f "~a:~a:~a"
462 file line column)
463 (simple-format #f "~a:~a" line column)))
464 "<unknown location>")))
465
466(define-syntax gexp
467 (lambda (s)
468 (define (collect-escapes exp)
469 ;; Return all the 'ungexp' present in EXP.
470 (let loop ((exp exp)
471 (result '()))
472 (syntax-case exp (ungexp ungexp-splicing)
473 ((ungexp _)
474 (cons exp result))
475 ((ungexp _ _)
476 (cons exp result))
477 ((ungexp-splicing _ ...)
478 (cons exp result))
479 ((exp0 exp ...)
480 (let ((result (loop #'exp0 result)))
481 (fold loop result #'(exp ...))))
482 (_
483 result))))
484
667b2508
LC
485 (define (collect-native-escapes exp)
486 ;; Return all the 'ungexp-native' forms present in EXP.
487 (let loop ((exp exp)
488 (result '()))
489 (syntax-case exp (ungexp-native ungexp-native-splicing)
490 ((ungexp-native _)
491 (cons exp result))
492 ((ungexp-native _ _)
493 (cons exp result))
494 ((ungexp-native-splicing _ ...)
495 (cons exp result))
496 ((exp0 exp ...)
497 (let ((result (loop #'exp0 result)))
498 (fold loop result #'(exp ...))))
499 (_
500 result))))
501
21b679f6
LC
502 (define (escape->ref exp)
503 ;; Turn 'ungexp' form EXP into a "reference".
667b2508
LC
504 (syntax-case exp (ungexp ungexp-splicing
505 ungexp-native ungexp-native-splicing
506 output)
21b679f6 507 ((ungexp output)
1e87da58 508 #'(gexp-output "out"))
21b679f6 509 ((ungexp output name)
1e87da58 510 #'(gexp-output name))
21b679f6 511 ((ungexp thing)
0dbea56b 512 #'(%gexp-input thing "out" #f))
21b679f6 513 ((ungexp drv-or-pkg out)
0dbea56b 514 #'(%gexp-input drv-or-pkg out #f))
21b679f6 515 ((ungexp-splicing lst)
0dbea56b 516 #'(%gexp-input lst "out" #f))
667b2508 517 ((ungexp-native thing)
0dbea56b 518 #'(%gexp-input thing "out" #t))
667b2508 519 ((ungexp-native drv-or-pkg out)
0dbea56b 520 #'(%gexp-input drv-or-pkg out #t))
667b2508 521 ((ungexp-native-splicing lst)
0dbea56b 522 #'(%gexp-input lst "out" #t))))
21b679f6 523
667b2508
LC
524 (define (substitute-ungexp exp substs)
525 ;; Given EXP, an 'ungexp' or 'ungexp-native' form, substitute it with
526 ;; the corresponding form in SUBSTS.
527 (match (assoc exp substs)
528 ((_ id)
529 id)
530 (_
531 #'(syntax-error "error: no 'ungexp' substitution"
532 #'ref))))
533
534 (define (substitute-ungexp-splicing exp substs)
535 (syntax-case exp ()
536 ((exp rest ...)
537 (match (assoc #'exp substs)
538 ((_ id)
539 (with-syntax ((id id))
540 #`(append id
541 #,(substitute-references #'(rest ...) substs))))
542 (_
543 #'(syntax-error "error: no 'ungexp-splicing' substitution"
544 #'ref))))))
545
21b679f6
LC
546 (define (substitute-references exp substs)
547 ;; Return a variant of EXP where all the cars of SUBSTS have been
548 ;; replaced by the corresponding cdr.
667b2508
LC
549 (syntax-case exp (ungexp ungexp-native
550 ungexp-splicing ungexp-native-splicing)
21b679f6 551 ((ungexp _ ...)
667b2508
LC
552 (substitute-ungexp exp substs))
553 ((ungexp-native _ ...)
554 (substitute-ungexp exp substs))
21b679f6 555 (((ungexp-splicing _ ...) rest ...)
667b2508
LC
556 (substitute-ungexp-splicing exp substs))
557 (((ungexp-native-splicing _ ...) rest ...)
558 (substitute-ungexp-splicing exp substs))
21b679f6
LC
559 ((exp0 exp ...)
560 #`(cons #,(substitute-references #'exp0 substs)
561 #,(substitute-references #'(exp ...) substs)))
562 (x #''x)))
563
564 (syntax-case s (ungexp output)
565 ((_ exp)
667b2508
LC
566 (let* ((normals (delete-duplicates (collect-escapes #'exp)))
567 (natives (delete-duplicates (collect-native-escapes #'exp)))
568 (escapes (append normals natives))
21b679f6
LC
569 (formals (generate-temporaries escapes))
570 (sexp (substitute-references #'exp (zip escapes formals)))
667b2508
LC
571 (refs (map escape->ref normals))
572 (nrefs (map escape->ref natives)))
e39d1461 573 #`(make-gexp (list #,@refs) (list #,@nrefs)
21b679f6
LC
574 (lambda #,formals
575 #,sexp)))))))
576
577\f
aa72d9af
LC
578;;;
579;;; Module handling.
580;;;
581
582(define %mkdir-p-definition
583 ;; The code for 'mkdir-p' is copied from (guix build utils). We use it in
584 ;; derivations that cannot use the #:modules argument of 'gexp->derivation'
585 ;; precisely because they implement that functionality.
586 (gexp
587 (define (mkdir-p dir)
588 (define absolute?
589 (string-prefix? "/" dir))
590
591 (define not-slash
592 (char-set-complement (char-set #\/)))
593
594 (let loop ((components (string-tokenize dir not-slash))
595 (root (if absolute? "" ".")))
596 (match components
597 ((head tail ...)
598 (let ((path (string-append root "/" head)))
599 (catch 'system-error
600 (lambda ()
601 (mkdir path)
602 (loop tail path))
603 (lambda args
604 (if (= EEXIST (system-error-errno args))
605 (loop tail path)
606 (apply throw args))))))
607 (() #t))))))
608
609(define* (imported-files files
610 #:key (name "file-import")
611 (system (%current-system))
612 (guile (%guile-for-build)))
613 "Return a derivation that imports FILES into STORE. FILES must be a list
614of (FINAL-PATH . FILE-NAME) pairs; each FILE-NAME is read from the file
615system, imported, and appears under FINAL-PATH in the resulting store path."
616 (define file-pair
617 (match-lambda
618 ((final-path . file-name)
619 (mlet %store-monad ((file (interned-file file-name
620 (basename final-path))))
621 (return (list final-path file))))))
622
623 (mlet %store-monad ((files (sequence %store-monad
624 (map file-pair files))))
625 (define build
626 (gexp
627 (begin
628 (use-modules (ice-9 match))
629
630 (ungexp %mkdir-p-definition)
631
632 (mkdir (ungexp output)) (chdir (ungexp output))
633 (for-each (match-lambda
634 ((final-path store-path)
635 (mkdir-p (dirname final-path))
636 (symlink store-path final-path)))
637 '(ungexp files)))))
638
639 ;; TODO: Pass FILES as an environment variable so that BUILD remains
640 ;; exactly the same regardless of FILES: less disk space, and fewer
641 ;; 'add-to-store' RPCs.
642 (gexp->derivation name build
643 #:system system
644 #:guile-for-build guile
645 #:local-build? #t)))
646
647(define search-path*
648 ;; A memoizing version of 'search-path' so 'imported-modules' does not end
649 ;; up looking for the same files over and over again.
650 (memoize search-path))
651
652(define* (imported-modules modules
653 #:key (name "module-import")
654 (system (%current-system))
655 (guile (%guile-for-build))
656 (module-path %load-path))
657 "Return a derivation that contains the source files of MODULES, a list of
658module names such as `(ice-9 q)'. All of MODULES must be in the MODULE-PATH
659search path."
660 ;; TODO: Determine the closure of MODULES, build the `.go' files,
661 ;; canonicalize the source files through read/write, etc.
662 (let ((files (map (lambda (m)
663 (let ((f (string-append
664 (string-join (map symbol->string m) "/")
665 ".scm")))
666 (cons f (search-path* module-path f))))
667 modules)))
668 (imported-files files #:name name #:system system
669 #:guile guile)))
670
671(define* (compiled-modules modules
672 #:key (name "module-import-compiled")
673 (system (%current-system))
674 (guile (%guile-for-build))
675 (module-path %load-path))
676 "Return a derivation that builds a tree containing the `.go' files
677corresponding to MODULES. All the MODULES are built in a context where
678they can refer to each other."
679 (mlet %store-monad ((modules (imported-modules modules
680 #:system system
681 #:guile guile
682 #:module-path
683 module-path)))
684 (define build
685 (gexp
686 (begin
687 (use-modules (ice-9 ftw)
688 (ice-9 match)
689 (srfi srfi-26)
690 (system base compile))
691
692 (ungexp %mkdir-p-definition)
693
694 (define (regular? file)
695 (not (member file '("." ".."))))
696
697 (define (process-directory directory output)
698 (let ((entries (map (cut string-append directory "/" <>)
699 (scandir directory regular?))))
700 (for-each (lambda (entry)
701 (if (file-is-directory? entry)
702 (let ((output (string-append output "/"
703 (basename entry))))
704 (mkdir-p output)
705 (process-directory entry output))
706 (let* ((base (string-drop-right
707 (basename entry)
708 4)) ;.scm
709 (output (string-append output "/" base
710 ".go")))
711 (compile-file entry
712 #:output-file output
713 #:opts
714 %auto-compilation-options))))
715 entries)))
716
717 (set! %load-path (cons (ungexp modules) %load-path))
718 (mkdir (ungexp output))
719 (chdir (ungexp modules))
720 (process-directory "." (ungexp output)))))
721
722 ;; TODO: Pass MODULES as an environment variable.
723 (gexp->derivation name build
724 #:system system
725 #:guile-for-build guile
726 #:local-build? #t)))
727
728\f
21b679f6
LC
729;;;
730;;; Convenience procedures.
731;;;
732
53e89b17
LC
733(define (default-guile)
734 ;; Lazily resolve 'guile-final'. This module must not refer to (gnu …)
735 ;; modules directly, to avoid circular dependencies, hence this hack.
bdb36958 736 (module-ref (resolve-interface '(gnu packages commencement))
53e89b17
LC
737 'guile-final))
738
21b679f6 739(define* (gexp->script name exp
53e89b17 740 #:key (modules '()) (guile (default-guile)))
21b679f6
LC
741 "Return an executable script NAME that runs EXP using GUILE with MODULES in
742its search path."
743 (mlet %store-monad ((modules (imported-modules modules))
744 (compiled (compiled-modules modules)))
745 (gexp->derivation name
746 (gexp
747 (call-with-output-file (ungexp output)
748 (lambda (port)
c17b5ab4
LC
749 ;; Note: that makes a long shebang. When the store
750 ;; is /gnu/store, that fits within the 128-byte
751 ;; limit imposed by Linux, but that may go beyond
752 ;; when running tests.
21b679f6
LC
753 (format port
754 "#!~a/bin/guile --no-auto-compile~%!#~%"
755 (ungexp guile))
4a4cbd0b
LC
756
757 ;; Write the 'eval-when' form so that it can be
758 ;; compiled.
21b679f6 759 (write
4a4cbd0b
LC
760 '(eval-when (expand load eval)
761 (set! %load-path
762 (cons (ungexp modules) %load-path))
763 (set! %load-compiled-path
764 (cons (ungexp compiled)
765 %load-compiled-path)))
21b679f6
LC
766 port)
767 (write '(ungexp exp) port)
768 (chmod port #o555)))))))
769
770(define (gexp->file name exp)
771 "Return a derivation that builds a file NAME containing EXP."
772 (gexp->derivation name
773 (gexp
774 (call-with-output-file (ungexp output)
775 (lambda (port)
dc254e05
LC
776 (write '(ungexp exp) port))))
777 #:local-build? #t))
21b679f6 778
462a3fa3
LC
779(define* (text-file* name #:rest text)
780 "Return as a monadic value a derivation that builds a text file containing
781all of TEXT. TEXT may list, in addition to strings, packages, derivations,
782and store file names; the resulting store file holds references to all these."
783 (define builder
784 (gexp (call-with-output-file (ungexp output "out")
785 (lambda (port)
786 (display (string-append (ungexp-splicing text)) port)))))
787
788 (gexp->derivation name builder))
789
21b679f6
LC
790\f
791;;;
792;;; Syntactic sugar.
793;;;
794
795(eval-when (expand load eval)
667b2508
LC
796 (define* (read-ungexp chr port #:optional native?)
797 "Read an 'ungexp' or 'ungexp-splicing' form from PORT. When NATIVE? is
798true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
21b679f6
LC
799 (define unquote-symbol
800 (match (peek-char port)
801 (#\@
802 (read-char port)
667b2508
LC
803 (if native?
804 'ungexp-native-splicing
805 'ungexp-splicing))
21b679f6 806 (_
667b2508
LC
807 (if native?
808 'ungexp-native
809 'ungexp))))
21b679f6
LC
810
811 (match (read port)
812 ((? symbol? symbol)
813 (let ((str (symbol->string symbol)))
814 (match (string-index-right str #\:)
815 (#f
816 `(,unquote-symbol ,symbol))
817 (colon
818 (let ((name (string->symbol (substring str 0 colon)))
819 (output (substring str (+ colon 1))))
820 `(,unquote-symbol ,name ,output))))))
821 (x
822 `(,unquote-symbol ,x))))
823
824 (define (read-gexp chr port)
825 "Read a 'gexp' form from PORT."
826 `(gexp ,(read port)))
827
828 ;; Extend the reader
829 (read-hash-extend #\~ read-gexp)
667b2508
LC
830 (read-hash-extend #\$ read-ungexp)
831 (read-hash-extend #\+ (cut read-ungexp <> <> #t)))
21b679f6
LC
832
833;;; gexp.scm ends here