diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2017-11-14 10:16:22 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2020-05-16 00:34:41 +0200 |
| commit | 644cb40cd83eff8a5bcdbd2d63887daa18228f41 (patch) | |
| tree | e470f35ad20a8ad6805d2a8e7b03897bc10f6098 | |
| parent | d03001a31a6d460b712825640dba11e3f1a53a14 (diff) | |
gexp: Add 'let-system'.
* guix/gexp.scm (<system-binding>): New record type.
(let-system): New macro.
(system-binding-compiler): New procedure.
(default-expander): Add 'self-quoting?' case.
(self-quoting?): New procedure.
(lower-inputs): Add 'filterm'. Pass the result of
'mapm/accumulate-builds' through FILTERM.
(gexp->sexp)[self-quoting?]: Remove.
* tests/gexp.scm ("let-system", "let-system, target")
("let-system, ungexp-native, target")
("let-system, nested"): New tests.
* doc/guix.texi (G-Expressions): Document it.
| -rw-r--r-- | .dir-locals.el | 1 | ||||
| -rw-r--r-- | doc/guix.texi | 26 | ||||
| -rw-r--r-- | guix/gexp.scm | 110 | ||||
| -rw-r--r-- | tests/gexp.scm | 54 |
4 files changed, 165 insertions, 26 deletions
diff --git a/.dir-locals.el b/.dir-locals.el index ce305602f23..fcde914e602 100644 --- a/.dir-locals.el +++ b/.dir-locals.el | |||
| @@ -85,6 +85,7 @@ | |||
| 85 | (eval . (put 'with-imported-modules 'scheme-indent-function 1)) | 85 | (eval . (put 'with-imported-modules 'scheme-indent-function 1)) |
| 86 | (eval . (put 'with-extensions 'scheme-indent-function 1)) | 86 | (eval . (put 'with-extensions 'scheme-indent-function 1)) |
| 87 | (eval . (put 'with-parameters 'scheme-indent-function 1)) | 87 | (eval . (put 'with-parameters 'scheme-indent-function 1)) |
| 88 | (eval . (put 'let-system 'scheme-indent-function 1)) | ||
| 88 | 89 | ||
| 89 | (eval . (put 'with-database 'scheme-indent-function 2)) | 90 | (eval . (put 'with-database 'scheme-indent-function 2)) |
| 90 | (eval . (put 'call-with-transaction 'scheme-indent-function 2)) | 91 | (eval . (put 'call-with-transaction 'scheme-indent-function 2)) |
diff --git a/doc/guix.texi b/doc/guix.texi index a36b9691fbc..d043852ac3c 100644 --- a/doc/guix.texi +++ b/doc/guix.texi | |||
| @@ -8123,6 +8123,32 @@ the second case, the resulting script contains a @code{(string-append | |||
| 8123 | @dots{})} expression to construct the file name @emph{at run time}. | 8123 | @dots{})} expression to construct the file name @emph{at run time}. |
| 8124 | @end deffn | 8124 | @end deffn |
| 8125 | 8125 | ||
| 8126 | @deffn {Scheme Syntax} let-system @var{system} @var{body}@dots{} | ||
| 8127 | @deffnx {Scheme Syntax} let-system (@var{system} @var{target}) @var{body}@dots{} | ||
| 8128 | Bind @var{system} to the currently targeted system---e.g., | ||
| 8129 | @code{"x86_64-linux"}---within @var{body}. | ||
| 8130 | |||
| 8131 | In the second case, additionally bind @var{target} to the current | ||
| 8132 | cross-compilation target---a GNU triplet such as | ||
| 8133 | @code{"arm-linux-gnueabihf"}---or @code{#f} if we are not | ||
| 8134 | cross-compiling. | ||
| 8135 | |||
| 8136 | @code{let-system} is useful in the occasional case where the object | ||
| 8137 | spliced into the gexp depends on the target system, as in this example: | ||
| 8138 | |||
| 8139 | @example | ||
| 8140 | #~(system* | ||
| 8141 | #+(let-system system | ||
| 8142 | (cond ((string-prefix? "armhf-" system) | ||
| 8143 | (file-append qemu "/bin/qemu-system-arm")) | ||
| 8144 | ((string-prefix? "x86_64-" system) | ||
| 8145 | (file-append qemu "/bin/qemu-system-x86_64")) | ||
| 8146 | (else | ||
| 8147 | (error "dunno!")))) | ||
| 8148 | "-net" "user" #$image) | ||
| 8149 | @end example | ||
| 8150 | @end deffn | ||
| 8151 | |||
| 8126 | @deffn {Scheme Syntax} with-parameters ((@var{parameter} @var{value}) @dots{}) @var{exp} | 8152 | @deffn {Scheme Syntax} with-parameters ((@var{parameter} @var{value}) @dots{}) @var{exp} |
| 8127 | This macro is similar to the @code{parameterize} form for | 8153 | This macro is similar to the @code{parameterize} form for |
| 8128 | dynamically-bound @dfn{parameters} (@pxref{Parameters,,, guile, GNU | 8154 | dynamically-bound @dfn{parameters} (@pxref{Parameters,,, guile, GNU |
diff --git a/guix/gexp.scm b/guix/gexp.scm index 5c614f3e127..78b8af6fbcb 100644 --- a/guix/gexp.scm +++ b/guix/gexp.scm | |||
| @@ -37,6 +37,7 @@ | |||
| 37 | gexp? | 37 | gexp? |
| 38 | with-imported-modules | 38 | with-imported-modules |
| 39 | with-extensions | 39 | with-extensions |
| 40 | let-system | ||
| 40 | 41 | ||
| 41 | gexp-input | 42 | gexp-input |
| 42 | gexp-input? | 43 | gexp-input? |
| @@ -195,7 +196,9 @@ returns its output file name of OBJ's OUTPUT." | |||
| 195 | ((? derivation? drv) | 196 | ((? derivation? drv) |
| 196 | (derivation->output-path drv output)) | 197 | (derivation->output-path drv output)) |
| 197 | ((? string? file) | 198 | ((? string? file) |
| 198 | file))) | 199 | file) |
| 200 | ((? self-quoting? obj) | ||
| 201 | obj))) | ||
| 199 | 202 | ||
| 200 | (define (register-compiler! compiler) | 203 | (define (register-compiler! compiler) |
| 201 | "Register COMPILER as a gexp compiler." | 204 | "Register COMPILER as a gexp compiler." |
| @@ -329,6 +332,52 @@ The expander specifies how an object is converted to its sexp representation." | |||
| 329 | 332 | ||
| 330 | 333 | ||
| 331 | ;;; | 334 | ;;; |
| 335 | ;;; System dependencies. | ||
| 336 | ;;; | ||
| 337 | |||
| 338 | ;; Binding form for the current system and cross-compilation target. | ||
| 339 | (define-record-type <system-binding> | ||
| 340 | (system-binding proc) | ||
| 341 | system-binding? | ||
| 342 | (proc system-binding-proc)) | ||
| 343 | |||
| 344 | (define-syntax let-system | ||
| 345 | (syntax-rules () | ||
| 346 | "Introduce a system binding in a gexp. The simplest form is: | ||
| 347 | |||
| 348 | (let-system system | ||
| 349 | (cond ((string=? system \"x86_64-linux\") ...) | ||
| 350 | (else ...))) | ||
| 351 | |||
| 352 | which binds SYSTEM to the currently targeted system. The second form is | ||
| 353 | similar, but it also shows the cross-compilation target: | ||
| 354 | |||
| 355 | (let-system (system target) | ||
| 356 | ...) | ||
| 357 | |||
| 358 | Here TARGET is bound to the cross-compilation triplet or #f." | ||
| 359 | ((_ (system target) exp0 exp ...) | ||
| 360 | (system-binding (lambda (system target) | ||
| 361 | exp0 exp ...))) | ||
| 362 | ((_ system exp0 exp ...) | ||
| 363 | (system-binding (lambda (system target) | ||
| 364 | exp0 exp ...))))) | ||
| 365 | |||
| 366 | (define-gexp-compiler system-binding-compiler <system-binding> | ||
| 367 | compiler => (lambda (binding system target) | ||
| 368 | (match binding | ||
| 369 | (($ <system-binding> proc) | ||
| 370 | (with-monad %store-monad | ||
| 371 | ;; PROC is expected to return a lowerable object. | ||
| 372 | ;; 'lower-object' takes care of residualizing it to a | ||
| 373 | ;; derivation or similar. | ||
| 374 | (return (proc system target)))))) | ||
| 375 | |||
| 376 | ;; Delegate to the expander of the object returned by PROC. | ||
| 377 | expander => #f) | ||
| 378 | |||
| 379 | |||
| 380 | ;;; | ||
| 332 | ;;; File declarations. | 381 | ;;; File declarations. |
| 333 | ;;; | 382 | ;;; |
| 334 | 383 | ||
| @@ -706,6 +755,15 @@ GEXP) is false, meaning that GEXP is a plain Scheme object, return the empty | |||
| 706 | list." | 755 | list." |
| 707 | (gexp-attribute gexp gexp-self-extensions)) | 756 | (gexp-attribute gexp gexp-self-extensions)) |
| 708 | 757 | ||
| 758 | (define (self-quoting? x) | ||
| 759 | (letrec-syntax ((one-of (syntax-rules () | ||
| 760 | ((_) #f) | ||
| 761 | ((_ pred rest ...) | ||
| 762 | (or (pred x) | ||
| 763 | (one-of rest ...)))))) | ||
| 764 | (one-of symbol? string? keyword? pair? null? array? | ||
| 765 | number? boolean? char?))) | ||
| 766 | |||
| 709 | (define* (lower-inputs inputs | 767 | (define* (lower-inputs inputs |
| 710 | #:key system target) | 768 | #:key system target) |
| 711 | "Turn any object from INPUTS into a derivation input for SYSTEM or a store | 769 | "Turn any object from INPUTS into a derivation input for SYSTEM or a store |
| @@ -714,23 +772,32 @@ When TARGET is true, use it as the cross-compilation target triplet." | |||
| 714 | (define (store-item? obj) | 772 | (define (store-item? obj) |
| 715 | (and (string? obj) (store-path? obj))) | 773 | (and (string? obj) (store-path? obj))) |
| 716 | 774 | ||
| 775 | (define filterm | ||
| 776 | (lift1 (cut filter ->bool <>) %store-monad)) | ||
| 777 | |||
| 717 | (with-monad %store-monad | 778 | (with-monad %store-monad |
| 718 | (mapm/accumulate-builds | 779 | (>>= (mapm/accumulate-builds |
| 719 | (match-lambda | 780 | (match-lambda |
| 720 | (((? struct? thing) sub-drv ...) | 781 | (((? struct? thing) sub-drv ...) |
| 721 | (mlet %store-monad ((obj (lower-object | 782 | (mlet %store-monad ((obj (lower-object |
| 722 | thing system #:target target))) | 783 | thing system #:target target))) |
| 723 | (return (match obj | 784 | (return (match obj |
| 724 | ((? derivation? drv) | 785 | ((? derivation? drv) |
| 725 | (let ((outputs (if (null? sub-drv) | 786 | (let ((outputs (if (null? sub-drv) |
| 726 | '("out") | 787 | '("out") |
| 727 | sub-drv))) | 788 | sub-drv))) |
| 728 | (derivation-input drv outputs))) | 789 | (derivation-input drv outputs))) |
| 729 | ((? store-item? item) | 790 | ((? store-item? item) |
| 730 | item))))) | 791 | item) |
| 731 | (((? store-item? item)) | 792 | ((? self-quoting?) |
| 732 | (return item))) | 793 | ;; Some inputs such as <system-binding> can lower to |
| 733 | inputs))) | 794 | ;; a self-quoting object that FILTERM will filter |
| 795 | ;; out. | ||
| 796 | #f))))) | ||
| 797 | (((? store-item? item)) | ||
| 798 | (return item))) | ||
| 799 | inputs) | ||
| 800 | filterm))) | ||
| 734 | 801 | ||
| 735 | (define* (lower-reference-graphs graphs #:key system target) | 802 | (define* (lower-reference-graphs graphs #:key system target) |
| 736 | "Given GRAPHS, a list of (FILE-NAME INPUT ...) lists for use as a | 803 | "Given GRAPHS, a list of (FILE-NAME INPUT ...) lists for use as a |
| @@ -1146,15 +1213,6 @@ references; otherwise, return only non-native references." | |||
| 1146 | (target (%current-target-system))) | 1213 | (target (%current-target-system))) |
| 1147 | "Return (monadically) the sexp corresponding to EXP for the given OUTPUT, | 1214 | "Return (monadically) the sexp corresponding to EXP for the given OUTPUT, |
| 1148 | and in the current monad setting (system type, etc.)" | 1215 | and in the current monad setting (system type, etc.)" |
| 1149 | (define (self-quoting? x) | ||
| 1150 | (letrec-syntax ((one-of (syntax-rules () | ||
| 1151 | ((_) #f) | ||
| 1152 | ((_ pred rest ...) | ||
| 1153 | (or (pred x) | ||
| 1154 | (one-of rest ...)))))) | ||
| 1155 | (one-of symbol? string? keyword? pair? null? array? | ||
| 1156 | number? boolean? char?))) | ||
| 1157 | |||
| 1158 | (define* (reference->sexp ref #:optional native?) | 1216 | (define* (reference->sexp ref #:optional native?) |
| 1159 | (with-monad %store-monad | 1217 | (with-monad %store-monad |
| 1160 | (match ref | 1218 | (match ref |
diff --git a/tests/gexp.scm b/tests/gexp.scm index 6a42d3eb57b..e073a7b8167 100644 --- a/tests/gexp.scm +++ b/tests/gexp.scm | |||
| @@ -321,6 +321,60 @@ | |||
| 321 | (string=? result | 321 | (string=? result |
| 322 | (string-append (derivation->output-path drv) | 322 | (string-append (derivation->output-path drv) |
| 323 | "/bin/touch")))))) | 323 | "/bin/touch")))))) |
| 324 | (test-equal "let-system" | ||
| 325 | (list `(begin ,(%current-system) #t) '(system-binding) '() | ||
| 326 | 'low '() '()) | ||
| 327 | (let* ((exp #~(begin | ||
| 328 | #$(let-system system system) | ||
| 329 | #t)) | ||
| 330 | (low (run-with-store %store (lower-gexp exp)))) | ||
| 331 | (list (lowered-gexp-sexp low) | ||
| 332 | (match (gexp-inputs exp) | ||
| 333 | (((($ (@@ (guix gexp) <system-binding>)) "out")) | ||
| 334 | '(system-binding)) | ||
| 335 | (x x)) | ||
| 336 | (gexp-native-inputs exp) | ||
| 337 | 'low | ||
| 338 | (lowered-gexp-inputs low) | ||
| 339 | (lowered-gexp-sources low)))) | ||
| 340 | |||
| 341 | (test-equal "let-system, target" | ||
| 342 | (list `(list ,(%current-system) #f) | ||
| 343 | `(list ,(%current-system) "aarch64-linux-gnu")) | ||
| 344 | (let ((exp #~(list #$@(let-system (system target) | ||
| 345 | (list system target))))) | ||
| 346 | (list (gexp->sexp* exp) | ||
| 347 | (gexp->sexp* exp "aarch64-linux-gnu")))) | ||
| 348 | |||
| 349 | (test-equal "let-system, ungexp-native, target" | ||
| 350 | `(here it is: ,(%current-system) #f) | ||
| 351 | (let ((exp #~(here it is: #+@(let-system (system target) | ||
| 352 | (list system target))))) | ||
| 353 | (gexp->sexp* exp "aarch64-linux-gnu"))) | ||
| 354 | |||
| 355 | (test-equal "let-system, nested" | ||
| 356 | (list `(system* ,(string-append "qemu-system-" (%current-system)) | ||
| 357 | "-m" "256") | ||
| 358 | '() | ||
| 359 | '(system-binding)) | ||
| 360 | (let ((exp #~(system* | ||
| 361 | #+(let-system (system target) | ||
| 362 | (file-append (@@ (gnu packages virtualization) | ||
| 363 | qemu) | ||
| 364 | "/bin/qemu-system-" | ||
| 365 | system)) | ||
| 366 | "-m" "256"))) | ||
| 367 | (list (match (gexp->sexp* exp) | ||
| 368 | (('system* command rest ...) | ||
| 369 | `(system* ,(and (string-prefix? (%store-prefix) command) | ||
| 370 | (basename command)) | ||
| 371 | ,@rest)) | ||
| 372 | (x x)) | ||
| 373 | (gexp-inputs exp) | ||
| 374 | (match (gexp-native-inputs exp) | ||
| 375 | (((($ (@@ (guix gexp) <system-binding>)) "out")) | ||
| 376 | '(system-binding)) | ||
| 377 | (x x))))) | ||
| 324 | 378 | ||
| 325 | (test-assert "ungexp + ungexp-native" | 379 | (test-assert "ungexp + ungexp-native" |
| 326 | (let* ((exp (gexp (list (ungexp-native %bootstrap-guile) | 380 | (let* ((exp (gexp (list (ungexp-native %bootstrap-guile) |
