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