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.
This commit is contained in:
Ludovic Courtès 2017-01-07 12:31:02 +01:00
parent 2c13d74181
commit ea7b5a8f3d
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
3 changed files with 174 additions and 37 deletions

View file

@ -34,6 +34,8 @@
gexp-input gexp-input
gexp-input? gexp-input?
gexp-grafts
local-file local-file
local-file? local-file?
local-file-file local-file-file
@ -131,11 +133,12 @@
;; Compiler for a type of objects that may be introduced in a gexp. ;; Compiler for a type of objects that may be introduced in a gexp.
(define-record-type <gexp-compiler> (define-record-type <gexp-compiler>
(gexp-compiler type lower expand) (gexp-compiler type lower expand grafts)
gexp-compiler? gexp-compiler?
(type gexp-compiler-type) ;record type descriptor (type gexp-compiler-type) ;record type descriptor
(lower gexp-compiler-lower) (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 (define %gexp-compilers
;; 'eq?' mapping of record type descriptor to <gexp-compiler>. ;; 'eq?' mapping of record type descriptor to <gexp-compiler>.
@ -150,6 +153,18 @@ returns its output file name of OBJ's OUTPUT."
((? string? file) ((? string? file)
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) (define (register-compiler! compiler)
"Register COMPILER as a gexp compiler." "Register COMPILER as a gexp compiler."
(hashq-set! %gexp-compilers (hashq-set! %gexp-compilers
@ -167,6 +182,12 @@ procedure to expand it; otherwise return #f."
(and=> (hashq-ref %gexp-compilers (struct-vtable object)) (and=> (hashq-ref %gexp-compilers (struct-vtable object))
gexp-compiler-expand)) 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 (define* (lower-object obj
#:optional (system (%current-system)) #:optional (system (%current-system))
#:key target) #:key target)
@ -178,7 +199,7 @@ OBJ must be an object that has an associated gexp compiler, such as a
(lower obj system target))) (lower obj system target)))
(define-syntax define-gexp-compiler (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 "Define NAME as a compiler for objects matching PREDICATE encountered in
gexps. 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: 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) ...) 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 ...) ((_ (name (param record-type) system target) body ...)
(define-gexp-compiler name record-type (define-gexp-compiler name record-type
compiler => (lambda (param system target) body ...) compiler => (lambda (param system target) body ...)
expander => default-expander)) applicable-grafts => default-applicable-grafts))
((_ name record-type ((_ name record-type
compiler => compile compiler => compile
expander => expand) applicable-grafts => grafts)
(define-gexp-compiler name record-type
compiler => compile
expander => default-expander
applicable-grafts => grafts))
((_ name record-type
compiler => compile
expander => expand
applicable-grafts => grafts)
(begin (begin
(define name (define name
(gexp-compiler record-type compile expand)) (gexp-compiler record-type compile expand grafts))
(register-compiler! name))))) (register-compiler! name)))))
(define-gexp-compiler (derivation-compiler (drv <derivation>) system target) (define-gexp-compiler (derivation-compiler (drv <derivation>) system target)
@ -320,13 +352,14 @@ to 'gexp->derivation'.
This is the declarative counterpart of 'gexp->derivation'." This is the declarative counterpart of 'gexp->derivation'."
(%computed-file name gexp options)) (%computed-file name gexp options))
(define-gexp-compiler (computed-file-compiler (file <computed-file>) (define-gexp-compiler computed-file-compiler <computed-file>
system target) compiler => (lambda (file system target)
;; Compile FILE by returning a derivation whose build expression is its ;; Compile FILE by returning a derivation whose build
;; gexp. ;; expression is its gexp.
(match file (match file
(($ <computed-file> name gexp options) (($ <computed-file> name gexp options)
(apply gexp->derivation name gexp options)))) (apply gexp->derivation name gexp options))))
applicable-grafts => (propagated-applicable-grafts computed-file-gexp))
(define-record-type <program-file> (define-record-type <program-file>
(%program-file name gexp guile) (%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'." This is the declarative counterpart of 'gexp->script'."
(%program-file name gexp guile)) (%program-file name gexp guile))
(define-gexp-compiler (program-file-compiler (file <program-file>) (define-gexp-compiler program-file-compiler <program-file>
system target) compiler => (lambda (file system target)
;; Compile FILE by returning a derivation that builds the script. ;; Compile FILE by returning a derivation that builds the
(match file ;; script.
(($ <program-file> name gexp guile) (match file
(gexp->script name gexp (($ <program-file> name gexp guile)
#:guile (or guile (default-guile)))))) (gexp->script name gexp
#:guile (or guile (default-guile))))))
applicable-grafts => (propagated-applicable-grafts program-file-gexp))
(define-record-type <scheme-file> (define-record-type <scheme-file>
(%scheme-file name gexp) (%scheme-file name gexp)
@ -362,12 +397,14 @@ This is the declarative counterpart of 'gexp->script'."
This is the declarative counterpart of 'gexp->file'." This is the declarative counterpart of 'gexp->file'."
(%scheme-file name gexp)) (%scheme-file name gexp))
(define-gexp-compiler (scheme-file-compiler (file <scheme-file>) (define-gexp-compiler scheme-file-compiler <scheme-file>
system target) compiler => (lambda (file system target)
;; Compile FILE by returning a derivation that builds the file. ;; Compile FILE by returning a derivation that builds the
(match file ;; file.
(($ <scheme-file> name gexp) (match file
(gexp->file name gexp)))) (($ <scheme-file> name gexp)
(gexp->file name gexp))))
applicable-grafts => (propagated-applicable-grafts scheme-file-gexp))
;; Appending SUFFIX to BASE's output file name. ;; Appending SUFFIX to BASE's output file name.
(define-record-type <file-append> (define-record-type <file-append>
@ -391,7 +428,12 @@ SUFFIX."
(($ <file-append> base suffix) (($ <file-append> base suffix)
(let* ((expand (lookup-expander base)) (let* ((expand (lookup-expander base))
(base (expand base lowered output))) (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) (lambda (system)
((force proc) 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 (define* (gexp->derivation name exp
#:key #:key
system (target 'current) system (target 'current)

View file

@ -1194,12 +1194,39 @@ cross-compilation target triplet."
(define package->cross-derivation (define package->cross-derivation
(store-lift package-cross-derivation)) (store-lift package-cross-derivation))
(define-gexp-compiler (package-compiler (package <package>) system target) (define replacement-graft*
;; Compile PACKAGE to a derivation for SYSTEM, optionally cross-compiled for (let ((native (store-lift replacement-graft))
;; TARGET. This is used when referring to a package from within a gexp. (cross (store-lift replacement-cross-graft)))
(if target (lambda (package system target)
(package->cross-derivation package target system) "Return, as a monadic value, the replacement graft for PACKAGE, assuming
(package->derivation package system))) 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 (define* (origin->derivation origin
#:optional (system (%current-system))) #:optional (system (%current-system)))

View file

@ -453,6 +453,39 @@
(string=? (derivation->output-path drv0) (string=? (derivation->output-path drv0)
(derivation->output-path drv1*)))))) (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" (test-assertm "gexp->derivation, composed gexps"
(mlet* %store-monad ((exp0 -> (gexp (begin (mlet* %store-monad ((exp0 -> (gexp (begin
(mkdir (ungexp output)) (mkdir (ungexp output))