gnu: Add armips.
[jackhill/guix/guix.git] / guix / gexp.scm
index 8a59599..67b6121 100644 (file)
@@ -37,6 +37,7 @@
             gexp?
             with-imported-modules
             with-extensions
+            let-system
 
             gexp-input
             gexp-input?
@@ -50,6 +51,7 @@
             local-file-absolute-file-name
             local-file-name
             local-file-recursive?
+            local-file-select?
 
             plain-file
             plain-file?
@@ -82,6 +84,9 @@
             raw-derivation-file
             raw-derivation-file?
 
+            with-parameters
+            parameterized?
+
             load-path-expression
             gexp-modules
 
@@ -192,7 +197,9 @@ returns its output file name of OBJ's OUTPUT."
     ((? derivation? drv)
      (derivation->output-path drv output))
     ((? string? file)
-     file)))
+     file)
+    ((? self-quoting? obj)
+     obj)))
 
 (define (register-compiler! compiler)
   "Register COMPILER as a gexp compiler."
@@ -223,32 +230,64 @@ procedure to expand it; otherwise return #f."
 corresponding to OBJ for SYSTEM, cross-compiling for TARGET if TARGET is true.
 OBJ must be an object that has an associated gexp compiler, such as a
 <package>."
-  (match (lookup-compiler obj)
-    (#f
-     (raise (condition (&gexp-input-error (input obj)))))
-    (lower
-     ;; Cache in STORE the result of lowering OBJ.
-     (mlet %store-monad ((target (if (eq? target 'current)
-                                     (current-target-system)
-                                     (return target)))
-                         (graft? (grafting?)))
-       (mcached (let ((lower (lookup-compiler obj)))
-                  (lower obj system target))
-                obj
-                system target graft?)))))
+  (mlet %store-monad ((target (if (eq? target 'current)
+                                  (current-target-system)
+                                  (return target)))
+                      (graft? (grafting?)))
+    (let loop ((obj obj))
+      (match (lookup-compiler obj)
+        (#f
+         (raise (condition (&gexp-input-error (input obj)))))
+        (lower
+         ;; Cache in STORE the result of lowering OBJ.
+         (mcached (mlet %store-monad ((lowered (lower obj system target)))
+                    (if (and (struct? lowered)
+                             (not (derivation? lowered)))
+                        (loop lowered)
+                        (return lowered)))
+                  obj
+                  system target graft?))))))
+
+(define* (lower+expand-object obj
+                              #:optional (system (%current-system))
+                              #:key target (output "out"))
+  "Return as a value in %STORE-MONAD the output of object OBJ expands to for
+SYSTEM and TARGET.  Object such as <package>, <file-append>, or <plain-file>
+expand to file names, but it's possible to expand to a plain data type."
+  (let loop ((obj obj)
+             (expand (and (struct? obj) (lookup-expander obj))))
+    (match (lookup-compiler obj)
+      (#f
+       (raise (condition (&gexp-input-error (input obj)))))
+      (lower
+       (mlet* %store-monad ((graft?  (grafting?))
+                            (lowered (mcached (lower obj system target)
+                                              obj
+                                              system target graft?)))
+         ;; LOWER might return something that needs to be further
+         ;; lowered.
+         (if (struct? lowered)
+             ;; If we lack an expander, delegate to that of LOWERED.
+             (if (not expand)
+                 (loop lowered (lookup-expander lowered))
+                 (return (expand obj lowered output)))
+             (if (not expand)                     ;self-quoting
+                 (return lowered)
+                 (return (expand obj lowered output)))))))))
 
 (define-syntax define-gexp-compiler
   (syntax-rules (=> compiler expander)
     "Define NAME as a compiler for objects matching PREDICATE encountered in
 gexps.
 
-In the simplest form of the macro, BODY must return a derivation for PARAM, an
-object that matches PREDICATE, for SYSTEM and TARGET (the latter of which is
-#f except when cross-compiling.)
+In the simplest form of the macro, BODY must return (1) a derivation for
+a record of the specified type, for SYSTEM and TARGET (the latter of which is
+#f except when cross-compiling), (2) another record that can itself be
+compiled down to a derivation, or (3) an object of a primitive data type.
 
 The more elaborate form allows you to specify an expander:
 
-  (define-gexp-compiler something something?
+  (define-gexp-compiler something-compiler <something>
     compiler => (lambda (param system target) ...)
     expander => (lambda (param drv output) ...))
 
@@ -295,6 +334,52 @@ The expander specifies how an object is converted to its sexp representation."
                     lowered)))
 
 \f
+;;;
+;;; System dependencies.
+;;;
+
+;; Binding form for the current system and cross-compilation target.
+(define-record-type <system-binding>
+  (system-binding proc)
+  system-binding?
+  (proc system-binding-proc))
+
+(define-syntax let-system
+  (syntax-rules ()
+    "Introduce a system binding in a gexp.  The simplest form is:
+
+  (let-system system
+    (cond ((string=? system \"x86_64-linux\") ...)
+          (else ...)))
+
+which binds SYSTEM to the currently targeted system.  The second form is
+similar, but it also shows the cross-compilation target:
+
+  (let-system (system target)
+    ...)
+
+Here TARGET is bound to the cross-compilation triplet or #f."
+    ((_ (system target) exp0 exp ...)
+     (system-binding (lambda (system target)
+                       exp0 exp ...)))
+    ((_ system exp0 exp ...)
+     (system-binding (lambda (system target)
+                       exp0 exp ...)))))
+
+(define-gexp-compiler system-binding-compiler <system-binding>
+  compiler => (lambda (binding system target)
+                (match binding
+                  (($ <system-binding> proc)
+                   (with-monad %store-monad
+                     ;; PROC is expected to return a lowerable object.
+                     ;; 'lower-object' takes care of residualizing it to a
+                     ;; derivation or similar.
+                     (return (proc system target))))))
+
+  ;; Delegate to the expander of the object returned by PROC.
+  expander => #f)
+
+\f
 ;;;
 ;;; File declarations.
 ;;;
@@ -469,24 +554,26 @@ This is the declarative counterpart of 'gexp->script'."
                    #:target target))))
 
 (define-record-type <scheme-file>
-  (%scheme-file name gexp splice?)
+  (%scheme-file name gexp splice? load-path?)
   scheme-file?
   (name       scheme-file-name)                  ;string
   (gexp       scheme-file-gexp)                  ;gexp
-  (splice?    scheme-file-splice?))              ;Boolean
+  (splice?    scheme-file-splice?)               ;Boolean
+  (load-path? scheme-file-set-load-path?))       ;Boolean
 
-(define* (scheme-file name gexp #:key splice?)
+(define* (scheme-file name gexp #:key splice? (set-load-path? #t))
   "Return an object representing the Scheme file NAME that contains GEXP.
 
 This is the declarative counterpart of 'gexp->file'."
-  (%scheme-file name gexp splice?))
+  (%scheme-file name gexp splice? set-load-path?))
 
 (define-gexp-compiler (scheme-file-compiler (file <scheme-file>)
                                             system target)
   ;; Compile FILE by returning a derivation that builds the file.
   (match file
-    (($ <scheme-file> name gexp splice?)
+    (($ <scheme-file> name gexp splice? set-load-path?)
      (gexp->file name gexp
+                 #:set-load-path? set-load-path?
                  #:splice? splice?
                  #:system system
                  #:target target))))
@@ -523,6 +610,62 @@ SUFFIX."
                           (base   (expand base lowered output)))
                      (string-append base (string-concatenate suffix)))))))
 
+;; Representation of SRFI-39 parameter settings in the dynamic scope of an
+;; object lowering.
+(define-record-type <parameterized>
+  (parameterized bindings thunk)
+  parameterized?
+  (bindings parameterized-bindings)             ;list of parameter/value pairs
+  (thunk    parameterized-thunk))               ;thunk
+
+(define-syntax-rule (with-parameters ((param value) ...) body ...)
+  "Bind each PARAM to the corresponding VALUE for the extent during which BODY
+is lowered.  Consider this example:
+
+  (with-parameters ((%current-system \"x86_64-linux\"))
+    coreutils)
+
+It returns a <parameterized> object that ensures %CURRENT-SYSTEM is set to
+x86_64-linux when COREUTILS is lowered."
+  (parameterized (list (list param (lambda () value)) ...)
+                 (lambda ()
+                   body ...)))
+
+(define-gexp-compiler compile-parameterized <parameterized>
+  compiler =>
+  (lambda (parameterized system target)
+    (match (parameterized-bindings parameterized)
+      (((parameters values) ...)
+       (let ((fluids (map parameter-fluid parameters))
+             (thunk  (parameterized-thunk parameterized)))
+         ;; Install the PARAMETERS for the dynamic extent of THUNK.
+         (with-fluids* fluids
+           (map (lambda (thunk) (thunk)) values)
+           (lambda ()
+             ;; Special-case '%current-system' and '%current-target-system' to
+             ;; make sure we get the desired effect.
+             (let ((system (if (memq %current-system parameters)
+                               (%current-system)
+                               system))
+                   (target (if (memq %current-target-system parameters)
+                               (%current-target-system)
+                               target)))
+               (lower-object (thunk) system #:target target))))))))
+
+  expander => (lambda (parameterized lowered output)
+                (match (parameterized-bindings parameterized)
+                  (((parameters values) ...)
+                   (let ((fluids (map parameter-fluid parameters))
+                         (thunk  (parameterized-thunk parameterized)))
+                     ;; Install the PARAMETERS for the dynamic extent of THUNK.
+                     (with-fluids* fluids
+                       (map (lambda (thunk) (thunk)) values)
+                       (lambda ()
+                         ;; Delegate to the expander of the wrapped object.
+                         (let* ((base   (thunk))
+                                (expand (lookup-expander base)))
+                           (expand base lowered output)))))))))
+
 \f
 ;;;
 ;;; Inputs & outputs.
@@ -615,6 +758,15 @@ GEXP) is false, meaning that GEXP is a plain Scheme object, return the empty
 list."
   (gexp-attribute gexp gexp-self-extensions))
 
+(define (self-quoting? x)
+  (letrec-syntax ((one-of (syntax-rules ()
+                            ((_) #f)
+                            ((_ pred rest ...)
+                             (or (pred x)
+                                 (one-of rest ...))))))
+    (one-of symbol? string? keyword? pair? null? array?
+            number? boolean? char?)))
+
 (define* (lower-inputs inputs
                        #:key system target)
   "Turn any object from INPUTS into a derivation input for SYSTEM or a store
@@ -623,8 +775,11 @@ When TARGET is true, use it as the cross-compilation target triplet."
   (define (store-item? obj)
     (and (string? obj) (store-path? obj)))
 
+  (define filterm
+    (lift1 (cut filter ->bool <>) %store-monad))
+
   (with-monad %store-monad
-    (mapm %store-monad
+    (>>= (mapm/accumulate-builds
           (match-lambda
             (((? struct? thing) sub-drv ...)
              (mlet %store-monad ((obj (lower-object
@@ -636,10 +791,16 @@ When TARGET is true, use it as the cross-compilation target triplet."
                                              sub-drv)))
                             (derivation-input drv outputs)))
                          ((? store-item? item)
-                          item)))))
+                          item)
+                         ((? self-quoting?)
+                          ;; Some inputs such as <system-binding> can lower to
+                          ;; a self-quoting object that FILTERM will filter
+                          ;; out.
+                          #f)))))
             (((? store-item? item))
              (return item)))
-          inputs)))
+          inputs)
+         filterm)))
 
 (define* (lower-reference-graphs graphs #:key system target)
   "Given GRAPHS, a list of (FILE-NAME INPUT ...) lists for use as a
@@ -671,7 +832,7 @@ names and file names suitable for the #:allowed-references argument to
                                                #:target target)))
           (return (derivation->output-path drv))))))
 
-    (mapm %store-monad lower lst)))
+    (mapm/accumulate-builds lower lst)))
 
 (define default-guile-derivation
   ;; Here we break the abstraction by talking to the higher-level layer.
@@ -726,7 +887,7 @@ second element is the derivation to compile them."
                      (target 'current)
                      (graft? (%graft?))
                      (guile-for-build (%guile-for-build))
-                     (effective-version "2.2")
+                     (effective-version "3.0")
 
                      deprecation-warnings)
   "*Note: This API is subject to change; use at your own risk!*
@@ -827,7 +988,7 @@ derivations--e.g., code evaluated for its side effects."
                            (modules '())
                            (module-path %load-path)
                            (guile-for-build (%guile-for-build))
-                           (effective-version "2.2")
+                           (effective-version "3.0")
                            (graft? (%graft?))
                            references-graphs
                            allowed-references disallowed-references
@@ -1055,15 +1216,6 @@ references; otherwise, return only non-native references."
                      (target (%current-target-system)))
   "Return (monadically) the sexp corresponding to EXP for the given OUTPUT,
 and in the current monad setting (system type, etc.)"
-  (define (self-quoting? x)
-    (letrec-syntax ((one-of (syntax-rules ()
-                              ((_) #f)
-                              ((_ pred rest ...)
-                               (or (pred x)
-                                   (one-of rest ...))))))
-      (one-of symbol? string? keyword? pair? null? array?
-              number? boolean? char?)))
-
   (define* (reference->sexp ref #:optional native?)
     (with-monad %store-monad
       (match ref
@@ -1087,12 +1239,10 @@ and in the current monad setting (system type, etc.)"
                   (or n? native?)))
                refs))
         (($ <gexp-input> (? struct? thing) output n?)
-         (let ((target (if (or n? native?) #f target))
-               (expand (lookup-expander thing)))
-           (mlet %store-monad ((obj (lower-object thing system
-                                                  #:target target)))
-             ;; OBJ must be either a derivation or a store file name.
-             (return (expand thing obj output)))))
+         (let ((target (if (or n? native?) #f target)))
+           (lower+expand-object thing system
+                                #:target target
+                                #:output output)))
         (($ <gexp-input> (? self-quoting? x))
          (return x))
         (($ <gexp-input> x)
@@ -1293,6 +1443,7 @@ to the source files instead of copying them."
                       #:system system
                       #:guile-for-build guile
                       #:local-build? #t
+                      #:substitutable? #f
 
                       ;; Avoid deprecation warnings about the use of the _IO*
                       ;; constants in (guix build utils).
@@ -1494,12 +1645,12 @@ TARGET, a GNU triplet."
 ;;;
 
 (define (default-guile)
-  ;; Lazily resolve 'guile-2.2' (not 'guile-final' because this is for
+  ;; Lazily resolve 'guile-3.0' (not 'guile-final' because this is for
   ;; programs returned by 'program-file' and we don't want to keep references
   ;; to several Guile packages).  This module must not refer to (gnu …)
   ;; modules directly, to avoid circular dependencies, hence this hack.
   (module-ref (resolve-interface '(gnu packages guile))
-              'guile-2.2))
+              'guile-3.0))
 
 (define* (load-path-expression modules #:optional (path %load-path)
                                #:key (extensions '()) system target)