mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2024-12-25 05:48:07 -05:00
monads: Add 'text-file*'.
* guix/monads.scm (text-file*): New procedure. * tests/monads.scm ("text-file*"): New test. * doc/guix.texi (The Store Monad): Change example since the previous one would erroneously fail to retain a reference to Coreutils. Document 'text-file*'.
This commit is contained in:
parent
67995f4bea
commit
45adbd624f
3 changed files with 113 additions and 14 deletions
|
@ -1590,23 +1590,22 @@ in a monad---values that carry this additional context---are called
|
||||||
Consider this ``normal'' procedure:
|
Consider this ``normal'' procedure:
|
||||||
|
|
||||||
@example
|
@example
|
||||||
(define (profile.sh store)
|
(define (sh-symlink store)
|
||||||
;; Return the name of a shell script in the store that
|
;; Return a derivation that symlinks the 'bash' executable.
|
||||||
;; initializes the 'PATH' environment variable.
|
(let* ((drv (package-derivation store bash))
|
||||||
(let* ((drv (package-derivation store coreutils))
|
(out (derivation->output-path drv))
|
||||||
(out (derivation->output-path drv)))
|
(sh (string-append out "/bin/bash")))
|
||||||
(add-text-to-store store "profile.sh"
|
(build-expression->derivation store "sh"
|
||||||
(format #f "export PATH=~a/bin" out))))
|
`(symlink ,sh %output))))
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
Using @code{(guix monads)}, it may be rewritten as a monadic function:
|
Using @code{(guix monads)}, it may be rewritten as a monadic function:
|
||||||
|
|
||||||
@example
|
@example
|
||||||
(define (profile.sh)
|
(define (sh-symlink)
|
||||||
;; Same, but return a monadic value.
|
;; Same, but return a monadic value.
|
||||||
(mlet %store-monad ((bin (package-file coreutils "bin")))
|
(mlet %store-monad ((sh (package-file bash "bin")))
|
||||||
(text-file "profile.sh"
|
(derivation-expression "sh" `(symlink ,sh %output))))
|
||||||
(string-append "export PATH=" bin))))
|
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
There are two things to note in the second version: the @code{store}
|
There are two things to note in the second version: the @code{store}
|
||||||
|
@ -1672,7 +1671,32 @@ open store connection.
|
||||||
|
|
||||||
@deffn {Monadic Procedure} text-file @var{name} @var{text}
|
@deffn {Monadic Procedure} text-file @var{name} @var{text}
|
||||||
Return as a monadic value the absolute file name in the store of the file
|
Return as a monadic value the absolute file name in the store of the file
|
||||||
containing @var{text}.
|
containing @var{text}, a string.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Monadic Procedure} text-file* @var{name} @var{text} @dots{}
|
||||||
|
Return as a monadic value a derivation that builds a text file
|
||||||
|
containing all of @var{text}. @var{text} may list, in addition to
|
||||||
|
strings, packages, derivations, and store file names; the resulting
|
||||||
|
store file holds references to all these.
|
||||||
|
|
||||||
|
This variant should be preferred over @code{text-file} anytime the file
|
||||||
|
to create will reference items from the store. This is typically the
|
||||||
|
case when building a configuration file that embeds store file names,
|
||||||
|
like this:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define (profile.sh)
|
||||||
|
;; Return the name of a shell script in the store that
|
||||||
|
;; initializes the 'PATH' environment variable.
|
||||||
|
(text-file* "profile.sh"
|
||||||
|
"export PATH=" coreutils "/bin:"
|
||||||
|
grep "/bin:" sed "/bin\n"))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
In this example, the resulting @file{/nix/store/@dots{}-profile.sh} file
|
||||||
|
will references @var{coreutils}, @var{grep}, and @var{sed}, thereby
|
||||||
|
preventing them from being garbage-collected during its lifetime.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn {Monadic Procedure} package-file @var{package} [@var{file}] @
|
@deffn {Monadic Procedure} package-file @var{package} [@var{file}] @
|
||||||
|
|
|
@ -23,6 +23,7 @@ (define-module (guix monads)
|
||||||
#:use-module ((system syntax)
|
#:use-module ((system syntax)
|
||||||
#:select (syntax-local-binding))
|
#:select (syntax-local-binding))
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-9)
|
#:use-module (srfi srfi-9)
|
||||||
#:use-module (srfi srfi-26)
|
#:use-module (srfi srfi-26)
|
||||||
#:export (;; Monads.
|
#:export (;; Monads.
|
||||||
|
@ -53,6 +54,7 @@ (define-module (guix monads)
|
||||||
store-lift
|
store-lift
|
||||||
run-with-store
|
run-with-store
|
||||||
text-file
|
text-file
|
||||||
|
text-file*
|
||||||
package-file
|
package-file
|
||||||
package->derivation
|
package->derivation
|
||||||
built-derivations
|
built-derivations
|
||||||
|
@ -305,10 +307,59 @@ (define result
|
||||||
|
|
||||||
(define* (text-file name text)
|
(define* (text-file name text)
|
||||||
"Return as a monadic value the absolute file name in the store of the file
|
"Return as a monadic value the absolute file name in the store of the file
|
||||||
containing TEXT."
|
containing TEXT, a string."
|
||||||
(lambda (store)
|
(lambda (store)
|
||||||
(add-text-to-store store name text '())))
|
(add-text-to-store store name text '())))
|
||||||
|
|
||||||
|
(define* (text-file* name #:rest text)
|
||||||
|
"Return as a monadic value a derivation that builds a text file containing
|
||||||
|
all of TEXT. TEXT may list, in addition to strings, packages, derivations,
|
||||||
|
and store file names; the resulting store file holds references to all these."
|
||||||
|
(define inputs
|
||||||
|
;; Transform packages and derivations from TEXT into a valid input list.
|
||||||
|
(filter-map (match-lambda
|
||||||
|
((? package? p) `("x" ,p))
|
||||||
|
((? derivation? d) `("x" ,d))
|
||||||
|
((x ...) `("x" ,@x))
|
||||||
|
((? string? s)
|
||||||
|
(and (direct-store-path? s) `("x" ,s)))
|
||||||
|
(x x))
|
||||||
|
text))
|
||||||
|
|
||||||
|
(define (computed-text text inputs)
|
||||||
|
;; Using the lowered INPUTS, return TEXT with derivations replaced with
|
||||||
|
;; their output file name.
|
||||||
|
(define (real-string? s)
|
||||||
|
(and (string? s) (not (direct-store-path? s))))
|
||||||
|
|
||||||
|
(let loop ((inputs inputs)
|
||||||
|
(text text)
|
||||||
|
(result '()))
|
||||||
|
(match text
|
||||||
|
(()
|
||||||
|
(string-concatenate-reverse result))
|
||||||
|
(((? real-string? head) rest ...)
|
||||||
|
(loop inputs rest (cons head result)))
|
||||||
|
((_ rest ...)
|
||||||
|
(match inputs
|
||||||
|
(((_ (? derivation? drv) sub-drv ...) inputs ...)
|
||||||
|
(loop inputs rest
|
||||||
|
(cons (apply derivation->output-path drv
|
||||||
|
sub-drv)
|
||||||
|
result)))
|
||||||
|
(((_ file) inputs ...)
|
||||||
|
;; FILE is the result of 'add-text-to-store' or so.
|
||||||
|
(loop inputs rest (cons file result))))))))
|
||||||
|
|
||||||
|
(define (builder inputs)
|
||||||
|
`(call-with-output-file (assoc-ref %outputs "out")
|
||||||
|
(lambda (port)
|
||||||
|
(display ,(computed-text text inputs) port))))
|
||||||
|
|
||||||
|
(mlet %store-monad ((inputs (lower-inputs inputs)))
|
||||||
|
(derivation-expression name (builder inputs)
|
||||||
|
#:inputs inputs)))
|
||||||
|
|
||||||
(define* (package-file package
|
(define* (package-file package
|
||||||
#:optional file
|
#:optional file
|
||||||
#:key (system (%current-system)) (output "out"))
|
#:key (system (%current-system)) (output "out"))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;;; GNU Guix --- Functional package management for GNU
|
;;; GNU Guix --- Functional package management for GNU
|
||||||
;;; Copyright © 2013 Ludovic Courtès <ludo@gnu.org>
|
;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -126,6 +126,30 @@ (define (g x)
|
||||||
(readlink (string-append out "/guile-rocks"))))))
|
(readlink (string-append out "/guile-rocks"))))))
|
||||||
#:guile-for-build (package-derivation %store %bootstrap-guile)))
|
#:guile-for-build (package-derivation %store %bootstrap-guile)))
|
||||||
|
|
||||||
|
(test-assert "text-file*"
|
||||||
|
(let ((references (store-lift references)))
|
||||||
|
(run-with-store %store
|
||||||
|
(mlet* %store-monad
|
||||||
|
((drv (package->derivation %bootstrap-guile))
|
||||||
|
(guile -> (derivation->output-path drv))
|
||||||
|
(file (text-file "bar" "This is bar."))
|
||||||
|
(text (text-file* "foo"
|
||||||
|
%bootstrap-guile "/bin/guile "
|
||||||
|
`(,%bootstrap-guile "out") "/bin/guile "
|
||||||
|
drv "/bin/guile "
|
||||||
|
file))
|
||||||
|
(done (built-derivations (list text)))
|
||||||
|
(out -> (derivation->output-path text))
|
||||||
|
(refs (references out)))
|
||||||
|
;; Make sure we get the right references and the right content.
|
||||||
|
(return (and (lset= string=? refs (list guile file))
|
||||||
|
(equal? (call-with-input-file out get-string-all)
|
||||||
|
(string-append guile "/bin/guile "
|
||||||
|
guile "/bin/guile "
|
||||||
|
guile "/bin/guile "
|
||||||
|
file)))))
|
||||||
|
#:guile-for-build (package-derivation %store %bootstrap-guile))))
|
||||||
|
|
||||||
(test-assert "mapm"
|
(test-assert "mapm"
|
||||||
(every (lambda (monad run)
|
(every (lambda (monad run)
|
||||||
(with-monad monad
|
(with-monad monad
|
||||||
|
|
Loading…
Reference in a new issue