summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2017-01-07 12:31:02 +0100
committerLudovic Courtès <ludo@gnu.org>2017-01-11 10:11:46 +0100
commitea7b5a8f3d3f5d66ba9c45fb0bc76d25b6ba916f (patch)
tree48fff81fcbbcb8bd2005e9f958d6af5b8b4e219d
parent2c13d74181123fac02189807ecfb36b36cdad024 (diff)
downloadguix-ea7b5a8f3d3f5d66ba9c45fb0bc76d25b6ba916f.tar.gz
gexp: Compilers can now provide a procedure returning applicable grafts.
* guix/gexp.scm (<gexp-compiler>)[grafts]: New field.
(default-applicable-grafts, lookup-graft-procedure)
(propagated-applicable-grafts): New procedures.
(define-gexp-compiler): Support 'applicable-grafts' form.
(computed-file-compiler, program-file-compiler)
(scheme-file-compiler, file-append-compiler): Add 'applicable-grafts'
form.
(gexp-grafts): New procedure.
* guix/packages.scm (replacement-graft*): New procedure.
(package-compiler): Add 'applicable-grafts' form.
* tests/gexp.scm ("gexp-grafts"): New test.
-rw-r--r--guix/gexp.scm139
-rw-r--r--guix/packages.scm39
-rw-r--r--tests/gexp.scm33
3 files changed, 174 insertions, 37 deletions
diff --git a/guix/gexp.scm b/guix/gexp.scm
index 1f7fbef0a0..574d51e10d 100644
--- a/guix/gexp.scm
+++ b/guix/gexp.scm
@@ -34,6 +34,8 @@
             gexp-input
             gexp-input?
 
+            gexp-grafts
+
             local-file
             local-file?
             local-file-file
@@ -131,11 +133,12 @@
 
 ;; Compiler for a type of objects that may be introduced in a gexp.
 (define-record-type <gexp-compiler>
-  (gexp-compiler type lower expand)
+  (gexp-compiler type lower expand grafts)
   gexp-compiler?
-  (type       gexp-compiler-type)                 ;record type descriptor
+  (type       gexp-compiler-type)               ;record type descriptor
   (lower      gexp-compiler-lower)
-  (expand     gexp-compiler-expand))              ;#f | DRV -> sexp
+  (expand     gexp-compiler-expand)             ;DRV -> sexp
+  (grafts     gexp-compiler-applicable-grafts)) ;thing system target -> grafts
 
 (define %gexp-compilers
   ;; 'eq?' mapping of record type descriptor to <gexp-compiler>.
@@ -150,6 +153,18 @@ returns its output file name of OBJ's OUTPUT."
     ((? string? file)
      file)))
 
