summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/guix.texi60
-rw-r--r--guix/scripts/pack.scm104
-rw-r--r--tests/pack.scm40
3 files changed, 198 insertions, 6 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index 8297ad2ca6e..5953fcfa26a 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -6955,6 +6955,16 @@ directly be used as a file system container image with the
6955environment}, using commands like @command{singularity shell} or 6955environment}, using commands like @command{singularity shell} or
6956@command{singularity exec}. 6956@command{singularity exec}.
6957 6957
6958@cindex AppImage, create an AppImage file with @command{guix pack}
6959Another format internally based on SquashFS is
6960@uref{https://appimage.org/, AppImage}. An AppImage file can be created
6961and executed without any special privileges:
6962
6963@example
6964file=$(guix pack -f appimage --entry-point=bin/guile guile)
6965$file --help
6966@end example
6967
6958Several command-line options allow you to customize your pack: 6968Several command-line options allow you to customize your pack:
6959 6969
6960@table @code 6970@table @code
@@ -7071,6 +7081,48 @@ to install Guix-produced @samp{.rpm} packages on a system where
7071installation or other, non-rpm packs. 7081installation or other, non-rpm packs.
7072@end quotation 7082@end quotation
7073 7083
7084@item appimage
7085@cindex AppImage, create an AppImage file with @command{guix pack}
7086This produces an @uref{https://appimage.org/, AppImage file} with the
7087@samp{.AppImage} extension. AppImage is a SquashFS volume prefixed with
7088a runtime that mounts the SquashFS file system and executes the binary
7089provided with @option{--entry-point}. This results in a self-contained
7090archive that bundles the software and all its requirements into a single
7091file. When the file is made executable it runs the packaged software.
7092
7093@example
7094guix pack -f appimage --entry-point=bin/vlc vlc
7095@end example
7096
7097The runtime used by AppImages invokes the @command{fusermount3} command
7098to mount the image quickly. If that command is unavailable, the
7099AppImage fails to run, but it can still be started by passing the
7100@option{--appimage-extract-and-run} flag.
7101
7102@quotation Warning
7103When building an AppImage, always @emph{pass} the @option{--relocatable}
7104option (or @option{-R}, or @option{-RR}) to make sure the image can be
7105used on systems where Guix is not installed. A warning is printed when
7106this option is not used.
7107@end quotation
7108
7109@example
7110guix pack -f appimage --entry-point=bin/hello --relocatable hello
7111@end example
7112
7113@quotation Note
7114The resulting AppImage does not conform to the complete standard as it
7115currently does not contain a @file{.DirIcon} file. This does not impact
7116functionality of the AppImage itself, but possibly that of software used
7117to manage AppImages.
7118@end quotation
7119
7120@quotation Note
7121As the generated AppImage packages the complete dependency graph, it
7122will be larger than comparable AppImage files found online, which depend
7123on host system libraries.
7124@end quotation
7125
7074@end table 7126@end table
7075 7127
7076@cindex relocatable binaries 7128@cindex relocatable binaries
@@ -7160,10 +7212,10 @@ execution engines listed above by setting the
7160 7212
7161@cindex entry point, for Docker and Singularity images 7213@cindex entry point, for Docker and Singularity images
7162@item --entry-point=@var{command} 7214@item --entry-point=@var{command}
7163Use @var{command} as the @dfn{entry point} of the resulting pack, if the pack 7215Use @var{command} as the @dfn{entry point} of the resulting pack, if the
7164format supports it---currently @code{docker} and @code{squashfs} (Singularity) 7216pack format supports it---currently @code{docker}, @code{appimage}, and
7165support it. @var{command} must be relative to the profile contained in the 7217@code{squashfs} (Singularity) support it. @var{command} must be
7166pack. 7218relative to the profile contained in the pack.
7167 7219
7168The entry point specifies the command that tools like @code{docker run} or 7220The entry point specifies the command that tools like @code{docker run} or
7169@code{singularity run} automatically start by default. For example, you can 7221@code{singularity run} automatically start by default. For example, you can
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 7c5fe76fe0b..26ba80b80d8 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -10,6 +10,8 @@
10;;; Copyright © 2022 Alex Griffin <a@ajgrf.com> 10;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
11;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk> 11;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>
12;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com> 12;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
13;;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
14;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
13;;; 15;;;
14;;; This file is part of GNU Guix. 16;;; This file is part of GNU Guix.
15;;; 17;;;
@@ -56,6 +58,7 @@
56 #:use-module ((gnu packages compression) #:hide (zip)) 58 #:use-module ((gnu packages compression) #:hide (zip))
57 #:use-module (gnu packages guile) 59 #:use-module (gnu packages guile)
58 #:use-module (gnu packages base) 60 #:use-module (gnu packages base)
61 #:autoload (gnu packages appimage) (appimage-type2-runtime)
59 #:autoload (gnu packages gnupg) (guile-gcrypt) 62 #:autoload (gnu packages gnupg) (guile-gcrypt)
60 #:autoload (gnu packages guile) (guile2.0-json guile-json) 63 #:autoload (gnu packages guile) (guile2.0-json guile-json)
61 #:use-module (srfi srfi-1) 64 #:use-module (srfi srfi-1)
@@ -64,6 +67,7 @@
64 #:use-module (srfi srfi-35) 67 #:use-module (srfi srfi-35)
65 #:use-module (srfi srfi-37) 68 #:use-module (srfi srfi-37)
66 #:use-module (ice-9 match) 69 #:use-module (ice-9 match)
70 #:use-module (ice-9 optargs)
67 #:export (symlink-spec-option-parser 71 #:export (symlink-spec-option-parser
68 72
69 self-contained-tarball 73 self-contained-tarball
@@ -71,6 +75,7 @@
71 rpm-archive 75 rpm-archive
72 docker-image 76 docker-image
73 squashfs-image 77 squashfs-image
78 self-contained-appimage
74 79
75 %formats 80 %formats
76 guix-pack)) 81 guix-pack))
@@ -974,8 +979,100 @@ PREUN-FILE and POSTUN-FILE can be provided via EXTRA-OPTIONS."
974 (gexp->derivation (string-append name ".rpm") build 979 (gexp->derivation (string-append name ".rpm") build
975 #:target target 980 #:target target
976 #:references-graphs `(("profile" ,profile)))) 981 #:references-graphs `(("profile" ,profile))))
982
983;;;
984;;; AppImage format
985;;;
986(define* (self-contained-appimage name profile
987 #:key target
988 (profile-name "guix-profile")
989 entry-point
990 (compressor (lookup-compressor "zstd"))
991 localstatedir?
992 (symlinks '())
993 (archiver tar)
994 (extra-options '()))
995 "Return a self-contained AppImage containing a store initialized with the
996closure of PROFILE, a derivation. The AppImage contains /gnu/store unless
997RELOCATABLE option is used; if LOCALSTATEDIR? is true, it also contains
998/var/guix, including /var/guix/db with a properly initialized store database.
999
1000SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
1001added to the pack."
1002 (unless entry-point
1003 (leave (G_ "entry-point must be provided in the '~a' format~%")
1004 'appimage))
1005 (let-keywords extra-options #f ((relocatable? #f))
1006 (unless relocatable?
1007 (warning (G_ "AppImages should be built with the --relocatable flag~%"))))
1008
1009 (define runtime-package appimage-type2-runtime)
1010 (define runtime-path "bin/runtime-fuse3")
1011 (define %valid-compressors '("gzip" "zstd"))
1012
1013 (let ((compressor-name (compressor-name compressor)))
1014 (unless (member compressor-name %valid-compressors)
1015 (leave (G_ "~a is not a valid squashfs archive compressor used in
1016generating the AppImage. Valid compressors are: ~a~%")
1017 compressor-name
1018 %valid-compressors)))
977 1019
978 1020 (define builder
1021 (with-extensions (list guile-gcrypt)
1022 (with-imported-modules (source-module-closure
1023 '((guix build store-copy)
1024 (guix build utils))
1025 #:select? not-config?)
1026 #~(begin
1027 (use-modules (guix build utils)
1028 (guix build store-copy)
1029 (rnrs io ports)
1030 (srfi srfi-1)
1031 (srfi srfi-26))
1032
1033 (define (concatenate-files result file1 file2)
1034 "Creates a new file RESULT containing FILE1 followed by FILE2."
1035 (call-with-output-file result
1036 (lambda (output)
1037 (call-with-input-file file1
1038 (lambda (input)
1039 (dump-port input output)))
1040 (call-with-input-file file2
1041 (lambda (input)
1042 (dump-port input output))))))
1043
1044 (let* ((appdir "AppDir")
1045 (squashfs "squashfs")
1046 (profile-items (map store-info-item
1047 (call-with-input-file "profile" read-reference-graph)))
1048 (profile (find (lambda (item)
1049 (string-suffix? "-profile" item))
1050 profile-items)))
1051 (mkdir-p appdir)
1052 ;; Copy all store items from the profile to the AppDir.
1053 (populate-store '("profile") appdir)
1054 ;; Symlink the provided entry-point to AppDir/AppRun.
1055 (symlink (string-append "." profile "/" #$entry-point)
1056 (string-append appdir "/AppRun"))
1057 ;; Create .desktop file as required by the spec.
1058 (make-desktop-entry-file
1059 (string-append appdir "/" #$name ".desktop")
1060 #:name #$name
1061 #:exec #$entry-point)
1062 ;; Compress the AppDir.
1063 (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
1064 squashfs "-root-owned" "-noappend"
1065 "-comp" #+(compressor-name compressor))
1066 ;; Append runtime and squashFS into file AppImage.
1067 (concatenate-files #$output
1068 #$(file-append runtime-package "/" runtime-path)
1069 squashfs)
1070 ;; Add execution permission.
1071 (chmod #$output #o555))))))
1072 (gexp->derivation (string-append name ".AppImage") builder
1073 #:target target
1074 #:references-graphs `(("profile" ,profile))))
1075
979;;; 1076;;;
980;;; Compiling C programs. 1077;;; Compiling C programs.
981;;; 1078;;;
@@ -1311,6 +1408,7 @@ libfakechroot.so and related ld.so machinery as a fallback."
1311 (squashfs . ,squashfs-image) 1408 (squashfs . ,squashfs-image)
1312 (docker . ,docker-image) 1409 (docker . ,docker-image)
1313 (deb . ,debian-archive) 1410 (deb . ,debian-archive)
1411 (appimage . ,self-contained-appimage)
1314 (rpm . ,rpm-archive))) 1412 (rpm . ,rpm-archive)))
1315 1413
1316(define (show-formats) 1414(define (show-formats)
@@ -1327,6 +1425,8 @@ libfakechroot.so and related ld.so machinery as a fallback."
1327 deb Debian archive installable via dpkg/apt")) 1425 deb Debian archive installable via dpkg/apt"))
1328 (display (G_ " 1426 (display (G_ "
1329 rpm RPM archive installable via rpm/yum")) 1427 rpm RPM archive installable via rpm/yum"))
1428 (display (G_ "
1429 appimage AppImage self-contained and executable format"))
1330 (newline)) 1430 (newline))
1331 1431
1332(define (required-option symbol) 1432(define (required-option symbol)
@@ -1694,6 +1794,8 @@ Create a bundle of PACKAGE.\n"))
1694 (process-file-arg opts 'preun-file) 1794 (process-file-arg opts 'preun-file)
1695 #:postun-file 1795 #:postun-file
1696 (process-file-arg opts 'postun-file))) 1796 (process-file-arg opts 'postun-file)))
1797 ('appimage
1798 (list #:relocatable? relocatable?))
1697 (_ '()))) 1799 (_ '())))
1698 (target (assoc-ref opts 'target)) 1800 (target (assoc-ref opts 'target))
1699 (bootstrap? (assoc-ref opts 'bootstrap?)) 1801 (bootstrap? (assoc-ref opts 'bootstrap?))
diff --git a/tests/pack.scm b/tests/pack.scm
index f8a9e09c284..1c1e3125575 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -3,6 +3,7 @@
3;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net> 3;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
4;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com> 4;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
5;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com> 5;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
6;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
6;;; 7;;;
7;;; This file is part of GNU Guix. 8;;; This file is part of GNU Guix.
8;;; 9;;;
@@ -32,7 +33,8 @@
32 #:use-module (guix utils) 33 #:use-module (guix utils)
33 #:use-module ((guix build utils) #:select (%store-directory)) 34 #:use-module ((guix build utils) #:select (%store-directory))
34 #:use-module (gnu packages) 35 #:use-module (gnu packages)
35 #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target)) 36 #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target
37 hello))
36 #:use-module (gnu packages bootstrap) 38 #:use-module (gnu packages bootstrap)
37 #:use-module ((gnu packages package-management) #:select (rpm)) 39 #:use-module ((gnu packages package-management) #:select (rpm))
38 #:use-module ((gnu packages compression) #:select (squashfs-tools)) 40 #:use-module ((gnu packages compression) #:select (squashfs-tools))
@@ -341,6 +343,42 @@
341 (built-derivations (list check)))) 343 (built-derivations (list check))))
342 344
343 (unless store (test-skip 1)) 345 (unless store (test-skip 1))
346 (test-assertm "appimage"
347 (mlet* %store-monad
348 ((guile (set-guile-for-build (default-guile)))
349 (profile -> (profile
350 (content (packages->manifest (list %bootstrap-guile hello)))
351 (hooks '())
352 (locales? #f)))
353 (image (self-contained-appimage "hello-appimage" profile
354 #:entry-point "bin/hello"
355 #:extra-options
356 (list #:relocatable? #t)))
357 (check (gexp->derivation
358 "check-appimage"
359 #~(invoke #$image))))
360 (built-derivations (list check))))
361
362 (unless store (test-skip 1))
363 (test-assertm "appimage + localstatedir"
364 (mlet* %store-monad
365 ((guile (set-guile-for-build (default-guile)))
366 (profile -> (profile
367 (content (packages->manifest (list %bootstrap-guile hello)))
368 (hooks '())
369 (locales? #f)))
370 (image (self-contained-appimage "hello-appimage" profile
371 #:entry-point "bin/hello"
372 #:localstatedir? #t
373 #:extra-options
374 (list #:relocatable? #t)))
375 (check (gexp->derivation
376 "check-appimage"
377 #~(begin
378 (invoke #$image)))))
379 (built-derivations (list check))))
380
381 (unless store (test-skip 1))
344 (test-assertm "deb archive with symlinks and control files" 382 (test-assertm "deb archive with symlinks and control files"
345 (mlet* %store-monad 383 (mlet* %store-monad
346 ((guile (set-guile-for-build (default-guile))) 384 ((guile (set-guile-for-build (default-guile)))