gnu: Re-synchronize a couple of synopses with the Womb.
[jackhill/guix/guix.git] / guix / gexp.scm
CommitLineData
21b679f6
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2014 Ludovic Courtès <ludo@gnu.org>
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)
20 #:use-module ((guix store)
21 #:select (direct-store-path?))
22 #:use-module (guix monads)
23 #:use-module ((guix derivations)
24 #:select (derivation? derivation->output-path
25 %guile-for-build derivation))
26 #:use-module (guix packages)
27 #:use-module (srfi srfi-1)
28 #:use-module (srfi srfi-9)
7560b00b 29 #:use-module (srfi srfi-9 gnu)
21b679f6
LC
30 #:use-module (srfi srfi-26)
31 #:use-module (ice-9 match)
32 #:export (gexp
33 gexp?
34 gexp->derivation
35 gexp->file
36 gexp->script))
37
38;;; Commentary:
39;;;
40;;; This module implements "G-expressions", or "gexps". Gexps are like
41;;; S-expressions (sexps), with two differences:
42;;;
43;;; 1. References (un-quotations) to derivations or packages in a gexp are
667b2508
LC
44;;; replaced by the corresponding output file name; in addition, the
45;;; 'ungexp-native' unquote-like form allows code to explicitly refer to
46;;; the native code of a given package, in case of cross-compilation;
21b679f6
LC
47;;;
48;;; 2. Gexps embed information about the derivations they refer to.
49;;;
50;;; Gexps make it easy to write to files Scheme code that refers to store
51;;; items, or to write Scheme code to build derivations.
52;;;
53;;; Code:
54
55;; "G expressions".
56(define-record-type <gexp>
667b2508 57 (make-gexp references natives proc)
21b679f6
LC
58 gexp?
59 (references gexp-references) ; ((DRV-OR-PKG OUTPUT) ...)
667b2508 60 (natives gexp-native-references) ; ((DRV-OR-PKG OUTPUT) ...)
21b679f6
LC
61 (proc gexp-proc)) ; procedure
62
7560b00b
LC
63(define (write-gexp gexp port)
64 "Write GEXP on PORT."
65 (display "#<gexp " port)
2cf0ea0d
LC
66
67 ;; Try to write the underlying sexp. Now, this trick doesn't work when
68 ;; doing things like (ungexp-splicing (gexp ())) because GEXP's procedure
69 ;; tries to use 'append' on that, which fails with wrong-type-arg.
70 (false-if-exception
667b2508
LC
71 (write (apply (gexp-proc gexp)
72 (append (gexp-references gexp)
73 (gexp-native-references gexp)))
74 port))
7560b00b
LC
75 (format port " ~a>"
76 (number->string (object-address gexp) 16)))
77
78(set-record-type-printer! <gexp> write-gexp)
79
21b679f6
LC
80;; Reference to one of the derivation's outputs, for gexps used in
81;; derivations.
82(define-record-type <output-ref>
83 (output-ref name)
84 output-ref?
85 (name output-ref-name))
86
87(define raw-derivation
88 (store-lift derivation))
89
68a61e9f
LC
90(define* (lower-inputs inputs
91 #:key system target)
92 "Turn any package from INPUTS into a derivation for SYSTEM; return the
93corresponding input list as a monadic value. When TARGET is true, use it as
94the cross-compilation target triplet."
21b679f6
LC
95 (with-monad %store-monad
96 (sequence %store-monad
97 (map (match-lambda
98 (((? package? package) sub-drv ...)
68a61e9f
LC
99 (mlet %store-monad
100 ((drv (if target
101 (package->cross-derivation package target
102 system)
103 (package->derivation package system))))
21b679f6 104 (return `(,drv ,@sub-drv))))
79c0c8cd
LC
105 (((? origin? origin) sub-drv ...)
106 (mlet %store-monad ((drv (origin->derivation origin)))
107 (return `(,drv ,@sub-drv))))
21b679f6
LC
108 (input
109 (return input)))
110 inputs))))
111
b53833b2
LC
112(define* (lower-reference-graphs graphs #:key system target)
113 "Given GRAPHS, a list of (FILE-NAME INPUT ...) lists for use as a
114#:reference-graphs argument, lower it such that each INPUT is replaced by the
115corresponding derivation."
116 (match graphs
117 (((file-names . inputs) ...)
118 (mlet %store-monad ((inputs (lower-inputs inputs
119 #:system system
120 #:target target)))
121 (return (map cons file-names inputs))))))
122
21b679f6
LC
123(define* (gexp->derivation name exp
124 #:key
68a61e9f 125 system (target 'current)
21b679f6
LC
126 hash hash-algo recursive?
127 (env-vars '())
128 (modules '())
129 (guile-for-build (%guile-for-build))
130 references-graphs
131 local-build?)
132 "Return a derivation NAME that runs EXP (a gexp) with GUILE-FOR-BUILD (a
68a61e9f
LC
133derivation) on SYSTEM. When TARGET is true, it is used as the
134cross-compilation target triplet for packages referred to by EXP.
21b679f6
LC
135
136Make MODULES available in the evaluation context of EXP; MODULES is a list of
137names of Guile modules from the current search path to be copied in the store,
138compiled, and made available in the load path during the execution of
139EXP---e.g., '((guix build utils) (guix build gnu-build-system)).
140
b53833b2
LC
141When REFERENCES-GRAPHS is true, it must be a list of tuples of one of the
142following forms:
143
144 (FILE-NAME PACKAGE)
145 (FILE-NAME PACKAGE OUTPUT)
146 (FILE-NAME DERIVATION)
147 (FILE-NAME DERIVATION OUTPUT)
148 (FILE-NAME STORE-ITEM)
149
150The right-hand-side of each element of REFERENCES-GRAPHS is automatically made
151an input of the build process of EXP. In the build environment, each
152FILE-NAME contains the reference graph of the corresponding item, in a simple
153text format.
154
155In that case, the reference graph of each store path is exported in
156the build environment in the corresponding file, in a simple text format.
157
21b679f6
LC
158The other arguments are as for 'derivation'."
159 (define %modules modules)
160 (define outputs (gexp-outputs exp))
161
b53833b2
LC
162 (define (graphs-file-names graphs)
163 ;; Return a list of (FILE-NAME . STORE-PATH) pairs made from GRAPHS.
164 (map (match-lambda
165 ((file-name (? derivation? drv))
166 (cons file-name (derivation->output-path drv)))
167 ((file-name (? derivation? drv) sub-drv)
168 (cons file-name (derivation->output-path drv sub-drv)))
169 ((file-name thing)
170 (cons file-name thing)))
171 graphs))
172
68a61e9f
LC
173 (mlet* %store-monad (;; The following binding is here to force
174 ;; '%current-system' and '%current-target-system' to be
175 ;; looked up at >>= time.
176 (unused (return #f))
177
5d098459 178 (system -> (or system (%current-system)))
68a61e9f
LC
179 (target -> (if (eq? target 'current)
180 (%current-target-system)
181 target))
667b2508 182 (normals (lower-inputs (gexp-inputs exp)
68a61e9f
LC
183 #:system system
184 #:target target))
667b2508
LC
185 (natives (lower-inputs (gexp-native-inputs exp)
186 #:system system
187 #:target #f))
188 (inputs -> (append normals natives))
68a61e9f
LC
189 (sexp (gexp->sexp exp
190 #:system system
191 #:target target))
21b679f6
LC
192 (builder (text-file (string-append name "-builder")
193 (object->string sexp)))
194 (modules (if (pair? %modules)
195 (imported-modules %modules
196 #:system system
197 #:guile guile-for-build)
198 (return #f)))
199 (compiled (if (pair? %modules)
200 (compiled-modules %modules
201 #:system system
202 #:guile guile-for-build)
203 (return #f)))
b53833b2
LC
204 (graphs (if references-graphs
205 (lower-reference-graphs references-graphs
206 #:system system
207 #:target target)
208 (return #f)))
21b679f6
LC
209 (guile (if guile-for-build
210 (return guile-for-build)
53e89b17
LC
211 (package->derivation (default-guile)
212 system))))
21b679f6
LC
213 (raw-derivation name
214 (string-append (derivation->output-path guile)
215 "/bin/guile")
216 `("--no-auto-compile"
217 ,@(if (pair? %modules)
218 `("-L" ,(derivation->output-path modules)
219 "-C" ,(derivation->output-path compiled))
220 '())
221 ,builder)
222 #:outputs outputs
223 #:env-vars env-vars
224 #:system system
225 #:inputs `((,guile)
226 (,builder)
227 ,@(if modules
228 `((,modules) (,compiled) ,@inputs)
b53833b2
LC
229 inputs)
230 ,@(match graphs
231 (((_ . inputs) ...) inputs)
232 (_ '())))
21b679f6 233 #:hash hash #:hash-algo hash-algo #:recursive? recursive?
b53833b2 234 #:references-graphs (and=> graphs graphs-file-names)
21b679f6
LC
235 #:local-build? local-build?)))
236
667b2508
LC
237(define* (gexp-inputs exp #:optional (references gexp-references))
238 "Return the input list for EXP, using REFERENCES to get its list of
239references."
21b679f6
LC
240 (define (add-reference-inputs ref result)
241 (match ref
242 (((? derivation?) (? string?))
243 (cons ref result))
244 (((? package?) (? string?))
245 (cons ref result))
79c0c8cd
LC
246 (((? origin?) (? string?))
247 (cons ref result))
21b679f6 248 ((? gexp? exp)
667b2508 249 (append (gexp-inputs exp references) result))
21b679f6
LC
250 (((? string? file))
251 (if (direct-store-path? file)
252 (cons ref result)
253 result))
254 ((refs ...)
255 (fold-right add-reference-inputs result refs))
256 (_
257 ;; Ignore references to other kinds of objects.
258 result)))
259
260 (fold-right add-reference-inputs
261 '()
667b2508
LC
262 (references exp)))
263
264(define gexp-native-inputs
265 (cut gexp-inputs <> gexp-native-references))
21b679f6
LC
266
267(define (gexp-outputs exp)
268 "Return the outputs referred to by EXP as a list of strings."
269 (define (add-reference-output ref result)
270 (match ref
271 (($ <output-ref> name)
272 (cons name result))
273 ((? gexp? exp)
274 (append (gexp-outputs exp) result))
275 (_
276 result)))
277
278 (fold-right add-reference-output
279 '()
280 (gexp-references exp)))
281
68a61e9f
LC
282(define* (gexp->sexp exp #:key
283 (system (%current-system))
284 (target (%current-target-system)))
21b679f6
LC
285 "Return (monadically) the sexp corresponding to EXP for the given OUTPUT,
286and in the current monad setting (system type, etc.)"
667b2508 287 (define* (reference->sexp ref #:optional native?)
21b679f6
LC
288 (with-monad %store-monad
289 (match ref
290 (((? derivation? drv) (? string? output))
291 (return (derivation->output-path drv output)))
292 (((? package? p) (? string? output))
68a61e9f
LC
293 (package-file p
294 #:output output
295 #:system system
667b2508 296 #:target (if native? #f target)))
79c0c8cd
LC
297 (((? origin? o) (? string? output))
298 (mlet %store-monad ((drv (origin->derivation o)))
299 (return (derivation->output-path drv output))))
21b679f6 300 (($ <output-ref> output)
bfd9eed9
LC
301 ;; Output file names are not known in advance but the daemon defines
302 ;; an environment variable for each of them at build time, so use
303 ;; that trick.
304 (return `((@ (guile) getenv) ,output)))
21b679f6 305 ((? gexp? exp)
667b2508
LC
306 (gexp->sexp exp
307 #:system system
308 #:target (if native? #f target)))
21b679f6
LC
309 (((? string? str))
310 (return (if (direct-store-path? str) str ref)))
311 ((refs ...)
667b2508
LC
312 (sequence %store-monad
313 (map (cut reference->sexp <> native?) refs)))
21b679f6
LC
314 (x
315 (return x)))))
316
317 (mlet %store-monad
318 ((args (sequence %store-monad
667b2508
LC
319 (append (map reference->sexp (gexp-references exp))
320 (map (cut reference->sexp <> #t)
321 (gexp-native-references exp))))))
21b679f6
LC
322 (return (apply (gexp-proc exp) args))))
323
324(define (canonicalize-reference ref)
325 "Return a canonical variant of REF, which adds any missing output part in
326package/derivation references."
327 (match ref
328 ((? package? p)
329 `(,p "out"))
79c0c8cd
LC
330 ((? origin? o)
331 `(,o "out"))
21b679f6
LC
332 ((? derivation? d)
333 `(,d "out"))
334 (((? package?) (? string?))
335 ref)
79c0c8cd
LC
336 (((? origin?) (? string?))
337 ref)
21b679f6
LC
338 (((? derivation?) (? string?))
339 ref)
340 ((? string? s)
341 (if (direct-store-path? s) `(,s) s))
342 ((refs ...)
343 (map canonicalize-reference refs))
344 (x x)))
345
346(define (syntax-location-string s)
347 "Return a string representing the source code location of S."
348 (let ((props (syntax-source s)))
349 (if props
350 (let ((file (assoc-ref props 'filename))
351 (line (and=> (assoc-ref props 'line) 1+))
352 (column (assoc-ref props 'column)))
353 (if file
354 (simple-format #f "~a:~a:~a"
355 file line column)
356 (simple-format #f "~a:~a" line column)))
357 "<unknown location>")))
358
359(define-syntax gexp
360 (lambda (s)
361 (define (collect-escapes exp)
362 ;; Return all the 'ungexp' present in EXP.
363 (let loop ((exp exp)
364 (result '()))
365 (syntax-case exp (ungexp ungexp-splicing)
366 ((ungexp _)
367 (cons exp result))
368 ((ungexp _ _)
369 (cons exp result))
370 ((ungexp-splicing _ ...)
371 (cons exp result))
372 ((exp0 exp ...)
373 (let ((result (loop #'exp0 result)))
374 (fold loop result #'(exp ...))))
375 (_
376 result))))
377
667b2508
LC
378 (define (collect-native-escapes exp)
379 ;; Return all the 'ungexp-native' forms present in EXP.
380 (let loop ((exp exp)
381 (result '()))
382 (syntax-case exp (ungexp-native ungexp-native-splicing)
383 ((ungexp-native _)
384 (cons exp result))
385 ((ungexp-native _ _)
386 (cons exp result))
387 ((ungexp-native-splicing _ ...)
388 (cons exp result))
389 ((exp0 exp ...)
390 (let ((result (loop #'exp0 result)))
391 (fold loop result #'(exp ...))))
392 (_
393 result))))
394
21b679f6
LC
395 (define (escape->ref exp)
396 ;; Turn 'ungexp' form EXP into a "reference".
667b2508
LC
397 (syntax-case exp (ungexp ungexp-splicing
398 ungexp-native ungexp-native-splicing
399 output)
21b679f6
LC
400 ((ungexp output)
401 #'(output-ref "out"))
402 ((ungexp output name)
403 #'(output-ref name))
404 ((ungexp thing)
405 #'thing)
406 ((ungexp drv-or-pkg out)
407 #'(list drv-or-pkg out))
408 ((ungexp-splicing lst)
667b2508
LC
409 #'lst)
410 ((ungexp-native thing)
411 #'thing)
412 ((ungexp-native drv-or-pkg out)
413 #'(list drv-or-pkg out))
414 ((ungexp-native-splicing lst)
21b679f6
LC
415 #'lst)))
416
667b2508
LC
417 (define (substitute-ungexp exp substs)
418 ;; Given EXP, an 'ungexp' or 'ungexp-native' form, substitute it with
419 ;; the corresponding form in SUBSTS.
420 (match (assoc exp substs)
421 ((_ id)
422 id)
423 (_
424 #'(syntax-error "error: no 'ungexp' substitution"
425 #'ref))))
426
427 (define (substitute-ungexp-splicing exp substs)
428 (syntax-case exp ()
429 ((exp rest ...)
430 (match (assoc #'exp substs)
431 ((_ id)
432 (with-syntax ((id id))
433 #`(append id
434 #,(substitute-references #'(rest ...) substs))))
435 (_
436 #'(syntax-error "error: no 'ungexp-splicing' substitution"
437 #'ref))))))
438
21b679f6
LC
439 (define (substitute-references exp substs)
440 ;; Return a variant of EXP where all the cars of SUBSTS have been
441 ;; replaced by the corresponding cdr.
667b2508
LC
442 (syntax-case exp (ungexp ungexp-native
443 ungexp-splicing ungexp-native-splicing)
21b679f6 444 ((ungexp _ ...)
667b2508
LC
445 (substitute-ungexp exp substs))
446 ((ungexp-native _ ...)
447 (substitute-ungexp exp substs))
21b679f6 448 (((ungexp-splicing _ ...) rest ...)
667b2508
LC
449 (substitute-ungexp-splicing exp substs))
450 (((ungexp-native-splicing _ ...) rest ...)
451 (substitute-ungexp-splicing exp substs))
21b679f6
LC
452 ((exp0 exp ...)
453 #`(cons #,(substitute-references #'exp0 substs)
454 #,(substitute-references #'(exp ...) substs)))
455 (x #''x)))
456
457 (syntax-case s (ungexp output)
458 ((_ exp)
667b2508
LC
459 (let* ((normals (delete-duplicates (collect-escapes #'exp)))
460 (natives (delete-duplicates (collect-native-escapes #'exp)))
461 (escapes (append normals natives))
21b679f6
LC
462 (formals (generate-temporaries escapes))
463 (sexp (substitute-references #'exp (zip escapes formals)))
667b2508
LC
464 (refs (map escape->ref normals))
465 (nrefs (map escape->ref natives)))
21b679f6 466 #`(make-gexp (map canonicalize-reference (list #,@refs))
667b2508 467 (map canonicalize-reference (list #,@nrefs))
21b679f6
LC
468 (lambda #,formals
469 #,sexp)))))))
470
471\f
472;;;
473;;; Convenience procedures.
474;;;
475
53e89b17
LC
476(define (default-guile)
477 ;; Lazily resolve 'guile-final'. This module must not refer to (gnu …)
478 ;; modules directly, to avoid circular dependencies, hence this hack.
bdb36958 479 (module-ref (resolve-interface '(gnu packages commencement))
53e89b17
LC
480 'guile-final))
481
21b679f6 482(define* (gexp->script name exp
53e89b17 483 #:key (modules '()) (guile (default-guile)))
21b679f6
LC
484 "Return an executable script NAME that runs EXP using GUILE with MODULES in
485its search path."
486 (mlet %store-monad ((modules (imported-modules modules))
487 (compiled (compiled-modules modules)))
488 (gexp->derivation name
489 (gexp
490 (call-with-output-file (ungexp output)
491 (lambda (port)
c17b5ab4
LC
492 ;; Note: that makes a long shebang. When the store
493 ;; is /gnu/store, that fits within the 128-byte
494 ;; limit imposed by Linux, but that may go beyond
495 ;; when running tests.
21b679f6
LC
496 (format port
497 "#!~a/bin/guile --no-auto-compile~%!#~%"
498 (ungexp guile))
4a4cbd0b
LC
499
500 ;; Write the 'eval-when' form so that it can be
501 ;; compiled.
21b679f6 502 (write
4a4cbd0b
LC
503 '(eval-when (expand load eval)
504 (set! %load-path
505 (cons (ungexp modules) %load-path))
506 (set! %load-compiled-path
507 (cons (ungexp compiled)
508 %load-compiled-path)))
21b679f6
LC
509 port)
510 (write '(ungexp exp) port)
511 (chmod port #o555)))))))
512
513(define (gexp->file name exp)
514 "Return a derivation that builds a file NAME containing EXP."
515 (gexp->derivation name
516 (gexp
517 (call-with-output-file (ungexp output)
518 (lambda (port)
dc254e05
LC
519 (write '(ungexp exp) port))))
520 #:local-build? #t))
21b679f6 521
21b679f6
LC
522\f
523;;;
524;;; Syntactic sugar.
525;;;
526
527(eval-when (expand load eval)
667b2508
LC
528 (define* (read-ungexp chr port #:optional native?)
529 "Read an 'ungexp' or 'ungexp-splicing' form from PORT. When NATIVE? is
530true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
21b679f6
LC
531 (define unquote-symbol
532 (match (peek-char port)
533 (#\@
534 (read-char port)
667b2508
LC
535 (if native?
536 'ungexp-native-splicing
537 'ungexp-splicing))
21b679f6 538 (_
667b2508
LC
539 (if native?
540 'ungexp-native
541 'ungexp))))
21b679f6
LC
542
543 (match (read port)
544 ((? symbol? symbol)
545 (let ((str (symbol->string symbol)))
546 (match (string-index-right str #\:)
547 (#f
548 `(,unquote-symbol ,symbol))
549 (colon
550 (let ((name (string->symbol (substring str 0 colon)))
551 (output (substring str (+ colon 1))))
552 `(,unquote-symbol ,name ,output))))))
553 (x
554 `(,unquote-symbol ,x))))
555
556 (define (read-gexp chr port)
557 "Read a 'gexp' form from PORT."
558 `(gexp ,(read port)))
559
560 ;; Extend the reader
561 (read-hash-extend #\~ read-gexp)
667b2508
LC
562 (read-hash-extend #\$ read-ungexp)
563 (read-hash-extend #\+ (cut read-ungexp <> <> #t)))
21b679f6
LC
564
565;;; gexp.scm ends here