+(define (default-applicable-grafts thing system target)
+  "This is the default procedure returning applicable grafts for THING.  It
+returns the empty list---i.e., no grafts need to be applied."
+  (with-monad %store-monad
+    (return '())))
+
+(define (propagated-applicable-grafts field)
+  "Return a monadic procedure that propagates applicable grafts of the gexp
+returned by applying FIELD to the object."
+  (lambda (thing system target)
+    (gexp-grafts (field thing) #:target target)))
+
 (define (register-compiler! compiler)
   "Register COMPILER as a gexp compiler."
   (hashq-set! %gexp-compilers
@@ -167,6 +182,12 @@ procedure to expand it; otherwise return #f."
   (and=> (hashq-ref %gexp-compilers (struct-vtable object))
          gexp-compiler-expand))
 
+(define (lookup-graft-procedure object)
+  "Search for a procedure returning the list of applicable grafts for OBJECT.
+Upon success, return the three argument procedure; otherwise return #f."
+  (and=> (hashq-ref %gexp-compilers (struct-vtable object))
+         gexp-compiler-applicable-grafts))
+
 (define* (lower-object obj
                        #:optional (system (%current-system))
                        #:key target)
@@ -178,7 +199,7 @@ OBJ must be an object that has an associated gexp compiler, such as a
     (lower obj system target)))
 
 (define-syntax define-gexp-compiler
-  (syntax-rules (=> compiler expander)
+  (syntax-rules (=> compiler expander applicable-grafts)
     "Define NAME as a compiler for objects matching PREDICATE encountered in
 gexps.
 
@@ -188,21 +209,32 @@ object that matches PREDICATE, for SYSTEM and TARGET (the latter of which is
 
 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) ...))
+    expander => (lambda (param drv output) ...)
+    applicable-grafts => (lambda (param system target) ...))
 
-The expander specifies how an object is converted to its sexp representation."
+The expander specifies how an object is converted to its sexp representation.
+The 'applicable-grafts' monadic procedure returns a list of grafts that can be
+applied to the object."
     ((_ (name (param record-type) system target) body ...)
      (define-gexp-compiler name record-type
        compiler => (lambda (param system target) body ...)
-       expander => default-expander))
+       applicable-grafts => default-applicable-grafts))
+    ((_ name record-type
+        compiler => compile
+        applicable-grafts => grafts)
+     (define-gexp-compiler name record-type
+       compiler => compile
+       expander => default-expander
+       applicable-grafts => grafts))
     ((_ name record-type
         compiler => compile
-        expander => expand)
+        expander => expand
+        applicable-grafts => grafts)
      (begin
        (define name
-         (gexp-compiler record-type compile expand))
+         (gexp-compiler record-type compile expand grafts))
        (register-compiler! name)))))
 
 (define-gexp-compiler (derivation-compiler (drv <derivation>) system target)
@@ -320,13 +352,14 @@ to 'gexp->derivation'.
 This is the declarative counterpart of 'gexp->derivation'."
   (%computed-file name gexp options))
 
-(define-gexp-compiler (computed-file-compiler (file <computed-file>)
-                                              system target)
-  ;; Compile FILE by returning a derivation whose build expression is its
-  ;; gexp.
-  (match file
-    (($ <computed-file> name gexp options)
-     (apply gexp->derivation name gexp options))))
+(define-gexp-compiler computed-file-compiler <computed-file>
+  compiler => (lambda (file system target)
+                ;; Compile FILE by returning a derivation whose build
+                ;; expression is its gexp.
+                (match file
+                  (($ <computed-file> name gexp options)
+                   (apply gexp->derivation name gexp options))))
+  applicable-grafts => (propagated-applicable-grafts computed-file-gexp))
 
 (define-record-type <program-file>
   (%program-file name gexp guile)
@@ -342,13 +375,15 @@ GEXP.  GUILE is the Guile package used to execute that script.
 This is the declarative counterpart of 'gexp->script'."
   (%program-file name gexp guile))
 
-(define-gexp-compiler (program-file-compiler (file <program-file>)
-                                             system target)
-  ;; Compile FILE by returning a derivation that builds the script.
-  (match file
-    (($ <program-file> name gexp guile)
-     (gexp->script name gexp
-                   #:guile (or guile (default-guile))))))
+(define-gexp-compiler program-file-compiler <program-file>
+  compiler => (lambda (file system target)
+                ;; Compile FILE by returning a derivation that builds the
+                ;; script.
+                (match file
+                  (($ <program-file> name gexp guile)
+                   (gexp->script name gexp
+                                 #:guile (or guile (default-guile))))))
+  applicable-grafts => (propagated-applicable-grafts program-file-gexp))
 
 (define-record-type <scheme-file>
   (%scheme-file name gexp)
@@ -362,12 +397,14 @@ This is the declarative counterpart of 'gexp->script'."
 This is the declarative counterpart of 'gexp->file'."
   (%scheme-file name gexp))
 
-(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)
-     (gexp->file name gexp))))
+(define-gexp-compiler scheme-file-compiler <scheme-file>
+  compiler => (lambda (file system target)
+                ;; Compile FILE by returning a derivation that builds the
+                ;; file.
+                (match file
+                  (($ <scheme-file> name gexp)
+                   (gexp->file name gexp))))
+  applicable-grafts => (propagated-applicable-grafts scheme-file-gexp))
 
 ;; Appending SUFFIX to BASE's output file name.
 (define-record-type <file-append>
@@ -391,7 +428,12 @@ SUFFIX."
                   (($ <file-append> base suffix)
                    (let* ((expand (lookup-expander base))
                           (base   (expand base lowered output)))
-                     (string-append base (string-concatenate suffix)))))))
+                     (string-append base (string-concatenate suffix))))))
+  applicable-grafts => (lambda (obj system target)
+                         (match obj
+                           (($ <file-append> base _)
+                            (let ((proc (lookup-graft-procedure base)))
+                              (proc base system target))))))
 
 
 ;;;
@@ -510,6 +552,41 @@ names and file names suitable for the #:allowed-references argument to
     (lambda (system)
       ((force proc) system))))
 
