summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2013-04-12 17:30:27 +0200
committerLudovic Courtès <ludo@gnu.org>2013-04-12 17:31:01 +0200
commitfe0cff14f6c5facee4192529f5c7b7a972f185ca (patch)
tree6e8c21cbcb5a4b0656d2184940457fe87dd92095
parentd7c5d27795500c1db3bca6c2ebf9066e32d36adb (diff)
substitute-binary: Implement `--substitute'.
This allows build outputs to be transparently downloaded from http://hydra.gnu.org, for example. * config-daemon.ac: Check for `gzip', `bzip2', and `xz'. * guix/config.scm.in (%gzip, %bzip2, %xz): New variable. * guix/scripts/substitute-binary.scm (fetch): Return SIZE as a second value. (<narinfo>): Change `url' to `uri'. (make-narinfo): Rename to... (narinfo-maker): ... this. Handle relative URLs. (fetch-narinfo): Adjust accordingly. (filtered-port, decompressed-port): New procedures. (guix-substitute-binary): Implement the `--substitute' case. * tests/store.scm ("substitute query"): Use (%store-prefix) instead of (getenv "NIX_STORE_DIR"). ("substitute"): New test.
-rw-r--r--config-daemon.ac8
-rw-r--r--guix/config.scm.in14
-rwxr-xr-xguix/scripts/substitute-binary.scm100
-rw-r--r--tests/store.scm55
4 files changed, 154 insertions, 23 deletions
diff --git a/config-daemon.ac b/config-daemon.ac
index eed1e23f9e5..7c51f2b95c5 100644
--- a/config-daemon.ac
+++ b/config-daemon.ac
@@ -11,6 +11,14 @@ if test "x$guix_build_daemon" = "xyes"; then
11 AC_PROG_RANLIB 11 AC_PROG_RANLIB
12 AC_CONFIG_HEADER([nix/config.h]) 12 AC_CONFIG_HEADER([nix/config.h])
13 13
14 dnl Decompressors, for use by the substituter.
15 AC_PATH_PROG([GZIP], [gzip])
16 AC_PATH_PROG([BZIP2], [bzip2])
17 AC_PATH_PROG([XZ], [xz])
18 AC_SUBST([GZIP])
19 AC_SUBST([BZIP2])
20 AC_SUBST([XZ])
21
14 dnl Use 64-bit file system calls so that we can support files > 2 GiB. 22 dnl Use 64-bit file system calls so that we can support files > 2 GiB.
15 AC_SYS_LARGEFILE 23 AC_SYS_LARGEFILE
16 24
diff --git a/guix/config.scm.in b/guix/config.scm.in
index ab7b0669b85..772ea8c289c 100644
--- a/guix/config.scm.in
+++ b/guix/config.scm.in
@@ -26,7 +26,10 @@
26 %system 26 %system
27 %libgcrypt 27 %libgcrypt
28 %nixpkgs 28 %nixpkgs
29 %nix-instantiate)) 29 %nix-instantiate
30 %gzip
31 %bzip2
32 %xz))
30 33
31;;; Commentary: 34;;; Commentary:
32;;; 35;;;
@@ -67,4 +70,13 @@
67(define %nix-instantiate 70(define %nix-instantiate
68 "@NIX_INSTANTIATE@") 71 "@NIX_INSTANTIATE@")
69 72
73(define %gzip
74 "@GZIP@")
75
76(define %bzip2
77 "@BZIP2@")
78
79(define %xz
80 "@XZ@")
81
70;;; config.scm ends here 82;;; config.scm ends here
diff --git a/guix/scripts/substitute-binary.scm b/guix/scripts/substitute-binary.scm
index 64df4f09d69..2b447ce7f2b 100755
--- a/guix/scripts/substitute-binary.scm
+++ b/guix/scripts/substitute-binary.scm
@@ -20,10 +20,13 @@
20 #:use-module (guix ui) 20 #:use-module (guix ui)
21 #:use-module (guix store) 21 #:use-module (guix store)
22 #:use-module (guix utils) 22 #:use-module (guix utils)
23 #:use-module (guix config)
24 #:use-module (guix nar)
23 #:use-module (ice-9 rdelim) 25 #:use-module (ice-9 rdelim)
24 #:use-module (ice-9 regex) 26 #:use-module (ice-9 regex)
25 #:use-module (ice-9 match) 27 #:use-module (ice-9 match)
26 #:use-module (ice-9 threads) 28 #:use-module (ice-9 threads)
29 #:use-module (ice-9 format)
27 #:use-module (srfi srfi-1) 30 #:use-module (srfi srfi-1)
28 #:use-module (srfi srfi-9) 31 #:use-module (srfi srfi-9)
29 #:use-module (srfi srfi-11) 32 #:use-module (srfi srfi-11)
@@ -70,9 +73,12 @@ pairs."
70 (apply make args))) 73 (apply make args)))
71 74
72(define (fetch uri) 75(define (fetch uri)
76 "Return a binary input port to URI and the number of bytes it's expected to
77provide."
73 (case (uri-scheme uri) 78 (case (uri-scheme uri)
74 ((file) 79 ((file)
75 (open-input-file (uri-path uri))) 80 (let ((port (open-input-file (uri-path uri))))
81 (values port (stat:size (stat port)))))
76 ((http) 82 ((http)
77 (let*-values (((resp port) 83 (let*-values (((resp port)
78 ;; XXX: `http-get*' was introduced in 2.0.7, and deprecated 84 ;; XXX: `http-get*' was introduced in 2.0.7, and deprecated
@@ -86,7 +92,7 @@ pairs."
86 (response-content-length resp))) 92 (response-content-length resp)))
87 (case code 93 (case code
88 ((200) ; OK 94 ((200) ; OK
89 port) 95 (values port size))
90 ((301 ; moved permanently 96 ((301 ; moved permanently
91 302) ; found (redirection) 97 302) ; found (redirection)
92 (let ((uri (response-location resp))) 98 (let ((uri (response-location resp)))
@@ -120,11 +126,11 @@ failure."
120 '("StoreDir" "WantMassQuery"))))) 126 '("StoreDir" "WantMassQuery")))))
121 127
122(define-record-type <narinfo> 128(define-record-type <narinfo>
123 (%make-narinfo path url compression file-hash file-size nar-hash nar-size 129 (%make-narinfo path uri compression file-hash file-size nar-hash nar-size
124 references deriver system) 130 references deriver system)
125 narinfo? 131 narinfo?
126 (path narinfo-path) 132 (path narinfo-path)
127 (url narinfo-url) 133 (uri narinfo-uri)
128 (compression narinfo-compression) 134 (compression narinfo-compression)
129 (file-hash narinfo-file-hash) 135 (file-hash narinfo-file-hash)
130 (file-size narinfo-file-size) 136 (file-size narinfo-file-size)
@@ -134,18 +140,26 @@ failure."
134 (deriver narinfo-deriver) 140 (deriver narinfo-deriver)
135 (system narinfo-system)) 141 (system narinfo-system))
136 142
137(define (make-narinfo path url compression file-hash file-size nar-hash nar-size 143(define (narinfo-maker cache-url)
138 references deriver system) 144 "Return a narinfo constructor for narinfos originating from CACHE-URL."
139 "Return a new <narinfo> object." 145 (lambda (path url compression file-hash file-size nar-hash nar-size
140 (%make-narinfo path url compression file-hash 146 references deriver system)
141 (and=> file-size string->number) 147 "Return a new <narinfo> object."
142 nar-hash 148 (%make-narinfo path
143 (and=> nar-size string->number) 149
144 (string-tokenize references) 150 ;; Handle the case where URL is a relative URL.
145 (match deriver 151 (or (string->uri url)
146 ((or #f "") #f) 152 (string->uri (string-append cache-url "/" url)))
147 (_ deriver)) 153
148 system)) 154 compression file-hash
155 (and=> file-size string->number)
156 nar-hash
157 (and=> nar-size string->number)
158 (string-tokenize references)
159 (match deriver
160 ((or #f "") #f)
161 (_ deriver))
162 system)))
149 163
150(define (fetch-narinfo cache path) 164(define (fetch-narinfo cache path)
151 "Return the <narinfo> record for PATH, or #f if CACHE does not hold PATH." 165 "Return the <narinfo> record for PATH, or #f if CACHE does not hold PATH."
@@ -159,11 +173,36 @@ failure."
159 (store-path-hash-part path) 173 (store-path-hash-part path)
160 ".narinfo")) 174 ".narinfo"))
161 (lambda (properties) 175 (lambda (properties)
162 (alist->record properties make-narinfo 176 (alist->record properties (narinfo-maker (cache-url cache))
163 '("StorePath" "URL" "Compression" 177 '("StorePath" "URL" "Compression"
164 "FileHash" "FileSize" "NarHash" "NarSize" 178 "FileHash" "FileSize" "NarHash" "NarSize"
165 "References" "Deriver" "System"))))) 179 "References" "Deriver" "System")))))
166 180
181(define (filtered-port command input)
182 "Return an input port (and PID) where data drained from INPUT is filtered
183through COMMAND. INPUT must be a file input port."
184 (let ((i+o (pipe)))
185 (match (primitive-fork)
186 (0
187 (close-port (car i+o))
188 (close-port (current-input-port))
189 (dup2 (fileno input) 0)
190 (close-port (current-output-port))
191 (dup2 (fileno (cdr i+o)) 1)
192 (apply execl (car command) command))
193 (child
194 (close-port (cdr i+o))
195 (values (car i+o) child)))))
196
197(define (decompressed-port compression input)
198 "Return an input port where INPUT is decompressed according to COMPRESSION."
199 (match compression
200 ("none" (values input #f))
201 ("bzip2" (filtered-port `(,%bzip2 "-dc") input))
202 ("xz" (filtered-port `(,%xz "-dc") input))
203 ("gzip" (filtered-port `(,%gzip "-dc") input))
204 (else (error "unsupported compression scheme" compression))))
205
167(define %cache-url 206(define %cache-url
168 (or (getenv "GUIX_BINARY_SUBSTITUTE_URL") 207 (or (getenv "GUIX_BINARY_SUBSTITUTE_URL")
169 "http://hydra.gnu.org")) 208 "http://hydra.gnu.org"))
@@ -222,10 +261,29 @@ failure."
222 (error "unknown `--query' command" wtf))) 261 (error "unknown `--query' command" wtf)))
223 (loop (read-line))))))) 262 (loop (read-line)))))))
224 (("--substitute" store-path destination) 263 (("--substitute" store-path destination)
225 ;; Download PATH and add it to the store. 264 ;; Download STORE-PATH and add store it as a Nar in file DESTINATION.
226 ;; TODO: Implement. 265 (let* ((cache (open-cache %cache-url))
227 (format (current-error-port) "substitution not implemented yet~%") 266 (narinfo (fetch-narinfo cache store-path))
228 #f) 267 (uri (narinfo-uri narinfo)))
268 ;; Tell the daemon what the expected hash of the Nar itself is.
269 (format #t "~a~%" (narinfo-hash narinfo))
270
271 (let*-values (((raw download-size)
272 (fetch uri))
273 ((input pid)
274 (decompressed-port (narinfo-compression narinfo)
275 raw)))
276 ;; Note that Hydra currently generates Nars on the fly and doesn't
277 ;; specify a Content-Length, so DOWNLOAD-SIZE is #f in practice.
278 (format (current-error-port)
279 (_ "downloading `~a' from `~a'~:[~*~; (~,1f KiB)~]...~%")
280 store-path (uri->string uri)
281 download-size
282 (and=> download-size (cut / <> 1024.0)))
283
284 ;; Unpack the Nar at INPUT into DESTINATION.
285 (restore-file input destination)
286 (or (not pid) (zero? (cdr (waitpid pid)))))))
229 (("--version") 287 (("--version")
230 (show-version-and-exit "guix substitute-binary")))) 288 (show-version-and-exit "guix substitute-binary"))))
231 289
diff --git a/tests/store.scm b/tests/store.scm
index c75b99c6a9e..4ee20a93522 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -23,9 +23,11 @@
23 #:use-module (guix base32) 23 #:use-module (guix base32)
24 #:use-module (guix packages) 24 #:use-module (guix packages)
25 #:use-module (guix derivations) 25 #:use-module (guix derivations)
26 #:use-module (guix nar)
26 #:use-module (gnu packages) 27 #:use-module (gnu packages)
27 #:use-module (gnu packages bootstrap) 28 #:use-module (gnu packages bootstrap)
28 #:use-module (ice-9 match) 29 #:use-module (ice-9 match)
30 #:use-module (rnrs io ports)
29 #:use-module (web uri) 31 #:use-module (web uri)
30 #:use-module (srfi srfi-1) 32 #:use-module (srfi srfi-1)
31 #:use-module (srfi srfi-11) 33 #:use-module (srfi srfi-11)
@@ -141,7 +143,7 @@
141 (call-with-output-file (string-append dir "/nix-cache-info") 143 (call-with-output-file (string-append dir "/nix-cache-info")
142 (lambda (p) 144 (lambda (p)
143 (format p "StoreDir: ~a\nWantMassQuery: 0\n" 145 (format p "StoreDir: ~a\nWantMassQuery: 0\n"
144 (getenv "NIX_STORE_DIR")))) 146 (%store-prefix))))
145 (call-with-output-file (string-append dir "/" (store-path-hash-part o) 147 (call-with-output-file (string-append dir "/" (store-path-hash-part o)
146 ".narinfo") 148 ".narinfo")
147 (lambda (p) 149 (lambda (p)
@@ -167,6 +169,57 @@ Deriver: ~a~%"
167 (null? (substitutable-references s)) 169 (null? (substitutable-references s))
168 (equal? (substitutable-nar-size s) 1234))))))) 170 (equal? (substitutable-nar-size s) 1234)))))))
169 171
172(test-assert "substitute"
173 (let* ((s (open-connection))
174 (c (random-text)) ; contents of the output
175 (d (build-expression->derivation
176 s "substitute-me" (%current-system)
177 `(call-with-output-file %output
178 (lambda (p)
179 (exit 1) ; would actually fail
180 (display ,c p)))
181 '()
182 #:guile-for-build
183 (package-derivation s %bootstrap-guile (%current-system))))
184 (o (derivation-path->output-path d))
185 (dir (and=> (getenv "GUIX_BINARY_SUBSTITUTE_URL")
186 (compose uri-path string->uri))))
187 ;; Create fake substituter data, to be read by `substitute-binary'.
188 (call-with-output-file (string-append dir "/nix-cache-info")
189 (lambda (p)
190 (format p "StoreDir: ~a\nWantMassQuery: 0\n"
191 (%store-prefix))))
192 (call-with-output-file (string-append dir "/example.out")
193 (lambda (p)
194 (display c p)))
195 (call-with-output-file (string-append dir "/example.nar")
196 (lambda (p)
197 (write-file (string-append dir "/example.out") p)))
198 (call-with-output-file (string-append dir "/" (store-path-hash-part o)
199 ".narinfo")
200 (lambda (p)
201 (format p "StorePath: ~a
202URL: ~a
203Compression: none
204NarSize: 1234
205NarHash: sha256:~a
206References:
207System: ~a
208Deriver: ~a~%"
209 o ; StorePath
210 "example.nar" ; relative URL
211 (call-with-input-file (string-append dir "/example.nar")
212 (compose bytevector->nix-base32-string sha256
213 get-bytevector-all))
214 (%current-system) ; System
215 (basename d)))) ; Deriver
216
217 ;; Make sure we use `substitute-binary'.
218 (set-build-options s #:use-substitutes? #t)
219 (and (has-substitutes? s o)
220 (build-derivations s (list d))
221 (equal? c (call-with-input-file o get-string-all)))))
222
170(test-end "store") 223(test-end "store")
171 224
172 225