1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2015, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2017, 2018 Ricardo Wurmus <rekado@elephly.net>
4 ;;; Copyright © 2018 Konrad Hinsen <konrad.hinsen@fastmail.net>
5 ;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
6 ;;; Copyright © 2018 Efraim Flashner <efraim@flashner.co.il>
8 ;;; This file is part of GNU Guix.
10 ;;; GNU Guix is free software; you can redistribute it and/or modify it
11 ;;; under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or (at
13 ;;; your option) any later version.
15 ;;; GNU Guix is distributed in the hope that it will be useful, but
16 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
23 (define-module (guix scripts pack)
24 #:use-module (guix scripts)
25 #:use-module (guix ui)
26 #:use-module (guix gexp)
27 #:use-module (guix utils)
28 #:use-module (guix store)
29 #:use-module ((guix status) #:select (with-status-verbosity))
30 #:use-module ((guix self) #:select (make-config.scm))
31 #:use-module (guix grafts)
32 #:autoload (guix inferior) (inferior-package?
34 inferior-package-version)
35 #:use-module (guix monads)
36 #:use-module (guix modules)
37 #:use-module (guix packages)
38 #:use-module (guix profiles)
39 #:use-module (guix describe)
40 #:use-module (guix derivations)
41 #:use-module (guix search-paths)
42 #:use-module (guix build-system gnu)
43 #:use-module (guix scripts build)
44 #:use-module ((guix self) #:select (make-config.scm))
45 #:use-module (gnu packages)
46 #:use-module (gnu packages bootstrap)
47 #:use-module ((gnu packages compression) #:hide (zip))
48 #:use-module (gnu packages guile)
49 #:use-module (gnu packages base)
50 #:autoload (gnu packages package-management) (guix)
51 #:autoload (gnu packages gnupg) (guile-gcrypt)
52 #:autoload (gnu packages guile) (guile2.0-json guile-json)
53 #:use-module (srfi srfi-1)
54 #:use-module (srfi srfi-9)
55 #:use-module (srfi srfi-26)
56 #:use-module (srfi srfi-37)
57 #:use-module (ice-9 match)
60 self-contained-tarball
66 ;; Type of a compression tool.
67 (define-record-type <compressor>
68 (compressor name extension command)
70 (name compressor-name) ;string (e.g., "gzip")
71 (extension compressor-extension) ;string (e.g., ".lz")
72 (command compressor-command)) ;gexp (e.g., #~("/gnu/store/…/gzip" "-9n"))
75 ;; Available compression tools.
76 (list (compressor "gzip" ".gz"
77 #~(#+(file-append gzip "/bin/gzip") "-9n"))
78 (compressor "lzip" ".lz"
79 #~(#+(file-append lzip "/bin/lzip") "-9"))
80 (compressor "xz" ".xz"
81 #~(#+(file-append xz "/bin/xz") "-e"))
82 (compressor "bzip2" ".bz2"
83 #~(#+(file-append bzip2 "/bin/bzip2") "-9"))
84 (compressor "none" "" #f)))
86 ;; This one is only for use in this module, so don't put it in %compressors.
88 (compressor "bootstrap-xz" ".xz"
89 #~(#+(file-append %bootstrap-coreutils&co "/bin/xz") "-e")))
91 (define (lookup-compressor name)
92 "Return the compressor object called NAME. Error out if it could not be
94 (or (find (match-lambda
95 (($ <compressor> name*)
96 (string=? name* name)))
98 (leave (G_ "~a: compressor not found~%") name)))
101 ;; Select (guix …) and (gnu …) modules, except (guix config).
108 (define gcrypt-sqlite3&co
109 ;; Guile-Gcrypt, Guile-SQLite3, and their propagated inputs.
110 (append-map (lambda (package)
112 (match (package-transitive-propagated-inputs package)
113 (((labels packages) ...)
115 (list guile-gcrypt guile-sqlite3)))
117 (define (store-database items)
118 "Return a directory containing a store database where all of ITEMS and their
119 dependencies are registered."
121 (local-file (search-path %load-path
122 "guix/store/schema.sql")))
127 (string-append "closure" (number->string n)))
128 (iota (length items))))
131 (with-extensions gcrypt-sqlite3&co
132 (with-imported-modules (source-module-closure
133 '((guix build store-copy)
134 (guix store database)))
136 (use-modules (guix store database)
137 (guix build store-copy)
140 (define (read-closure closure)
141 (call-with-input-file closure read-reference-graph))
143 (let ((items (append-map read-closure '#$labels)))
144 (register-items items
145 #:state-directory #$output
147 #:reset-timestamps? #f
148 #:registration-time %epoch
149 #:schema #$schema))))))
151 (computed-file "store-database" build
152 #:options `(#:references-graphs ,(zip labels items))))
154 (define* (self-contained-tarball name profile
156 (profile-name "guix-profile")
159 (compressor (first %compressors))
163 "Return a self-contained tarball containing a store initialized with the
164 closure of PROFILE, a derivation. The tarball contains /gnu/store; if
165 LOCALSTATEDIR? is true, it also contains /var/guix, including /var/guix/db
166 with a properly initialized store database.
168 SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
172 (file-append (store-database (list profile))
176 (with-imported-modules (source-module-closure
180 #:select? not-config?)
182 (use-modules (guix build utils)
183 ((guix build union) #:select (relative-file-name))
189 (define %root "root")
191 (define symlink->directives
192 ;; Return "populate directives" to make the given symlink and its
193 ;; parent directories.
196 (let ((target (string-append #$profile "/" target))
197 (parent (dirname source)))
198 ;; Never add a 'directory' directive for "/" so as to
199 ;; preserve its ownnership when extracting the archive (see
200 ;; below), and also because this would lead to adding the
201 ;; same entries twice in the tarball.
202 `(,@(if (string=? parent "/")
204 `((directory ,parent)))
206 -> ,(relative-file-name parent target)))))))
209 ;; Fully-qualified symlinks.
210 (append-map symlink->directives '#$symlinks))
212 ;; The --sort option was added to GNU tar in version 1.28, released
213 ;; 2014-07-28. For testing, we use the bootstrap tar, which is
214 ;; older and doesn't support it.
215 (define tar-supports-sort?
216 (zero? (system* (string-append #+archiver "/bin/tar")
217 "cf" "/dev/null" "--files-from=/dev/null"
220 ;; Add 'tar' to the search path.
221 (setenv "PATH" #+(file-append archiver "/bin"))
223 ;; Note: there is not much to gain here with deduplication and there
224 ;; is the overhead of the '.links' directory, so turn it off.
225 ;; Furthermore GNU tar < 1.30 sometimes fails to extract tarballs
227 ;; <http://lists.gnu.org/archive/html/bug-tar/2017-11/msg00009.html>.
228 (populate-single-profile-directory %root
230 #:profile-name #$profile-name
232 #:database #+database)
235 (for-each (cut evaluate-populate-directive <> %root)
238 ;; Create the tarball. Use GNU format so there's no file name
239 ;; length limitation.
240 (with-directory-excursion %root
242 (zero? (apply system* "tar"
243 #+@(if (compressor-command compressor)
246 '#+(compressor-command compressor)))
250 ;; Avoid non-determinism in the archive. Use
251 ;; mtime = 1, not zero, because that is what the
252 ;; daemon does for files in the store (see the
253 ;; 'mtimeStore' constant in local-store.cc.)
254 (if tar-supports-sort? "--sort=name" "--mtime=@1")
255 "--mtime=@1" ;for files in /var/guix
261 ;; Avoid adding / and /var to the tarball, so
262 ;; that the ownership and permissions of those
263 ;; directories will not be overwritten when
264 ;; extracting the archive. Do not include /root
265 ;; because the root account might have a
266 ;; different home directory.
267 #$@(if localstatedir?
271 (string-append "." (%store-directory))
274 (filter-map (match-lambda
275 (('directory directory)
276 (string-append "." directory))
278 (string-append "." source))
283 (warning (G_ "entry point not supported in the '~a' format~%")
286 (gexp->derivation (string-append name ".tar"
287 (compressor-extension compressor))
289 #:references-graphs `(("profile" ,profile))))
291 (define (singularity-environment-file profile)
292 "Return a shell script that defines the environment variables corresponding
293 to the search paths of PROFILE."
295 (with-extensions (list guile-gcrypt)
296 (with-imported-modules `(((guix config) => ,(make-config.scm))
297 ,@(source-module-closure
300 #:select? not-config?))
302 (use-modules (guix profiles) (guix search-paths)
305 (call-with-output-file #$output
307 (for-each (match-lambda
309 (format port "~a=~a~%export ~a~%"
310 (search-path-specification-variable spec)
312 (search-path-specification-variable spec))))
313 (profile-search-paths #$profile))))))))
315 (computed-file "singularity-environment.sh" build))
317 (define* (squashfs-image name profile
319 (profile-name "guix-profile")
320 (compressor (first %compressors))
324 (archiver squashfs-tools))
325 "Return a squashfs image containing a store initialized with the closure of
326 PROFILE, a derivation. The image contains a subset of /gnu/store, empty mount
327 points for virtual file systems (like procfs), and optional symlinks.
329 SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
333 (file-append (store-database (list profile))
337 (singularity-environment-file profile))
340 ;; Singularity requires /bin (specifically /bin/sh), so ensure that
341 ;; symlink is created.
342 (if (find (match-lambda
347 `(("/bin" -> "bin") ,@symlinks)))
350 (with-imported-modules (source-module-closure
352 (guix build store-copy)
355 #:select? not-config?)
357 (use-modules (guix build utils)
358 (guix build store-copy)
359 ((guix build union) #:select (relative-file-name))
365 (define database #+database)
366 (define entry-point #$entry-point)
368 (define (mksquashfs args)
369 (apply invoke "mksquashfs"
372 ;; Do not create a "recovery file" when appending to the
373 ;; file system since it's useless in this case.
376 ;; Do not attempt to store extended attributes.
377 ;; See <https://bugs.gnu.org/40043>.
380 ;; Set file times and the file system creation time to
381 ;; one second after the Epoch.
382 "-all-time" "1" "-mkfs-time" "1"
384 ;; Reset all UIDs and GIDs.
385 "-force-uid" "0" "-force-gid" "0")))
387 (setenv "PATH" (string-append #$archiver "/bin"))
389 ;; We need an empty file in order to have a valid file argument when
390 ;; we reparent the root file system. Read on for why that's
392 (with-output-to-file ".empty" (lambda () (display "")))
394 ;; Create the squashfs image in several steps.
395 ;; Add all store items. Unfortunately mksquashfs throws away all
396 ;; ancestor directories and only keeps the basename. We fix this
397 ;; in the following invocations of mksquashfs.
398 (mksquashfs `(,@(map store-info-item
399 (call-with-input-file "profile"
400 read-reference-graph))
404 ;; Do not perform duplicate checking because we
405 ;; don't have any dupes.
408 ,#+(compressor-name compressor)))
410 ;; Here we reparent the store items. For each sub-directory of
411 ;; the store prefix we need one invocation of "mksquashfs".
412 (for-each (lambda (dir)
413 (mksquashfs `(".empty"
415 "-root-becomes" ,dir)))
416 (reverse (string-tokenize (%store-directory)
417 (char-set-complement (char-set #\/)))))
419 ;; Add symlinks and mount points.
423 ;; Create SYMLINKS via pseudo file definitions.
427 ;; Create relative symlinks to work around a bug in
429 ;; https://bugs.gnu.org/34913
430 ;; https://github.com/sylabs/singularity/issues/1487
431 (let ((target (string-append #$profile "/" target)))
434 ;; name s mode uid gid symlink
437 (relative-file-name (dirname source)
441 "-p" "/.singularity.d d 555 0 0"
443 ;; Create the environment file.
444 "-p" "/.singularity.d/env d 555 0 0"
446 "/.singularity.d/env/90-environment.sh s 777 0 0 "
447 (relative-file-name "/.singularity.d/env"
450 ;; Create /.singularity.d/actions, and optionally the 'run'
451 ;; script, used by 'singularity run'.
452 "-p" "/.singularity.d/actions d 555 0 0"
455 `(;; This one if for Singularity 2.x.
458 "/.singularity.d/actions/run s 777 0 0 "
459 (relative-file-name "/.singularity.d/actions"
460 (string-append #$profile "/"
463 ;; This one is for Singularity 3.x.
466 "/.singularity.d/runscript s 777 0 0 "
467 (relative-file-name "/.singularity.d"
468 (string-append #$profile "/"
472 ;; Create empty mount points.
473 "-p" "/proc d 555 0 0"
474 "-p" "/sys d 555 0 0"
475 "-p" "/dev d 555 0 0"
476 "-p" "/home d 555 0 0"))
479 ;; Initialize /var/guix.
480 (install-database-and-gc-roots "var-etc" database #$profile)
481 (mksquashfs `("var-etc" ,#$output))))))
483 (gexp->derivation (string-append name
484 (compressor-extension compressor)
487 #:references-graphs `(("profile" ,profile))))
489 (define* (docker-image name profile
491 (profile-name "guix-profile")
492 (compressor (first %compressors))
497 "Return a derivation to construct a Docker image of PROFILE. The
498 image is a tarball conforming to the Docker Image Specification, compressed
499 with COMPRESSOR. It can be passed to 'docker load'. If TARGET is true, it
500 must a be a GNU triplet and it is used to derive the architecture metadata in
504 (file-append (store-database (list profile))
507 (define defmod 'define-module) ;trick Geiser
510 ;; Guile-JSON and Guile-Gcrypt are required by (guix docker).
511 (with-extensions (list guile-json-3 guile-gcrypt)
512 (with-imported-modules `(((guix config) => ,(make-config.scm))
513 ,@(source-module-closure
515 (guix build store-copy)
518 #:select? not-config?))
520 (use-modules (guix docker) (guix build store-copy)
521 (guix profiles) (guix search-paths)
522 (srfi srfi-1) (srfi srfi-19)
528 (cons (search-path-specification-variable spec)
530 (profile-search-paths #$profile)))
532 (define symlink->directives
533 ;; Return "populate directives" to make the given symlink and its
534 ;; parent directories.
537 (let ((target (string-append #$profile "/" target))
538 (parent (dirname source)))
539 `((directory ,parent)
540 (,source -> ,target))))))
543 ;; Create a /tmp directory, as some programs expect it, and
545 `((directory "/tmp" ,(getuid) ,(getgid) #o1777)
546 ,@(append-map symlink->directives '#$symlinks)))
549 ;; Compute a meaningful "repository" name, which will show up in
550 ;; the output of "docker images".
551 (let ((manifest (profile-manifest #$profile)))
552 (let loop ((names (map manifest-entry-name
553 (manifest-entries manifest))))
554 (define str (string-join names "-"))
555 (if (< (string-length str) 40)
559 ((names ... _) (loop names))))))) ;drop one entry
561 (setenv "PATH" (string-append #$archiver "/bin"))
563 (build-docker-image #$output
565 (call-with-input-file "profile"
566 read-reference-graph))
569 #:database #+database
570 #:system (or #$target (utsname:machine (uname)))
571 #:environment environment
574 #~(list (string-append #$profile "/"
576 #:extra-files directives
577 #:compressor '#$(compressor-command compressor)
578 #:creation-time (make-time time-utc 0 1))))))
580 (gexp->derivation (string-append name ".tar"
581 (compressor-extension compressor))
583 #:references-graphs `(("profile" ,profile))))
587 ;;; Compiling C programs.
590 ;; A C compiler. That lowers to a single program that can be passed typical C
591 ;; compiler flags, and it makes sure the whole toolchain is available.
592 (define-record-type <c-compiler>
593 (%c-compiler toolchain guile)
595 (toolchain c-compiler-toolchain)
596 (guile c-compiler-guile))
598 (define* (c-compiler #:optional inputs
599 #:key (guile (default-guile)))
600 (%c-compiler inputs guile))
602 (define (bootstrap-c-compiler)
603 "Return the C compiler that uses the bootstrap toolchain. This is used only
604 by '--bootstrap', for testing purposes."
605 (define bootstrap-toolchain
606 (list (first (assoc-ref (%bootstrap-inputs) "gcc"))
607 (first (assoc-ref (%bootstrap-inputs) "binutils"))
608 (first (assoc-ref (%bootstrap-inputs) "libc"))))
610 (c-compiler bootstrap-toolchain
611 #:guile %bootstrap-guile))
613 (define-gexp-compiler (c-compiler-compiler (compiler <c-compiler>) system target)
614 "Lower COMPILER to a single script that does the right thing."
616 (or (c-compiler-toolchain compiler)
617 (list (first (assoc-ref (standard-packages) "gcc"))
618 (first (assoc-ref (standard-packages) "ld-wrapper"))
619 (first (assoc-ref (standard-packages) "binutils"))
620 (first (assoc-ref (standard-packages) "libc"))
621 (gexp-input (first (assoc-ref (standard-packages) "libc"))
625 (match (append-map package-propagated-inputs
626 (filter package? toolchain))
627 (((labels things . _) ...)
628 (append toolchain things))))
632 (append-map package-native-search-paths
633 (filter package? inputs))))
636 (with-imported-modules (source-module-closure
638 (guix search-paths)))
640 (use-modules (guix build utils) (guix search-paths)
643 (define (output-file args)
644 (let loop ((args args))
647 (("-o" file _ ...) file)
648 ((head rest ...) (loop rest)))))
650 (set-search-paths (map sexp->search-path-specification
651 '#$(map search-path-specification->sexp
655 (let ((output (output-file (command-line))))
656 (apply invoke "gcc" (cdr (command-line)))
657 (invoke "strip" output)))))
660 ;; TODO: Yep, we'll have to do it someday!
661 (leave (G_ "cross-compilation not implemented here;
662 please email '~a'~%")
663 (@ (guix config) %guix-bug-report-address)))
665 (gexp->script "c-compiler" run
666 #:guile (c-compiler-guile compiler)))
673 (define* (wrapped-package package
676 (compiler (c-compiler))
678 "Return the OUTPUT of PACKAGE with its binaries wrapped such that they are
679 relocatable. When PROOT? is true, include PRoot in the result and use it as a
680 last resort for relocation."
682 (local-file (search-auxiliary-file "run-in-namespace.c")))
685 (specification->package "proot-static"))
688 (with-imported-modules (source-module-closure
692 (use-modules (guix build utils)
693 ((guix build union) #:select (relative-file-name))
698 ;; The OUTPUT* output of PACKAGE.
699 (ungexp package output*))
702 ;; The output we are producing.
703 (ungexp output output*))
705 (define (strip-store-prefix file)
706 ;; Given a file name like "/gnu/store/…-foo-1.2/bin/foo", return
708 (let* ((len (string-length (%store-directory)))
709 (base (string-drop file (+ 1 len))))
710 (match (string-index base #\/)
712 (index (string-drop base index)))))
714 (define (build-wrapper program)
715 ;; Build a user-namespace wrapper for PROGRAM.
716 (format #t "building wrapper for '~a'...~%" program)
717 (copy-file #$runner "run.c")
720 (("@WRAPPED_PROGRAM@") program)
721 (("@STORE_DIRECTORY@") (%store-directory)))
723 (let* ((base (strip-store-prefix program))
724 (result (string-append target "/" base))
727 #$(file-append (proot) "/bin/proot")
728 (+ (string-length (%store-directory))
730 (mkdir-p (dirname result))
731 (apply invoke #$compiler "-std=gnu99" "-static" "-Os" "-g0" "-Wall"
734 (list (string-append "-DPROOT_PROGRAM=\""
737 (delete-file "run.c")))
739 (setvbuf (current-output-port) 'line)
741 ;; Link the top-level files of PACKAGE so that search paths are
742 ;; properly defined in PROFILE/etc/profile.
744 (for-each (lambda (file)
745 (unless (member file '("." ".." "bin" "sbin" "libexec"))
746 (let ((file* (string-append input "/" file)))
747 (symlink (relative-file-name target file*)
748 (string-append target "/" file)))))
751 (for-each build-wrapper
752 (append (find-files (string-append input "/bin"))
753 (find-files (string-append input "/sbin"))
754 (find-files (string-append input "/libexec")))))))
756 (computed-file (string-append
757 (cond ((package? package)
758 (package-full-name package "-"))
759 ((inferior-package? package)
760 (string-append (inferior-package-name package)
762 (inferior-package-version package)))
767 (define (wrapped-manifest-entry entry . args)
770 (item (apply wrapped-package
771 (manifest-entry-item entry)
772 (manifest-entry-output entry)
777 ;;; Command-line options.
780 (define %default-options
781 ;; Alist of default option values.
783 (profile-name . "guix-profile")
784 (system . ,(%current-system))
788 (print-build-trace? . #t)
789 (print-extended-build-trace? . #t)
790 (multiplexed-build-output? . #t)
794 (compressor . ,(first %compressors))))
797 ;; Supported pack formats.
798 `((tarball . ,self-contained-tarball)
799 (squashfs . ,squashfs-image)
800 (docker . ,docker-image)))
802 (define (show-formats)
803 ;; Print the supported pack formats.
804 (display (G_ "The supported formats for 'guix pack' are:"))
807 tarball Self-contained tarball, ready to run on another machine"))
809 squashfs Squashfs image suitable for Singularity"))
811 docker Tarball ready for 'docker load'"))
815 ;; Specifications of the command-line options.
816 (cons* (option '(#\h "help") #f #f
820 (option '(#\V "version") #f #f
822 (show-version-and-exit "guix pack")))
824 (option '(#\n "dry-run") #f #f
825 (lambda (opt name arg result)
826 (alist-cons 'dry-run? #t result)))
827 (option '(#\d "derivation") #f #f
828 (lambda (opt name arg result)
829 (alist-cons 'derivation-only? #t result)))
831 (option '(#\f "format") #t #f
832 (lambda (opt name arg result)
833 (alist-cons 'format (string->symbol arg) result)))
834 (option '("list-formats") #f #f
838 (option '(#\R "relocatable") #f #f
839 (lambda (opt name arg result)
840 (match (assq-ref result 'relocatable?)
842 (alist-cons 'relocatable? #t result))
844 (alist-cons 'relocatable? 'proot
845 (alist-delete 'relocatable? result))))))
846 (option '(#\e "expression") #t #f
847 (lambda (opt name arg result)
848 (alist-cons 'expression arg result)))
849 (option '(#\m "manifest") #t #f
850 (lambda (opt name arg result)
851 (alist-cons 'manifest arg result)))
852 (option '(#\s "system") #t #f
853 (lambda (opt name arg result)
854 (alist-cons 'system arg
855 (alist-delete 'system result eq?))))
856 (option '("entry-point") #t #f
857 (lambda (opt name arg result)
858 (alist-cons 'entry-point arg result)))
859 (option '("target") #t #f
860 (lambda (opt name arg result)
861 (alist-cons 'target arg
862 (alist-delete 'target result eq?))))
863 (option '(#\C "compression") #t #f
864 (lambda (opt name arg result)
865 (alist-cons 'compressor (lookup-compressor arg)
867 (option '(#\S "symlink") #t #f
868 (lambda (opt name arg result)
869 ;; Note: Using 'string-split' allows us to handle empty
870 ;; TARGET (as in "/opt/guile=", meaning that /opt/guile is
871 ;; a symlink to the profile) correctly.
872 (match (string-split arg (char-set #\=))
874 (let ((symlinks (assoc-ref result 'symlinks)))
875 (alist-cons 'symlinks
876 `((,source -> ,target) ,@symlinks)
877 (alist-delete 'symlinks result eq?))))
879 (leave (G_ "~a: invalid symlink specification~%")
881 (option '("save-provenance") #f #f
882 (lambda (opt name arg result)
883 (alist-cons 'save-provenance? #t result)))
884 (option '("localstatedir") #f #f
885 (lambda (opt name arg result)
886 (alist-cons 'localstatedir? #t result)))
887 (option '("profile-name") #t #f
888 (lambda (opt name arg result)
890 ((or "guix-profile" "current-guix")
891 (alist-cons 'profile-name arg result))
893 (leave (G_ "~a: unsupported profile name~%") arg)))))
894 (option '(#\r "root") #t #f
895 (lambda (opt name arg result)
896 (alist-cons 'gc-root arg result)))
898 (option '(#\v "verbosity") #t #f
899 (lambda (opt name arg result)
900 (let ((level (string->number* arg)))
901 (alist-cons 'verbosity level
902 (alist-delete 'verbosity result)))))
903 (option '("bootstrap") #f #f
904 (lambda (opt name arg result)
905 (alist-cons 'bootstrap? #t result)))
907 (append %transformation-options
908 %standard-build-options)))
911 (display (G_ "Usage: guix pack [OPTION]... PACKAGE...
912 Create a bundle of PACKAGE.\n"))
913 (show-build-options-help)
915 (show-transformation-options-help)
918 -f, --format=FORMAT build a pack in the given FORMAT"))
920 --list-formats list the formats available"))
922 -R, --relocatable produce relocatable executables"))
924 -e, --expression=EXPR consider the package EXPR evaluates to"))
926 -s, --system=SYSTEM attempt to build for SYSTEM--e.g., \"i686-linux\""))
928 --target=TRIPLET cross-build for TRIPLET--e.g., \"armel-linux-gnu\""))
930 -C, --compression=TOOL compress using TOOL--e.g., \"lzip\""))
932 -S, --symlink=SPEC create symlinks to the profile according to SPEC"))
934 -m, --manifest=FILE create a pack with the manifest from FILE"))
936 --entry-point=PROGRAM
937 use PROGRAM as the entry point of the pack"))
939 --save-provenance save provenance information"))
941 --localstatedir include /var/guix in the resulting pack"))
944 populate /var/guix/profiles/.../NAME"))
946 -r, --root=FILE make FILE a symlink to the result, and register it
947 as a garbage collector root"))
949 -d, --derivation return the derivation of the pack"))
951 -v, --verbosity=LEVEL use the given verbosity LEVEL"))
953 --bootstrap use the bootstrap binaries to build the pack"))
956 -h, --help display this help and exit"))
958 -V, --version display version information and exit"))
960 (show-bug-report-information))
967 (define (guix-pack . args)
969 (parse-command-line args %options (list %default-options)))
971 (define maybe-package-argument
972 ;; Given an option pair, return a package, a package/output tuple, or #f.
977 (specification->package+output spec))
980 (read/eval-package-expression exp))
983 (define (manifest-from-args store opts)
984 (let* ((transform (options->transformation opts))
985 (packages (map (match-lambda
986 (((? package? package) output)
987 (list (transform store package) output))
988 ((? package? package)
989 (list (transform store package) "out")))
991 (filter-map maybe-package-argument opts))))
992 (manifests (filter-map (match-lambda
993 (('manifest . file) file)
996 (define with-provenance
997 (if (assoc-ref opts 'save-provenance?)
999 (map-manifest-entries
1001 (let ((entry (manifest-entry-with-provenance entry)))
1002 (unless (assq 'provenance (manifest-entry-properties entry))
1003 (warning (G_ "could not determine provenance of package ~a~%")
1004 (manifest-entry-name entry)))
1011 ((and (not (null? manifests)) (not (null? packages)))
1012 (leave (G_ "both a manifest and a package list were given~%")))
1013 ((not (null? manifests))
1014 (concatenate-manifests
1016 (let ((user-module (make-user-module
1017 '((guix profiles) (gnu)))))
1018 (load* file user-module)))
1021 (packages->manifest packages))))))
1023 (with-error-handling
1025 (with-status-verbosity (assoc-ref opts 'verbosity)
1026 ;; Set the build options before we do anything else.
1027 (set-build-options-from-command-line store opts)
1029 (with-build-handler (build-notifier #:dry-run?
1030 (assoc-ref opts 'dry-run?)
1032 (assoc-ref opts 'substitutes?))
1033 (parameterize ((%graft? (assoc-ref opts 'graft?))
1034 (%guile-for-build (package-derivation
1036 (if (assoc-ref opts 'bootstrap?)
1038 (canonical-package guile-2.2))
1039 (assoc-ref opts 'system)
1040 #:graft? (assoc-ref opts 'graft?))))
1041 (let* ((derivation? (assoc-ref opts 'derivation-only?))
1042 (relocatable? (assoc-ref opts 'relocatable?))
1043 (proot? (eq? relocatable? 'proot))
1044 (manifest (let ((manifest (manifest-from-args store opts)))
1045 ;; Note: We cannot honor '--bootstrap' here because
1046 ;; 'glibc-bootstrap' lacks 'libc.a'.
1048 (map-manifest-entries
1049 (cut wrapped-manifest-entry <> #:proot? proot?)
1052 (pack-format (assoc-ref opts 'format))
1053 (name (string-append (symbol->string pack-format)
1055 (target (assoc-ref opts 'target))
1056 (bootstrap? (assoc-ref opts 'bootstrap?))
1057 (compressor (if bootstrap?
1059 (assoc-ref opts 'compressor)))
1060 (archiver (if (equal? pack-format 'squashfs)
1063 %bootstrap-coreutils&co
1065 (symlinks (assoc-ref opts 'symlinks))
1066 (build-image (match (assq-ref %formats pack-format)
1067 ((? procedure? proc) proc)
1069 (leave (G_ "~a: unknown pack format~%")
1071 (localstatedir? (assoc-ref opts 'localstatedir?))
1072 (entry-point (assoc-ref opts 'entry-point))
1073 (profile-name (assoc-ref opts 'profile-name))
1074 (gc-root (assoc-ref opts 'gc-root)))
1075 (define (lookup-package package)
1076 (manifest-lookup manifest (manifest-pattern (name package))))
1078 (when (null? (manifest-entries manifest))
1079 (warning (G_ "no packages specified; building an empty pack~%")))
1081 (when (and (eq? pack-format 'squashfs)
1082 (not (any lookup-package '("bash" "bash-minimal"))))
1083 (warning (G_ "Singularity requires you to provide a shell~%"))
1084 (display-hint (G_ "Add @code{bash} or @code{bash-minimal} \
1085 to your package list.")))
1087 (run-with-store store
1088 (mlet* %store-monad ((profile (profile-derivation
1091 ;; Always produce relative
1092 ;; symlinks for Singularity (see
1093 ;; <https://bugs.gnu.org/34913>).
1094 #:relative-symlinks?
1096 (eq? 'squashfs pack-format))
1098 #:hooks (if bootstrap?
1100 %default-profile-hooks)
1101 #:locales? (not bootstrap?)
1103 (drv (build-image name profile
1118 (mbegin %store-monad
1120 (return (format #t "~a~%"
1121 (derivation-file-name drv))))
1122 (munless derivation?
1123 (built-derivations (list drv))
1125 (register-root* (match (derivation->output-paths drv)
1126 (((names . items) ...)
1129 (return (format #t "~a~%"
1130 (derivation->output-path drv))))))
1131 #:system (assoc-ref opts 'system)))))))))