+(define* (gexp-grafts exp
+                      #:optional (system (%current-system))
+                      #:key target)
+  "Return the list of grafts applicable to a derivation built by EXP, a gexp,
+for SYSTEM and TARGET (the latter is #f when building natively).
+
+This works by querying the list applicable grafts of each object EXP
+references---e.g., packages."
+  (with-monad %store-monad
+    (define gexp-input-grafts
+      (match-lambda
+        (($ <gexp-input> (? gexp? exp) _ #t)
+         (gexp-grafts exp system #:target #f))
+        (($ <gexp-input> (? gexp? exp) _ #f)
+         (gexp-grafts exp system #:target target))
+        (($ <gexp-input> (? struct? obj) _ #t)
+         (let ((applicable-grafts (lookup-graft-procedure obj)))
+          (applicable-grafts obj system #f)))
+        (($ <gexp-input> (? struct? obj) _ #f)
+         (let ((applicable-grafts (lookup-graft-procedure obj)))
+          (applicable-grafts obj system target)))
+        (($ <gexp-input> (lst ...) _ native?)
+         (foldm %store-monad
+                (lambda (input grafts)
+                  (mlet %store-monad ((g (gexp-input-grafts input)))
+                    (return (append g grafts))))
+                '()
+                lst))
+        (_                            ;another <gexp-input> or a <gexp-output>
+         (return '()))))
+
+    (>>= (mapm %store-monad gexp-input-grafts (gexp-references exp))
+         (lift1 (compose delete-duplicates concatenate)
+                %store-monad))))
+
 (define* (gexp->derivation name exp
                            #:key
                            system (target 'current)
diff --git a/guix/packages.scm b/guix/packages.scm
index efa1623bc5..57ae7f9584 100644
--- a/guix/packages.scm
+++ b/guix/packages.scm
@@ -1194,12 +1194,39 @@ cross-compilation target triplet."
 (define package->cross-derivation
   (store-lift package-cross-derivation))
 
-(define-gexp-compiler (package-compiler (package <package>) system target)
-  ;; Compile PACKAGE to a derivation for SYSTEM, optionally cross-compiled for
-  ;; TARGET.  This is used when referring to a package from within a gexp.
-  (if target
-      (package->cross-derivation package target system)
-      (package->derivation package system)))
+(define replacement-graft*
+  (let ((native (store-lift replacement-graft))
+        (cross  (store-lift replacement-cross-graft)))
+    (lambda (package system target)
+      "Return, as a monadic value, the replacement graft for PACKAGE, assuming
+it has a replacement."
+      (if target
+          (cross package system target)
+          (native package system)))))
+
+(define-gexp-compiler package-compiler <package>
+  compiler
+  => (lambda (package system target)
+       ;; Compile PACKAGE to a derivation for SYSTEM, optionally
+       ;; cross-compiled for TARGET.  This is used when referring to a package
+       ;; from within a gexp.
+       (if target
+           (package->cross-derivation package target system)
+           (package->derivation package system)))
+
+  applicable-grafts
+  => (let ((bag-grafts* (store-lift bag-grafts)))
+       (lambda (package system target)
+         ;; Return the list of grafts that apply to things that reference
+         ;; PACKAGE.
+         (mlet* %store-monad ((bag ->  (package->bag package
+                                                     system target))
+                              (grafts  (bag-grafts* bag)))
+           (if (package-replacement package)
+               (mlet %store-monad ((repl (replacement-graft* package
+                                                             system target)))
+                 (return (cons repl grafts)))
+               (return grafts))))))
 
 (define* (origin->derivation origin
                              #:optional (system (%current-system)))
diff --git a/tests/gexp.scm b/tests/gexp.scm
index baf78837ae..ea4243a3a6 100644
--- a/tests/gexp.scm
+++ b/tests/gexp.scm
@@ -453,6 +453,39 @@
                  (string=? (derivation->output-path drv0)
                            (derivation->output-path drv1*))))))
 
+(test-assertm "gexp-grafts"
+  ;; Make sure 'gexp-grafts' returns the graft to replace P1 by R.
+  (let* ((p0    (dummy-package "dummy"
+                               (arguments
+                                '(#:implicit-inputs? #f))))
+         (r     (package (inherit p0) (name "DuMMY")))
+         (p1    (package (inherit p0) (replacement r)))
+         (exp0  (gexp (frob (ungexp p0) (ungexp output))))
+         (exp1  (gexp (frob (ungexp p1) (ungexp output))))
+         (exp2  (gexp (frob (ungexp (list (gexp-input p1))))))
+         (exp3  (gexp (stuff (ungexp exp1))))
+         (exp4  (gexp (frob (ungexp (file-append p1 "/bin/foo")))))
+         (exp5  (gexp (frob (ungexp (computed-file "foo" exp1)))))
+         (exp6  (gexp (frob (ungexp (program-file "foo" exp1)))))
+         (exp7  (gexp (frob (ungexp (scheme-file "foo" exp1))))))
+    (mlet* %store-monad ((grafts0 (gexp-grafts exp0))
+                         (grafts1 (gexp-grafts exp1))
+                         (grafts2 (gexp-grafts exp2))
+                         (grafts3 (gexp-grafts exp3))
+                         (grafts4 (gexp-grafts exp4))
+                         (grafts5 (gexp-grafts exp5))
+                         (grafts6 (gexp-grafts exp6))
+                         (grafts7 (gexp-grafts exp7))
+                         (p0-drv  (package->derivation p0))
+                         (r-drv   (package->derivation r))
+                         (expected -> (graft
+                                        (origin p0-drv)
+                                        (replacement r-drv))))
+      (return (and (null? grafts0)
+                   (equal? grafts1 grafts2 grafts3 grafts4
+                           grafts5 grafts6 grafts7
+                           (list expected)))))))
+
 (test-assertm "gexp->derivation, composed gexps"
   (mlet* %store-monad ((exp0 -> (gexp (begin
                                         (mkdir (ungexp output))