diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2020-12-10 15:12:34 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2020-12-15 17:32:10 +0100 |
| commit | 6a060ff27ff68384d7c90076baa36c349fff689d (patch) | |
| tree | f7b1f9c7a52e84848fbcaa90d4dc38c25d7d65eb | |
| parent | dea1ee1fd740248307f74ca4cb70b94742264098 (diff) | |
store-copy: 'populate-store' can optionally deduplicate files.
Until now deduplication was performed as an additional pass after
copying files, which involve re-traversing all the files that had just
been copied.
* guix/store/deduplication.scm (copy-file/deduplicate): New procedure.
* tests/store-deduplication.scm ("copy-file/deduplicate"): New test.
* guix/build/store-copy.scm (populate-store): Add #:deduplicate?
parameter and honor it.
* tests/gexp.scm ("gexp->derivation, store copy"): Pass #:deduplicate? #f
to 'populate-store'.
* gnu/build/image.scm (initialize-root-partition): Pass #:deduplicate?
to 'populate-store'. Pass #:deduplicate? #f to 'register-closure'.
* gnu/build/vm.scm (root-partition-initializer): Likewise.
* gnu/build/install.scm (populate-single-profile-directory): Pass
#:deduplicate? #f to 'populate-store'.
* gnu/build/linux-initrd.scm (build-initrd): Likewise.
* guix/scripts/pack.scm (self-contained-tarball)[import-module?]: New
procedure.
[build]: Pass it as an argument to 'source-module-closure'.
* guix/scripts/pack.scm (squashfs-image)[build]: Wrap in
'with-extensions'.
* gnu/system/linux-initrd.scm (expression->initrd)[import-module?]: New
procedure.
[builder]: Pass it to 'source-module-closure'.
* gnu/system/install.scm (cow-store-service-type)[import-module?]: New
procedure. Pass it to 'source-module-closure'.
| -rw-r--r-- | gnu/build/image.scm | 5 | ||||
| -rw-r--r-- | gnu/build/install.scm | 3 | ||||
| -rw-r--r-- | gnu/build/linux-initrd.scm | 3 | ||||
| -rw-r--r-- | gnu/build/vm.scm | 5 | ||||
| -rw-r--r-- | gnu/system/install.scm | 12 | ||||
| -rw-r--r-- | gnu/system/linux-initrd.scm | 10 | ||||
| -rw-r--r-- | guix/build/store-copy.scm | 13 | ||||
| -rw-r--r-- | guix/scripts/pack.scm | 258 | ||||
| -rw-r--r-- | guix/store/deduplication.scm | 16 | ||||
| -rw-r--r-- | tests/gexp.scm | 3 | ||||
| -rw-r--r-- | tests/store-deduplication.scm | 18 |
11 files changed, 207 insertions, 139 deletions
diff --git a/gnu/build/image.scm b/gnu/build/image.scm index 0deea10a9d5..8f50f27f78a 100644 --- a/gnu/build/image.scm +++ b/gnu/build/image.scm | |||
| @@ -186,7 +186,8 @@ rest of the store when registering the closures. SYSTEM-DIRECTORY is the name | |||
| 186 | of the directory of the 'system' derivation. Pass WAL-MODE? to | 186 | of the directory of the 'system' derivation. Pass WAL-MODE? to |
| 187 | register-closure." | 187 | register-closure." |
| 188 | (populate-root-file-system system-directory root) | 188 | (populate-root-file-system system-directory root) |
| 189 | (populate-store references-graphs root) | 189 | (populate-store references-graphs root |
| 190 | #:deduplicate? deduplicate?) | ||
| 190 | 191 | ||
| 191 | ;; Populate /dev. | 192 | ;; Populate /dev. |
| 192 | (when make-device-nodes | 193 | (when make-device-nodes |
| @@ -195,7 +196,7 @@ register-closure." | |||
| 195 | (when register-closures? | 196 | (when register-closures? |
| 196 | (for-each (lambda (closure) | 197 | (for-each (lambda (closure) |
| 197 | (register-closure root closure | 198 | (register-closure root closure |
| 198 | #:deduplicate? deduplicate? | 199 | #:deduplicate? #f |
| 199 | #:wal-mode? wal-mode?)) | 200 | #:wal-mode? wal-mode?)) |
| 200 | references-graphs)) | 201 | references-graphs)) |
| 201 | 202 | ||
diff --git a/gnu/build/install.scm b/gnu/build/install.scm index 63995e1d09f..f5c8407b894 100644 --- a/gnu/build/install.scm +++ b/gnu/build/install.scm | |||
| @@ -214,7 +214,8 @@ This is used to create the self-contained tarballs with 'guix pack'." | |||
| 214 | (symlink old (scope new))) | 214 | (symlink old (scope new))) |
| 215 | 215 | ||
| 216 | ;; Populate the store. | 216 | ;; Populate the store. |
| 217 | (populate-store (list closure) directory) | 217 | (populate-store (list closure) directory |
| 218 | #:deduplicate? #f) | ||
| 218 | 219 | ||
| 219 | (when database | 220 | (when database |
| 220 | (install-database-and-gc-roots directory database profile | 221 | (install-database-and-gc-roots directory database profile |
diff --git a/gnu/build/linux-initrd.scm b/gnu/build/linux-initrd.scm index 99796adba65..bb2ed0db0c7 100644 --- a/gnu/build/linux-initrd.scm +++ b/gnu/build/linux-initrd.scm | |||
| @@ -127,7 +127,8 @@ REFERENCES-GRAPHS." | |||
| 127 | (mkdir "contents") | 127 | (mkdir "contents") |
| 128 | 128 | ||
| 129 | ;; Copy the closures of all the items referenced in REFERENCES-GRAPHS. | 129 | ;; Copy the closures of all the items referenced in REFERENCES-GRAPHS. |
| 130 | (populate-store references-graphs "contents") | 130 | (populate-store references-graphs "contents" |
| 131 | #:deduplicate? #f) | ||
| 131 | 132 | ||
| 132 | (with-directory-excursion "contents" | 133 | (with-directory-excursion "contents" |
| 133 | ;; Make '/init'. | 134 | ;; Make '/init'. |
diff --git a/gnu/build/vm.scm b/gnu/build/vm.scm index abb0317faf6..03be5697b75 100644 --- a/gnu/build/vm.scm +++ b/gnu/build/vm.scm | |||
| @@ -395,7 +395,8 @@ system that is passed to 'populate-root-file-system'." | |||
| 395 | (when copy-closures? | 395 | (when copy-closures? |
| 396 | ;; Populate the store. | 396 | ;; Populate the store. |
| 397 | (populate-store (map (cut string-append "/xchg/" <>) closures) | 397 | (populate-store (map (cut string-append "/xchg/" <>) closures) |
| 398 | target)) | 398 | target |
| 399 | #:deduplicate? deduplicate?)) | ||
| 399 | 400 | ||
| 400 | ;; Populate /dev. | 401 | ;; Populate /dev. |
| 401 | (make-device-nodes target) | 402 | (make-device-nodes target) |
| @@ -412,7 +413,7 @@ system that is passed to 'populate-root-file-system'." | |||
| 412 | (for-each (lambda (closure) | 413 | (for-each (lambda (closure) |
| 413 | (register-closure target | 414 | (register-closure target |
| 414 | (string-append "/xchg/" closure) | 415 | (string-append "/xchg/" closure) |
| 415 | #:deduplicate? deduplicate?)) | 416 | #:deduplicate? #f)) |
| 416 | closures) | 417 | closures) |
| 417 | (unless copy-closures? | 418 | (unless copy-closures? |
| 418 | (umount target-store))) | 419 | (umount target-store))) |
diff --git a/gnu/system/install.scm b/gnu/system/install.scm index a6b9e3d9523..e7534634739 100644 --- a/gnu/system/install.scm +++ b/gnu/system/install.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; Copyright © 2015 Mark H Weaver <mhw@netris.org> | 3 | ;;; Copyright © 2015 Mark H Weaver <mhw@netris.org> |
| 4 | ;;; Copyright © 2016 Andreas Enge <andreas@enge.fr> | 4 | ;;; Copyright © 2016 Andreas Enge <andreas@enge.fr> |
| 5 | ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com> | 5 | ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com> |
| @@ -176,6 +176,13 @@ manual." | |||
| 176 | (shepherd-service-type | 176 | (shepherd-service-type |
| 177 | 'cow-store | 177 | 'cow-store |
| 178 | (lambda _ | 178 | (lambda _ |
| 179 | (define (import-module? module) | ||
| 180 | ;; Since we don't use deduplication support in 'populate-store', don't | ||
| 181 | ;; import (guix store deduplication) and its dependencies, which | ||
| 182 | ;; includes Guile-Gcrypt. | ||
| 183 | (and (guix-module-name? module) | ||
| 184 | (not (equal? module '(guix store deduplication))))) | ||
| 185 | |||
| 179 | (shepherd-service | 186 | (shepherd-service |
| 180 | (requirement '(root-file-system user-processes)) | 187 | (requirement '(root-file-system user-processes)) |
| 181 | (provision '(cow-store)) | 188 | (provision '(cow-store)) |
| @@ -190,7 +197,8 @@ the given target.") | |||
| 190 | ,@%default-modules)) | 197 | ,@%default-modules)) |
| 191 | (start | 198 | (start |
| 192 | (with-imported-modules (source-module-closure | 199 | (with-imported-modules (source-module-closure |
| 193 | '((gnu build install))) | 200 | '((gnu build install)) |
| 201 | #:select? import-module?) | ||
| 194 | #~(case-lambda | 202 | #~(case-lambda |
| 195 | ((target) | 203 | ((target) |
| 196 | (mount-cow-store target #$%backing-directory) | 204 | (mount-cow-store target #$%backing-directory) |
diff --git a/gnu/system/linux-initrd.scm b/gnu/system/linux-initrd.scm index 4fb1d863c9d..c6ba9bb5605 100644 --- a/gnu/system/linux-initrd.scm +++ b/gnu/system/linux-initrd.scm | |||
| @@ -76,12 +76,20 @@ the derivations referenced by EXP are automatically copied to the initrd." | |||
| 76 | (define init | 76 | (define init |
| 77 | (program-file "init" exp #:guile guile)) | 77 | (program-file "init" exp #:guile guile)) |
| 78 | 78 | ||
| 79 | (define (import-module? module) | ||
| 80 | ;; Since we don't use deduplication support in 'populate-store', don't | ||
| 81 | ;; import (guix store deduplication) and its dependencies, which includes | ||
| 82 | ;; Guile-Gcrypt. That way we can run tests with '--bootstrap'. | ||
| 83 | (and (guix-module-name? module) | ||
| 84 | (not (equal? module '(guix store deduplication))))) | ||
| 85 | |||
| 79 | (define builder | 86 | (define builder |
| 80 | ;; Do not use "guile-zlib" extension here, otherwise it would drag the | 87 | ;; Do not use "guile-zlib" extension here, otherwise it would drag the |
| 81 | ;; non-static "zlib" package to the initrd closure. It is not needed | 88 | ;; non-static "zlib" package to the initrd closure. It is not needed |
| 82 | ;; anyway because the modules are stored uncompressed within the initrd. | 89 | ;; anyway because the modules are stored uncompressed within the initrd. |
| 83 | (with-imported-modules (source-module-closure | 90 | (with-imported-modules (source-module-closure |
| 84 | '((gnu build linux-initrd))) | 91 | '((gnu build linux-initrd)) |
| 92 | #:select? import-module?) | ||
| 85 | #~(begin | 93 | #~(begin |
| 86 | (use-modules (gnu build linux-initrd)) | 94 | (use-modules (gnu build linux-initrd)) |
| 87 | 95 | ||
diff --git a/guix/build/store-copy.scm b/guix/build/store-copy.scm index 95dcb8e1140..7f0672cd9de 100644 --- a/guix/build/store-copy.scm +++ b/guix/build/store-copy.scm | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #:use-module ((guix build utils) #:hide (copy-recursively)) | 20 | #:use-module ((guix build utils) #:hide (copy-recursively)) |
| 21 | #:use-module (guix sets) | 21 | #:use-module (guix sets) |
| 22 | #:use-module (guix progress) | 22 | #:use-module (guix progress) |
| 23 | #:autoload (guix store deduplication) (copy-file/deduplicate) | ||
| 23 | #:use-module (srfi srfi-1) | 24 | #:use-module (srfi srfi-1) |
| 24 | #:use-module (srfi srfi-9) | 25 | #:use-module (srfi srfi-9) |
| 25 | #:use-module (srfi srfi-26) | 26 | #:use-module (srfi srfi-26) |
| @@ -242,10 +243,13 @@ permissions. Write verbose output to the LOG port." | |||
| 242 | lstat))) | 243 | lstat))) |
| 243 | 244 | ||
| 244 | (define* (populate-store reference-graphs target | 245 | (define* (populate-store reference-graphs target |
| 245 | #:key (log-port (current-error-port))) | 246 | #:key |
| 247 | (deduplicate? #t) | ||
| 248 | (log-port (current-error-port))) | ||
| 246 | "Populate the store under directory TARGET with the items specified in | 249 | "Populate the store under directory TARGET with the items specified in |
| 247 | REFERENCE-GRAPHS, a list of reference-graph files. Items copied to TARGET | 250 | REFERENCE-GRAPHS, a list of reference-graph files. Items copied to TARGET |
| 248 | maintain timestamps and permissions." | 251 | maintain timestamps and permissions. When DEDUPLICATE? is true, deduplicate |
| 252 | regular files as they are copied to TARGET." | ||
| 249 | (define store | 253 | (define store |
| 250 | (string-append target (%store-directory))) | 254 | (string-append target (%store-directory))) |
| 251 | 255 | ||
| @@ -273,6 +277,11 @@ maintain timestamps and permissions." | |||
| 273 | (string-append target thing) | 277 | (string-append target thing) |
| 274 | #:keep-mtime? #t | 278 | #:keep-mtime? #t |
| 275 | #:keep-permissions? #t | 279 | #:keep-permissions? #t |
| 280 | #:copy-file | ||
| 281 | (if deduplicate? | ||
| 282 | (cut copy-file/deduplicate <> <> | ||
| 283 | #:store store) | ||
| 284 | copy-file) | ||
| 276 | #:log (%make-void-port "w")) | 285 | #:log (%make-void-port "w")) |
| 277 | (report)) | 286 | (report)) |
| 278 | things))))) | 287 | things))))) |
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm index 1612ec8f04b..440c4b0903f 100644 --- a/guix/scripts/pack.scm +++ b/guix/scripts/pack.scm | |||
| @@ -203,12 +203,19 @@ added to the pack." | |||
| 203 | #+(file-append glibc-utf8-locales "/lib/locale")) | 203 | #+(file-append glibc-utf8-locales "/lib/locale")) |
| 204 | (setlocale LC_ALL "en_US.utf8")))) | 204 | (setlocale LC_ALL "en_US.utf8")))) |
| 205 | 205 | ||
| 206 | (define (import-module? module) | ||
| 207 | ;; Since we don't use deduplication support in 'populate-store', don't | ||
| 208 | ;; import (guix store deduplication) and its dependencies, which includes | ||
| 209 | ;; Guile-Gcrypt. That way we can run tests with '--bootstrap'. | ||
| 210 | (and (not-config? module) | ||
| 211 | (not (equal? '(guix store deduplication) module)))) | ||
| 212 | |||
| 206 | (define build | 213 | (define build |
| 207 | (with-imported-modules (source-module-closure | 214 | (with-imported-modules (source-module-closure |
| 208 | `((guix build utils) | 215 | `((guix build utils) |
| 209 | (guix build union) | 216 | (guix build union) |
| 210 | (gnu build install)) | 217 | (gnu build install)) |
| 211 | #:select? not-config?) | 218 | #:select? import-module?) |
| 212 | #~(begin | 219 | #~(begin |
| 213 | (use-modules (guix build utils) | 220 | (use-modules (guix build utils) |
| 214 | ((guix build union) #:select (relative-file-name)) | 221 | ((guix build union) #:select (relative-file-name)) |
| @@ -382,138 +389,139 @@ added to the pack." | |||
| 382 | `(("/bin" -> "bin") ,@symlinks))) | 389 | `(("/bin" -> "bin") ,@symlinks))) |
| 383 | 390 | ||
| 384 | (define build | 391 | (define build |
| 385 | (with-imported-modules (source-module-closure | 392 | (with-extensions (list guile-gcrypt) |
| 386 | '((guix build utils) | 393 | (with-imported-modules (source-module-closure |
| 387 | (guix build store-copy) | 394 | '((guix build utils) |
| 388 | (guix build union) | 395 | (guix build store-copy) |
| 389 | (gnu build install)) | 396 | (guix build union) |
| 390 | #:select? not-config?) | 397 | (gnu build install)) |
| 391 | #~(begin | 398 | #:select? not-config?) |
| 392 | (use-modules (guix build utils) | 399 | #~(begin |
| 393 | (guix build store-copy) | 400 | (use-modules (guix build utils) |
| 394 | ((guix build union) #:select (relative-file-name)) | 401 | (guix build store-copy) |
| 395 | (gnu build install) | 402 | ((guix build union) #:select (relative-file-name)) |
| 396 | (srfi srfi-1) | 403 | (gnu build install) |
| 397 | (srfi srfi-26) | 404 | (srfi srfi-1) |
| 398 | (ice-9 match)) | 405 | (srfi srfi-26) |
| 406 | (ice-9 match)) | ||
| 399 | 407 | ||
| 400 | (define database #+database) | 408 | (define database #+database) |
| 401 | (define entry-point #$entry-point) | 409 | (define entry-point #$entry-point) |
| 402 | 410 | ||
| 403 | (define (mksquashfs args) | 411 | (define (mksquashfs args) |
| 404 | (apply invoke "mksquashfs" | 412 | (apply invoke "mksquashfs" |
| 405 | `(,@args | 413 | `(,@args |
| 406 | 414 | ||
| 407 | ;; Do not create a "recovery file" when appending to the | 415 | ;; Do not create a "recovery file" when appending to the |
| 408 | ;; file system since it's useless in this case. | 416 | ;; file system since it's useless in this case. |
| 409 | "-no-recovery" | 417 | "-no-recovery" |
| 410 | 418 | ||
| 411 | ;; Do not attempt to store extended attributes. | 419 | ;; Do not attempt to store extended attributes. |
| 412 | ;; See <https://bugs.gnu.org/40043>. | 420 | ;; See <https://bugs.gnu.org/40043>. |
| 413 | "-no-xattrs" | 421 | "-no-xattrs" |
| 414 | 422 | ||
| 415 | ;; Set file times and the file system creation time to | 423 | ;; Set file times and the file system creation time to |
| 416 | ;; one second after the Epoch. | 424 | ;; one second after the Epoch. |
| 417 | "-all-time" "1" "-mkfs-time" "1" | 425 | "-all-time" "1" "-mkfs-time" "1" |
| 418 | 426 | ||
| 419 | ;; Reset all UIDs and GIDs. | 427 | ;; Reset all UIDs and GIDs. |
| 420 | "-force-uid" "0" "-force-gid" "0"))) | 428 | "-force-uid" "0" "-force-gid" "0"))) |
| 421 | 429 | ||
| 422 | (setenv "PATH" #+(file-append archiver "/bin")) | 430 | (setenv "PATH" #+(file-append archiver "/bin")) |
| 423 | 431 | ||
| 424 | ;; We need an empty file in order to have a valid file argument when | 432 | ;; We need an empty file in order to have a valid file argument when |
| 425 | ;; we reparent the root file system. Read on for why that's | 433 | ;; we reparent the root file system. Read on for why that's |
| 426 | ;; necessary. | 434 | ;; necessary. |
| 427 | (with-output-to-file ".empty" (lambda () (display ""))) | 435 | (with-output-to-file ".empty" (lambda () (display ""))) |
| 428 | 436 | ||
| 429 | ;; Create the squashfs image in several steps. | 437 | ;; Create the squashfs image in several steps. |
| 430 | ;; Add all store items. Unfortunately mksquashfs throws away all | 438 | ;; Add all store items. Unfortunately mksquashfs throws away all |
| 431 | ;; ancestor directories and only keeps the basename. We fix this | 439 | ;; ancestor directories and only keeps the basename. We fix this |
| 432 | ;; in the following invocations of mksquashfs. | 440 | ;; in the following invocations of mksquashfs. |
| 433 | (mksquashfs `(,@(map store-info-item | 441 | (mksquashfs `(,@(map store-info-item |
| 434 | (call-with-input-file "profile" | 442 | (call-with-input-file "profile" |
| 435 | read-reference-graph)) | 443 | read-reference-graph)) |
| 436 | #$environment | 444 | #$environment |
| 437 | ,#$output | 445 | ,#$output |
| 438 | 446 | ||
| 439 | ;; Do not perform duplicate checking because we | 447 | ;; Do not perform duplicate checking because we |
| 440 | ;; don't have any dupes. | 448 | ;; don't have any dupes. |
| 441 | "-no-duplicates" | 449 | "-no-duplicates" |
| 442 | "-comp" | 450 | "-comp" |
| 443 | ,#+(compressor-name compressor))) | 451 | ,#+(compressor-name compressor))) |
| 444 | 452 | ||
| 445 | ;; Here we reparent the store items. For each sub-directory of | 453 | ;; Here we reparent the store items. For each sub-directory of |
| 446 | ;; the store prefix we need one invocation of "mksquashfs". | 454 | ;; the store prefix we need one invocation of "mksquashfs". |
| 447 | (for-each (lambda (dir) | 455 | (for-each (lambda (dir) |
| 448 | (mksquashfs `(".empty" | 456 | (mksquashfs `(".empty" |
| 449 | ,#$output | 457 | ,#$output |
| 450 | "-root-becomes" ,dir))) | 458 | "-root-becomes" ,dir))) |
| 451 | (reverse (string-tokenize (%store-directory) | 459 | (reverse (string-tokenize (%store-directory) |
| 452 | (char-set-complement (char-set #\/))))) | 460 | (char-set-complement (char-set #\/))))) |
| 453 | 461 | ||
| 454 | ;; Add symlinks and mount points. | 462 | ;; Add symlinks and mount points. |
| 455 | (mksquashfs | 463 | (mksquashfs |
| 456 | `(".empty" | 464 | `(".empty" |
| 457 | ,#$output | 465 | ,#$output |
| 458 | ;; Create SYMLINKS via pseudo file definitions. | 466 | ;; Create SYMLINKS via pseudo file definitions. |
| 459 | ,@(append-map | 467 | ,@(append-map |
| 460 | (match-lambda | 468 | (match-lambda |
| 461 | ((source '-> target) | 469 | ((source '-> target) |
| 462 | ;; Create relative symlinks to work around a bug in | 470 | ;; Create relative symlinks to work around a bug in |
| 463 | ;; Singularity 2.x: | 471 | ;; Singularity 2.x: |
| 464 | ;; https://bugs.gnu.org/34913 | 472 | ;; https://bugs.gnu.org/34913 |
| 465 | ;; https://github.com/sylabs/singularity/issues/1487 | 473 | ;; https://github.com/sylabs/singularity/issues/1487 |
| 466 | (let ((target (string-append #$profile "/" target))) | 474 | (let ((target (string-append #$profile "/" target))) |
| 467 | (list "-p" | 475 | (list "-p" |
| 468 | (string-join | 476 | (string-join |
| 469 | ;; name s mode uid gid symlink | 477 | ;; name s mode uid gid symlink |
| 470 | (list source | 478 | (list source |
| 471 | "s" "777" "0" "0" | 479 | "s" "777" "0" "0" |
| 472 | (relative-file-name (dirname source) | 480 | (relative-file-name (dirname source) |
| 473 | target))))))) | 481 | target))))))) |
| 474 | '#$symlinks*) | 482 | '#$symlinks*) |
| 475 | 483 | ||
| 476 | "-p" "/.singularity.d d 555 0 0" | 484 | "-p" "/.singularity.d d 555 0 0" |
| 477 | 485 | ||
| 478 | ;; Create the environment file. | 486 | ;; Create the environment file. |
| 479 | "-p" "/.singularity.d/env d 555 0 0" | 487 | "-p" "/.singularity.d/env d 555 0 0" |
| 480 | "-p" ,(string-append | 488 | "-p" ,(string-append |
| 481 | "/.singularity.d/env/90-environment.sh s 777 0 0 " | 489 | "/.singularity.d/env/90-environment.sh s 777 0 0 " |
| 482 | (relative-file-name "/.singularity.d/env" | 490 | (relative-file-name "/.singularity.d/env" |
| 483 | #$environment)) | 491 | #$environment)) |
| 484 | 492 | ||
| 485 | ;; Create /.singularity.d/actions, and optionally the 'run' | 493 | ;; Create /.singularity.d/actions, and optionally the 'run' |
| 486 | ;; script, used by 'singularity run'. | 494 | ;; script, used by 'singularity run'. |
| 487 | "-p" "/.singularity.d/actions d 555 0 0" | 495 | "-p" "/.singularity.d/actions d 555 0 0" |
| 488 | 496 | ||
| 489 | ,@(if entry-point | 497 | ,@(if entry-point |
| 490 | `(;; This one if for Singularity 2.x. | 498 | `( ;; This one if for Singularity 2.x. |
| 491 | "-p" | 499 | "-p" |
| 492 | ,(string-append | 500 | ,(string-append |
| 493 | "/.singularity.d/actions/run s 777 0 0 " | 501 | "/.singularity.d/actions/run s 777 0 0 " |
| 494 | (relative-file-name "/.singularity.d/actions" | 502 | (relative-file-name "/.singularity.d/actions" |
| 495 | (string-append #$profile "/" | 503 | (string-append #$profile "/" |
| 496 | entry-point))) | 504 | entry-point))) |
| 497 | 505 | ||
| 498 | ;; This one is for Singularity 3.x. | 506 | ;; This one is for Singularity 3.x. |
| 499 | "-p" | 507 | "-p" |
| 500 | ,(string-append | 508 | ,(string-append |
| 501 | "/.singularity.d/runscript s 777 0 0 " | 509 | "/.singularity.d/runscript s 777 0 0 " |
| 502 | (relative-file-name "/.singularity.d" | 510 | (relative-file-name "/.singularity.d" |
| 503 | (string-append #$profile "/" | 511 | (string-append #$profile "/" |
| 504 | entry-point)))) | 512 | entry-point)))) |
| 505 | '()) | 513 | '()) |
| 506 | 514 | ||
| 507 | ;; Create empty mount points. | 515 | ;; Create empty mount points. |
| 508 | "-p" "/proc d 555 0 0" | 516 | "-p" "/proc d 555 0 0" |
| 509 | "-p" "/sys d 555 0 0" | 517 | "-p" "/sys d 555 0 0" |
| 510 | "-p" "/dev d 555 0 0" | 518 | "-p" "/dev d 555 0 0" |
| 511 | "-p" "/home d 555 0 0")) | 519 | "-p" "/home d 555 0 0")) |
| 512 | 520 | ||
| 513 | (when database | 521 | (when database |
| 514 | ;; Initialize /var/guix. | 522 | ;; Initialize /var/guix. |
| 515 | (install-database-and-gc-roots "var-etc" database #$profile) | 523 | (install-database-and-gc-roots "var-etc" database #$profile) |
| 516 | (mksquashfs `("var-etc" ,#$output)))))) | 524 | (mksquashfs `("var-etc" ,#$output))))))) |
| 517 | 525 | ||
| 518 | (gexp->derivation (string-append name | 526 | (gexp->derivation (string-append name |
| 519 | (compressor-extension compressor) | 527 | (compressor-extension compressor) |
diff --git a/guix/store/deduplication.scm b/guix/store/deduplication.scm index b4d37d45255..8564f12107f 100644 --- a/guix/store/deduplication.scm +++ b/guix/store/deduplication.scm | |||
| @@ -34,7 +34,8 @@ | |||
| 34 | #:use-module (guix serialization) | 34 | #:use-module (guix serialization) |
| 35 | #:export (nar-sha256 | 35 | #:export (nar-sha256 |
| 36 | deduplicate | 36 | deduplicate |
| 37 | dump-file/deduplicate)) | 37 | dump-file/deduplicate |
| 38 | copy-file/deduplicate)) | ||
| 38 | 39 | ||
| 39 | ;; XXX: This port is used as a workaround on Guile <= 2.2.4 where | 40 | ;; XXX: This port is used as a workaround on Guile <= 2.2.4 where |
| 40 | ;; 'port-position' throws to 'out-of-range' when the offset is great than or | 41 | ;; 'port-position' throws to 'out-of-range' when the offset is great than or |
| @@ -256,3 +257,16 @@ down the road." | |||
| 256 | (get-hash))))) | 257 | (get-hash))))) |
| 257 | 258 | ||
| 258 | (deduplicate file hash #:store store)) | 259 | (deduplicate file hash #:store store)) |
| 260 | |||
| 261 | (define* (copy-file/deduplicate source target | ||
| 262 | #:key (store (%store-directory))) | ||
| 263 | "Like 'copy-file', but additionally deduplicate TARGET in STORE." | ||
| 264 | (call-with-input-file source | ||
| 265 | (lambda (input) | ||
| 266 | (let ((stat (stat input))) | ||
| 267 | (dump-file/deduplicate target input (stat:size stat) | ||
| 268 | (if (zero? (logand (stat:mode stat) | ||
| 269 | #o100)) | ||
| 270 | 'regular | ||
| 271 | 'executable) | ||
| 272 | #:store store))))) | ||
diff --git a/tests/gexp.scm b/tests/gexp.scm index a0e55178fa6..6e92f0e4b39 100644 --- a/tests/gexp.scm +++ b/tests/gexp.scm | |||
| @@ -736,7 +736,8 @@ | |||
| 736 | (zero? (logand #o222 (stat:mode st))))))) | 736 | (zero? (logand #o222 (stat:mode st))))))) |
| 737 | 737 | ||
| 738 | (mkdir #$output) | 738 | (mkdir #$output) |
| 739 | (populate-store '("graph") #$output) | 739 | (populate-store '("graph") #$output |
| 740 | #:deduplicate? #f) | ||
| 740 | 741 | ||
| 741 | ;; Check whether 'populate-store' canonicalizes | 742 | ;; Check whether 'populate-store' canonicalizes |
| 742 | ;; permissions and timestamps. | 743 | ;; permissions and timestamps. |
diff --git a/tests/store-deduplication.scm b/tests/store-deduplication.scm index e2870a363dc..7b01acae24c 100644 --- a/tests/store-deduplication.scm +++ b/tests/store-deduplication.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2018, 2020 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 | ;;; |
| @@ -25,6 +25,7 @@ | |||
| 25 | #:use-module (rnrs bytevectors) | 25 | #:use-module (rnrs bytevectors) |
| 26 | #:use-module (ice-9 binary-ports) | 26 | #:use-module (ice-9 binary-ports) |
| 27 | #:use-module (srfi srfi-1) | 27 | #:use-module (srfi srfi-1) |
| 28 | #:use-module (srfi srfi-26) | ||
| 28 | #:use-module (srfi srfi-64)) | 29 | #:use-module (srfi srfi-64)) |
| 29 | 30 | ||
| 30 | (test-begin "store-deduplication") | 31 | (test-begin "store-deduplication") |
| @@ -106,4 +107,19 @@ | |||
| 106 | (cons (apply = (map (compose stat:ino stat) identical)) | 107 | (cons (apply = (map (compose stat:ino stat) identical)) |
| 107 | (map (compose stat:nlink stat) identical)))))) | 108 | (map (compose stat:nlink stat) identical)))))) |
| 108 | 109 | ||
| 110 | (test-assert "copy-file/deduplicate" | ||
| 111 | (call-with-temporary-directory | ||
| 112 | (lambda (store) | ||
| 113 | (let ((source (search-path %load-path "gnu/packages/emacs-xyz.scm"))) | ||
| 114 | (for-each (lambda (target) | ||
| 115 | (copy-file/deduplicate source | ||
| 116 | (string-append store target) | ||
| 117 | #:store store)) | ||
| 118 | '("/a" "/b" "/c")) | ||
| 119 | (and (directory-exists? (string-append store "/.links")) | ||
| 120 | (file=? source (string-append store "/a")) | ||
| 121 | (apply = (map (compose stat:ino stat | ||
| 122 | (cut string-append store <>)) | ||
| 123 | '("/a" "/b" "/c")))))))) | ||
| 124 | |||
| 109 | (test-end "store-deduplication") | 125 | (test-end "store-deduplication") |
