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