Add (guix nar) and (guix serialization).
[jackhill/guix/guix.git] / guix / nar.scm
CommitLineData
0f41c26f
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013 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 nar)
20 #:use-module (guix utils)
21 #:use-module (guix serialization)
22 #:use-module (rnrs bytevectors)
23 #:use-module (rnrs io ports)
24 #:use-module (srfi srfi-1)
25 #:use-module (srfi srfi-26)
26 #:use-module (ice-9 ftw)
27 #:export (write-file))
28
29;;; Comment:
30;;;
31;;; Read and write Nix archives, aka. ‘nar’.
32;;;
33;;; Code:
34
35(define (write-contents file p size)
36 "Write SIZE bytes from FILE to output port P."
37 (define (call-with-binary-input-file file proc)
38 ;; Open FILE as a binary file. This avoids scan-for-encoding, and thus
39 ;; avoids any initial buffering. Disable file name canonicalization to
40 ;; avoid stat'ing like crazy.
41 (with-fluids ((%file-port-name-canonicalization #f))
42 (let ((port (open-file file "rb")))
43 (catch #t (cut proc port)
44 (lambda args
45 (close-port port)
46 (apply throw args))))))
47
48 (define (dump in size)
49 (define buf-size 65536)
50 (define buf (make-bytevector buf-size))
51
52 (let loop ((left size))
53 (if (<= left 0)
54 0
55 (let ((read (get-bytevector-n! in buf 0 buf-size)))
56 (if (eof-object? read)
57 left
58 (begin
59 (put-bytevector p buf 0 read)
60 (loop (- left read))))))))
61
62 (write-string "contents" p)
63 (write-long-long size p)
64 (call-with-binary-input-file file
65 ;; Use `sendfile' when available (Guile 2.0.8+).
66 (if (compile-time-value (defined? 'sendfile))
67 (cut sendfile p <> size 0)
68 (cut dump <> size)))
69 (write-padding size p))
70
71(define (write-file file port)
72 "Write the contents of FILE to PORT in Nar format, recursing into
73sub-directories of FILE as needed."
74 (define %archive-version-1 "nix-archive-1")
75 (define p port)
76
77 (write-string %archive-version-1 p)
78
79 (let dump ((f file))
80 (let ((s (lstat f)))
81 (write-string "(" p)
82 (case (stat:type s)
83 ((regular)
84 (write-string "type" p)
85 (write-string "regular" p)
86 (if (not (zero? (logand (stat:mode s) #o100)))
87 (begin
88 (write-string "executable" p)
89 (write-string "" p)))
90 (write-contents f p (stat:size s)))
91 ((directory)
92 (write-string "type" p)
93 (write-string "directory" p)
94 (let ((entries (remove (cut member <> '("." ".."))
95 (scandir f))))
96 (for-each (lambda (e)
97 (let ((f (string-append f "/" e)))
98 (write-string "entry" p)
99 (write-string "(" p)
100 (write-string "name" p)
101 (write-string e p)
102 (write-string "node" p)
103 (dump f)
104 (write-string ")" p)))
105 entries)))
106 (else
107 (error "ENOSYS")))
108 (write-string ")" p))))
109
110;;; nar.scm ends here