summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--guix/store.scm100
-rw-r--r--tests/store.scm46
2 files changed, 146 insertions, 0 deletions
diff --git a/guix/store.scm b/guix/store.scm
index cc5c24a77de..f41a1e26900 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -78,6 +78,7 @@
78 add-data-to-store 78 add-data-to-store
79 add-text-to-store 79 add-text-to-store
80 add-to-store 80 add-to-store
81 add-file-tree-to-store
81 binary-file 82 binary-file
82 build-things 83 build-things
83 build 84 build
@@ -137,6 +138,7 @@
137 set-current-system 138 set-current-system
138 text-file 139 text-file
139 interned-file 140 interned-file
141 interned-file-tree
140 142
141 %store-prefix 143 %store-prefix
142 store-path 144 store-path
@@ -951,6 +953,101 @@ where FILE is the entry's absolute file name and STAT is the result of
951 (hash-set! cache args path) 953 (hash-set! cache args path)
952 path)))))) 954 path))))))
953 955
956(define %not-slash
957 (char-set-complement (char-set #\/)))
958
959(define* (add-file-tree-to-store server tree
960 #:key
961 (hash-algo "sha256")
962 (recursive? #t))
963 "Add the given TREE to the store on SERVER. TREE must be an entry such as:
964
965 (\"my-tree\" directory
966 (\"a\" regular (data \"hello\"))
967 (\"b\" symlink \"a\")
968 (\"c\" directory
969 (\"d\" executable (file \"/bin/sh\"))))
970
971This is a generalized version of 'add-to-store'. It allows you to reproduce
972an arbitrary directory layout in the store without creating a derivation."
973
974 ;; Note: The format of TREE was chosen to allow trees to be compared with
975 ;; 'equal?', which in turn allows us to memoize things.
976
977 (define root
978 ;; TREE is a single entry.
979 (list tree))
980
981 (define basename
982 (match tree
983 ((name . _) name)))
984
985 (define (lookup file)
986 (let loop ((components (string-tokenize file %not-slash))
987 (tree root))
988 (match components
989 ((basename)
990 (assoc basename tree))
991 ((head . rest)
992 (loop rest
993 (match (assoc-ref tree head)
994 (('directory . entries) entries)))))))
995
996 (define (file-type+size file)
997 (match (lookup file)
998 ((_ (and type (or 'directory 'symlink)) . _)
999 (values type 0))
1000 ((_ type ('file file))
1001 (values type (stat:size (stat file))))
1002 ((_ type ('data (? string? data)))
1003 (values type (string-length data)))
1004 ((_ type ('data (? bytevector? data)))
1005 (values type (bytevector-length data)))))
1006
1007 (define (file-port file)
1008 (match (lookup file)
1009 ((_ (or 'regular 'executable) content)
1010 (match content
1011 (('file (? string? file))
1012 (open-file file "r0b"))
1013 (('data (? string? str))
1014 (open-input-string str))
1015 (('data (? bytevector? bv))
1016 (open-bytevector-input-port bv))))))
1017
1018 (define (symlink-target file)
1019 (match (lookup file)
1020 ((_ 'symlink target) target)))
1021
1022 (define (directory-entries directory)
1023 (match (lookup directory)
1024 ((_ 'directory (names . _) ...) names)))
1025
1026 (define cache
1027 (nix-server-add-to-store-cache server))
1028
1029 (or (hash-ref cache tree)
1030 (begin
1031 ;; We don't use the 'operation' macro so we can use 'write-file-tree'
1032 ;; instead of 'write-file'.
1033 (record-operation 'add-to-store/tree)
1034 (let ((port (nix-server-socket server)))
1035 (write-int (operation-id add-to-store) port)
1036 (write-string basename port)
1037 (write-int 1 port) ;obsolete, must be #t
1038 (write-int (if recursive? 1 0) port)
1039 (write-string hash-algo port)
1040 (write-file-tree basename port
1041 #:file-type+size file-type+size
1042 #:file-port file-port
1043 #:symlink-target symlink-target
1044 #:directory-entries directory-entries)
1045 (let loop ((done? (process-stderr server)))
1046 (or done? (loop (process-stderr server))))
1047 (let ((result (read-store-path port)))
1048 (hash-set! cache tree result)
1049 result)))))
1050
954(define build-things 1051(define build-things
955 (let ((build (operation (build-things (string-list things) 1052 (let ((build (operation (build-things (string-list things)
956 (integer mode)) 1053 (integer mode))
@@ -1402,6 +1499,9 @@ where FILE is the entry's absolute file name and STAT is the result of
1402 #:select? select?) 1499 #:select? select?)
1403 store))) 1500 store)))
1404 1501
1502(define interned-file-tree
1503 (store-lift add-file-tree-to-store))
1504
1405(define build 1505(define build
1406 ;; Monadic variant of 'build-things'. 1506 ;; Monadic variant of 'build-things'.
1407 (store-lift build-things)) 1507 (store-lift build-things))
diff --git a/tests/store.scm b/tests/store.scm
index afecec940ad..47fab0df18a 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -210,6 +210,52 @@
210 (valid-path? store path) 210 (valid-path? store path)
211 (file-exists? path))))) 211 (file-exists? path)))))
212 212
213(test-equal "add-file-tree-to-store"
214 `(42
215 ("." directory #t)
216 ("./bar" directory #t)
217 ("./foo" directory #t)
218 ("./foo/a" regular "file a")
219 ("./foo/b" symlink "a")
220 ("./foo/c" directory #t)
221 ("./foo/c/p" regular "file p")
222 ("./foo/c/q" directory #t)
223 ("./foo/c/q/x" regular "#!/bin/sh\nexit 42")
224 ("./foo/c/q/y" symlink "..")
225 ("./foo/c/q/z" directory #t))
226 (let* ((tree `("file-tree" directory
227 ("foo" directory
228 ("a" regular (data "file a"))
229 ("b" symlink "a")
230 ("c" directory
231 ("p" regular (data ,(string->utf8 "file p")))
232 ("q" directory
233 ("x" executable
234 (data "#!/bin/sh\nexit 42"))
235 ("y" symlink "..")
236 ("z" directory))))
237 ("bar" directory)))
238 (result (add-file-tree-to-store %store tree)))
239 (cons (status:exit-val (system* (string-append result "/foo/c/q/x")))
240 (with-directory-excursion result
241 (map (lambda (file)
242 (let ((type (stat:type (lstat file))))
243 `(,file ,type
244 ,(match type
245 ((or 'regular 'executable)
246 (call-with-input-file file
247 get-string-all))
248 ('symlink (readlink file))
249 ('directory #t)))))
250 (find-files "." #:directories? #t))))))
251
252(test-equal "add-file-tree-to-store, flat"
253 "Hello, world!"
254 (let* ((tree `("flat-file" regular (data "Hello, world!")))
255 (result (add-file-tree-to-store %store tree)))
256 (and (file-exists? result)
257 (call-with-input-file result get-string-all))))
258
213(test-assert "references" 259(test-assert "references"
214 (let* ((t1 (add-text-to-store %store "random1" 260 (let* ((t1 (add-text-to-store %store "random1"
215 (random-text))) 261 (random-text)))