gexp: Add 'file-union'.
[jackhill/guix/guix.git] / guix / gexp.scm
CommitLineData
21b679f6 1;;; GNU Guix --- Functional package management for GNU
4a6e889f 2;;; Copyright © 2014, 2015, 2016, 2017 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)
7adf9b84 23 #:use-module (guix grafts)
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 28 #:use-module (srfi srfi-26)
3e43166f
LC
29 #:use-module (srfi srfi-34)
30 #:use-module (srfi srfi-35)
21b679f6
LC
31 #:use-module (ice-9 match)
32 #:export (gexp
33 gexp?
0bb9929e 34 with-imported-modules
0dbea56b
LC
35
36 gexp-input
37 gexp-input?
558e8b11 38
d9ae938f
LC
39 local-file
40 local-file?
74d441ab 41 local-file-file
9d3994f7 42 local-file-absolute-file-name
74d441ab
LC
43 local-file-name
44 local-file-recursive?
0dbea56b 45
558e8b11
LC
46 plain-file
47 plain-file?
48 plain-file-name
49 plain-file-content
50
91937029
LC
51 computed-file
52 computed-file?
53 computed-file-name
54 computed-file-gexp
91937029
LC
55 computed-file-options
56
15a01c72
LC
57 program-file
58 program-file?
59 program-file-name
60 program-file-gexp
15a01c72
LC
61 program-file-guile
62
e1c153e0
LC
63 scheme-file
64 scheme-file?
65 scheme-file-name
66 scheme-file-gexp
67
a9e5e92f
LC
68 file-append
69 file-append?
70 file-append-base
71 file-append-suffix
72
64fc9f65
RJ
73 load-path-expression
74 gexp-modules
75
21b679f6
LC
76 gexp->derivation
77 gexp->file
462a3fa3 78 gexp->script
aa72d9af 79 text-file*
b751cde3 80 mixed-text-file
dedb512f 81 file-union
aa72d9af
LC
82 imported-files
83 imported-modules
ff40e9b7
LC
84 compiled-modules
85
86 define-gexp-compiler
6b6298ae 87 gexp-compiler?
c2b84676 88 lower-object
6b6298ae 89
3e43166f
LC
90 lower-inputs
91
92 &gexp-error
93 gexp-error?
94 &gexp-input-error
95 gexp-input-error?
96 gexp-error-invalid-input))
21b679f6
LC
97
98;;; Commentary:
99;;;
100;;; This module implements "G-expressions", or "gexps". Gexps are like
101;;; S-expressions (sexps), with two differences:
102;;;
103;;; 1. References (un-quotations) to derivations or packages in a gexp are
667b2508
LC
104;;; replaced by the corresponding output file name; in addition, the
105;;; 'ungexp-native' unquote-like form allows code to explicitly refer to
106;;; the native code of a given package, in case of cross-compilation;
21b679f6
LC
107;;;
108;;; 2. Gexps embed information about the derivations they refer to.
109;;;
110;;; Gexps make it easy to write to files Scheme code that refers to store
111;;; items, or to write Scheme code to build derivations.
112;;;
113;;; Code:
114
115;; "G expressions".
116(define-record-type <gexp>
0bb9929e 117 (make-gexp references modules proc)
21b679f6 118 gexp?
affd7761 119 (references gexp-references) ;list of <gexp-input>
0bb9929e 120 (modules gexp-self-modules) ;list of module names
affd7761 121 (proc gexp-proc)) ;procedure
21b679f6 122
7560b00b
LC
123(define (write-gexp gexp port)
124 "Write GEXP on PORT."
125 (display "#<gexp " port)
2cf0ea0d
LC
126
127 ;; Try to write the underlying sexp. Now, this trick doesn't work when
128 ;; doing things like (ungexp-splicing (gexp ())) because GEXP's procedure
129 ;; tries to use 'append' on that, which fails with wrong-type-arg.
130 (false-if-exception
667b2508 131 (write (apply (gexp-proc gexp)
affd7761 132 (gexp-references gexp))
667b2508 133 port))
7560b00b
LC
134 (format port " ~a>"
135 (number->string (object-address gexp) 16)))
136
137(set-record-type-printer! <gexp> write-gexp)
138
bcb13287
LC
139\f
140;;;
141;;; Methods.
142;;;
143
144;; Compiler for a type of objects that may be introduced in a gexp.
145(define-record-type <gexp-compiler>
1cdecf24 146 (gexp-compiler type lower expand)
bcb13287 147 gexp-compiler?
1cdecf24 148 (type gexp-compiler-type) ;record type descriptor
ebdfd776 149 (lower gexp-compiler-lower)
1cdecf24 150 (expand gexp-compiler-expand)) ;#f | DRV -> sexp
bcb13287 151
3e43166f
LC
152(define-condition-type &gexp-error &error
153 gexp-error?)
154
155(define-condition-type &gexp-input-error &gexp-error
156 gexp-input-error?
157 (input gexp-error-invalid-input))
158
159
bcb13287 160(define %gexp-compilers
1cdecf24
LC
161 ;; 'eq?' mapping of record type descriptor to <gexp-compiler>.
162 (make-hash-table 20))
bcb13287 163
ebdfd776
LC
164(define (default-expander thing obj output)
165 "This is the default expander for \"things\" that appear in gexps. It
166returns its output file name of OBJ's OUTPUT."
167 (match obj
168 ((? derivation? drv)
169 (derivation->output-path drv output))
170 ((? string? file)
171 file)))
172
bcb13287
LC
173(define (register-compiler! compiler)
174 "Register COMPILER as a gexp compiler."
1cdecf24
LC
175 (hashq-set! %gexp-compilers
176 (gexp-compiler-type compiler) compiler))
bcb13287
LC
177
178(define (lookup-compiler object)
ebdfd776 179 "Search for a compiler for OBJECT. Upon success, return the three argument
bcb13287 180procedure to lower it; otherwise return #f."
1cdecf24
LC
181 (and=> (hashq-ref %gexp-compilers (struct-vtable object))
182 gexp-compiler-lower))
bcb13287 183
ebdfd776
LC
184(define (lookup-expander object)
185 "Search for an expander for OBJECT. Upon success, return the three argument
186procedure to expand it; otherwise return #f."
1cdecf24
LC
187 (and=> (hashq-ref %gexp-compilers (struct-vtable object))
188 gexp-compiler-expand))
ebdfd776 189
c2b84676
LC
190(define* (lower-object obj
191 #:optional (system (%current-system))
192 #:key target)
193 "Return as a value in %STORE-MONAD the derivation or store item
194corresponding to OBJ for SYSTEM, cross-compiling for TARGET if TARGET is true.
195OBJ must be an object that has an associated gexp compiler, such as a
196<package>."
3e43166f
LC
197 (match (lookup-compiler obj)
198 (#f
199 (raise (condition (&gexp-input-error (input obj)))))
200 (lower
201 (lower obj system target))))
c2b84676 202
ebdfd776
LC
203(define-syntax define-gexp-compiler
204 (syntax-rules (=> compiler expander)
205 "Define NAME as a compiler for objects matching PREDICATE encountered in
206gexps.
207
208In the simplest form of the macro, BODY must return a derivation for PARAM, an
209object that matches PREDICATE, for SYSTEM and TARGET (the latter of which is
210#f except when cross-compiling.)
211
212The more elaborate form allows you to specify an expander:
213
214 (define-gexp-compiler something something?
215 compiler => (lambda (param system target) ...)
216 expander => (lambda (param drv output) ...))
217
218The expander specifies how an object is converted to its sexp representation."
1cdecf24
LC
219 ((_ (name (param record-type) system target) body ...)
220 (define-gexp-compiler name record-type
ebdfd776
LC
221 compiler => (lambda (param system target) body ...)
222 expander => default-expander))
1cdecf24 223 ((_ name record-type
ebdfd776
LC
224 compiler => compile
225 expander => expand)
226 (begin
227 (define name
1cdecf24 228 (gexp-compiler record-type compile expand))
ebdfd776 229 (register-compiler! name)))))
bcb13287 230
1cdecf24 231(define-gexp-compiler (derivation-compiler (drv <derivation>) system target)
2924f0d6
LC
232 ;; Derivations are the lowest-level representation, so this is the identity
233 ;; compiler.
234 (with-monad %store-monad
235 (return drv)))
236
bcb13287 237\f
d9ae938f 238;;;
558e8b11 239;;; File declarations.
d9ae938f
LC
240;;;
241
9d3994f7
LC
242;; A local file name. FILE is the file name the user entered, which can be a
243;; relative file name, and ABSOLUTE is a promise that computes its canonical
244;; absolute file name. We keep it in a promise to compute it lazily and avoid
245;; repeated 'stat' calls.
d9ae938f 246(define-record-type <local-file>
0687fc9c 247 (%%local-file file absolute name recursive? select?)
d9ae938f
LC
248 local-file?
249 (file local-file-file) ;string
9d3994f7 250 (absolute %local-file-absolute-file-name) ;promise string
d9ae938f 251 (name local-file-name) ;string
0687fc9c
LC
252 (recursive? local-file-recursive?) ;Boolean
253 (select? local-file-select?)) ;string stat -> Boolean
254
255(define (true file stat) #t)
d9ae938f 256
9d3994f7 257(define* (%local-file file promise #:optional (name (basename file))
0687fc9c 258 #:key recursive? (select? true))
9d3994f7
LC
259 ;; This intermediate procedure is part of our ABI, but the underlying
260 ;; %%LOCAL-FILE is not.
0687fc9c 261 (%%local-file file promise name recursive? select?))
9d3994f7 262
9d3994f7
LC
263(define (absolute-file-name file directory)
264 "Return the canonical absolute file name for FILE, which lives in the
265vicinity of DIRECTORY."
266 (canonicalize-path
267 (cond ((string-prefix? "/" file) file)
268 ((not directory) file)
269 ((string-prefix? "/" directory)
270 (string-append directory "/" file))
271 (else file))))
272
302d46e6
LC
273(define-syntax local-file
274 (lambda (s)
275 "Return an object representing local file FILE to add to the store; this
9d3994f7
LC
276object can be used in a gexp. If FILE is a relative file name, it is looked
277up relative to the source file where this form appears. FILE will be added to
278the store under NAME--by default the base name of FILE.
d9ae938f
LC
279
280When RECURSIVE? is true, the contents of FILE are added recursively; if FILE
281designates a flat file and RECURSIVE? is true, its contents are added, and its
282permission bits are kept.
283
0687fc9c
LC
284When RECURSIVE? is true, call (SELECT? FILE STAT) for each directory entry,
285where FILE is the entry's absolute file name and STAT is the result of
286'lstat'; exclude entries for which SELECT? does not return true.
287
302d46e6
LC
288This is the declarative counterpart of the 'interned-file' monadic procedure.
289It is implemented as a macro to capture the current source directory where it
290appears."
291 (syntax-case s ()
292 ((_ file rest ...)
293 #'(%local-file file
294 (delay (absolute-file-name file (current-source-directory)))
295 rest ...))
296 ((_)
297 #'(syntax-error "missing file name"))
298 (id
299 (identifier? #'id)
300 ;; XXX: We could return #'(lambda (file . rest) ...). However,
301 ;; (syntax-source #'id) is #f so (current-source-directory) would not
302 ;; work. Thus, simply forbid this form.
303 #'(syntax-error
304 "'local-file' is a macro and cannot be used like this")))))
9d3994f7
LC
305
306(define (local-file-absolute-file-name file)
307 "Return the absolute file name for FILE, a <local-file> instance. A
308'system-error' exception is raised if FILE could not be found."
309 (force (%local-file-absolute-file-name file)))
d9ae938f 310
1cdecf24 311(define-gexp-compiler (local-file-compiler (file <local-file>) system target)
d9ae938f
LC
312 ;; "Compile" FILE by adding it to the store.
313 (match file
0687fc9c 314 (($ <local-file> file (= force absolute) name recursive? select?)
9d3994f7
LC
315 ;; Canonicalize FILE so that if it's a symlink, it is resolved. Failing
316 ;; to do that, when RECURSIVE? is #t, we could end up creating a dangling
317 ;; symlink in the store, and when RECURSIVE? is #f 'add-to-store' would
318 ;; just throw an error, both of which are inconvenient.
0687fc9c
LC
319 (interned-file absolute name
320 #:recursive? recursive? #:select? select?))))
d9ae938f 321
558e8b11
LC
322(define-record-type <plain-file>
323 (%plain-file name content references)
324 plain-file?
325 (name plain-file-name) ;string
326 (content plain-file-content) ;string
327 (references plain-file-references)) ;list (currently unused)
328
329(define (plain-file name content)
330 "Return an object representing a text file called NAME with the given
331CONTENT (a string) to be added to the store.
332
333This is the declarative counterpart of 'text-file'."
334 ;; XXX: For now just ignore 'references' because it's not clear how to use
335 ;; them in a declarative context.
336 (%plain-file name content '()))
337
1cdecf24 338(define-gexp-compiler (plain-file-compiler (file <plain-file>) system target)
558e8b11
LC
339 ;; "Compile" FILE by adding it to the store.
340 (match file
341 (($ <plain-file> name content references)
342 (text-file name content references))))
343
91937029 344(define-record-type <computed-file>
a769bffb 345 (%computed-file name gexp options)
91937029
LC
346 computed-file?
347 (name computed-file-name) ;string
348 (gexp computed-file-gexp) ;gexp
91937029
LC
349 (options computed-file-options)) ;list of arguments
350
351(define* (computed-file name gexp
a769bffb 352 #:key (options '(#:local-build? #t)))
91937029 353 "Return an object representing the store item NAME, a file or directory
a769bffb 354computed by GEXP. OPTIONS is a list of additional arguments to pass
91937029
LC
355to 'gexp->derivation'.
356
357This is the declarative counterpart of 'gexp->derivation'."
a769bffb 358 (%computed-file name gexp options))
91937029 359
1cdecf24 360(define-gexp-compiler (computed-file-compiler (file <computed-file>)
91937029
LC
361 system target)
362 ;; Compile FILE by returning a derivation whose build expression is its
363 ;; gexp.
364 (match file
a769bffb
LC
365 (($ <computed-file> name gexp options)
366 (apply gexp->derivation name gexp options))))
91937029 367
15a01c72 368(define-record-type <program-file>
9c14a487 369 (%program-file name gexp guile)
15a01c72
LC
370 program-file?
371 (name program-file-name) ;string
372 (gexp program-file-gexp) ;gexp
15a01c72
LC
373 (guile program-file-guile)) ;package
374
9c14a487 375(define* (program-file name gexp #:key (guile #f))
15a01c72 376 "Return an object representing the executable store item NAME that runs
9c14a487 377GEXP. GUILE is the Guile package used to execute that script.
15a01c72
LC
378
379This is the declarative counterpart of 'gexp->script'."
9c14a487 380 (%program-file name gexp guile))
15a01c72 381
1cdecf24 382(define-gexp-compiler (program-file-compiler (file <program-file>)
15a01c72
LC
383 system target)
384 ;; Compile FILE by returning a derivation that builds the script.
385 (match file
9c14a487 386 (($ <program-file> name gexp guile)
15a01c72 387 (gexp->script name gexp
15a01c72
LC
388 #:guile (or guile (default-guile))))))
389
e1c153e0
LC
390(define-record-type <scheme-file>
391 (%scheme-file name gexp)
392 scheme-file?
393 (name scheme-file-name) ;string
394 (gexp scheme-file-gexp)) ;gexp
395
396(define* (scheme-file name gexp)
397 "Return an object representing the Scheme file NAME that contains GEXP.
398
399This is the declarative counterpart of 'gexp->file'."
400 (%scheme-file name gexp))
401
1cdecf24 402(define-gexp-compiler (scheme-file-compiler (file <scheme-file>)
e1c153e0
LC
403 system target)
404 ;; Compile FILE by returning a derivation that builds the file.
405 (match file
406 (($ <scheme-file> name gexp)
407 (gexp->file name gexp))))
408
a9e5e92f
LC
409;; Appending SUFFIX to BASE's output file name.
410(define-record-type <file-append>
411 (%file-append base suffix)
412 file-append?
413 (base file-append-base) ;<package> | <derivation> | ...
414 (suffix file-append-suffix)) ;list of strings
415
416(define (file-append base . suffix)
417 "Return a <file-append> object that expands to the concatenation of BASE and
418SUFFIX."
419 (%file-append base suffix))
420
1cdecf24 421(define-gexp-compiler file-append-compiler <file-append>
a9e5e92f
LC
422 compiler => (lambda (obj system target)
423 (match obj
424 (($ <file-append> base _)
425 (lower-object base system #:target target))))
426 expander => (lambda (obj lowered output)
427 (match obj
428 (($ <file-append> base suffix)
429 (let* ((expand (lookup-expander base))
430 (base (expand base lowered output)))
431 (string-append base (string-concatenate suffix)))))))
432
d9ae938f 433\f
bcb13287
LC
434;;;
435;;; Inputs & outputs.
436;;;
437
e39d1461
LC
438;; The input of a gexp.
439(define-record-type <gexp-input>
0dbea56b 440 (%gexp-input thing output native?)
e39d1461
LC
441 gexp-input?
442 (thing gexp-input-thing) ;<package> | <origin> | <derivation> | ...
443 (output gexp-input-output) ;string
444 (native? gexp-input-native?)) ;Boolean
445
f7328634
LC
446(define (write-gexp-input input port)
447 (match input
448 (($ <gexp-input> thing output #f)
449 (format port "#<gexp-input ~s:~a>" thing output))
450 (($ <gexp-input> thing output #t)
451 (format port "#<gexp-input native ~s:~a>" thing output))))
452
453(set-record-type-printer! <gexp-input> write-gexp-input)
454
0dbea56b
LC
455(define* (gexp-input thing ;convenience procedure
456 #:optional (output "out")
457 #:key native?)
458 "Return a new <gexp-input> for the OUTPUT of THING; NATIVE? determines
459whether this should be considered a \"native\" input or not."
460 (%gexp-input thing output native?))
461
21b679f6
LC
462;; Reference to one of the derivation's outputs, for gexps used in
463;; derivations.
1e87da58
LC
464(define-record-type <gexp-output>
465 (gexp-output name)
466 gexp-output?
467 (name gexp-output-name))
21b679f6 468
f7328634
LC
469(define (write-gexp-output output port)
470 (match output
471 (($ <gexp-output> name)
472 (format port "#<gexp-output ~a>" name))))
473
474(set-record-type-printer! <gexp-output> write-gexp-output)
475
0bb9929e 476(define (gexp-modules gexp)
2363bdd7
LC
477 "Return the list of Guile module names GEXP relies on. If (gexp? GEXP) is
478false, meaning that GEXP is a plain Scheme object, return the empty list."
479 (if (gexp? gexp)
480 (delete-duplicates
481 (append (gexp-self-modules gexp)
482 (append-map (match-lambda
483 (($ <gexp-input> (? gexp? exp))
484 (gexp-modules exp))
485 (($ <gexp-input> (lst ...))
486 (append-map (lambda (item)
487 (if (gexp? item)
488 (gexp-modules item)
489 '()))
490 lst))
491 (_
492 '()))
493 (gexp-references gexp))))
494 '())) ;plain Scheme data type
0bb9929e 495
68a61e9f
LC
496(define* (lower-inputs inputs
497 #:key system target)
498 "Turn any package from INPUTS into a derivation for SYSTEM; return the
499corresponding input list as a monadic value. When TARGET is true, use it as
500the cross-compilation target triplet."
21b679f6
LC
501 (with-monad %store-monad
502 (sequence %store-monad
503 (map (match-lambda
2242ff45 504 (((? struct? thing) sub-drv ...)
c2b84676
LC
505 (mlet %store-monad ((drv (lower-object
506 thing system #:target target)))
2242ff45
LC
507 (return `(,drv ,@sub-drv))))
508 (input
509 (return input)))
21b679f6
LC
510 inputs))))
511
b53833b2
LC
512(define* (lower-reference-graphs graphs #:key system target)
513 "Given GRAPHS, a list of (FILE-NAME INPUT ...) lists for use as a
514#:reference-graphs argument, lower it such that each INPUT is replaced by the
515corresponding derivation."
516 (match graphs
517 (((file-names . inputs) ...)
518 (mlet %store-monad ((inputs (lower-inputs inputs
519 #:system system
520 #:target target)))
521 (return (map cons file-names inputs))))))
522
c8351d9a
LC
523(define* (lower-references lst #:key system target)
524 "Based on LST, a list of output names and packages, return a list of output
525names and file names suitable for the #:allowed-references argument to
526'derivation'."
c8351d9a
LC
527 (with-monad %store-monad
528 (define lower
529 (match-lambda
530 ((? string? output)
531 (return output))
accb682c 532 (($ <gexp-input> thing output native?)
c2b84676
LC
533 (mlet %store-monad ((drv (lower-object thing system
534 #:target (if native?
535 #f target))))
accb682c 536 (return (derivation->output-path drv output))))
bcb13287 537 (thing
c2b84676
LC
538 (mlet %store-monad ((drv (lower-object thing system
539 #:target target)))
c8351d9a
LC
540 (return (derivation->output-path drv))))))
541
542 (sequence %store-monad (map lower lst))))
543
ff40e9b7
LC
544(define default-guile-derivation
545 ;; Here we break the abstraction by talking to the higher-level layer.
546 ;; Thus, do the resolution lazily to hide the circular dependency.
547 (let ((proc (delay
548 (let ((iface (resolve-interface '(guix packages))))
549 (module-ref iface 'default-guile-derivation)))))
550 (lambda (system)
551 ((force proc) system))))
552
21b679f6
LC
553(define* (gexp->derivation name exp
554 #:key
68a61e9f 555 system (target 'current)
21b679f6
LC
556 hash hash-algo recursive?
557 (env-vars '())
558 (modules '())
4684f301 559 (module-path %load-path)
21b679f6 560 (guile-for-build (%guile-for-build))
ce45eb4c 561 (graft? (%graft?))
21b679f6 562 references-graphs
3f4ecf32 563 allowed-references disallowed-references
c0468155 564 leaked-env-vars
0309e1b0
LC
565 local-build? (substitutable? #t)
566 (script-name (string-append name "-builder")))
21b679f6 567 "Return a derivation NAME that runs EXP (a gexp) with GUILE-FOR-BUILD (a
0309e1b0
LC
568derivation) on SYSTEM; EXP is stored in a file called SCRIPT-NAME. When
569TARGET is true, it is used as the cross-compilation target triplet for
570packages referred to by EXP.
21b679f6 571
0bb9929e
LC
572MODULES is deprecated in favor of 'with-imported-modules'. Its meaning is to
573make MODULES available in the evaluation context of EXP; MODULES is a list of
4684f301 574names of Guile modules searched in MODULE-PATH to be copied in the store,
21b679f6
LC
575compiled, and made available in the load path during the execution of
576EXP---e.g., '((guix build utils) (guix build gnu-build-system)).
577
ce45eb4c
LC
578GRAFT? determines whether packages referred to by EXP should be grafted when
579applicable.
580
b53833b2
LC
581When REFERENCES-GRAPHS is true, it must be a list of tuples of one of the
582following forms:
583
584 (FILE-NAME PACKAGE)
585 (FILE-NAME PACKAGE OUTPUT)
586 (FILE-NAME DERIVATION)
587 (FILE-NAME DERIVATION OUTPUT)
588 (FILE-NAME STORE-ITEM)
589
590The right-hand-side of each element of REFERENCES-GRAPHS is automatically made
591an input of the build process of EXP. In the build environment, each
592FILE-NAME contains the reference graph of the corresponding item, in a simple
593text format.
594
c8351d9a
LC
595ALLOWED-REFERENCES must be either #f or a list of output names and packages.
596In the latter case, the list denotes store items that the result is allowed to
597refer to. Any reference to another store item will lead to a build error.
3f4ecf32
LC
598Similarly for DISALLOWED-REFERENCES, which can list items that must not be
599referenced by the outputs.
b53833b2 600
21b679f6 601The other arguments are as for 'derivation'."
0bb9929e
LC
602 (define %modules
603 (delete-duplicates
604 (append modules (gexp-modules exp))))
21b679f6
LC
605 (define outputs (gexp-outputs exp))
606
b53833b2
LC
607 (define (graphs-file-names graphs)
608 ;; Return a list of (FILE-NAME . STORE-PATH) pairs made from GRAPHS.
609 (map (match-lambda
2924f0d6 610 ;; TODO: Remove 'derivation?' special cases.
b53833b2
LC
611 ((file-name (? derivation? drv))
612 (cons file-name (derivation->output-path drv)))
613 ((file-name (? derivation? drv) sub-drv)
614 (cons file-name (derivation->output-path drv sub-drv)))
615 ((file-name thing)
616 (cons file-name thing)))
617 graphs))
618
ce45eb4c
LC
619 (mlet* %store-monad (;; The following binding forces '%current-system' and
620 ;; '%current-target-system' to be looked up at >>=
621 ;; time.
622 (graft? (set-grafting graft?))
68a61e9f 623
5d098459 624 (system -> (or system (%current-system)))
68a61e9f
LC
625 (target -> (if (eq? target 'current)
626 (%current-target-system)
627 target))
667b2508 628 (normals (lower-inputs (gexp-inputs exp)
68a61e9f
LC
629 #:system system
630 #:target target))
667b2508
LC
631 (natives (lower-inputs (gexp-native-inputs exp)
632 #:system system
633 #:target #f))
634 (inputs -> (append normals natives))
68a61e9f
LC
635 (sexp (gexp->sexp exp
636 #:system system
637 #:target target))
0309e1b0 638 (builder (text-file script-name
21b679f6
LC
639 (object->string sexp)))
640 (modules (if (pair? %modules)
641 (imported-modules %modules
642 #:system system
4684f301 643 #:module-path module-path
21b679f6
LC
644 #:guile guile-for-build)
645 (return #f)))
646 (compiled (if (pair? %modules)
647 (compiled-modules %modules
648 #:system system
4684f301 649 #:module-path module-path
21b679f6
LC
650 #:guile guile-for-build)
651 (return #f)))
b53833b2
LC
652 (graphs (if references-graphs
653 (lower-reference-graphs references-graphs
654 #:system system
655 #:target target)
656 (return #f)))
c8351d9a
LC
657 (allowed (if allowed-references
658 (lower-references allowed-references
659 #:system system
660 #:target target)
661 (return #f)))
3f4ecf32
LC
662 (disallowed (if disallowed-references
663 (lower-references disallowed-references
664 #:system system
665 #:target target)
666 (return #f)))
21b679f6
LC
667 (guile (if guile-for-build
668 (return guile-for-build)
ff40e9b7 669 (default-guile-derivation system))))
ce45eb4c
LC
670 (mbegin %store-monad
671 (set-grafting graft?) ;restore the initial setting
672 (raw-derivation name
673 (string-append (derivation->output-path guile)
674 "/bin/guile")
675 `("--no-auto-compile"
676 ,@(if (pair? %modules)
677 `("-L" ,(derivation->output-path modules)
678 "-C" ,(derivation->output-path compiled))
679 '())
680 ,builder)
681 #:outputs outputs
682 #:env-vars env-vars
683 #:system system
684 #:inputs `((,guile)
685 (,builder)
686 ,@(if modules
687 `((,modules) (,compiled) ,@inputs)
688 inputs)
689 ,@(match graphs
690 (((_ . inputs) ...) inputs)
691 (_ '())))
692 #:hash hash #:hash-algo hash-algo #:recursive? recursive?
693 #:references-graphs (and=> graphs graphs-file-names)
694 #:allowed-references allowed
3f4ecf32 695 #:disallowed-references disallowed
c0468155 696 #:leaked-env-vars leaked-env-vars
4a6aeb67
LC
697 #:local-build? local-build?
698 #:substitutable? substitutable?))))
21b679f6 699
1123759b
LC
700(define* (gexp-inputs exp #:key native?)
701 "Return the input list for EXP. When NATIVE? is true, return only native
702references; otherwise, return only non-native references."
21b679f6
LC
703 (define (add-reference-inputs ref result)
704 (match ref
1123759b
LC
705 (($ <gexp-input> (? gexp? exp) _ #t)
706 (if native?
707 (append (gexp-inputs exp)
708 (gexp-inputs exp #:native? #t)
709 result)
710 result))
711 (($ <gexp-input> (? gexp? exp) _ #f)
d343a60f
LC
712 (append (gexp-inputs exp #:native? native?)
713 result))
e39d1461
LC
714 (($ <gexp-input> (? string? str))
715 (if (direct-store-path? str)
716 (cons `(,str) result)
21b679f6 717 result))
5b14a790
LC
718 (($ <gexp-input> (? struct? thing) output n?)
719 (if (and (eqv? n? native?) (lookup-compiler thing))
bcb13287
LC
720 ;; THING is a derivation, or a package, or an origin, etc.
721 (cons `(,thing ,output) result)
722 result))
1123759b 723 (($ <gexp-input> (lst ...) output n?)
578dfbe0
LC
724 (fold-right add-reference-inputs result
725 ;; XXX: For now, automatically convert LST to a list of
726 ;; gexp-inputs. Inherit N?.
727 (map (match-lambda
728 ((? gexp-input? x)
729 (%gexp-input (gexp-input-thing x)
730 (gexp-input-output x)
731 n?))
732 (x
733 (%gexp-input x "out" n?)))
734 lst)))
21b679f6
LC
735 (_
736 ;; Ignore references to other kinds of objects.
737 result)))
738
739 (fold-right add-reference-inputs
740 '()
5b14a790 741 (gexp-references exp)))
667b2508
LC
742
743(define gexp-native-inputs
1123759b 744 (cut gexp-inputs <> #:native? #t))
21b679f6
LC
745
746(define (gexp-outputs exp)
747 "Return the outputs referred to by EXP as a list of strings."
748 (define (add-reference-output ref result)
749 (match ref
1e87da58 750 (($ <gexp-output> name)
21b679f6 751 (cons name result))
e39d1461 752 (($ <gexp-input> (? gexp? exp))
21b679f6 753 (append (gexp-outputs exp) result))
e39d1461
LC
754 (($ <gexp-input> (lst ...) output native?)
755 ;; XXX: Automatically convert LST.
0dbea56b
LC
756 (add-reference-output (map (match-lambda
757 ((? gexp-input? x) x)
758 (x (%gexp-input x "out" native?)))
759 lst)
e39d1461 760 result))
f9efe568
LC
761 ((lst ...)
762 (fold-right add-reference-output result lst))
21b679f6
LC
763 (_
764 result)))
765
7e75a673
LC
766 (delete-duplicates
767 (add-reference-output (gexp-references exp) '())))
21b679f6 768
68a61e9f
LC
769(define* (gexp->sexp exp #:key
770 (system (%current-system))
771 (target (%current-target-system)))
21b679f6
LC
772 "Return (monadically) the sexp corresponding to EXP for the given OUTPUT,
773and in the current monad setting (system type, etc.)"
667b2508 774 (define* (reference->sexp ref #:optional native?)
21b679f6
LC
775 (with-monad %store-monad
776 (match ref
1e87da58 777 (($ <gexp-output> output)
bfd9eed9
LC
778 ;; Output file names are not known in advance but the daemon defines
779 ;; an environment variable for each of them at build time, so use
780 ;; that trick.
781 (return `((@ (guile) getenv) ,output)))
e39d1461 782 (($ <gexp-input> (? gexp? exp) output n?)
667b2508
LC
783 (gexp->sexp exp
784 #:system system
e39d1461
LC
785 #:target (if (or n? native?) #f target)))
786 (($ <gexp-input> (refs ...) output n?)
667b2508 787 (sequence %store-monad
e39d1461
LC
788 (map (lambda (ref)
789 ;; XXX: Automatically convert REF to an gexp-input.
0dbea56b
LC
790 (reference->sexp
791 (if (gexp-input? ref)
792 ref
793 (%gexp-input ref "out" n?))
affd7761 794 (or n? native?)))
e39d1461 795 refs)))
bcb13287 796 (($ <gexp-input> (? struct? thing) output n?)
ebdfd776
LC
797 (let ((target (if (or n? native?) #f target))
798 (expand (lookup-expander thing)))
c2b84676
LC
799 (mlet %store-monad ((obj (lower-object thing system
800 #:target target)))
d9ae938f 801 ;; OBJ must be either a derivation or a store file name.
ebdfd776 802 (return (expand thing obj output)))))
e39d1461
LC
803 (($ <gexp-input> x)
804 (return x))
21b679f6
LC
805 (x
806 (return x)))))
807
808 (mlet %store-monad
809 ((args (sequence %store-monad
affd7761 810 (map reference->sexp (gexp-references exp)))))
21b679f6
LC
811 (return (apply (gexp-proc exp) args))))
812
21b679f6
LC
813(define (syntax-location-string s)
814 "Return a string representing the source code location of S."
815 (let ((props (syntax-source s)))
816 (if props
817 (let ((file (assoc-ref props 'filename))
818 (line (and=> (assoc-ref props 'line) 1+))
819 (column (assoc-ref props 'column)))
820 (if file
821 (simple-format #f "~a:~a:~a"
822 file line column)
823 (simple-format #f "~a:~a" line column)))
824 "<unknown location>")))
825
0bb9929e
LC
826(define-syntax-parameter current-imported-modules
827 ;; Current list of imported modules.
828 (identifier-syntax '()))
829
830(define-syntax-rule (with-imported-modules modules body ...)
831 "Mark the gexps defined in BODY... as requiring MODULES in their execution
832environment."
833 (syntax-parameterize ((current-imported-modules
834 (identifier-syntax modules)))
835 body ...))
836
21b679f6
LC
837(define-syntax gexp
838 (lambda (s)
839 (define (collect-escapes exp)
840 ;; Return all the 'ungexp' present in EXP.
841 (let loop ((exp exp)
842 (result '()))
607e1b51
LC
843 (syntax-case exp (ungexp
844 ungexp-splicing
845 ungexp-native
846 ungexp-native-splicing)
21b679f6
LC
847 ((ungexp _)
848 (cons exp result))
849 ((ungexp _ _)
850 (cons exp result))
851 ((ungexp-splicing _ ...)
852 (cons exp result))
607e1b51 853 ((ungexp-native _ ...)
667b2508
LC
854 (cons exp result))
855 ((ungexp-native-splicing _ ...)
856 (cons exp result))
5e2e4a51 857 ((exp0 . exp)
667b2508 858 (let ((result (loop #'exp0 result)))
5e2e4a51 859 (loop #'exp result)))
667b2508
LC
860 (_
861 result))))
862
21b679f6
LC
863 (define (escape->ref exp)
864 ;; Turn 'ungexp' form EXP into a "reference".
667b2508
LC
865 (syntax-case exp (ungexp ungexp-splicing
866 ungexp-native ungexp-native-splicing
867 output)
21b679f6 868 ((ungexp output)
1e87da58 869 #'(gexp-output "out"))
21b679f6 870 ((ungexp output name)
1e87da58 871 #'(gexp-output name))
21b679f6 872 ((ungexp thing)
0dbea56b 873 #'(%gexp-input thing "out" #f))
21b679f6 874 ((ungexp drv-or-pkg out)
0dbea56b 875 #'(%gexp-input drv-or-pkg out #f))
21b679f6 876 ((ungexp-splicing lst)
0dbea56b 877 #'(%gexp-input lst "out" #f))
667b2508 878 ((ungexp-native thing)
0dbea56b 879 #'(%gexp-input thing "out" #t))
667b2508 880 ((ungexp-native drv-or-pkg out)
0dbea56b 881 #'(%gexp-input drv-or-pkg out #t))
667b2508 882 ((ungexp-native-splicing lst)
0dbea56b 883 #'(%gexp-input lst "out" #t))))
21b679f6 884
667b2508
LC
885 (define (substitute-ungexp exp substs)
886 ;; Given EXP, an 'ungexp' or 'ungexp-native' form, substitute it with
887 ;; the corresponding form in SUBSTS.
888 (match (assoc exp substs)
889 ((_ id)
890 id)
4a6e889f
LC
891 (_ ;internal error
892 (with-syntax ((exp exp))
893 #'(syntax-error "error: no 'ungexp' substitution" exp)))))
667b2508
LC
894
895 (define (substitute-ungexp-splicing exp substs)
896 (syntax-case exp ()
897 ((exp rest ...)
898 (match (assoc #'exp substs)
899 ((_ id)
900 (with-syntax ((id id))
901 #`(append id
902 #,(substitute-references #'(rest ...) substs))))
903 (_
904 #'(syntax-error "error: no 'ungexp-splicing' substitution"
4a6e889f 905 exp))))))
667b2508 906
21b679f6
LC
907 (define (substitute-references exp substs)
908 ;; Return a variant of EXP where all the cars of SUBSTS have been
909 ;; replaced by the corresponding cdr.
667b2508
LC
910 (syntax-case exp (ungexp ungexp-native
911 ungexp-splicing ungexp-native-splicing)
21b679f6 912 ((ungexp _ ...)
667b2508
LC
913 (substitute-ungexp exp substs))
914 ((ungexp-native _ ...)
915 (substitute-ungexp exp substs))
21b679f6 916 (((ungexp-splicing _ ...) rest ...)
667b2508
LC
917 (substitute-ungexp-splicing exp substs))
918 (((ungexp-native-splicing _ ...) rest ...)
919 (substitute-ungexp-splicing exp substs))
5e2e4a51 920 ((exp0 . exp)
21b679f6 921 #`(cons #,(substitute-references #'exp0 substs)
5e2e4a51 922 #,(substitute-references #'exp substs)))
21b679f6
LC
923 (x #''x)))
924
925 (syntax-case s (ungexp output)
926 ((_ exp)
affd7761 927 (let* ((escapes (delete-duplicates (collect-escapes #'exp)))
21b679f6
LC
928 (formals (generate-temporaries escapes))
929 (sexp (substitute-references #'exp (zip escapes formals)))
affd7761
LC
930 (refs (map escape->ref escapes)))
931 #`(make-gexp (list #,@refs)
0bb9929e 932 current-imported-modules
21b679f6
LC
933 (lambda #,formals
934 #,sexp)))))))
935
936\f
aa72d9af
LC
937;;;
938;;; Module handling.
939;;;
940
df2d51f0
LC
941(define %utils-module
942 ;; This file provides 'mkdir-p', needed to implement 'imported-files' and
a9601e23
LC
943 ;; other primitives below. Note: We give the file name relative to this
944 ;; file you are currently reading; 'search-path' could return a file name
945 ;; relative to the current working directory.
946 (local-file "build/utils.scm"
df2d51f0 947 "build-utils.scm"))
aa72d9af
LC
948
949(define* (imported-files files
950 #:key (name "file-import")
951 (system (%current-system))
952 (guile (%guile-for-build)))
953 "Return a derivation that imports FILES into STORE. FILES must be a list
d938a58b
LC
954of (FINAL-PATH . FILE) pairs. Each FILE is mapped to FINAL-PATH in the
955resulting store path. FILE can be either a file name, or a file-like object,
956as returned by 'local-file' for example."
aa72d9af
LC
957 (define file-pair
958 (match-lambda
d938a58b 959 ((final-path . (? string? file-name))
aa72d9af
LC
960 (mlet %store-monad ((file (interned-file file-name
961 (basename final-path))))
d938a58b
LC
962 (return (list final-path file))))
963 ((final-path . file-like)
964 (mlet %store-monad ((file (lower-object file-like system)))
aa72d9af
LC
965 (return (list final-path file))))))
966
967 (mlet %store-monad ((files (sequence %store-monad
968 (map file-pair files))))
969 (define build
970 (gexp
971 (begin
df2d51f0 972 (primitive-load (ungexp %utils-module)) ;for 'mkdir-p'
aa72d9af
LC
973 (use-modules (ice-9 match))
974
aa72d9af
LC
975 (mkdir (ungexp output)) (chdir (ungexp output))
976 (for-each (match-lambda
977 ((final-path store-path)
978 (mkdir-p (dirname final-path))
979 (symlink store-path final-path)))
980 '(ungexp files)))))
981
982 ;; TODO: Pass FILES as an environment variable so that BUILD remains
983 ;; exactly the same regardless of FILES: less disk space, and fewer
984 ;; 'add-to-store' RPCs.
985 (gexp->derivation name build
986 #:system system
987 #:guile-for-build guile
988 #:local-build? #t)))
989
aa72d9af
LC
990(define* (imported-modules modules
991 #:key (name "module-import")
992 (system (%current-system))
993 (guile (%guile-for-build))
994 (module-path %load-path))
995 "Return a derivation that contains the source files of MODULES, a list of
d938a58b
LC
996module names such as `(ice-9 q)'. All of MODULES must be either names of
997modules to be found in the MODULE-PATH search path, or a module name followed
998by an arrow followed by a file-like object. For example:
999
1000 (imported-modules `((guix build utils)
1001 (guix gcrypt)
1002 ((guix config) => ,(scheme-file …))))
1003
1004In this example, the first two modules are taken from MODULE-PATH, and the
1005last one is created from the given <scheme-file> object."
1006 (mlet %store-monad ((files
1007 (mapm %store-monad
1008 (match-lambda
1009 (((module ...) '=> file)
1010 (return
1011 (cons (module->source-file-name module)
1012 file)))
1013 ((module ...)
1014 (let ((f (module->source-file-name module)))
1015 (return
1016 (cons f (search-path* module-path f))))))
1017 modules)))
aa72d9af
LC
1018 (imported-files files #:name name #:system system
1019 #:guile guile)))
1020
1021(define* (compiled-modules modules
1022 #:key (name "module-import-compiled")
1023 (system (%current-system))
1024 (guile (%guile-for-build))
1025 (module-path %load-path))
1026 "Return a derivation that builds a tree containing the `.go' files
1027corresponding to MODULES. All the MODULES are built in a context where
1028they can refer to each other."
1029 (mlet %store-monad ((modules (imported-modules modules
1030 #:system system
1031 #:guile guile
1032 #:module-path
1033 module-path)))
1034 (define build
1035 (gexp
1036 (begin
df2d51f0
LC
1037 (primitive-load (ungexp %utils-module)) ;for 'mkdir-p'
1038
aa72d9af 1039 (use-modules (ice-9 ftw)
aa72d9af
LC
1040 (srfi srfi-26)
1041 (system base compile))
1042
aa72d9af
LC
1043 (define (regular? file)
1044 (not (member file '("." ".."))))
1045
1046 (define (process-directory directory output)
1047 (let ((entries (map (cut string-append directory "/" <>)
1048 (scandir directory regular?))))
1049 (for-each (lambda (entry)
1050 (if (file-is-directory? entry)
1051 (let ((output (string-append output "/"
1052 (basename entry))))
1053 (mkdir-p output)
1054 (process-directory entry output))
1055 (let* ((base (string-drop-right
1056 (basename entry)
1057 4)) ;.scm
1058 (output (string-append output "/" base
1059 ".go")))
1060 (compile-file entry
1061 #:output-file output
1062 #:opts
1063 %auto-compilation-options))))
1064 entries)))
1065
1066 (set! %load-path (cons (ungexp modules) %load-path))
1067 (mkdir (ungexp output))
1068 (chdir (ungexp modules))
1069 (process-directory "." (ungexp output)))))
1070
1071 ;; TODO: Pass MODULES as an environment variable.
1072 (gexp->derivation name build
1073 #:system system
1074 #:guile-for-build guile
1075 #:local-build? #t)))
1076
1077\f
21b679f6
LC
1078;;;
1079;;; Convenience procedures.
1080;;;
1081
53e89b17
LC
1082(define (default-guile)
1083 ;; Lazily resolve 'guile-final'. This module must not refer to (gnu …)
1084 ;; modules directly, to avoid circular dependencies, hence this hack.
bdb36958 1085 (module-ref (resolve-interface '(gnu packages commencement))
53e89b17
LC
1086 'guile-final))
1087
dd8d1a30
LC
1088(define (load-path-expression modules)
1089 "Return as a monadic value a gexp that sets '%load-path' and
1090'%load-compiled-path' to point to MODULES, a list of module names."
1091 (mlet %store-monad ((modules (imported-modules modules))
1092 (compiled (compiled-modules modules)))
1093 (return (gexp (eval-when (expand load eval)
1094 (set! %load-path
1095 (cons (ungexp modules) %load-path))
1096 (set! %load-compiled-path
1097 (cons (ungexp compiled)
1098 %load-compiled-path)))))))
1099
21b679f6 1100(define* (gexp->script name exp
9c14a487
LC
1101 #:key (guile (default-guile)))
1102 "Return an executable script NAME that runs EXP using GUILE, with EXP's
1103imported modules in its search path."
1104 (mlet %store-monad ((set-load-path
1105 (load-path-expression (gexp-modules exp))))
21b679f6
LC
1106 (gexp->derivation name
1107 (gexp
1108 (call-with-output-file (ungexp output)
1109 (lambda (port)
c17b5ab4
LC
1110 ;; Note: that makes a long shebang. When the store
1111 ;; is /gnu/store, that fits within the 128-byte
1112 ;; limit imposed by Linux, but that may go beyond
1113 ;; when running tests.
21b679f6
LC
1114 (format port
1115 "#!~a/bin/guile --no-auto-compile~%!#~%"
1116 (ungexp guile))
4a4cbd0b 1117
dd8d1a30 1118 (write '(ungexp set-load-path) port)
21b679f6
LC
1119 (write '(ungexp exp) port)
1120 (chmod port #o555)))))))
1121
2b418579
LC
1122(define* (gexp->file name exp #:key (set-load-path? #t))
1123 "Return a derivation that builds a file NAME containing EXP. When
1124SET-LOAD-PATH? is true, emit code in the resulting file to set '%load-path'
1125and '%load-compiled-path' to honor EXP's imported modules."
1126 (match (if set-load-path? (gexp-modules exp) '())
1127 (() ;zero modules
1128 (gexp->derivation name
1129 (gexp
1130 (call-with-output-file (ungexp output)
1131 (lambda (port)
1132 (write '(ungexp exp) port))))
1133 #:local-build? #t
1134 #:substitutable? #f))
1135 ((modules ...)
1136 (mlet %store-monad ((set-load-path (load-path-expression modules)))
1137 (gexp->derivation name
1138 (gexp
1139 (call-with-output-file (ungexp output)
1140 (lambda (port)
1141 (write '(ungexp set-load-path) port)
1142 (write '(ungexp exp) port))))
1143 #:local-build? #t
1144 #:substitutable? #f)))))
21b679f6 1145
462a3fa3
LC
1146(define* (text-file* name #:rest text)
1147 "Return as a monadic value a derivation that builds a text file containing
d9ae938f
LC
1148all of TEXT. TEXT may list, in addition to strings, objects of any type that
1149can be used in a gexp: packages, derivations, local file objects, etc. The
1150resulting store file holds references to all these."
462a3fa3
LC
1151 (define builder
1152 (gexp (call-with-output-file (ungexp output "out")
1153 (lambda (port)
1154 (display (string-append (ungexp-splicing text)) port)))))
1155
851b6f62
LC
1156 (gexp->derivation name builder
1157 #:local-build? #t
1158 #:substitutable? #f))
462a3fa3 1159
b751cde3
LC
1160(define* (mixed-text-file name #:rest text)
1161 "Return an object representing store file NAME containing TEXT. TEXT is a
1162sequence of strings and file-like objects, as in:
1163
1164 (mixed-text-file \"profile\"
1165 \"export PATH=\" coreutils \"/bin:\" grep \"/bin\")
1166
1167This is the declarative counterpart of 'text-file*'."
1168 (define build
1169 (gexp (call-with-output-file (ungexp output "out")
1170 (lambda (port)
1171 (display (string-append (ungexp-splicing text)) port)))))
1172
1173 (computed-file name build))
1174
dedb512f
LC
1175(define (file-union name files)
1176 "Return a <computed-file> that builds a directory containing all of FILES.
1177Each item in FILES must be a two-element list where the first element is the
1178file name to use in the new directory, and the second element is a gexp
1179denoting the target file. Here's an example:
1180
1181 (file-union \"etc\"
1182 `((\"hosts\" ,(plain-file \"hosts\"
1183 \"127.0.0.1 localhost\"))
1184 (\"bashrc\" ,(plain-file \"bashrc\"
1185 \"alias ls='ls --color'\"))))
1186
1187This yields an 'etc' directory containing these two files."
1188 (computed-file name
1189 (gexp
1190 (begin
1191 (mkdir (ungexp output))
1192 (chdir (ungexp output))
1193 (ungexp-splicing
1194 (map (match-lambda
1195 ((target source)
1196 (gexp
1197 (begin
1198 ;; Stat the source to abort early if it does
1199 ;; not exist.
1200 (stat (ungexp source))
1201
1202 (symlink (ungexp source)
1203 (ungexp target))))))
1204 files))))))
1205
21b679f6
LC
1206\f
1207;;;
1208;;; Syntactic sugar.
1209;;;
1210
1211(eval-when (expand load eval)
667b2508
LC
1212 (define* (read-ungexp chr port #:optional native?)
1213 "Read an 'ungexp' or 'ungexp-splicing' form from PORT. When NATIVE? is
1214true, use 'ungexp-native' and 'ungexp-native-splicing' instead."
21b679f6
LC
1215 (define unquote-symbol
1216 (match (peek-char port)
1217 (#\@
1218 (read-char port)
667b2508
LC
1219 (if native?
1220 'ungexp-native-splicing
1221 'ungexp-splicing))
21b679f6 1222 (_
667b2508
LC
1223 (if native?
1224 'ungexp-native
1225 'ungexp))))
21b679f6
LC
1226
1227 (match (read port)
1228 ((? symbol? symbol)
1229 (let ((str (symbol->string symbol)))
1230 (match (string-index-right str #\:)
1231 (#f
1232 `(,unquote-symbol ,symbol))
1233 (colon
1234 (let ((name (string->symbol (substring str 0 colon)))
1235 (output (substring str (+ colon 1))))
1236 `(,unquote-symbol ,name ,output))))))
1237 (x
1238 `(,unquote-symbol ,x))))
1239
1240 (define (read-gexp chr port)
1241 "Read a 'gexp' form from PORT."
1242 `(gexp ,(read port)))
1243
1244 ;; Extend the reader
1245 (read-hash-extend #\~ read-gexp)
667b2508
LC
1246 (read-hash-extend #\$ read-ungexp)
1247 (read-hash-extend #\+ (cut read-ungexp <> <> #t)))
21b679f6
LC
1248
1249;;; gexp.scm ends here