summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2016-07-18 23:14:14 +0200
committerLudovic Courtès <ludo@gnu.org>2016-07-19 00:07:12 +0200
commit721539026dda02e58addbb618f2102b31a2927f8 (patch)
treeadbf4ef7de758050ea1de575f3f0be2f5982295d
parent2c2ec261a8d3c37e5147038f47ad24c57cde4134 (diff)
Add (guix zlib).
* guix/zlib.scm, tests/zlib.scm: New files. * Makefile.am (MODULES): Add guix/zlib.scm. (SCM_TESTS): Add tests/zlib.scm. * m4/guix.m4 (GUIX_LIBGCRYPT_LIBDIR): New macro. * configure.ac (LIBGCRYPT_LIBDIR): Use it. Define and substitute 'LIBZ'. * guix/config.scm.in (%libz): New variable.
-rw-r--r--.dir-locals.el2
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac11
-rw-r--r--guix/config.scm.in6
-rw-r--r--guix/zlib.scm234
-rw-r--r--m4/guix.m411
-rw-r--r--tests/zlib.scm63
7 files changed, 328 insertions, 1 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index c7ceb9e9f0b..572a35f828f 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -41,6 +41,8 @@
41 (eval . (put 'with-atomic-file-output 'scheme-indent-function 1)) 41 (eval . (put 'with-atomic-file-output 'scheme-indent-function 1))
42 (eval . (put 'call-with-compressed-output-port 'scheme-indent-function 2)) 42 (eval . (put 'call-with-compressed-output-port 'scheme-indent-function 2))
43 (eval . (put 'call-with-decompressed-port 'scheme-indent-function 2)) 43 (eval . (put 'call-with-decompressed-port 'scheme-indent-function 2))
44 (eval . (put 'call-with-gzip-input-port 'scheme-indent-function 1))
45 (eval . (put 'call-with-gzip-output-port 'scheme-indent-function 1))
44 (eval . (put 'signature-case 'scheme-indent-function 1)) 46 (eval . (put 'signature-case 'scheme-indent-function 1))
45 (eval . (put 'emacs-batch-eval 'scheme-indent-function 0)) 47 (eval . (put 'emacs-batch-eval 'scheme-indent-function 0))
46 (eval . (put 'emacs-batch-edit-file 'scheme-indent-function 1)) 48 (eval . (put 'emacs-batch-edit-file 'scheme-indent-function 1))
diff --git a/Makefile.am b/Makefile.am
index 37a0aef7dc5..576177f6d70 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,6 +57,7 @@ MODULES = \
57 guix/licenses.scm \ 57 guix/licenses.scm \
58 guix/graph.scm \ 58 guix/graph.scm \
59 guix/cve.scm \ 59 guix/cve.scm \
60 guix/zlib.scm \
60 guix/build-system.scm \ 61 guix/build-system.scm \
61 guix/build-system/ant.scm \ 62 guix/build-system/ant.scm \
62 guix/build-system/cmake.scm \ 63 guix/build-system/cmake.scm \
@@ -258,6 +259,7 @@ SCM_TESTS = \
258 tests/graph.scm \ 259 tests/graph.scm \
259 tests/challenge.scm \ 260 tests/challenge.scm \
260 tests/cve.scm \ 261 tests/cve.scm \
262 tests/zlib.scm \
261 tests/file-systems.scm \ 263 tests/file-systems.scm \
262 tests/system.scm \ 264 tests/system.scm \
263 tests/services.scm \ 265 tests/services.scm \
diff --git a/configure.ac b/configure.ac
index 7c6fcc9ec9f..8367b41f3c5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,17 @@ AC_SUBST([LIBGCRYPT_LIBDIR])
194 194
195GUIX_ASSERT_LIBGCRYPT_USABLE 195GUIX_ASSERT_LIBGCRYPT_USABLE
196 196
197dnl Library name of zlib suitable for 'dynamic-link'.
198GUIX_LIBZ_LIBDIR([libz_libdir])
199if test "x$libz_libdir" = "x"; then
200 LIBZ="libz"
201else
202 LIBZ="$libz_libdir/libz"
203fi
204AC_MSG_CHECKING([for zlib's shared library name])
205AC_MSG_RESULT([$LIBZ])
206AC_SUBST([LIBZ])
207
197AC_CACHE_SAVE 208AC_CACHE_SAVE
198 209
199m4_include([config-daemon.ac]) 210m4_include([config-daemon.ac])
diff --git a/guix/config.scm.in b/guix/config.scm.in
index adffa0cfec6..6d42cf233c3 100644
--- a/guix/config.scm.in
+++ b/guix/config.scm.in
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
3;;; 3;;;
4;;; This file is part of GNU Guix. 4;;; This file is part of GNU Guix.
5;;; 5;;;
@@ -27,6 +27,7 @@
27 %guix-register-program 27 %guix-register-program
28 %system 28 %system
29 %libgcrypt 29 %libgcrypt
30 %libz
30 %nix-instantiate 31 %nix-instantiate
31 %gzip 32 %gzip
32 %bzip2 33 %bzip2
@@ -72,6 +73,9 @@
72(define %libgcrypt 73(define %libgcrypt
73 "@LIBGCRYPT@") 74 "@LIBGCRYPT@")
74 75
76(define %libz
77 "@LIBZ@")
78
75(define %nix-instantiate 79(define %nix-instantiate
76 "@NIX_INSTANTIATE@") 80 "@NIX_INSTANTIATE@")
77 81
diff --git a/guix/zlib.scm b/guix/zlib.scm
new file mode 100644
index 00000000000..51e5e9e4263
--- /dev/null
+++ b/guix/zlib.scm
@@ -0,0 +1,234 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2016 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix zlib)
20 #:use-module (rnrs bytevectors)
21 #:use-module (ice-9 binary-ports)
22 #:use-module (ice-9 match)
23 #:use-module (system foreign)
24 #:use-module (guix config)
25 #:export (zlib-available?
26 make-gzip-input-port
27 make-gzip-output-port
28 call-with-gzip-input-port
29 call-with-gzip-output-port
30 %default-buffer-size
31 %default-compression-level))
32
33;;; Commentary:
34;;;
35;;; Bindings to the gzip-related part of zlib's API. The main limitation of
36;;; this API is that it requires a file descriptor as the source or sink.
37;;;
38;;; Code:
39
40(define %zlib
41 ;; File name of zlib's shared library. When updating via 'guix pull',
42 ;; '%libz' might be undefined so protect against it.
43 (delay (dynamic-link (if (defined? '%libz)
44 %libz
45 "libz"))))
46
47(define (zlib-available?)
48 "Return true if zlib is available, #f otherwise."
49 (false-if-exception (force %zlib)))
50
51(define (zlib-procedure ret name parameters)
52 "Return a procedure corresponding to C function NAME in libz, or #f if
53either zlib or the function could not be found."
54 (match (false-if-exception (dynamic-func name (force %zlib)))
55 ((? pointer? ptr)
56 (pointer->procedure ret ptr parameters))
57 (#f
58 #f)))
59
60(define-wrapped-pointer-type <gzip-file>
61 ;; Scheme counterpart of the 'gzFile' opaque type.
62 gzip-file?
63 pointer->gzip-file
64 gzip-file->pointer
65 (lambda (obj port)
66 (format port "#<gzip-file ~a>"
67 (number->string (object-address obj) 16))))
68
69(define gzerror
70 (let ((proc (zlib-procedure '* "gzerror" '(* *))))
71 (lambda (gzfile)
72 (let* ((errnum* (make-bytevector (sizeof int)))
73 (ptr (proc (gzip-file->pointer gzfile)
74 (bytevector->pointer errnum*))))
75 (values (bytevector-sint-ref errnum* 0
76 (native-endianness) (sizeof int))
77 (pointer->string ptr))))))
78
79(define gzdopen
80 (let ((proc (zlib-procedure '* "gzdopen" (list int '*))))
81 (lambda (fd mode)
82 "Open file descriptor FD as a gzip stream with the given MODE. MODE must
83be a string denoting the how FD is to be opened, such as \"r\" for reading or
84\"w9\" for writing data compressed at level 9 to FD. Calling 'gzclose' also
85closes FD."
86 (let ((result (proc fd (string->pointer mode))))
87 (if (null-pointer? result)
88 (throw 'zlib-error 'gzdopen)
89 (pointer->gzip-file result))))))
90
91(define gzread!
92 (let ((proc (zlib-procedure int "gzread" (list '* '* unsigned-int))))
93 (lambda* (gzfile bv #:optional (start 0) (count (bytevector-length bv)))
94 "Read up to COUNT bytes from GZFILE into BV at offset START. Return the
95number of uncompressed bytes actually read."
96 (let ((ret (proc (gzip-file->pointer gzfile)
97 (bytevector->pointer bv start)
98 count)))
99 (if (< ret 0)
100 (throw 'zlib-error 'gzread! ret)
101 ret)))))
102
103(define gzwrite
104 (let ((proc (zlib-procedure int "gzwrite" (list '* '* unsigned-int))))
105 (lambda* (gzfile bv #:optional (start 0) (count (bytevector-length bv)))
106 "Write up to COUNT bytes from BV at offset START into GZFILE. Return
107the number of uncompressed bytes written, a strictly positive integer."
108 (let ((ret (proc (gzip-file->pointer gzfile)
109 (bytevector->pointer bv start)
110 count)))
111 (if (<= ret 0)
112 (throw 'zlib-error 'gzwrite ret)
113 ret)))))
114
115(define gzbuffer!
116 (let ((proc (zlib-procedure int "gzbuffer" (list '* unsigned-int))))
117 (lambda (gzfile size)
118 "Change the internal buffer size of GZFILE to SIZE bytes."
119 (let ((ret (proc (gzip-file->pointer gzfile) size)))
120 (unless (zero? ret)
121 (throw 'zlib-error 'gzbuffer! ret))))))
122
123(define gzeof?
124 (let ((proc (zlib-procedure int "gzeof" '(*))))
125 (lambda (gzfile)
126 "Return true if the end-of-file has been reached on GZFILE."
127 (not (zero? (proc (gzip-file->pointer gzfile)))))))
128
129(define gzclose
130 (let ((proc (zlib-procedure int "gzclose" '(*))))
131 (lambda (gzfile)
132 "Close GZFILE."
133 (let ((ret (proc (gzip-file->pointer gzfile))))
134 (unless (zero? ret)
135 (throw 'zlib-error 'gzclose ret (gzerror gzfile)))))))
136
137
138
139;;;
140;;; Port interface.
141;;;
142
143(define %default-buffer-size
144 ;; Default buffer size, as documented in <zlib.h>.
145 8192)
146
147(define %default-compression-level
148 ;; Z_DEFAULT_COMPRESSION.
149 -1)
150
151(define (close-procedure gzfile port)
152 "Return a procedure that closes GZFILE, ensuring its underlying PORT is
153closed even if closing GZFILE triggers an exception."
154 (lambda ()
155 (catch 'zlib-error
156 (lambda ()
157 ;; 'gzclose' closes the underlying file descriptor. 'close-port'
158 ;; calls close(2), gets EBADF, which is ignores.
159 (gzclose gzfile)
160 (close-port port))
161 (lambda args
162 ;; Make sure PORT is closed despite the zlib error.
163 (close-port port)
164 (apply throw args)))))
165
166(define* (make-gzip-input-port port #:key (buffer-size %default-buffer-size))
167 "Return an input port that decompresses data read from PORT, a file port.
168PORT is automatically closed when the resulting port is closed. BUFFER-SIZE
169is the size in bytes of the internal buffer, 8 KiB by default; using a larger
170buffer increases decompression speed."
171 (define gzfile
172 (gzdopen (fileno port) "r"))
173
174 (define (read! bv start count)
175 ;; XXX: Can 'gzread!' return zero even though we haven't reached the EOF?
176 (gzread! gzfile bv start count))
177
178 (unless (= buffer-size %default-buffer-size)
179 (gzbuffer! gzfile buffer-size))
180
181 (make-custom-binary-input-port "gzip-input" read! #f #f
182 (close-procedure gzfile port)))
183
184(define* (make-gzip-output-port port
185 #:key
186 (level %default-compression-level)
187 (buffer-size %default-buffer-size))
188 "Return an output port that compresses data at the given LEVEL, using PORT,
189a file port, as its sink. PORT is automatically closed when the resulting
190port is closed."
191 (define gzfile
192 (gzdopen (fileno port)
193 (string-append "w" (number->string level))))
194
195 (define (write! bv start count)
196 (gzwrite gzfile bv start count))
197
198 (unless (= buffer-size %default-buffer-size)
199 (gzbuffer! gzfile buffer-size))
200
201 (make-custom-binary-output-port "gzip-output" write! #f #f
202 (close-procedure gzfile port)))
203
204(define* (call-with-gzip-input-port port proc
205 #:key (buffer-size %default-buffer-size))
206 "Call PROC with a port that wraps PORT and decompresses data read from it.
207PORT is closed upon completion. The gzip internal buffer size is set to
208BUFFER-SIZE bytes."
209 (let ((gzip (make-gzip-input-port port #:buffer-size buffer-size)))
210 (dynamic-wind
211 (const #t)
212 (lambda ()
213 (proc gzip))
214 (lambda ()
215 (close-port gzip)))))
216
217(define* (call-with-gzip-output-port port proc
218 #:key
219 (level %default-compression-level)
220 (buffer-size %default-buffer-size))
221 "Call PROC with an output port that wraps PORT and compresses data. PORT is
222close upon completion. The gzip internal buffer size is set to BUFFER-SIZE
223bytes."
224 (let ((gzip (make-gzip-output-port port
225 #:level level
226 #:buffer-size buffer-size)))
227 (dynamic-wind
228 (const #t)
229 (lambda ()
230 (proc gzip))
231 (lambda ()
232 (close-port gzip)))))
233
234;;; zlib.scm ends here
diff --git a/m4/guix.m4 b/m4/guix.m4
index 2d3dfd282ec..a4f83f029ab 100644
--- a/m4/guix.m4
+++ b/m4/guix.m4
@@ -308,6 +308,17 @@ AC_DEFUN([GUIX_LIBGCRYPT_LIBDIR], [
308 $1="$guix_cv_libgcrypt_libdir" 308 $1="$guix_cv_libgcrypt_libdir"
309]) 309])
310 310
311dnl GUIX_LIBZ_LIBDIR VAR
312dnl
313dnl Attempt to determine libz's LIBDIR; store the result in VAR.
314AC_DEFUN([GUIX_LIBZ_LIBDIR], [
315 AC_REQUIRE([PKG_PROG_PKG_CONFIG])
316 AC_CACHE_CHECK([zlib's library directory],
317 [guix_cv_libz_libdir],
318 [guix_cv_libz_libdir="`$PKG_CONFIG zlib --variable=libdir 2> /dev/null`"])
319 $1="$guix_cv_libz_libdir"
320])
321
311dnl GUIX_CURRENT_LOCALSTATEDIR 322dnl GUIX_CURRENT_LOCALSTATEDIR
312dnl 323dnl
313dnl Determine the localstatedir of an existing Guix installation and set 324dnl Determine the localstatedir of an existing Guix installation and set
diff --git a/tests/zlib.scm b/tests/zlib.scm
new file mode 100644
index 00000000000..5455240a719
--- /dev/null
+++ b/tests/zlib.scm
@@ -0,0 +1,63 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2016 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (test-zlib)
20 #:use-module (guix zlib)
21 #:use-module (guix tests)
22 #:use-module (srfi srfi-64)
23 #:use-module (rnrs bytevectors)
24 #:use-module (rnrs io ports)
25 #:use-module (ice-9 match))
26
27;; Test the (guix zlib) module.
28
29(unless (zlib-available?)
30 (exit 77))
31
32(test-begin "zlib")
33
34(test-assert "compression/decompression pipe"
35 (let ((data (random-bytevector (+ (random 10000)
36 (* 20 1024)))))
37 (match (pipe)
38 ((parent . child)
39 (match (primitive-fork)
40 (0 ;compress
41 (dynamic-wind
42 (const #t)
43 (lambda ()
44 (close-port parent)
45 (call-with-gzip-output-port child
46 (lambda (port)
47 (put-bytevector port data))))
48 (lambda ()
49 (primitive-exit 0))))
50 (pid ;decompress
51 (begin
52 (close-port child)
53 (let ((received (call-with-gzip-input-port parent
54 (lambda (port)
55 (get-bytevector-all port))
56 #:buffer-size (* 64 1024))))
57 (match (waitpid pid)
58 ((_ . status)
59 (and (zero? status)
60 (port-closed? parent)
61 (bytevector=? received data))))))))))))
62
63(test-end)