[PATCH] pack: Add support for AppImage pack format.

  • Done
  • quality assurance status badge
Details
6 participants
  • outlook user
  • Ludovic Courtès
  • Maxim Cournoyer
  • Noé Lopez
  • Noé Lopez
  • pelzflorian (Florian Pelz)
Owner
unassigned
Submitted by
Noé Lopez
Severity
normal
N
N
Noé Lopez wrote on 16 Oct 23:51 +0200
(address . guix-patches@gnu.org)
da8f8eca32729bf35117107993b83359267e5638.1729115489.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* guix/scripts/pack.scm: Add Appimage format.
* gnu/packages/appimage.scm
(gnu packages appimage): New module.
(fuse-for-appimage, squashfuse-for-appimage)
(appimage-type2-runtime): New variables.
* doc/guix.texi: Document AppImage pack.

Co-authored-by: Noé Lopez <noelopez@free.fr>
Change-Id: I09f1241dfb9b267f94dce59914dea527d35ac60e
---
Hi,

This patch adds a new AppImage export format to « guix pack ». This
enables guix users to easily share packets or environments with their
peers that don’t have guix via a statically linked, runs-everywhere
executable.

Sebastian and I co-authored this patch, I did the runtime packaging
and Sebastian did the actual command. I’ve personally tested the
generated AppImages with my friend’s various distros, they work great!

Here are some cool examples to try (in ./pre-inst-env):
guix pack -f appimage -R --entry-point=bin/hello hello
guix pack -f appimage -R --entry-point=bin/openttd openttd
guix pack -f appimage -R --entry-point=bin/torbrowser torbrowser

Keep in mind the generated AppImage files don’t have execution
permission, so you need to copy them and chmod it :)

Have a great day,
Noé Lopez

doc/guix.texi | 56 +++++++++++-
gnu/packages/appimage.scm | 179 ++++++++++++++++++++++++++++++++++++++
guix/scripts/pack.scm | 100 ++++++++++++++++++++-
3 files changed, 333 insertions(+), 2 deletions(-)
create mode 100644 gnu/packages/appimage.scm

Toggle diff (425 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b91d229d7c..327876c92f 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -6949,6 +6949,18 @@ Invoking guix pack
environment}, using commands like @command{singularity shell} or
@command{singularity exec}.
+@cindex AppImage, create an AppImage file with guix pack
+Another format internally based on SquashFS is
+@uref{https://appimage.org/, AppImage}. An AppImage file can be made
+executable and run without any special privileges:
+
+@example
+guix pack -f appimage --entry-point=bin/guile guile
+cp @var{file} . && chmod u+x $(basename @var{file})
+./$(basename @var{file}) --help
+@end example
+where @var{file} is the image returned by @command{guix pack}.
+
Several command-line options allow you to customize your pack:
@table @code
@@ -7065,6 +7077,48 @@ Invoking guix pack
installation or other, non-rpm packs.
@end quotation
+@item appimage
+@cindex AppImage, create an AppImage file with guix pack
+This produces an AppImage file with the @samp{.AppImage} extension.
+AppImage is a SquashFS volume prefixed with a runtime that mounts the
+SquashFS file system and executes the binary provided with
+@option{--entry-point}. This results in a self-contained archive that
+bundles the software and all its requirements into a single file. When
+the file is made executable it runs the packaged software.
+
+@example
+guix pack -f appimage --entry-point=bin/vlc vlc
+@end example
+
+The runtime used by AppImages makes use of libfuse to mount the image
+quickly. If libfuse is not available, the AppImage can still be started
+using the @option{--appimage-extract-and-run} flag.
+
+@quotation Warning
+Unless @option{--relocatable} is used, the software will contain
+references to items inside the guix store, which are not present on
+systems without Guix. It is recommended to use @option{--relocatable}
+when distributing software to 3rd parties. A warning is printed when
+this option is not used.
+@end quotation
+
+@example
+guix pack -f appimage --entry-point=bin/hello --relocatable hello
+@end example
+
+@quotation Note
+The resulting AppImage does not conform to the complete standard as it
+currently does not contain a @code{.DirIcon} file. This does not impact
+functionality of the AppImage itself, but possibly that of software used
+to manage AppImages.
+@end quotation
+
+@quotation Note
+As the generated AppImage packages the complete dependency graph, it
+will be larger than comparable AppImage files found online, which depend
+on host system libraries.
+@end quotation
+
@end table
@cindex relocatable binaries
@@ -7155,7 +7209,7 @@ Invoking guix pack
@cindex entry point, for Docker and Singularity images
@item --entry-point=@var{command}
Use @var{command} as the @dfn{entry point} of the resulting pack, if the pack
-format supports it---currently @code{docker} and @code{squashfs} (Singularity)
+format supports it---currently @code{docker}, @code{appimage} and @code{squashfs} (Singularity)
support it. @var{command} must be relative to the profile contained in the
pack.
diff --git a/gnu/packages/appimage.scm b/gnu/packages/appimage.scm
new file mode 100644
index 0000000000..3bf9d84181
--- /dev/null
+++ b/gnu/packages/appimage.scm
@@ -0,0 +1,179 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages appimage)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (gnu packages)
+ #:use-module (gnu packages base)
+ #:use-module (gnu packages compression)
+ #:use-module (gnu packages file-systems)
+ #:use-module (gnu packages linux)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix download)
+ #:use-module (guix gexp)
+ #:use-module (guix git-download)
+ #:use-module (guix packages))
+
+(define fuse-for-appimage
+ (package
+ (inherit fuse)
+ (name "fuse")
+ (version "3.15.0")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/libfuse/libfuse/releases/"
+ "download/fuse-"
+ version
+ "/fuse-"
+ version
+ ".tar.xz"))
+ (sha256
+ (base32 "181cx8g40ki8m85j0pjw50kv4h733g08c769db6przqwbvyrqn3h"))))
+ (arguments
+ `(#:configure-flags ,#~(list (string-append "-Dudevrulesdir="
+ #$output "/udev/rules.d")
+ "-Duseroot=false" "--default-library=static")
+ #:tests? #f
+ #:phases ,#~(modify-phases %standard-phases
+ (add-after 'unpack 'set-file-names
+ (lambda* (#:key inputs #:allow-other-keys)
+ ;; libfuse calls out to mount(8) and umount(8). Make sure
+ ;; it refers to the right ones.
+ (substitute* '("lib/mount_util.c")
+ (("/bin/(u?)mount" _ maybe-u)
+ (search-input-file inputs
+ (string-append "bin/" maybe-u
+ "mount"))))
+ (substitute* '("util/mount.fuse.c")
+ (("/bin/sh")
+ (search-input-file inputs "/bin/sh")))
+
+ ;; This hack leads libfuse to search for 'fusermount' in
+ ;; $PATH, where it may find a setuid-root binary, instead of
+ ;; trying solely $out/sbin/fusermount and failing because
+ ;; it's not setuid.
+ (substitute* "lib/meson.build"
+ (("-DFUSERMOUNT_DIR=[[:graph:]]+")
+ "-DFUSERMOUNT_DIR=\"/var/empty\"'"))))
+ (add-after 'unpack 'fix-install
+ (lambda* (#:key inputs #:allow-other-keys)
+ (substitute* '("util/meson.build")
+ (("install_helper.sh")
+ "true"))
+ (substitute* '("util/meson.build")
+ (("fuseconf_path = .*")
+ "fuseconf_path = '/etc/fuse.conf'"))))
+ (add-before 'configure 'set-paths
+ (lambda* (#:key inputs outputs #:allow-other-keys)
+ (let ((dummy-init.d (string-append (getcwd)
+ "/etc/init.d")))
+ (setenv "MOUNT_FUSE_PATH"
+ (string-append #$output "/sbin"))
+ (setenv "UDEV_RULES_PATH"
+ (string-append #$output "/lib/udev/rules.d"))))))))))
+
+(define squashfuse-for-appimage
+ (let ((revision "0")
+ (commit "e51978cd6bb5c4d16fae9eee43d0b258f570bb0f"))
+ (package
+ (inherit squashfuse)
+ (name "squashfuse")
+ (version (git-version "0.1.104" revision commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/vasi/squashfuse")
+ (commit commit)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32 "02421zsgcvbip5ff999ci4c748zxsc34yycjfp7rq8afrlnlvl29"))))
+ (arguments
+ (list
+ #:configure-flags #~'("CFLAGS=-ffunction-sections -fdata-sections -Os -no-pie"
+ "LDFLAGS=-static")
+ #:phases #~(modify-phases %standard-phases
+ (add-after 'install 'install-private-headers
+ (lambda _
+ (install-file "fuseprivate.h"
+ (string-append #$output
+ "/include/squashfuse/")))))))
+ (inputs (list fuse-for-appimage
+ `(,zstd "lib")
+ `(,zstd "static")
+ `(,zlib "out")
+ `(,zlib "static"))))))
+
+(define-public appimage-type2-runtime
+ (let ((revision "0")
+ (commit "47b665594856b4e8928f8932adcf6d13061d8c30"))
+ (package
+ (name "appimage-type2-runtime")
+ (version (git-version "continuous" revision commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/AppImage/type2-runtime")
+ (commit commit)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32 "0954crhlbapxis96g1s0vfpf78ybr64zvjalak387ksxj560g44x"))))
+ (arguments
+ (list
+ #:make-flags #~(list "-Csrc/runtime" "runtime-fuse3"
+ (string-append "CFLAGS="
+ "-I"
+ #$fuse-2
+ "/include/fuse/"
+ " -DGIT_COMMIT='\""
+ "guix-"
+ #$version
+ "\"'"
+ " -D_FILE_OFFSET_BITS=64"
+ " -static"))
+ #:phases #~(modify-phases %standard-phases
+ (delete 'configure)
+ (delete 'check)
+ (replace 'install
+ (lambda _
+ (install-file "src/runtime/runtime-fuse3"
+ (string-append #$output "/bin"))))
+ ;; must be after all elf reliant phases
+ (add-after 'make-dynamic-linker-cache 'set-magic-bytes
+ (lambda _
+ (use-modules (ice-9 binary-ports))
+ (let ((port (open (string-append #$output
+ "/bin/runtime-fuse3")
+ (logior O_WRONLY))))
+ (seek port 8 SEEK_SET)
+ (put-bytevector port #vu8(#x41 #x49 #x02))
+ (close-port port)))))
+ #:disallowed-references (list squashfuse-for-appimage
+ fuse-for-appimage zstd zlib)))
+ ;; only needed at build time
+ (inputs (list squashfuse-for-appimage fuse-for-appimage
+ `(,zstd "static")
+ `(,zlib "static")))
+ (build-system gnu-build-system)
+ (home-page "https://github.com/AppImage/type2-runtime")
+ (synopsis "Runtime for AppImages")
+ (description "The runtime is the executable part of every AppImage. It
+mounts the payload via FUSE and executes the entrypoint.")
+ (license license:expat))))
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 7c5fe76fe0..f517100fcf 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -10,6 +10,8 @@
;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -56,6 +58,7 @@ (define-module (guix scripts pack)
#:use-module ((gnu packages compression) #:hide (zip))
#:use-module (gnu packages guile)
#:use-module (gnu packages base)
+ #:autoload (gnu packages appimage) (appimage-type2-runtime)
#:autoload (gnu packages gnupg) (guile-gcrypt)
#:autoload (gnu packages guile) (guile2.0-json guile-json)
#:use-module (srfi srfi-1)
@@ -64,6 +67,7 @@ (define-module (guix scripts pack)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
#:use-module (ice-9 match)
+ #:use-module (ice-9 optargs)
#:export (symlink-spec-option-parser
self-contained-tarball
@@ -71,6 +75,7 @@ (define-module (guix scripts pack)
rpm-archive
docker-image
squashfs-image
+ self-contained-appimage
%formats
guix-pack))
@@ -974,8 +979,96 @@ (define* (rpm-archive name profile
(gexp->derivation (string-append name ".rpm") build
#:target target
#:references-graphs `(("profile" ,profile))))
+
+;;;
+;;; AppImage format
+;;;
+(define* (self-contained-appimage name profile
+ #:key target
+ (profile-name "guix-profile")
+ entry-point
+ (compressor "zstd")
+ localstatedir?
+ (symlinks '())
+ (archiver tar)
+ (extra-options '()))
+ "Return a self-contained AppImage containing a store initialized with the
+closure of PROFILE, a derivation. The AppImage contains /gnu/store unless
+RELOCATABLE option is used; if LOCALSTATEDIR? is true, it also contains
+/var/guix, including /var/guix/db with a properly initialized store database.
+
+SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
+added to the pack."
+ (unless entry-point
+ (leave (G_ "entry-point must be provided in the '~a' format~%")
+ 'appimage))
+ (let-keywords extra-options #f ((relocatable? #f))
+ (unless relocatable?
+ (warning (G_ "AppImages should be built with the --relocatable flag~%"))))
+
+ (define runtime-package appimage-type2-runtime)
+ (define runtime-path "bin/runtime-fuse3")
+ (define %valid-compressors '("gzip" "zstd"))
+
+ (let ((compressor-name (compressor-name compressor)))
+ (unless (member compressor-name %valid-compressors)
+ (leave (G_ "~a is not a valid squashfs archive compressor used in
+generating the AppImage. Valid compressors are: ~a~%")
+ compressor-name
+ %valid-compressors)))
-
+ (define builder
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules (source-module-closure
+ '((guix build store-copy)
+ (guix build utils))
+ #:select? not-config?)
+ #~(begin
+ (use-modules (guix build utils)
+ (guix build store-copy)
+ (rnrs io ports)
+ (srfi srfi-1)
+ (srfi srfi-26))
+
+ (define (concatenate result file1 file2)
+ (let ((p (open-file-output-port result))
+ (f1 (open-file-input-port file1))
+ (f2 (open-file-input-port file2)))
+ (put-bytevector p (get-bytevector-all f1))
+ (close-port f1)
+ (put-bytevector p (get-bytevector-all f2))
+ (close-port f2)
+ (close-port p)))
+
+ (let* ((appdir "AppDir")
+ (squashfs "squashfs")
+ (profile-items (map store-info-item
+ (call-with-input-file "profile" read-reference-graph)))
+ (profile-path (find (cut (file-name-predicate "profile$") <> #f) profile-items)))
+ (mkdir-p appdir)
+ ;; copy all store items from the profile to the AppDir
+ (for-each (lambda (f)
+ (copy-store-item f appdir)) profile-items)
+ ;; symlink the provided entry-point to AppDir/AppRun
+ (symlink (string-append "." profile-path "/" #$entry-point)
+ (string-append appdir "/AppRun"))
+ ;; create .desktop file as required by the spec
+ (make-desktop-entry-file
+ (string-append appdir "/" #$name ".desktop")
+ #:name #$name
+ #:exec #$entry-point)
+ ;; compress the AppDir
+ (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
+ squashfs "-root-owned" "-noappend"
+ "-comp" #+(compressor-name compressor))
+ ;; append runtime and squashFS into file AppImage
+ (concatenate #$output
+ #$(file-append runtime-package "/" runtime-path)
+ squashfs))))))
+ (gexp->derivation (string-append name ".AppImage") builder
+ #:target target
+ #:references-graphs `(("profile" ,profile))))
+
;;;
;;; Compiling C programs.
;;;
@@ -1311,6 +1404,7 @@ (define %formats
(squashfs . ,squashfs-image)
(docker . ,docker-image)
(deb . ,debian-archive)
+ (appimage . ,self-contained-appimage)
(rpm . ,rpm-archive)))
(define (show-formats)
@@ -1327,6 +1421,8 @@ (define (show-formats)
deb Debian archive installable via dpkg/apt"))
(display (G_ "
rpm RPM archive installable via rpm/yum"))
+ (display (G_ "
+ appimage AppImage self-contained and executable format"))
(newline))
(define (required-option symbol)
@@ -1694,6 +1790,8 @@ (define-command (guix-pack . args)
(process-file-arg opts 'preun-file)
#:postun-file
(process-file-arg opts 'postun-file)))
+ ('appimage
+ (list #:relocatable? relocatable?))
(_ '()))
This message was truncated. Download the full message here.
L
L
Ludovic Courtès wrote on 18 Oct 14:20 +0200
(name . Noé Lopez)(address . noelopez@free.fr)
877ca5r4a6.fsf@gnu.org
Hi Noé & Sebastian,

Noé Lopez <noelopez@free.fr> skribis:

Toggle quote (12 lines)
> From: Sebastian Dümcke <code@sam-d.com>
>
> * guix/scripts/pack.scm: Add Appimage format.
> * gnu/packages/appimage.scm
> (gnu packages appimage): New module.
> (fuse-for-appimage, squashfuse-for-appimage)
> (appimage-type2-runtime): New variables.
> * doc/guix.texi: Document AppImage pack.
>
> Co-authored-by: Noé Lopez <noelopez@free.fr>
> Change-Id: I09f1241dfb9b267f94dce59914dea527d35ac60e

[...]

Toggle quote (9 lines)
> This patch adds a new AppImage export format to « guix pack ». This
> enables guix users to easily share packets or environments with their
> peers that don’t have guix via a statically linked, runs-everywhere
> executable.
>
> Sebastian and I co-authored this patch, I did the runtime packaging
> and Sebastian did the actual command. I’ve personally tested the
> generated AppImages with my friend’s various distros, they work great!

Nice work! Overall looks great to me. What follows are pretty minor
suggestions.

Toggle quote (2 lines)
> +@cindex AppImage, create an AppImage file with guix pack

s/guix pack/@command{guix pack}/

Toggle quote (2 lines)
> +Another format internally based on SquashFS is
> +@uref{https://appimage.org/, AppImage}. An AppImage file can be made
^
Nitpick: please leave two spaces after end-of-sentence periods
throughout this patch.

Toggle quote (3 lines)
> +The resulting AppImage does not conform to the complete standard as it
> +currently does not contain a @code{.DirIcon} file. This does not impact

s/@code/@file/

Toggle quote (7 lines)
> +@quotation Warning
> +Unless @option{--relocatable} is used, the software will contain
> +references to items inside the guix store, which are not present on
> +systems without Guix. It is recommended to use @option{--relocatable}
> +when distributing software to 3rd parties. A warning is printed when
> +this option is not used.

I would it perhaps more upfront, like:

When building an AppImage, always @emph{pass} the
@option{--relocatable} option (or @option{-R}, or @option{-RR}) to
make sure the image can be used on systems where Guix is not
installed.

In practice, if Guix is installed but the image’s dependencies are not
in the store, it won’t work either. So I think the “always” bit is not
exaggerated.

WDYT?

Toggle quote (2 lines)
> +++ b/gnu/packages/appimage.scm

Could you add this file as a separate commit, before the one adding
AppImage support to ‘guix pack’?

Toggle quote (6 lines)
> + (arguments
> + `(#:configure-flags ,#~(list (string-append "-Dudevrulesdir="
> + #$output "/udev/rules.d")
> + "-Duseroot=false" "--default-library=static")
> + #:tests? #f

Please add a short comment explaining why tests are skipped.

Toggle quote (2 lines)
> + #:phases ,#~(modify-phases %standard-phases

The recommended style is to avoid quasiquote/unquote and instead write:

(arguments
(list #:configure-flags …
#:phases #~(…)))

Toggle quote (9 lines)
> + (add-before 'configure 'set-paths
> + (lambda* (#:key inputs outputs #:allow-other-keys)
> + (let ((dummy-init.d (string-append (getcwd)
> + "/etc/init.d")))
> + (setenv "MOUNT_FUSE_PATH"
> + (string-append #$output "/sbin"))
> + (setenv "UDEV_RULES_PATH"
> + (string-append #$output "/lib/udev/rules.d"))))))))))

Maybe call this phase ‘set-fuse+udev+paths’, to avoid confusion with the
standard ‘set-paths’ phase.

Toggle quote (8 lines)
> +(define squashfuse-for-appimage
> + (let ((revision "0")
> + (commit "e51978cd6bb5c4d16fae9eee43d0b258f570bb0f"))
> + (package
> + (inherit squashfuse)
> + (name "squashfuse")
> + (version (git-version "0.1.104" revision commit))

Can you add a comment explaining why this specific commit is needed?
That way, our future selves will know when ‘squashfuse-for-appimage’ can
be removed.

Toggle quote (6 lines)
> +(define-public appimage-type2-runtime
> + (let ((revision "0")
> + (commit "47b665594856b4e8928f8932adcf6d13061d8c30"))
> + (package
> + (name "appimage-type2-runtime")

Likewise.

Toggle quote (12 lines)
> + #:phases #~(modify-phases %standard-phases
> + (delete 'configure)
> + (delete 'check)
> + (replace 'install
> + (lambda _
> + (install-file "src/runtime/runtime-fuse3"
> + (string-append #$output "/bin"))))
> + ;; must be after all elf reliant phases
> + (add-after 'make-dynamic-linker-cache 'set-magic-bytes
> + (lambda _
> + (use-modules (ice-9 binary-ports))

Please do not use ‘use-modules’ in a non-top-level context; it’s not
guaranteed to work in that context.

Instead use #:modules (check the repo for examples).

Toggle quote (4 lines)
> + (synopsis "Runtime for AppImages")
> + (description "The runtime is the executable part of every AppImage. It

Toggle quote (2 lines)
> + (define (concatenate result file1 file2)

Please add a short comment below ‘define’ explaining what it does,
similar to docstrings.

Toggle quote (2 lines)
> + (let ((p (open-file-output-port result))

Rather:

(call-with-output-file result
(lambda (output)
…))

Toggle quote (6 lines)
> + (f1 (open-file-input-port file1))
> + (f2 (open-file-input-port file2)))
> + (put-bytevector p (get-bytevector-all f1))
> + (close-port f1)
> + (put-bytevector p (get-bytevector-all f2))

To avoid loading it all in memory, rather use:

(call-with-input-file file1
(lambda (input)
(dump-port input output)))

Same with FILE2.

Toggle quote (6 lines)
> + (let* ((appdir "AppDir")
> + (squashfs "squashfs")
> + (profile-items (map store-info-item
> + (call-with-input-file "profile" read-reference-graph)))
> + (profile-path (find (cut (file-name-predicate "profile$") <> #f) profile-items)))

s/-path//

Also, rather: (find (lambda (item)
(string-suffix? "-profile" item))
items)

Toggle quote (5 lines)
> + (mkdir-p appdir)
> + ;; copy all store items from the profile to the AppDir
> + (for-each (lambda (f)
> + (copy-store-item f appdir)) profile-items)

I believe you could write:

(populate-store '("profile") appdir)

Toggle quote (17 lines)
> + ;; symlink the provided entry-point to AppDir/AppRun
> + (symlink (string-append "." profile-path "/" #$entry-point)
> + (string-append appdir "/AppRun"))
> + ;; create .desktop file as required by the spec
> + (make-desktop-entry-file
> + (string-append appdir "/" #$name ".desktop")
> + #:name #$name
> + #:exec #$entry-point)
> + ;; compress the AppDir
> + (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
> + squashfs "-root-owned" "-noappend"
> + "-comp" #+(compressor-name compressor))
> + ;; append runtime and squashFS into file AppImage
> + (concatenate #$output
> + #$(file-append runtime-package "/" runtime-path)
> + squashfs))))))

And you can finish with: (chmod #$output #o555).

(Then you can remove the doc that says to “chmod +x” the result.)

It looks like this procedure ignores its #:symlinks argument. Could you
add support for it?

Could you send an updated patch?

At any rate, kudos for this new backend! This had been suggested many
times, so I’m sure it’ll find some good use.

Thank you!

Ludo’.
L
L
Ludovic Courtès wrote on 18 Oct 14:22 +0200
(name . Noé Lopez)(address . noelopez@free.fr)
87zfn1ppmg.fsf@gnu.org
Oh, I forgot one thing: could you add a followup commit adding a news
entry in ‘etc/news.scm’?

Make sure the synopsis is intelligible to the Guix user base at large
and that the target audience is clear.

Thanks,
Ludo’.
L
L
Ludovic Courtès wrote on 18 Oct 22:34 +0200
(name . Noé Lopez)(address . noelopez@free.fr)
871q0dp2v5.fsf@gnu.org
Noé Lopez <noelopez@free.fr> skribis:

Toggle quote (12 lines)
> From: Sebastian Dümcke <code@sam-d.com>
>
> * guix/scripts/pack.scm: Add Appimage format.
> * gnu/packages/appimage.scm
> (gnu packages appimage): New module.
> (fuse-for-appimage, squashfuse-for-appimage)
> (appimage-type2-runtime): New variables.
> * doc/guix.texi: Document AppImage pack.
>
> Co-authored-by: Noé Lopez <noelopez@free.fr>
> Change-Id: I09f1241dfb9b267f94dce59914dea527d35ac60e

One last thing: could you add a test?

Since such a test needs to build a whole bunch of things, it would only
be executed when a connection to a “real” store is available. See the
examples in ‘tests/pack.scm’ and ‘tests/guix-pack-relocatable.sh’. I
guess a test could go in either of these two files, or in a third one
similar to ‘guix-pack-relocatable.sh’.

Let me know what you think!

Ludo’.
N
N
Noé Lopez wrote on 26 Oct 19:28 +0200
[PATCH v2 2/3] pack: Add support for AppImage pack format.
(address . 73842@debbugs.gnu.org)
ea1fe6e106f2abc9fce5cf7e686227c0c1f9e9e1.1729963690.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* guix/scripts/pack.scm: Add Appimage format.
* doc/guix.texi: Document AppImage pack.

Co-authored-by: Noé Lopez <noelopez@free.fr>
Change-Id: I33ebfec623cff1cfcd6f029d2d3054c23ab1949a
---
doc/guix.texi | 55 +++++++++++++++++++++-
guix/scripts/pack.scm | 104 +++++++++++++++++++++++++++++++++++++++++-
tests/pack.scm | 41 ++++++++++++++++-
3 files changed, 197 insertions(+), 3 deletions(-)

Toggle diff (313 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index ac3a7adef0..18edf8e550 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -6949,6 +6949,18 @@ Invoking guix pack
environment}, using commands like @command{singularity shell} or
@command{singularity exec}.
+@cindex AppImage, create an AppImage file with @command{guix pack}
+Another format internally based on SquashFS is
+@uref{https://appimage.org/, AppImage}. An AppImage file can be made
+executable and run without any special privileges:
+
+@example
+guix pack -f appimage --entry-point=bin/guile guile
+cp @var{file} . && chmod u+x $(basename @var{file})
+./$(basename @var{file}) --help
+@end example
+where @var{file} is the image returned by @command{guix pack}.
+
Several command-line options allow you to customize your pack:
@table @code
@@ -7065,6 +7077,47 @@ Invoking guix pack
installation or other, non-rpm packs.
@end quotation
+@item appimage
+@cindex AppImage, create an AppImage file with @command{guix pack}
+This produces an AppImage file with the @samp{.AppImage} extension.
+AppImage is a SquashFS volume prefixed with a runtime that mounts the
+SquashFS file system and executes the binary provided with
+@option{--entry-point}. This results in a self-contained archive that
+bundles the software and all its requirements into a single file. When
+the file is made executable it runs the packaged software.
+
+@example
+guix pack -f appimage --entry-point=bin/vlc vlc
+@end example
+
+The runtime used by AppImages makes use of libfuse to mount the image
+quickly. If libfuse is not available, the AppImage can still be started
+using the @option{--appimage-extract-and-run} flag.
+
+@quotation Warning
+ When building an AppImage, always @emph{pass} the
+@option{--relocatable} option (or @option{-R}, or @option{-RR}) to make
+sure the image can be used on systems where Guix is not installed. A
+warning is printed when this option is not used.
+@end quotation
+
+@example
+guix pack -f appimage --entry-point=bin/hello --relocatable hello
+@end example
+
+@quotation Note
+The resulting AppImage does not conform to the complete standard as it
+currently does not contain a @file{.DirIcon} file. This does not impact
+functionality of the AppImage itself, but possibly that of software used
+to manage AppImages.
+@end quotation
+
+@quotation Note
+As the generated AppImage packages the complete dependency graph, it
+will be larger than comparable AppImage files found online, which depend
+on host system libraries.
+@end quotation
+
@end table
@cindex relocatable binaries
@@ -7155,7 +7208,7 @@ Invoking guix pack
@cindex entry point, for Docker and Singularity images
@item --entry-point=@var{command}
Use @var{command} as the @dfn{entry point} of the resulting pack, if the pack
-format supports it---currently @code{docker} and @code{squashfs} (Singularity)
+format supports it---currently @code{docker}, @code{appimage} and @code{squashfs} (Singularity)
support it. @var{command} must be relative to the profile contained in the
pack.
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 7c5fe76fe0..95b40a743b 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -10,6 +10,8 @@
;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -56,6 +58,7 @@ (define-module (guix scripts pack)
#:use-module ((gnu packages compression) #:hide (zip))
#:use-module (gnu packages guile)
#:use-module (gnu packages base)
+ #:autoload (gnu packages appimage) (appimage-type2-runtime)
#:autoload (gnu packages gnupg) (guile-gcrypt)
#:autoload (gnu packages guile) (guile2.0-json guile-json)
#:use-module (srfi srfi-1)
@@ -64,6 +67,7 @@ (define-module (guix scripts pack)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
#:use-module (ice-9 match)
+ #:use-module (ice-9 optargs)
#:export (symlink-spec-option-parser
self-contained-tarball
@@ -71,6 +75,7 @@ (define-module (guix scripts pack)
rpm-archive
docker-image
squashfs-image
+ self-contained-appimage
%formats
guix-pack))
@@ -974,8 +979,100 @@ (define* (rpm-archive name profile
(gexp->derivation (string-append name ".rpm") build
#:target target
#:references-graphs `(("profile" ,profile))))
+
+;;;
+;;; AppImage format
+;;;
+(define* (self-contained-appimage name profile
+ #:key target
+ (profile-name "guix-profile")
+ entry-point
+ (compressor (lookup-compressor "zstd"))
+ localstatedir?
+ (symlinks '())
+ (archiver tar)
+ (extra-options '()))
+ "Return a self-contained AppImage containing a store initialized with the
+closure of PROFILE, a derivation. The AppImage contains /gnu/store unless
+RELOCATABLE option is used; if LOCALSTATEDIR? is true, it also contains
+/var/guix, including /var/guix/db with a properly initialized store database.
+
+SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
+added to the pack."
+ (unless entry-point
+ (leave (G_ "entry-point must be provided in the '~a' format~%")
+ 'appimage))
+ (let-keywords extra-options #f ((relocatable? #f))
+ (unless relocatable?
+ (warning (G_ "AppImages should be built with the --relocatable flag~%"))))
+
+ (define runtime-package appimage-type2-runtime)
+ (define runtime-path "bin/runtime-fuse3")
+ (define %valid-compressors '("gzip" "zstd"))
+
+ (let ((compressor-name (compressor-name compressor)))
+ (unless (member compressor-name %valid-compressors)
+ (leave (G_ "~a is not a valid squashfs archive compressor used in
+generating the AppImage. Valid compressors are: ~a~%")
+ compressor-name
+ %valid-compressors)))
-
+ (define builder
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules (source-module-closure
+ '((guix build store-copy)
+ (guix build utils))
+ #:select? not-config?)
+ #~(begin
+ (use-modules (guix build utils)
+ (guix build store-copy)
+ (rnrs io ports)
+ (srfi srfi-1)
+ (srfi srfi-26))
+
+ (define (concatenate-files result file1 file2)
+ "Creates a new file RESULT containing FILE1 followed by FILE2."
+ (call-with-output-file result
+ (lambda (output)
+ (call-with-input-file file1
+ (lambda (input)
+ (dump-port input output)))
+ (call-with-input-file file2
+ (lambda (input)
+ (dump-port input output))))))
+
+ (let* ((appdir "AppDir")
+ (squashfs "squashfs")
+ (profile-items (map store-info-item
+ (call-with-input-file "profile" read-reference-graph)))
+ (profile (find (lambda (item)
+ (string-suffix? "-profile" item))
+ profile-items)))
+ (mkdir-p appdir)
+ ;; copy all store items from the profile to the AppDir
+ (populate-store '("profile") appdir)
+ ;; symlink the provided entry-point to AppDir/AppRun
+ (symlink (string-append "." profile "/" #$entry-point)
+ (string-append appdir "/AppRun"))
+ ;; create .desktop file as required by the spec
+ (make-desktop-entry-file
+ (string-append appdir "/" #$name ".desktop")
+ #:name #$name
+ #:exec #$entry-point)
+ ;; compress the AppDir
+ (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
+ squashfs "-root-owned" "-noappend"
+ "-comp" #+(compressor-name compressor))
+ ;; append runtime and squashFS into file AppImage
+ (concatenate-files #$output
+ #$(file-append runtime-package "/" runtime-path)
+ squashfs)
+ ;; add execution permission
+ (chmod #$output #o555))))))
+ (gexp->derivation (string-append name ".AppImage") builder
+ #:target target
+ #:references-graphs `(("profile" ,profile))))
+
;;;
;;; Compiling C programs.
;;;
@@ -1311,6 +1408,7 @@ (define %formats
(squashfs . ,squashfs-image)
(docker . ,docker-image)
(deb . ,debian-archive)
+ (appimage . ,self-contained-appimage)
(rpm . ,rpm-archive)))
(define (show-formats)
@@ -1327,6 +1425,8 @@ (define (show-formats)
deb Debian archive installable via dpkg/apt"))
(display (G_ "
rpm RPM archive installable via rpm/yum"))
+ (display (G_ "
+ appimage AppImage self-contained and executable format"))
(newline))
(define (required-option symbol)
@@ -1694,6 +1794,8 @@ (define-command (guix-pack . args)
(process-file-arg opts 'preun-file)
#:postun-file
(process-file-arg opts 'postun-file)))
+ ('appimage
+ (list #:relocatable? relocatable?))
(_ '())))
(target (assoc-ref opts 'target))
(bootstrap? (assoc-ref opts 'bootstrap?))
diff --git a/tests/pack.scm b/tests/pack.scm
index f8a9e09c28..6ac9a966af 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -3,6 +3,7 @@
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -32,7 +33,8 @@ (define-module (test-pack)
#:use-module (guix utils)
#:use-module ((guix build utils) #:select (%store-directory))
#:use-module (gnu packages)
- #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target))
+ #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target
+ hello))
#:use-module (gnu packages bootstrap)
#:use-module ((gnu packages package-management) #:select (rpm))
#:use-module ((gnu packages compression) #:select (squashfs-tools))
@@ -340,6 +342,43 @@ (define rpm-for-tests
(mkdir #$output))))))))
(built-derivations (list check))))
+ (unless store (test-skip 1))
+ (test-assertm "appimage"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
+ (unless store (test-skip 1))
+ (test-assertm "appimage + localstatedir"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:localstatedir? #t
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
(unless store (test-skip 1))
(test-assertm "deb archive with symlinks and control files"
(mlet* %store-monad
--
2.46.0
N
N
Noé Lopez wrote on 26 Oct 19:28 +0200
[PATCH v2 0/3] pack: Add support for AppImage pack format.
(address . 73842@debbugs.gnu.org)(name . Noé Lopez)(address . noelopez@free.fr)
cover.1729963690.git.noelopez@free.fr
Hi Ludovic,

Thank you for the complete review, here is a new version of the patch
containing the changes you asked for. Sebastian did the news entry in
three languages (!!!) while I made the tests and git magic.

Can you explain why you want support for symlinks? I can’t think of a
use for it since AppImages are only meant to be ran through their
entrypoint, not unpacked.

A summary of changes since previous patch:

– Improved package definition for [squash]fuse-for-appimage and
bumped versions;
– Added news entry;
– Added tests;
– Applied code suggestions from review:
– Don’t use (use-modules) on non-toplevel;
– Change -R warning in documentation, and ensure two spaces
after dots;
– Change appimage-type2-runtime synopsis and description;
– Document and improve (concatenate-files)
– Use better lambdas for `profile' and populating the store.
– chmod 555 the resulting AppImage

Have a wonderful week-end,
Noé Lopez

Noé Lopez (1):
gnu: appimage: New packages for the appimage runtime.

Sebastian Dümcke (2):
pack: Add support for AppImage pack format.
news: Add entry for guix pack’s AppImage format

doc/guix.texi | 55 +++++++++++++++-
etc/news.scm | 38 +++++++++++
gnu/packages/appimage.scm | 133 ++++++++++++++++++++++++++++++++++++++
guix/scripts/pack.scm | 104 ++++++++++++++++++++++++++++-
tests/pack.scm | 41 +++++++++++-
5 files changed, 368 insertions(+), 3 deletions(-)
create mode 100644 gnu/packages/appimage.scm


base-commit: df666602c7936f7d87354374b148ef6269844c01
--
2.46.0
N
N
Noé Lopez wrote on 26 Oct 19:28 +0200
[PATCH v2 1/3] gnu: appimage: New packages for the appimage runtime.
(address . 73842@debbugs.gnu.org)(name . Noé Lopez)(address . noelopez@free.fr)
0f3279bf893470c70bdd535b81cd95fd108d5d5f.1729963690.git.noelopez@free.fr
* gnu/packages/appimage.scm
(gnu packages appimage): New module.
(fuse-for-appimage, squashfuse-for-appimage)
(appimage-type2-runtime): New variables.

Change-Id: I857a8eb5399a6a493e52db70b6c8cf0c71360930
---
gnu/packages/appimage.scm | 133 ++++++++++++++++++++++++++++++++++++++
1 file changed, 133 insertions(+)
create mode 100644 gnu/packages/appimage.scm

Toggle diff (141 lines)
diff --git a/gnu/packages/appimage.scm b/gnu/packages/appimage.scm
new file mode 100644
index 0000000000..028b59ab30
--- /dev/null
+++ b/gnu/packages/appimage.scm
@@ -0,0 +1,133 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages appimage)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (gnu packages base)
+ #:use-module (gnu packages compression)
+ #:use-module (gnu packages file-systems)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix download)
+ #:use-module (guix gexp)
+ #:use-module (guix git-download)
+ #:use-module (guix packages)
+ #:use-module (guix utils))
+
+(define fuse-for-appimage
+ (package
+ (inherit fuse)
+ (name "fuse")
+ (version "3.16.2")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/libfuse/libfuse/releases/"
+ "download/fuse-"
+ version
+ "/fuse-"
+ version
+ ".tar.gz"))
+ (sha256
+ (base32 "11yfl2w2a445hllyzlakq97n32g06972vxpmh7lpbclnj9fhb5zp"))))
+ (arguments
+ (substitute-keyword-arguments (package-arguments fuse)
+ ((#:configure-flags original-flags #~(list))
+ #~(append #$original-flags '("--default-library=static")))))))
+
+(define squashfuse-for-appimage
+ (package
+ (inherit squashfuse)
+ (arguments
+ (list
+ #:configure-flags #~'("CFLAGS=-ffunction-sections -fdata-sections -Os -no-pie"
+ "LDFLAGS=-static")
+ #:phases #~(modify-phases %standard-phases
+ (add-after 'install 'install-private-headers
+ (lambda _
+ (install-file "fuseprivate.h"
+ (string-append #$output
+ "/include/squashfuse/")))))))
+ (inputs (list fuse-for-appimage
+ `(,zstd "lib")
+ `(,zstd "static")
+ `(,zlib "out")
+ `(,zlib "static")))))
+
+(define-public appimage-type2-runtime
+ (let ((revision "0")
+ ;; No releases, just the latest commit.
+ (commit "47b665594856b4e8928f8932adcf6d13061d8c30"))
+ (package
+ (name "appimage-type2-runtime")
+ (version (git-version "continuous" revision commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/AppImage/type2-runtime")
+ (commit commit)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32 "0954crhlbapxis96g1s0vfpf78ybr64zvjalak387ksxj560g44x"))))
+ (arguments
+ (list
+ #:make-flags #~(list "-Csrc/runtime" "runtime-fuse3"
+ (string-append "CFLAGS="
+ "-I"
+ #$fuse-2
+ "/include/fuse/"
+ " -DGIT_COMMIT='\""
+ "guix-"
+ #$version
+ "\"'"
+ " -D_FILE_OFFSET_BITS=64"
+ " -static"))
+ #:modules
+ `((guix build gnu-build-system)
+ (guix build utils)
+ (ice-9 binary-ports))
+ #:phases #~(modify-phases %standard-phases
+ (delete 'configure)
+ (delete 'check) ; No tests.
+ (replace 'install
+ (lambda _
+ (install-file "src/runtime/runtime-fuse3"
+ (string-append #$output "/bin"))))
+ ;; must be after all elf reliant phases
+ (add-after 'make-dynamic-linker-cache 'set-magic-bytes
+ (lambda _
+ (let ((port (open (string-append #$output
+ "/bin/runtime-fuse3")
+ (logior O_WRONLY))))
+ (seek port 8 SEEK_SET)
+ (put-bytevector port #vu8(#x41 #x49 #x02))
+ (close-port port)))))
+ #:disallowed-references (list squashfuse-for-appimage
+ fuse-for-appimage zstd zlib)))
+ ;; only needed at build time
+ (inputs (list squashfuse-for-appimage fuse-for-appimage
+ `(,zstd "static")
+ `(,zlib "static")))
+ (build-system gnu-build-system)
+ (home-page "https://github.com/AppImage/type2-runtime")
+ (synopsis "Runtime for executing AppImages")
+ (description "The runtime is the executable part of every AppImage, it mounts
+the payload via FUSE and executes the entrypoint.")
+ (license license:expat))))
--
2.46.0
N
N
Noé Lopez wrote on 26 Oct 19:28 +0200
[PATCH v2 3/3] news: Add entry for guix pack ’s AppImage format
(address . 73842@debbugs.gnu.org)(name . Sebastian Dümcke)(address . code@sam-d.com)
a2fa7d05a4a2afaafae2bc7476b62c2020bd74eb.1729963690.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* etc/news.scm: Add entry.

Change-Id: If37b3b578517c15c2868239998975f7718f7d689
---
etc/news.scm | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

Toggle diff (58 lines)
diff --git a/etc/news.scm b/etc/news.scm
index 4929fa2a9a..59fdd94dd8 100644
--- a/etc/news.scm
+++ b/etc/news.scm
@@ -25,6 +25,7 @@
;; Copyright © 2024 Vivien Kraus <vivien@planete-kraus.eu>
;; Copyright © 2024 Guillaume Le Vaillant <glv@posteo.net>
;; Copyright © 2024 Zheng Junjie <873216071@qq.com>
+;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
;;
;; Copying and distribution of this file, with or without modification, are
;; permitted in any medium without royalty provided the copyright notice and
@@ -32,6 +33,43 @@
(channel-news
(version 0)
+ (entry (commit "ea1fe6e106f2abc9fce5cf7e686227c0c1f9e9e1")
+ (title
+ (de "Neues Format @samp{AppImage} für den Befehl @command{guix pack}")
+ (en "New @samp{AppImage} format for the @command{guix pack} command")
+ (fr "Nouveau format @samp{AppImage} pour la commande @command{guix pack}"))
+ (body
+ (de "@command{guix pack} kann nun AppImage Dateien erstellen. Das
+AppImage Dateiformat erlaubt es in einer einzelnen Datei Software zu verteilen. Die
+AppImage Datei lässt sich ohne besondere Benutzerrechte ausführen. Hier ist
+ein Beispiel:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Siehe @command{info \"(guix.de) Aufruf von guix pack\"} für mehr
+Informationen.")
+ (en "@command{guix pack} can now produce AppImage a single file,
+self-contained software archive. AppImage files are easily distributed and can
+be run with user privileges. Here is an example for the @code{hello} package:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+See @command{info \"(guix) Invoking guix pack\"} for more information.")
+ (fr "@command{guix pack} peut désormais produire un fichier
+AppImage. AppImage est une manière de distribuer les logiciels en un seul
+fichier, qui peut être executer avec des droits d’utilisateur. Voici un
+exemple pour le paquet @code{hello} :
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Consultez @command{info \"(guix.fr) Invoquer guix pack\"} pour plus
+d’informations.")))
(entry (commit "5966e0fdc78771c562e0f484a22f381a77908be0")
(title
--
2.46.0
P
P
pelzflorian (Florian Pelz) wrote on 27 Oct 14:38 +0100
Re: [bug#73842] [PATCH v2 3/3] news: Add entry for guix pack’s AppImage format
(name . Noé Lopez)(address . noelopez@free.fr)
877c9t3buq.fsf@pelzflorian.de
Thank you Noé and Sebastian for not only the command but also including
German news. Some minor suggestions:

Noé Lopez <noelopez@free.fr> writes:
Toggle quote (4 lines)
> + (body
> + (de "@command{guix pack} kann nun AppImage Dateien erstellen. Das
> +AppImage Dateiformat erlaubt es in einer

comma: erlaubt es, in einer

please put in hyphens: AppImage-Dateien, AppImage-Dateiformat,

Toggle quote (4 lines)
> einzelnen Datei Software zu verteilen. Die
> +AppImage Datei lässt sich ohne besondere Benutzerrechte ausführen. Hier ist
> +ein Beispiel:

AppImage-Datei



Toggle quote (9 lines)
> +
> +@example
> +guix pack --format=appimage --entry-point=bin/hello hello
> +@end example
> +
> +Siehe @command{info \"(guix.de) Aufruf von guix pack\"} für mehr
> +Informationen.")


Regards,
Florian
M
M
Maxim Cournoyer wrote on 1 Nov 14:19 +0100
Re: [bug#73842] [PATCH v2 2/3] pack: Add support for AppImage pack format.
(name . Noé Lopez)(address . noelopez@free.fr)
87frob147v.fsf@gmail.com
Hi,

Noé Lopez <noelopez@free.fr> writes:

Toggle quote (7 lines)
> From: Sebastian Dümcke <code@sam-d.com>
>
> * guix/scripts/pack.scm: Add Appimage format.
> * doc/guix.texi: Document AppImage pack.
>
> Co-authored-by: Noé Lopez <noelopez@free.fr>

Thanks for this great addition.

Toggle quote (27 lines)
> Change-Id: I33ebfec623cff1cfcd6f029d2d3054c23ab1949a
> ---
> doc/guix.texi | 55 +++++++++++++++++++++-
> guix/scripts/pack.scm | 104 +++++++++++++++++++++++++++++++++++++++++-
> tests/pack.scm | 41 ++++++++++++++++-
> 3 files changed, 197 insertions(+), 3 deletions(-)
>
> diff --git a/doc/guix.texi b/doc/guix.texi
> index ac3a7adef0..18edf8e550 100644
> --- a/doc/guix.texi
> +++ b/doc/guix.texi
> @@ -6949,6 +6949,18 @@ Invoking guix pack
> environment}, using commands like @command{singularity shell} or
> @command{singularity exec}.
>
> +@cindex AppImage, create an AppImage file with @command{guix pack}
> +Another format internally based on SquashFS is
> +@uref{https://appimage.org/, AppImage}. An AppImage file can be made
> +executable and run without any special privileges:
> +
> +@example
> +guix pack -f appimage --entry-point=bin/guile guile
> +cp @var{file} . && chmod u+x $(basename @var{file})
> +./$(basename @var{file}) --help
> +@end example
> +where @var{file} is the image returned by @command{guix pack}.

The chmod is no longer needed in this v2. Also, it could be nicer
perhaps to capture the output file in a real variable:

@example
file=$(guix pack -f appimage --entry-point=bin/guile guile)
$file
@end example

Toggle quote (31 lines)
> +
> Several command-line options allow you to customize your pack:
>
> @table @code
> @@ -7065,6 +7077,47 @@ Invoking guix pack
> installation or other, non-rpm packs.
> @end quotation
>
> +@item appimage
> +@cindex AppImage, create an AppImage file with @command{guix pack}
> +This produces an AppImage file with the @samp{.AppImage} extension.
> +AppImage is a SquashFS volume prefixed with a runtime that mounts the
> +SquashFS file system and executes the binary provided with
> +@option{--entry-point}. This results in a self-contained archive that
> +bundles the software and all its requirements into a single file. When
> +the file is made executable it runs the packaged software.
> +
> +@example
> +guix pack -f appimage --entry-point=bin/vlc vlc
> +@end example
> +
> +The runtime used by AppImages makes use of libfuse to mount the image
> +quickly. If libfuse is not available, the AppImage can still be started
> +using the @option{--appimage-extract-and-run} flag.
> +
> +@quotation Warning
> + When building an AppImage, always @emph{pass} the
> +@option{--relocatable} option (or @option{-R}, or @option{-RR}) to make
> +sure the image can be used on systems where Guix is not installed. A
> +warning is printed when this option is not used.

Instead of a warning, could we just make it always -RR by default and
document that these are always relocatable, by design? The no
relocatable flags would be treated as an implicit -RR, and otherwise a
single -R would be treated as a single -R (and -RR as -RR, eh).

Toggle quote (13 lines)
> +@end quotation
> +
> +@example
> +guix pack -f appimage --entry-point=bin/hello --relocatable hello
> +@end example
> +
> +@quotation Note
> +The resulting AppImage does not conform to the complete standard as it
> +currently does not contain a @file{.DirIcon} file. This does not impact
> +functionality of the AppImage itself, but possibly that of software used
> +to manage AppImages.
> +@end quotation

Are there plans to add support for .DirIcon in the future?

Toggle quote (150 lines)
> +@quotation Note
> +As the generated AppImage packages the complete dependency graph, it
> +will be larger than comparable AppImage files found online, which depend
> +on host system libraries.
> +@end quotation
> +
> @end table
>
> @cindex relocatable binaries
> @@ -7155,7 +7208,7 @@ Invoking guix pack
> @cindex entry point, for Docker and Singularity images
> @item --entry-point=@var{command}
> Use @var{command} as the @dfn{entry point} of the resulting pack, if the pack
> -format supports it---currently @code{docker} and @code{squashfs} (Singularity)
> +format supports it---currently @code{docker}, @code{appimage} and @code{squashfs} (Singularity)
> support it. @var{command} must be relative to the profile contained in the
> pack.
>
> diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
> index 7c5fe76fe0..95b40a743b 100644
> --- a/guix/scripts/pack.scm
> +++ b/guix/scripts/pack.scm
> @@ -10,6 +10,8 @@
> ;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
> ;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>
> ;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
> +;;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
> +;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
> ;;;
> ;;; This file is part of GNU Guix.
> ;;;
> @@ -56,6 +58,7 @@ (define-module (guix scripts pack)
> #:use-module ((gnu packages compression) #:hide (zip))
> #:use-module (gnu packages guile)
> #:use-module (gnu packages base)
> + #:autoload (gnu packages appimage) (appimage-type2-runtime)
> #:autoload (gnu packages gnupg) (guile-gcrypt)
> #:autoload (gnu packages guile) (guile2.0-json guile-json)
> #:use-module (srfi srfi-1)
> @@ -64,6 +67,7 @@ (define-module (guix scripts pack)
> #:use-module (srfi srfi-35)
> #:use-module (srfi srfi-37)
> #:use-module (ice-9 match)
> + #:use-module (ice-9 optargs)
> #:export (symlink-spec-option-parser
>
> self-contained-tarball
> @@ -71,6 +75,7 @@ (define-module (guix scripts pack)
> rpm-archive
> docker-image
> squashfs-image
> + self-contained-appimage
>
> %formats
> guix-pack))
> @@ -974,8 +979,100 @@ (define* (rpm-archive name profile
> (gexp->derivation (string-append name ".rpm") build
> #:target target
> #:references-graphs `(("profile" ,profile))))
> +
> +;;;
> +;;; AppImage format
> +;;;
> +(define* (self-contained-appimage name profile
> + #:key target
> + (profile-name "guix-profile")
> + entry-point
> + (compressor (lookup-compressor "zstd"))
> + localstatedir?
> + (symlinks '())
> + (archiver tar)
> + (extra-options '()))
> + "Return a self-contained AppImage containing a store initialized with the
> +closure of PROFILE, a derivation. The AppImage contains /gnu/store unless
> +RELOCATABLE option is used; if LOCALSTATEDIR? is true, it also contains
> +/var/guix, including /var/guix/db with a properly initialized store database.
> +
> +SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
> +added to the pack."
> + (unless entry-point
> + (leave (G_ "entry-point must be provided in the '~a' format~%")
> + 'appimage))
> + (let-keywords extra-options #f ((relocatable? #f))
> + (unless relocatable?
> + (warning (G_ "AppImages should be built with the --relocatable flag~%"))))
> +
> + (define runtime-package appimage-type2-runtime)
> + (define runtime-path "bin/runtime-fuse3")
> + (define %valid-compressors '("gzip" "zstd"))
> +
> + (let ((compressor-name (compressor-name compressor)))
> + (unless (member compressor-name %valid-compressors)
> + (leave (G_ "~a is not a valid squashfs archive compressor used in
> +generating the AppImage. Valid compressors are: ~a~%")
> + compressor-name
> + %valid-compressors)))
>
> -
> + (define builder
> + (with-extensions (list guile-gcrypt)
> + (with-imported-modules (source-module-closure
> + '((guix build store-copy)
> + (guix build utils))
> + #:select? not-config?)
> + #~(begin
> + (use-modules (guix build utils)
> + (guix build store-copy)
> + (rnrs io ports)
> + (srfi srfi-1)
> + (srfi srfi-26))
> +
> + (define (concatenate-files result file1 file2)
> + "Creates a new file RESULT containing FILE1 followed by FILE2."
> + (call-with-output-file result
> + (lambda (output)
> + (call-with-input-file file1
> + (lambda (input)
> + (dump-port input output)))
> + (call-with-input-file file2
> + (lambda (input)
> + (dump-port input output))))))
> +
> + (let* ((appdir "AppDir")
> + (squashfs "squashfs")
> + (profile-items (map store-info-item
> + (call-with-input-file "profile" read-reference-graph)))
> + (profile (find (lambda (item)
> + (string-suffix? "-profile" item))
> + profile-items)))
> + (mkdir-p appdir)
> + ;; copy all store items from the profile to the AppDir
> + (populate-store '("profile") appdir)
> + ;; symlink the provided entry-point to AppDir/AppRun
> + (symlink (string-append "." profile "/" #$entry-point)
> + (string-append appdir "/AppRun"))
> + ;; create .desktop file as required by the spec
> + (make-desktop-entry-file
> + (string-append appdir "/" #$name ".desktop")
> + #:name #$name
> + #:exec #$entry-point)
> + ;; compress the AppDir
> + (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
> + squashfs "-root-owned" "-noappend"
> + "-comp" #+(compressor-name compressor))
> + ;; append runtime and squashFS into file AppImage
> + (concatenate-files #$output
> + #$(file-append runtime-package "/" runtime-path)
> + squashfs)
> + ;; add execution permission

Standalone comments (those starting on their own line with ;;) should be
fully punctuated (complete sentence), e.g.: "Add execution permission."
for the last one.

Toggle quote (101 lines)
> + (chmod #$output #o555))))))
> + (gexp->derivation (string-append name ".AppImage") builder
> + #:target target
> + #:references-graphs `(("profile" ,profile))))
> +
> ;;;
> ;;; Compiling C programs.
> ;;;
> @@ -1311,6 +1408,7 @@ (define %formats
> (squashfs . ,squashfs-image)
> (docker . ,docker-image)
> (deb . ,debian-archive)
> + (appimage . ,self-contained-appimage)
> (rpm . ,rpm-archive)))
>
> (define (show-formats)
> @@ -1327,6 +1425,8 @@ (define (show-formats)
> deb Debian archive installable via dpkg/apt"))
> (display (G_ "
> rpm RPM archive installable via rpm/yum"))
> + (display (G_ "
> + appimage AppImage self-contained and executable format"))
> (newline))
>
> (define (required-option symbol)
> @@ -1694,6 +1794,8 @@ (define-command (guix-pack . args)
> (process-file-arg opts 'preun-file)
> #:postun-file
> (process-file-arg opts 'postun-file)))
> + ('appimage
> + (list #:relocatable? relocatable?))
> (_ '())))
> (target (assoc-ref opts 'target))
> (bootstrap? (assoc-ref opts 'bootstrap?))
> diff --git a/tests/pack.scm b/tests/pack.scm
> index f8a9e09c28..6ac9a966af 100644
> --- a/tests/pack.scm
> +++ b/tests/pack.scm
> @@ -3,6 +3,7 @@
> ;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
> ;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
> ;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
> +;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
> ;;;
> ;;; This file is part of GNU Guix.
> ;;;
> @@ -32,7 +33,8 @@ (define-module (test-pack)
> #:use-module (guix utils)
> #:use-module ((guix build utils) #:select (%store-directory))
> #:use-module (gnu packages)
> - #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target))
> + #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target
> + hello))
> #:use-module (gnu packages bootstrap)
> #:use-module ((gnu packages package-management) #:select (rpm))
> #:use-module ((gnu packages compression) #:select (squashfs-tools))
> @@ -340,6 +342,43 @@ (define rpm-for-tests
> (mkdir #$output))))))))
> (built-derivations (list check))))
>
> + (unless store (test-skip 1))
> + (test-assertm "appimage"
> + (mlet* %store-monad
> + ((guile (set-guile-for-build (default-guile)))
> + (profile -> (profile
> + (content (packages->manifest (list %bootstrap-guile hello)))
> + (hooks '())
> + (locales? #f)))
> + (image (self-contained-appimage "hello-appimage" profile
> + #:entry-point "bin/hello"
> + #:extra-options
> + (list #:relocatable? #t)))
> + (check (gexp->derivation
> + "check-appimage"
> + #~(begin
> + (invoke #$image)))))
> + (built-derivations (list check))))
> +
> + (unless store (test-skip 1))
> + (test-assertm "appimage + localstatedir"
> + (mlet* %store-monad
> + ((guile (set-guile-for-build (default-guile)))
> + (profile -> (profile
> + (content (packages->manifest (list %bootstrap-guile hello)))
> + (hooks '())
> + (locales? #f)))
> + (image (self-contained-appimage "hello-appimage" profile
> + #:entry-point "bin/hello"
> + #:localstatedir? #t
> + #:extra-options
> + (list #:relocatable? #t)))
> + (check (gexp->derivation
> + "check-appimage"
> + #~(begin
> + (invoke #$image)))))
> + (built-derivations (list check))))
> +
> (unless store (test-skip 1))
> (test-assertm "deb archive with symlinks and control files"
> (mlet* %store-monad

The rest looks good to me, from a quick read. If you could send a v3,
I'll then give it a real try.

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 1 Nov 14:24 +0100
Re: [bug#73842] [PATCH v2 1/3] gnu: appimage: New packages for the appimage runtime.
(name . Noé Lopez)(address . noelopez@free.fr)
87bjyz1407.fsf@gmail.com
Hi,

Noé Lopez <noelopez@free.fr> writes:

[...]

Toggle quote (19 lines)
> +(define squashfuse-for-appimage
> + (package
> + (inherit squashfuse)
> + (arguments
> + (list
> + #:configure-flags #~'("CFLAGS=-ffunction-sections -fdata-sections -Os -no-pie"
> + "LDFLAGS=-static")
> + #:phases #~(modify-phases %standard-phases
> + (add-after 'install 'install-private-headers
> + (lambda _
> + (install-file "fuseprivate.h"
> + (string-append #$output
> + "/include/squashfuse/")))))))
> + (inputs (list fuse-for-appimage
> + `(,zstd "lib")
> + `(,zstd "static")
> + `(,zlib "out")
> + `(,zlib "static")))))
>
Some lines are a bit too long (our coding style says 80 columns max);
you could place the gexps starting with #~ on the line below to make
things a bit easier.

Toggle quote (31 lines)
> +
> +(define-public appimage-type2-runtime
> + (let ((revision "0")
> + ;; No releases, just the latest commit.
> + (commit "47b665594856b4e8928f8932adcf6d13061d8c30"))
> + (package
> + (name "appimage-type2-runtime")
> + (version (git-version "continuous" revision commit))
> + (source
> + (origin
> + (method git-fetch)
> + (uri (git-reference
> + (url "https://github.com/AppImage/type2-runtime")
> + (commit commit)))
> + (file-name (git-file-name name version))
> + (sha256
> + (base32 "0954crhlbapxis96g1s0vfpf78ybr64zvjalak387ksxj560g44x"))))
> + (arguments
> + (list
> + #:make-flags #~(list "-Csrc/runtime" "runtime-fuse3"
> + (string-append "CFLAGS="
> + "-I"
> + #$fuse-2
> + "/include/fuse/"
> + " -DGIT_COMMIT='\""
> + "guix-"
> + #$version
> + "\"'"
> + " -D_FILE_OFFSET_BITS=64"
> + " -static"))

This feels cramped; start on a new line to give yourself a bit more
horizontal width.

Toggle quote (17 lines)
> + #:modules
> + `((guix build gnu-build-system)
> + (guix build utils)
> + (ice-9 binary-ports))
> + #:phases #~(modify-phases %standard-phases
> + (delete 'configure)
> + (delete 'check) ; No tests.
> + (replace 'install
> + (lambda _
> + (install-file "src/runtime/runtime-fuse3"
> + (string-append #$output "/bin"))))
> + ;; must be after all elf reliant phases
> + (add-after 'make-dynamic-linker-cache 'set-magic-bytes
> + (lambda _
> + (let ((port (open (string-append #$output
> + "/bin/runtime-fuse3")

The indentation is off here. Try running it with 'guix style' or the
old Emacs-based indenter to see.

Toggle quote (8 lines)
> + (logior O_WRONLY))))
> + (seek port 8 SEEK_SET)
> + (put-bytevector port #vu8(#x41 #x49 #x02))
> + (close-port port)))))
> + #:disallowed-references (list squashfuse-for-appimage
> + fuse-for-appimage zstd zlib)))
> + ;; only needed at build time

As mentioned earlier, please use complete sentences for standalone
comments.

Toggle quote (10 lines)
> + (inputs (list squashfuse-for-appimage fuse-for-appimage
> + `(,zstd "static")
> + `(,zlib "static")))
> + (build-system gnu-build-system)
> + (home-page "https://github.com/AppImage/type2-runtime")
> + (synopsis "Runtime for executing AppImages")
> + (description "The runtime is the executable part of every AppImage, it mounts
> +the payload via FUSE and executes the entrypoint.")
> + (license license:expat))))

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 1 Nov 14:27 +0100
Re: [bug#73842] [PATCH v2 3/3] news: Add entry for guix pack’s AppImage format
(name . Noé Lopez)(address . noelopez@free.fr)
877c9n13tv.fsf@gmail.com
Hi,

Noé Lopez <noelopez@free.fr> writes:

Toggle quote (55 lines)
> From: Sebastian Dümcke <code@sam-d.com>
>
> * etc/news.scm: Add entry.
>
> Change-Id: If37b3b578517c15c2868239998975f7718f7d689
> ---
> etc/news.scm | 38 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
>
> diff --git a/etc/news.scm b/etc/news.scm
> index 4929fa2a9a..59fdd94dd8 100644
> --- a/etc/news.scm
> +++ b/etc/news.scm
> @@ -25,6 +25,7 @@
> ;; Copyright © 2024 Vivien Kraus <vivien@planete-kraus.eu>
> ;; Copyright © 2024 Guillaume Le Vaillant <glv@posteo.net>
> ;; Copyright © 2024 Zheng Junjie <873216071@qq.com>
> +;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
> ;;
> ;; Copying and distribution of this file, with or without modification, are
> ;; permitted in any medium without royalty provided the copyright notice and
> @@ -32,6 +33,43 @@
>
> (channel-news
> (version 0)
> + (entry (commit "ea1fe6e106f2abc9fce5cf7e686227c0c1f9e9e1")
> + (title
> + (de "Neues Format @samp{AppImage} für den Befehl @command{guix pack}")
> + (en "New @samp{AppImage} format for the @command{guix pack} command")
> + (fr "Nouveau format @samp{AppImage} pour la commande @command{guix pack}"))
> + (body
> + (de "@command{guix pack} kann nun AppImage Dateien erstellen. Das
> +AppImage Dateiformat erlaubt es in einer einzelnen Datei Software zu verteilen. Die
> +AppImage Datei lässt sich ohne besondere Benutzerrechte ausführen. Hier ist
> +ein Beispiel:
> +
> +@example
> +guix pack --format=appimage --entry-point=bin/hello hello
> +@end example
> +
> +Siehe @command{info \"(guix.de) Aufruf von guix pack\"} für mehr
> +Informationen.")
> + (en "@command{guix pack} can now produce AppImage a single file,
> +self-contained software archive. AppImage files are easily distributed and can
> +be run with user privileges. Here is an example for the @code{hello} package:
> +
> +@example
> +guix pack --format=appimage --entry-point=bin/hello hello
> +@end example
> +
> +See @command{info \"(guix) Invoking guix pack\"} for more information.")
> + (fr "@command{guix pack} peut désormais produire un fichier
> +AppImage. AppImage est une manière de distribuer les logiciels en un seul
> +fichier, qui peut être executer avec des droits d’utilisateur. Voici un

s/executer/exécuté/

I believe double spacing should be used in this patch as well between
sentences.

--
Thanks,
Maxim
N
N
Noé Lopez wrote on 1 Nov 16:22 +0100
Re: [bug#73842] [PATCH v2 2/3] pack: Add support for AppImage pack format.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
87r07vou6u.fsf@xn--no-cja.eu
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (5 lines)
> Instead of a warning, could we just make it always -RR by default and
> document that these are always relocatable, by design? The no
> relocatable flags would be treated as an implicit -RR, and otherwise a
> single -R would be treated as a single -R (and -RR as -RR, eh).

I think it would be confusing to have default options change between
pack formats, additionaly I’m not sure the -RR option has any effect
compared to -R as we already use a runtime that requires fuse.

The command will output a warning when the -R option is not used though:
« guix pack: warning: AppImages should be built with the --relocatable
flag »

Toggle quote (2 lines)
> Are there plans to add support for .DirIcon in the future?

It was originally planned but with the added complexity it was kept for
a later version. Notably, you need a full XDG icon structure inside
your AppImage for the specification, that means multiple resolutions of
your image and other icon stuff I don’t know about. It’s worth retrying
though, as a single 256x256 png might work.

Toggle quote (3 lines)
> The rest looks good to me, from a quick read. If you could send a v3,
> I'll then give it a real try.

I’ve applied the rest of your suggestions, sending the v3 just after
this.

Thanks for reviewing,
Noé
N
[PATCH v3 1/3] gnu: appimage: New packages for the appimage runtime.
(address . 73842@debbugs.gnu.org)(name . Noé Lopez)(address . noelopez@free.fr)
ca12b1b7f176bb6ae81eb2ec844d90fd9421d290.1730475702.git.noelopez@free.fr
From: Noé Lopez <noelopez@free.fr>

* gnu/packages/appimage.scm
(gnu packages appimage): New module.
(fuse-for-appimage, squashfuse-for-appimage)
(appimage-type2-runtime): New variables.

Change-Id: I857a8eb5399a6a493e52db70b6c8cf0c71360930
---
gnu/packages/appimage.scm | 126 ++++++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
create mode 100644 gnu/packages/appimage.scm

Toggle diff (136 lines)
diff --git a/gnu/packages/appimage.scm b/gnu/packages/appimage.scm
new file mode 100644
index 0000000000..df17be214e
--- /dev/null
+++ b/gnu/packages/appimage.scm
@@ -0,0 +1,126 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages appimage)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (gnu packages base)
+ #:use-module (gnu packages compression)
+ #:use-module (gnu packages file-systems)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix download)
+ #:use-module (guix gexp)
+ #:use-module (guix git-download)
+ #:use-module (guix packages)
+ #:use-module (guix utils))
+
+(define fuse-for-appimage
+ (package
+ (inherit fuse)
+ (name "fuse")
+ (version "3.16.2")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/libfuse/libfuse/releases/"
+ "download/fuse-" version "/fuse-" version ".tar.gz"))
+ (sha256
+ (base32 "11yfl2w2a445hllyzlakq97n32g06972vxpmh7lpbclnj9fhb5zp"))))
+ (arguments
+ (substitute-keyword-arguments (package-arguments fuse)
+ ((#:configure-flags original-flags #~(list))
+ #~(append #$original-flags '("--default-library=static")))))))
+
+(define squashfuse-for-appimage
+ (package
+ (inherit squashfuse)
+ (arguments
+ (list
+ #:configure-flags
+ #~'("CFLAGS=-ffunction-sections -fdata-sections -Os -no-pie"
+ "LDFLAGS=-static")
+ #:phases
+ #~(modify-phases %standard-phases
+ (add-after 'install 'install-private-headers
+ (lambda _
+ (install-file "fuseprivate.h"
+ (string-append #$output
+ "/include/squashfuse/")))))))
+ (inputs (list fuse-for-appimage
+ `(,zstd "lib")
+ `(,zstd "static")
+ `(,zlib "out")
+ `(,zlib "static")))))
+
+(define-public appimage-type2-runtime
+ (let ((revision "0")
+ ;; No releases, just the latest commit.
+ (commit "47b665594856b4e8928f8932adcf6d13061d8c30"))
+ (package
+ (name "appimage-type2-runtime")
+ (version (git-version "continuous" revision commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/AppImage/type2-runtime")
+ (commit commit)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32 "0954crhlbapxis96g1s0vfpf78ybr64zvjalak387ksxj560g44x"))))
+ (arguments
+ (list
+ #:make-flags
+ #~(list "-Csrc/runtime" "runtime-fuse3"
+ (string-append "CFLAGS=" "-I" #$fuse-2 "/include/fuse/"
+ " -DGIT_COMMIT='\"" "guix-" #$version "\"'"
+ " -D_FILE_OFFSET_BITS=64"
+ " -static"))
+ #:modules
+ `((guix build gnu-build-system)
+ (guix build utils)
+ (ice-9 binary-ports))
+ #:phases #~(modify-phases %standard-phases
+ (delete 'configure)
+ (delete 'check) ; No tests.
+ (replace 'install
+ (lambda _
+ (install-file "src/runtime/runtime-fuse3"
+ (string-append #$output "/bin"))))
+ ;; Must be after all elf reliant phases.
+ (add-after 'make-dynamic-linker-cache 'set-magic-bytes
+ (lambda _
+ (let ((port (open (string-append #$output
+ "/bin/runtime-fuse3")
+ (logior O_WRONLY))))
+ (seek port 8 SEEK_SET)
+ (put-bytevector port #vu8(#x41 #x49 #x02))
+ (close-port port)))))
+ #:disallowed-references (list squashfuse-for-appimage
+ fuse-for-appimage zstd zlib)))
+ ;; Only needed at build time.
+ (inputs (list squashfuse-for-appimage fuse-for-appimage
+ `(,zstd "static")
+ `(,zlib "static")))
+ (build-system gnu-build-system)
+ (home-page "https://github.com/AppImage/type2-runtime")
+ (synopsis "Runtime for executing AppImages")
+ (description "The runtime is the executable part of every AppImage, it
+mounts the payload via FUSE and executes the entrypoint.")
+ (license license:expat))))

base-commit: 870b7d69b4b06d01cdf6bfca71bcc161c0b832bf
--
2.46.0
N
[PATCH v3 2/3] pack: Add support for AppImage pack format.
(address . 73842@debbugs.gnu.org)
e6534c6ba947d3edbdcb090ae4a410cd9b5ef1c1.1730475702.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* guix/scripts/pack.scm: Add Appimage format.
* doc/guix.texi: Document AppImage pack.

Co-authored-by: Noé Lopez <noelopez@free.fr>
Change-Id: I33ebfec623cff1cfcd6f029d2d3054c23ab1949a
---
doc/guix.texi | 53 ++++++++++++++++++++-
guix/scripts/pack.scm | 104 +++++++++++++++++++++++++++++++++++++++++-
tests/pack.scm | 41 ++++++++++++++++-
3 files changed, 195 insertions(+), 3 deletions(-)

Toggle diff (311 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 187bae6898..46108dc3f1 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -6949,6 +6949,16 @@ Invoking guix pack
environment}, using commands like @command{singularity shell} or
@command{singularity exec}.
+@cindex AppImage, create an AppImage file with @command{guix pack}
+Another format internally based on SquashFS is
+@uref{https://appimage.org/, AppImage}. An AppImage file can be created
+and executed without any special privileges:
+
+@example
+file=$(guix pack -f appimage --entry-point=bin/guile guile)
+$file --help
+@end example
+
Several command-line options allow you to customize your pack:
@table @code
@@ -7065,6 +7075,47 @@ Invoking guix pack
installation or other, non-rpm packs.
@end quotation
+@item appimage
+@cindex AppImage, create an AppImage file with @command{guix pack}
+This produces an AppImage file with the @samp{.AppImage} extension.
+AppImage is a SquashFS volume prefixed with a runtime that mounts the
+SquashFS file system and executes the binary provided with
+@option{--entry-point}. This results in a self-contained archive that
+bundles the software and all its requirements into a single file. When
+the file is made executable it runs the packaged software.
+
+@example
+guix pack -f appimage --entry-point=bin/vlc vlc
+@end example
+
+The runtime used by AppImages makes use of libfuse to mount the image
+quickly. If libfuse is not available, the AppImage can still be started
+using the @option{--appimage-extract-and-run} flag.
+
+@quotation Warning
+ When building an AppImage, always @emph{pass} the
+@option{--relocatable} option (or @option{-R}, or @option{-RR}) to make
+sure the image can be used on systems where Guix is not installed. A
+warning is printed when this option is not used.
+@end quotation
+
+@example
+guix pack -f appimage --entry-point=bin/hello --relocatable hello
+@end example
+
+@quotation Note
+The resulting AppImage does not conform to the complete standard as it
+currently does not contain a @file{.DirIcon} file. This does not impact
+functionality of the AppImage itself, but possibly that of software used
+to manage AppImages.
+@end quotation
+
+@quotation Note
+As the generated AppImage packages the complete dependency graph, it
+will be larger than comparable AppImage files found online, which depend
+on host system libraries.
+@end quotation
+
@end table
@cindex relocatable binaries
@@ -7155,7 +7206,7 @@ Invoking guix pack
@cindex entry point, for Docker and Singularity images
@item --entry-point=@var{command}
Use @var{command} as the @dfn{entry point} of the resulting pack, if the pack
-format supports it---currently @code{docker} and @code{squashfs} (Singularity)
+format supports it---currently @code{docker}, @code{appimage} and @code{squashfs} (Singularity)
support it. @var{command} must be relative to the profile contained in the
pack.
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 7c5fe76fe0..26ba80b80d 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -10,6 +10,8 @@
;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -56,6 +58,7 @@ (define-module (guix scripts pack)
#:use-module ((gnu packages compression) #:hide (zip))
#:use-module (gnu packages guile)
#:use-module (gnu packages base)
+ #:autoload (gnu packages appimage) (appimage-type2-runtime)
#:autoload (gnu packages gnupg) (guile-gcrypt)
#:autoload (gnu packages guile) (guile2.0-json guile-json)
#:use-module (srfi srfi-1)
@@ -64,6 +67,7 @@ (define-module (guix scripts pack)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
#:use-module (ice-9 match)
+ #:use-module (ice-9 optargs)
#:export (symlink-spec-option-parser
self-contained-tarball
@@ -71,6 +75,7 @@ (define-module (guix scripts pack)
rpm-archive
docker-image
squashfs-image
+ self-contained-appimage
%formats
guix-pack))
@@ -974,8 +979,100 @@ (define* (rpm-archive name profile
(gexp->derivation (string-append name ".rpm") build
#:target target
#:references-graphs `(("profile" ,profile))))
+
+;;;
+;;; AppImage format
+;;;
+(define* (self-contained-appimage name profile
+ #:key target
+ (profile-name "guix-profile")
+ entry-point
+ (compressor (lookup-compressor "zstd"))
+ localstatedir?
+ (symlinks '())
+ (archiver tar)
+ (extra-options '()))
+ "Return a self-contained AppImage containing a store initialized with the
+closure of PROFILE, a derivation. The AppImage contains /gnu/store unless
+RELOCATABLE option is used; if LOCALSTATEDIR? is true, it also contains
+/var/guix, including /var/guix/db with a properly initialized store database.
+
+SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
+added to the pack."
+ (unless entry-point
+ (leave (G_ "entry-point must be provided in the '~a' format~%")
+ 'appimage))
+ (let-keywords extra-options #f ((relocatable? #f))
+ (unless relocatable?
+ (warning (G_ "AppImages should be built with the --relocatable flag~%"))))
+
+ (define runtime-package appimage-type2-runtime)
+ (define runtime-path "bin/runtime-fuse3")
+ (define %valid-compressors '("gzip" "zstd"))
+
+ (let ((compressor-name (compressor-name compressor)))
+ (unless (member compressor-name %valid-compressors)
+ (leave (G_ "~a is not a valid squashfs archive compressor used in
+generating the AppImage. Valid compressors are: ~a~%")
+ compressor-name
+ %valid-compressors)))
-
+ (define builder
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules (source-module-closure
+ '((guix build store-copy)
+ (guix build utils))
+ #:select? not-config?)
+ #~(begin
+ (use-modules (guix build utils)
+ (guix build store-copy)
+ (rnrs io ports)
+ (srfi srfi-1)
+ (srfi srfi-26))
+
+ (define (concatenate-files result file1 file2)
+ "Creates a new file RESULT containing FILE1 followed by FILE2."
+ (call-with-output-file result
+ (lambda (output)
+ (call-with-input-file file1
+ (lambda (input)
+ (dump-port input output)))
+ (call-with-input-file file2
+ (lambda (input)
+ (dump-port input output))))))
+
+ (let* ((appdir "AppDir")
+ (squashfs "squashfs")
+ (profile-items (map store-info-item
+ (call-with-input-file "profile" read-reference-graph)))
+ (profile (find (lambda (item)
+ (string-suffix? "-profile" item))
+ profile-items)))
+ (mkdir-p appdir)
+ ;; Copy all store items from the profile to the AppDir.
+ (populate-store '("profile") appdir)
+ ;; Symlink the provided entry-point to AppDir/AppRun.
+ (symlink (string-append "." profile "/" #$entry-point)
+ (string-append appdir "/AppRun"))
+ ;; Create .desktop file as required by the spec.
+ (make-desktop-entry-file
+ (string-append appdir "/" #$name ".desktop")
+ #:name #$name
+ #:exec #$entry-point)
+ ;; Compress the AppDir.
+ (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
+ squashfs "-root-owned" "-noappend"
+ "-comp" #+(compressor-name compressor))
+ ;; Append runtime and squashFS into file AppImage.
+ (concatenate-files #$output
+ #$(file-append runtime-package "/" runtime-path)
+ squashfs)
+ ;; Add execution permission.
+ (chmod #$output #o555))))))
+ (gexp->derivation (string-append name ".AppImage") builder
+ #:target target
+ #:references-graphs `(("profile" ,profile))))
+
;;;
;;; Compiling C programs.
;;;
@@ -1311,6 +1408,7 @@ (define %formats
(squashfs . ,squashfs-image)
(docker . ,docker-image)
(deb . ,debian-archive)
+ (appimage . ,self-contained-appimage)
(rpm . ,rpm-archive)))
(define (show-formats)
@@ -1327,6 +1425,8 @@ (define (show-formats)
deb Debian archive installable via dpkg/apt"))
(display (G_ "
rpm RPM archive installable via rpm/yum"))
+ (display (G_ "
+ appimage AppImage self-contained and executable format"))
(newline))
(define (required-option symbol)
@@ -1694,6 +1794,8 @@ (define-command (guix-pack . args)
(process-file-arg opts 'preun-file)
#:postun-file
(process-file-arg opts 'postun-file)))
+ ('appimage
+ (list #:relocatable? relocatable?))
(_ '())))
(target (assoc-ref opts 'target))
(bootstrap? (assoc-ref opts 'bootstrap?))
diff --git a/tests/pack.scm b/tests/pack.scm
index f8a9e09c28..6ac9a966af 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -3,6 +3,7 @@
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -32,7 +33,8 @@ (define-module (test-pack)
#:use-module (guix utils)
#:use-module ((guix build utils) #:select (%store-directory))
#:use-module (gnu packages)
- #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target))
+ #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target
+ hello))
#:use-module (gnu packages bootstrap)
#:use-module ((gnu packages package-management) #:select (rpm))
#:use-module ((gnu packages compression) #:select (squashfs-tools))
@@ -340,6 +342,43 @@ (define rpm-for-tests
(mkdir #$output))))))))
(built-derivations (list check))))
+ (unless store (test-skip 1))
+ (test-assertm "appimage"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
+ (unless store (test-skip 1))
+ (test-assertm "appimage + localstatedir"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:localstatedir? #t
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
(unless store (test-skip 1))
(test-assertm "deb archive with symlinks and control files"
(mlet* %store-monad
--
2.46.0
N
[PATCH v3 3/3] news: Add entry for guix pack ’s AppImage format
(address . 73842@debbugs.gnu.org)(name . Sebastian Dümcke)(address . code@sam-d.com)
ebb3fe56348b6203345659e52c9bf111342350c1.1730475703.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* etc/news.scm: Add entry.

Change-Id: If37b3b578517c15c2868239998975f7718f7d689
---
etc/news.scm | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

Toggle diff (58 lines)
diff --git a/etc/news.scm b/etc/news.scm
index 4929fa2a9a..4b9f768d8e 100644
--- a/etc/news.scm
+++ b/etc/news.scm
@@ -25,6 +25,7 @@
;; Copyright © 2024 Vivien Kraus <vivien@planete-kraus.eu>
;; Copyright © 2024 Guillaume Le Vaillant <glv@posteo.net>
;; Copyright © 2024 Zheng Junjie <873216071@qq.com>
+;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
;;
;; Copying and distribution of this file, with or without modification, are
;; permitted in any medium without royalty provided the copyright notice and
@@ -32,6 +33,43 @@
(channel-news
(version 0)
+ (entry (commit "e6534c6ba947d3edbdcb090ae4a410cd9b5ef1c1")
+ (title
+ (de "Neues Format @samp{AppImage} für den Befehl @command{guix pack}")
+ (en "New @samp{AppImage} format for the @command{guix pack} command")
+ (fr "Nouveau format @samp{AppImage} pour la commande @command{guix pack}"))
+ (body
+ (de "@command{guix pack} kann nun AppImage Dateien erstellen. Das
+AppImage Dateiformat erlaubt es in einer einzelnen Datei Software zu verteilen. Die
+AppImage Datei lässt sich ohne besondere Benutzerrechte ausführen. Hier ist
+ein Beispiel:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Siehe @command{info \"(guix.de) Aufruf von guix pack\"} für mehr
+Informationen.")
+ (en "@command{guix pack} can now produce AppImage a single file,
+self-contained software archive. AppImage files are easily distributed and can
+be run with user privileges. Here is an example for the @code{hello} package:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+See @command{info \"(guix) Invoking guix pack\"} for more information.")
+ (fr "@command{guix pack} peut désormais produire un fichier
+AppImage. AppImage est une manière de distribuer les logiciels en un seul
+fichier, qui peut être executé avec des droits d’utilisateur. Voici un
+exemple pour le paquet @code{hello} :
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Consultez @command{info \"(guix.fr) Invoquer guix pack\"} pour plus
+d’informations.")))
(entry (commit "5966e0fdc78771c562e0f484a22f381a77908be0")
(title
--
2.46.0
N
N
Noé Lopez wrote on 3 Nov 23:37 +0100
[PATCH v4 0/3] pack: Add support for AppImage pack format.
(address . 73842@debbugs.gnu.org)(name . Noé Lopez)(address . noelopez@free.fr)
cover.1730673402.git.noelopez@free.fr
From: Noé Lopez <noelopez@free.fr>

Hi,

I forgot to apply Sebastian’s patch for the german news entry following
Florian’s suggestions. Here is a new version.

Good evening,
Noé Lopez


Noé Lopez (1):
gnu: appimage: New packages for the appimage runtime.

Sebastian Dümcke (2):
pack: Add support for AppImage pack format.
news: Add entry for guix pack’s AppImage format

doc/guix.texi | 53 +++++++++++++++-
etc/news.scm | 38 ++++++++++++
gnu/packages/appimage.scm | 126 ++++++++++++++++++++++++++++++++++++++
guix/scripts/pack.scm | 104 ++++++++++++++++++++++++++++++-
tests/pack.scm | 41 ++++++++++++-
5 files changed, 359 insertions(+), 3 deletions(-)
create mode 100644 gnu/packages/appimage.scm


base-commit: 870b7d69b4b06d01cdf6bfca71bcc161c0b832bf
--
2.46.0
N
N
Noé Lopez wrote on 3 Nov 23:37 +0100
[PATCH v4 1/3] gnu: appimage: New packages for the appimage runtime.
(address . 73842@debbugs.gnu.org)(name . Noé Lopez)(address . noelopez@free.fr)
ca12b1b7f176bb6ae81eb2ec844d90fd9421d290.1730673402.git.noelopez@free.fr
From: Noé Lopez <noelopez@free.fr>

* gnu/packages/appimage.scm
(gnu packages appimage): New module.
(fuse-for-appimage, squashfuse-for-appimage)
(appimage-type2-runtime): New variables.

Change-Id: I857a8eb5399a6a493e52db70b6c8cf0c71360930
---
gnu/packages/appimage.scm | 126 ++++++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
create mode 100644 gnu/packages/appimage.scm

Toggle diff (134 lines)
diff --git a/gnu/packages/appimage.scm b/gnu/packages/appimage.scm
new file mode 100644
index 0000000000..df17be214e
--- /dev/null
+++ b/gnu/packages/appimage.scm
@@ -0,0 +1,126 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages appimage)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (gnu packages base)
+ #:use-module (gnu packages compression)
+ #:use-module (gnu packages file-systems)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix download)
+ #:use-module (guix gexp)
+ #:use-module (guix git-download)
+ #:use-module (guix packages)
+ #:use-module (guix utils))
+
+(define fuse-for-appimage
+ (package
+ (inherit fuse)
+ (name "fuse")
+ (version "3.16.2")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/libfuse/libfuse/releases/"
+ "download/fuse-" version "/fuse-" version ".tar.gz"))
+ (sha256
+ (base32 "11yfl2w2a445hllyzlakq97n32g06972vxpmh7lpbclnj9fhb5zp"))))
+ (arguments
+ (substitute-keyword-arguments (package-arguments fuse)
+ ((#:configure-flags original-flags #~(list))
+ #~(append #$original-flags '("--default-library=static")))))))
+
+(define squashfuse-for-appimage
+ (package
+ (inherit squashfuse)
+ (arguments
+ (list
+ #:configure-flags
+ #~'("CFLAGS=-ffunction-sections -fdata-sections -Os -no-pie"
+ "LDFLAGS=-static")
+ #:phases
+ #~(modify-phases %standard-phases
+ (add-after 'install 'install-private-headers
+ (lambda _
+ (install-file "fuseprivate.h"
+ (string-append #$output
+ "/include/squashfuse/")))))))
+ (inputs (list fuse-for-appimage
+ `(,zstd "lib")
+ `(,zstd "static")
+ `(,zlib "out")
+ `(,zlib "static")))))
+
+(define-public appimage-type2-runtime
+ (let ((revision "0")
+ ;; No releases, just the latest commit.
+ (commit "47b665594856b4e8928f8932adcf6d13061d8c30"))
+ (package
+ (name "appimage-type2-runtime")
+ (version (git-version "continuous" revision commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/AppImage/type2-runtime")
+ (commit commit)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32 "0954crhlbapxis96g1s0vfpf78ybr64zvjalak387ksxj560g44x"))))
+ (arguments
+ (list
+ #:make-flags
+ #~(list "-Csrc/runtime" "runtime-fuse3"
+ (string-append "CFLAGS=" "-I" #$fuse-2 "/include/fuse/"
+ " -DGIT_COMMIT='\"" "guix-" #$version "\"'"
+ " -D_FILE_OFFSET_BITS=64"
+ " -static"))
+ #:modules
+ `((guix build gnu-build-system)
+ (guix build utils)
+ (ice-9 binary-ports))
+ #:phases #~(modify-phases %standard-phases
+ (delete 'configure)
+ (delete 'check) ; No tests.
+ (replace 'install
+ (lambda _
+ (install-file "src/runtime/runtime-fuse3"
+ (string-append #$output "/bin"))))
+ ;; Must be after all elf reliant phases.
+ (add-after 'make-dynamic-linker-cache 'set-magic-bytes
+ (lambda _
+ (let ((port (open (string-append #$output
+ "/bin/runtime-fuse3")
+ (logior O_WRONLY))))
+ (seek port 8 SEEK_SET)
+ (put-bytevector port #vu8(#x41 #x49 #x02))
+ (close-port port)))))
+ #:disallowed-references (list squashfuse-for-appimage
+ fuse-for-appimage zstd zlib)))
+ ;; Only needed at build time.
+ (inputs (list squashfuse-for-appimage fuse-for-appimage
+ `(,zstd "static")
+ `(,zlib "static")))
+ (build-system gnu-build-system)
+ (home-page "https://github.com/AppImage/type2-runtime")
+ (synopsis "Runtime for executing AppImages")
+ (description "The runtime is the executable part of every AppImage, it
+mounts the payload via FUSE and executes the entrypoint.")
+ (license license:expat))))
--
2.46.0
N
N
Noé Lopez wrote on 3 Nov 23:37 +0100
[PATCH v4 2/3] pack: Add support for AppImage pack format.
(address . 73842@debbugs.gnu.org)
e6534c6ba947d3edbdcb090ae4a410cd9b5ef1c1.1730673402.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* guix/scripts/pack.scm: Add Appimage format.
* doc/guix.texi: Document AppImage pack.

Co-authored-by: Noé Lopez <noelopez@free.fr>
Change-Id: I33ebfec623cff1cfcd6f029d2d3054c23ab1949a
---
doc/guix.texi | 53 ++++++++++++++++++++-
guix/scripts/pack.scm | 104 +++++++++++++++++++++++++++++++++++++++++-
tests/pack.scm | 41 ++++++++++++++++-
3 files changed, 195 insertions(+), 3 deletions(-)

Toggle diff (311 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 187bae6898..46108dc3f1 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -6949,6 +6949,16 @@ Invoking guix pack
environment}, using commands like @command{singularity shell} or
@command{singularity exec}.
+@cindex AppImage, create an AppImage file with @command{guix pack}
+Another format internally based on SquashFS is
+@uref{https://appimage.org/, AppImage}. An AppImage file can be created
+and executed without any special privileges:
+
+@example
+file=$(guix pack -f appimage --entry-point=bin/guile guile)
+$file --help
+@end example
+
Several command-line options allow you to customize your pack:
@table @code
@@ -7065,6 +7075,47 @@ Invoking guix pack
installation or other, non-rpm packs.
@end quotation
+@item appimage
+@cindex AppImage, create an AppImage file with @command{guix pack}
+This produces an AppImage file with the @samp{.AppImage} extension.
+AppImage is a SquashFS volume prefixed with a runtime that mounts the
+SquashFS file system and executes the binary provided with
+@option{--entry-point}. This results in a self-contained archive that
+bundles the software and all its requirements into a single file. When
+the file is made executable it runs the packaged software.
+
+@example
+guix pack -f appimage --entry-point=bin/vlc vlc
+@end example
+
+The runtime used by AppImages makes use of libfuse to mount the image
+quickly. If libfuse is not available, the AppImage can still be started
+using the @option{--appimage-extract-and-run} flag.
+
+@quotation Warning
+ When building an AppImage, always @emph{pass} the
+@option{--relocatable} option (or @option{-R}, or @option{-RR}) to make
+sure the image can be used on systems where Guix is not installed. A
+warning is printed when this option is not used.
+@end quotation
+
+@example
+guix pack -f appimage --entry-point=bin/hello --relocatable hello
+@end example
+
+@quotation Note
+The resulting AppImage does not conform to the complete standard as it
+currently does not contain a @file{.DirIcon} file. This does not impact
+functionality of the AppImage itself, but possibly that of software used
+to manage AppImages.
+@end quotation
+
+@quotation Note
+As the generated AppImage packages the complete dependency graph, it
+will be larger than comparable AppImage files found online, which depend
+on host system libraries.
+@end quotation
+
@end table
@cindex relocatable binaries
@@ -7155,7 +7206,7 @@ Invoking guix pack
@cindex entry point, for Docker and Singularity images
@item --entry-point=@var{command}
Use @var{command} as the @dfn{entry point} of the resulting pack, if the pack
-format supports it---currently @code{docker} and @code{squashfs} (Singularity)
+format supports it---currently @code{docker}, @code{appimage} and @code{squashfs} (Singularity)
support it. @var{command} must be relative to the profile contained in the
pack.
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 7c5fe76fe0..26ba80b80d 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -10,6 +10,8 @@
;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -56,6 +58,7 @@ (define-module (guix scripts pack)
#:use-module ((gnu packages compression) #:hide (zip))
#:use-module (gnu packages guile)
#:use-module (gnu packages base)
+ #:autoload (gnu packages appimage) (appimage-type2-runtime)
#:autoload (gnu packages gnupg) (guile-gcrypt)
#:autoload (gnu packages guile) (guile2.0-json guile-json)
#:use-module (srfi srfi-1)
@@ -64,6 +67,7 @@ (define-module (guix scripts pack)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
#:use-module (ice-9 match)
+ #:use-module (ice-9 optargs)
#:export (symlink-spec-option-parser
self-contained-tarball
@@ -71,6 +75,7 @@ (define-module (guix scripts pack)
rpm-archive
docker-image
squashfs-image
+ self-contained-appimage
%formats
guix-pack))
@@ -974,8 +979,100 @@ (define* (rpm-archive name profile
(gexp->derivation (string-append name ".rpm") build
#:target target
#:references-graphs `(("profile" ,profile))))
+
+;;;
+;;; AppImage format
+;;;
+(define* (self-contained-appimage name profile
+ #:key target
+ (profile-name "guix-profile")
+ entry-point
+ (compressor (lookup-compressor "zstd"))
+ localstatedir?
+ (symlinks '())
+ (archiver tar)
+ (extra-options '()))
+ "Return a self-contained AppImage containing a store initialized with the
+closure of PROFILE, a derivation. The AppImage contains /gnu/store unless
+RELOCATABLE option is used; if LOCALSTATEDIR? is true, it also contains
+/var/guix, including /var/guix/db with a properly initialized store database.
+
+SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
+added to the pack."
+ (unless entry-point
+ (leave (G_ "entry-point must be provided in the '~a' format~%")
+ 'appimage))
+ (let-keywords extra-options #f ((relocatable? #f))
+ (unless relocatable?
+ (warning (G_ "AppImages should be built with the --relocatable flag~%"))))
+
+ (define runtime-package appimage-type2-runtime)
+ (define runtime-path "bin/runtime-fuse3")
+ (define %valid-compressors '("gzip" "zstd"))
+
+ (let ((compressor-name (compressor-name compressor)))
+ (unless (member compressor-name %valid-compressors)
+ (leave (G_ "~a is not a valid squashfs archive compressor used in
+generating the AppImage. Valid compressors are: ~a~%")
+ compressor-name
+ %valid-compressors)))
-
+ (define builder
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules (source-module-closure
+ '((guix build store-copy)
+ (guix build utils))
+ #:select? not-config?)
+ #~(begin
+ (use-modules (guix build utils)
+ (guix build store-copy)
+ (rnrs io ports)
+ (srfi srfi-1)
+ (srfi srfi-26))
+
+ (define (concatenate-files result file1 file2)
+ "Creates a new file RESULT containing FILE1 followed by FILE2."
+ (call-with-output-file result
+ (lambda (output)
+ (call-with-input-file file1
+ (lambda (input)
+ (dump-port input output)))
+ (call-with-input-file file2
+ (lambda (input)
+ (dump-port input output))))))
+
+ (let* ((appdir "AppDir")
+ (squashfs "squashfs")
+ (profile-items (map store-info-item
+ (call-with-input-file "profile" read-reference-graph)))
+ (profile (find (lambda (item)
+ (string-suffix? "-profile" item))
+ profile-items)))
+ (mkdir-p appdir)
+ ;; Copy all store items from the profile to the AppDir.
+ (populate-store '("profile") appdir)
+ ;; Symlink the provided entry-point to AppDir/AppRun.
+ (symlink (string-append "." profile "/" #$entry-point)
+ (string-append appdir "/AppRun"))
+ ;; Create .desktop file as required by the spec.
+ (make-desktop-entry-file
+ (string-append appdir "/" #$name ".desktop")
+ #:name #$name
+ #:exec #$entry-point)
+ ;; Compress the AppDir.
+ (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
+ squashfs "-root-owned" "-noappend"
+ "-comp" #+(compressor-name compressor))
+ ;; Append runtime and squashFS into file AppImage.
+ (concatenate-files #$output
+ #$(file-append runtime-package "/" runtime-path)
+ squashfs)
+ ;; Add execution permission.
+ (chmod #$output #o555))))))
+ (gexp->derivation (string-append name ".AppImage") builder
+ #:target target
+ #:references-graphs `(("profile" ,profile))))
+
;;;
;;; Compiling C programs.
;;;
@@ -1311,6 +1408,7 @@ (define %formats
(squashfs . ,squashfs-image)
(docker . ,docker-image)
(deb . ,debian-archive)
+ (appimage . ,self-contained-appimage)
(rpm . ,rpm-archive)))
(define (show-formats)
@@ -1327,6 +1425,8 @@ (define (show-formats)
deb Debian archive installable via dpkg/apt"))
(display (G_ "
rpm RPM archive installable via rpm/yum"))
+ (display (G_ "
+ appimage AppImage self-contained and executable format"))
(newline))
(define (required-option symbol)
@@ -1694,6 +1794,8 @@ (define-command (guix-pack . args)
(process-file-arg opts 'preun-file)
#:postun-file
(process-file-arg opts 'postun-file)))
+ ('appimage
+ (list #:relocatable? relocatable?))
(_ '())))
(target (assoc-ref opts 'target))
(bootstrap? (assoc-ref opts 'bootstrap?))
diff --git a/tests/pack.scm b/tests/pack.scm
index f8a9e09c28..6ac9a966af 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -3,6 +3,7 @@
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -32,7 +33,8 @@ (define-module (test-pack)
#:use-module (guix utils)
#:use-module ((guix build utils) #:select (%store-directory))
#:use-module (gnu packages)
- #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target))
+ #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target
+ hello))
#:use-module (gnu packages bootstrap)
#:use-module ((gnu packages package-management) #:select (rpm))
#:use-module ((gnu packages compression) #:select (squashfs-tools))
@@ -340,6 +342,43 @@ (define rpm-for-tests
(mkdir #$output))))))))
(built-derivations (list check))))
+ (unless store (test-skip 1))
+ (test-assertm "appimage"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
+ (unless store (test-skip 1))
+ (test-assertm "appimage + localstatedir"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:localstatedir? #t
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
(unless store (test-skip 1))
(test-assertm "deb archive with symlinks and control files"
(mlet* %store-monad
--
2.46.0
N
N
Noé Lopez wrote on 3 Nov 23:37 +0100
[PATCH v4 3/3] news: Add entry for guix pack ’s AppImage format
(address . 73842@debbugs.gnu.org)(name . Sebastian Dümcke)(address . code@sam-d.com)
6215f728ad5651476412d043d640d52aa51b2179.1730673402.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* etc/news.scm: Add entry.

Change-Id: If37b3b578517c15c2868239998975f7718f7d689
---
etc/news.scm | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

Toggle diff (58 lines)
diff --git a/etc/news.scm b/etc/news.scm
index 4929fa2a9a..30d907f2ed 100644
--- a/etc/news.scm
+++ b/etc/news.scm
@@ -25,6 +25,7 @@
;; Copyright © 2024 Vivien Kraus <vivien@planete-kraus.eu>
;; Copyright © 2024 Guillaume Le Vaillant <glv@posteo.net>
;; Copyright © 2024 Zheng Junjie <873216071@qq.com>
+;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
;;
;; Copying and distribution of this file, with or without modification, are
;; permitted in any medium without royalty provided the copyright notice and
@@ -32,6 +33,43 @@
(channel-news
(version 0)
+ (entry (commit "e6534c6ba947d3edbdcb090ae4a410cd9b5ef1c1")
+ (title
+ (de "Neues Format @samp{appimage} für den Befehl @command{guix pack}")
+ (en "New @samp{AppImage} format for the @command{guix pack} command")
+ (fr "Nouveau format @samp{AppImage} pour la commande @command{guix pack}"))
+ (body
+ (de "@command{guix pack} kann nun AppImage-Dateien erstellen. Das
+AppImage-Dateiformat erlaubt es, in einer einzelnen Datei Software zu verteilen. Die
+AppImage-Datei lässt sich ohne besondere Benutzerrechte ausführen. Hier ist
+ein Beispiel:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Siehe @command{info \"(guix.de) Aufruf von guix pack\"} für mehr
+Informationen.")
+ (en "@command{guix pack} can now produce AppImage a single file,
+self-contained software archive. AppImage files are easily distributed and can
+be run with user privileges. Here is an example for the @code{hello} package:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+See @command{info \"(guix) Invoking guix pack\"} for more information.")
+ (fr "@command{guix pack} peut désormais produire un fichier
+AppImage. AppImage est une manière de distribuer les logiciels en un seul
+fichier, qui peut être executé avec des droits d’utilisateur. Voici un
+exemple pour le paquet @code{hello} :
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Consultez @command{info \"(guix.fr) Invoquer guix pack\"} pour plus
+d’informations.")))
(entry (commit "5966e0fdc78771c562e0f484a22f381a77908be0")
(title
--
2.46.0
L
L
Ludovic Courtès wrote on 5 Nov 15:22 +0100
Re: [bug#73842] [PATCH v4 1/3] gnu: appimage: New packages for the appimage runtime.
(name . Noé Lopez)(address . noe@xn--no-cja.eu)
87r07pyd39.fsf@gnu.org
Hi!

Noé Lopez <noe@noé.eu> skribis:

Toggle quote (11 lines)
> From: Noé Lopez <noelopez@free.fr>
>
> * gnu/packages/appimage.scm
> (gnu packages appimage): New module.
> (fuse-for-appimage, squashfuse-for-appimage)
> (appimage-type2-runtime): New variables.
>
> Change-Id: I857a8eb5399a6a493e52db70b6c8cf0c71360930
> ---
> gnu/packages/appimage.scm | 126 ++++++++++++++++++++++++++++++++++++++

Please add this file to ‘gnu/local.mk’.

Toggle quote (4 lines)
> +(define fuse-for-appimage
> + (package
> + (inherit fuse)

Due to restrictions having to do with circular module dependencies, this
package must be defined in the same file as ‘fuse’ itself (apologies for
overlooking it!). You could add the ‘hidden?’ property if needed, but
IMO it perhaps not even necessary to hide it.

Toggle quote (4 lines)
> +(define squashfuse-for-appimage
> + (package
> + (inherit squashfuse)

Likewise, this should be in the same file as ‘squashfuse’.

Toggle quote (6 lines)
> + (arguments
> + (list
> + #:make-flags
> + #~(list "-Csrc/runtime" "runtime-fuse3"
> + (string-append "CFLAGS=" "-I" #$fuse-2 "/include/fuse/"

Rather: #$(this-package-input "fuse").

Toggle quote (4 lines)
> + " -DGIT_COMMIT='\"" "guix-" #$version "\"'"
> + " -D_FILE_OFFSET_BITS=64"
> + " -static"))

You may need to add “-O2 -g” here, or the code might be compiled as with
“-O0 -g0”.

Toggle quote (10 lines)
> + ;; Must be after all elf reliant phases.
> + (add-after 'make-dynamic-linker-cache 'set-magic-bytes
> + (lambda _
> + (let ((port (open (string-append #$output
> + "/bin/runtime-fuse3")
> + (logior O_WRONLY))))
> + (seek port 8 SEEK_SET)
> + (put-bytevector port #vu8(#x41 #x49 #x02))
> + (close-port port)))))

What’s the story with magic bytes here? :-)

Could you add a comment explaining what the phase does and why it’s
necessary?

Toggle quote (7 lines)
> + #:disallowed-references (list squashfuse-for-appimage
> + fuse-for-appimage zstd zlib)))
> + ;; Only needed at build time.
> + (inputs (list squashfuse-for-appimage fuse-for-appimage
> + `(,zstd "static")
> + `(,zlib "static")))

It should probably be ‘native-inputs’, then.

Also, in #:disallowed-references, I think you need:

`(,zstd "static") `(,zlib "static")

Toggle quote (4 lines)
> + (synopsis "Runtime for executing AppImages")
> + (description "The runtime is the executable part of every AppImage, it
> +mounts the payload via FUSE and executes the entrypoint.")

L
L
Ludovic Courtès wrote on 5 Nov 15:27 +0100
Re: [bug#73842] [PATCH v4 2/3] pack: Add support for AppImage pack format.
(name . Noé Lopez)(address . noe@xn--no-cja.eu)
87msidycvu.fsf@gnu.org
Noé Lopez <noe@noé.eu> skribis:

Toggle quote (8 lines)
> From: Sebastian Dümcke <code@sam-d.com>
>
> * guix/scripts/pack.scm: Add Appimage format.
> * doc/guix.texi: Document AppImage pack.
>
> Co-authored-by: Noé Lopez <noelopez@free.fr>
> Change-Id: I33ebfec623cff1cfcd6f029d2d3054c23ab1949a

Please mention ‘tests/pack.scm’ in the log.

Toggle quote (3 lines)
> +@cindex AppImage, create an AppImage file with @command{guix pack}
> +This produces an AppImage file with the @samp{.AppImage} extension.

s/AppImage file/@uref{https://appimage.org/,AppImage file}/

Toggle quote (3 lines)
> -format supports it---currently @code{docker} and @code{squashfs} (Singularity)
> +format supports it---currently @code{docker}, @code{appimage} and @code{squashfs} (Singularity)

Nitpick: s/ and/, and/

Toggle quote (5 lines)
> + (check (gexp->derivation
> + "check-appimage"
> + #~(begin
> + (invoke #$image)))))

#~(invoke #$image)
L
L
Ludovic Courtès wrote on 5 Nov 15:28 +0100
Re: [bug#73842] [PATCH v4 3/3] news: Add entry for guix pack’s AppImage format
(name . Noé Lopez)(address . noe@xn--no-cja.eu)
87ikt1ycu1.fsf@gnu.org
Noé Lopez <noe@noé.eu> skribis:

Toggle quote (6 lines)
> From: Sebastian Dümcke <code@sam-d.com>
>
> * etc/news.scm: Add entry.
>
> Change-Id: If37b3b578517c15c2868239998975f7718f7d689

Perfect!

As you can see, these comments are mostly nitpicking, so hopefully v5
will be the final one.

Great job, thank you!

Ludo’.
N
N
Noé Lopez wrote on 7 Nov 17:50 +0100
[PATCH v5 1/3] gnu: appimage: New packages for the appimage runtime.
(address . 73842@debbugs.gnu.org)(name . Noé Lopez)(address . noelopez@free.fr)
4a7795be82c20d9476dbefcbaf3c380dcb61581e.1730998051.git.noelopez@free.fr
From: Noé Lopez <noelopez@free.fr>

* gnu/packages/appimage.scm
(gnu packages appimage): New module.
(appimage-type2-runtime): New variable.
* gnu/packages/file-systems.scm (squashfuse-for-appimage):
New variable.
* gnu/packages/linux.scm (fuse-for-appimage): New variable.

Change-Id: I857a8eb5399a6a493e52db70b6c8cf0c71360930
---
gnu/local.mk | 2 +
gnu/packages/appimage.scm | 97 +++++++++++++++++++++++++++++++++++
gnu/packages/file-systems.scm | 21 ++++++++
gnu/packages/linux.scm | 17 ++++++
4 files changed, 137 insertions(+)
create mode 100644 gnu/packages/appimage.scm

Toggle diff (187 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index 945d89d550..0deab9065f 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -67,6 +67,7 @@
# Copyright © 2024 David Elsing <david.elsing@posteo.net>
# Copyright © 2024 Ashish SHUKLA <ashish.is@lostca.se>
# Copyright © 2024 Fabio Natali <me@fabionatali.com>
+# Copyright © 2024 Noé Lopez <noelopez@free.fr>
#
# This file is part of GNU Guix.
#
@@ -139,6 +140,7 @@ GNU_SYSTEM_MODULES = \
%D%/packages/apl.scm \
%D%/packages/apple.scm \
%D%/packages/apparmor.scm \
+ %D%/packages/appimage.scm \
%D%/packages/apr.scm \
%D%/packages/arcan.scm \
%D%/packages/aspell.scm \
diff --git a/gnu/packages/appimage.scm b/gnu/packages/appimage.scm
new file mode 100644
index 0000000000..1d0caada46
--- /dev/null
+++ b/gnu/packages/appimage.scm
@@ -0,0 +1,97 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages appimage)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (gnu packages base)
+ #:use-module (gnu packages compression)
+ #:use-module (gnu packages file-systems)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix gexp)
+ #:use-module (guix git-download)
+ #:use-module (guix packages)
+ #:use-module (guix utils))
+
+(define-public appimage-type2-runtime
+ (let ((revision "0")
+ ;; No releases, just the latest commit.
+ (commit "47b665594856b4e8928f8932adcf6d13061d8c30"))
+ (package
+ (name "appimage-type2-runtime")
+ (version (git-version "continuous" revision commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/AppImage/type2-runtime")
+ (commit commit)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32 "0954crhlbapxis96g1s0vfpf78ybr64zvjalak387ksxj560g44x"))))
+ (arguments
+ (list
+ #:make-flags
+ #~(list "-Csrc/runtime" "runtime-fuse3"
+ (string-append
+ "CFLAGS=" "-I" #$(this-package-input "fuse") "/include/fuse/"
+ " -DGIT_COMMIT='\"" "guix-" #$version "\"'"
+ " -D_FILE_OFFSET_BITS=64"
+ " -static"))
+ #:modules
+ `((guix build gnu-build-system)
+ (guix build utils)
+ (ice-9 binary-ports))
+ #:phases #~(modify-phases %standard-phases
+ (delete 'configure)
+ (delete 'check) ; No tests.
+ (replace 'install
+ (lambda _
+ (install-file "src/runtime/runtime-fuse3"
+ (string-append #$output "/bin"))))
+ ;; Must be after all elf reliant phases. Used to identify the
+ ;; executable as an AppImage as per the specification.
+ (add-after 'make-dynamic-linker-cache 'set-magic-bytes
+ (lambda _
+ (let ((port (open (string-append #$output
+ "/bin/runtime-fuse3")
+ (logior O_WRONLY))))
+ (seek port 8 SEEK_SET)
+ (put-bytevector port #vu8(#x41 #x49 #x02))
+ (close-port port)))))
+ #:disallowed-references (list squashfuse-for-appimage
+ fuse-for-appimage
+ (gexp-input zstd "static")
+ (gexp-input zlib "static"))))
+ ;; Only needed at build time.
+ (inputs (list squashfuse-for-appimage
+ fuse-for-appimage
+ `(,zstd "static")
+ `(,zlib "static")))
+ (build-system gnu-build-system)
+ (home-page "https://github.com/AppImage/type2-runtime")
+ (synopsis "Runtime for executing AppImages")
+ (description "The runtime is the executable part of every AppImage. It mounts
+the payload via @acronym{FUSE} and executes the entrypoint, allowing users to run
+applications in a portable manner without the need for installation. This runtime
+ensures that the AppImage can access its bundled libraries and resources seamlessly,
+providing a consistent environment across different Linux distributions. In the
+absence of @acronym{FUSE}, the AppImage can still be started using the
+@option{--appimage-extract-and-run} flag.")
+ (license license:expat))))
diff --git a/gnu/packages/file-systems.scm b/gnu/packages/file-systems.scm
index 059bdf67a9..e36751e774 100644
--- a/gnu/packages/file-systems.scm
+++ b/gnu/packages/file-systems.scm
@@ -2059,6 +2059,27 @@ (define-public squashfuse
memory-efficient.")
(license license:bsd-2)))
+(define-public squashfuse-for-appimage
+ (package
+ (inherit squashfuse)
+ (arguments
+ (list
+ #:configure-flags
+ #~'("CFLAGS=-ffunction-sections -fdata-sections -Os -no-pie"
+ "LDFLAGS=-static")
+ #:phases
+ #~(modify-phases %standard-phases
+ (add-after 'install 'install-private-headers
+ (lambda _
+ (install-file "fuseprivate.h"
+ (string-append #$output
+ "/include/squashfuse/")))))))
+ (inputs (list fuse-for-appimage
+ `(,zstd "lib")
+ `(,zstd "static")
+ `(,zlib "out")
+ `(,zlib "static")))))
+
(define-public tmsu
(package
(name "tmsu")
diff --git a/gnu/packages/linux.scm b/gnu/packages/linux.scm
index 992790188a..58403c5b17 100644
--- a/gnu/packages/linux.scm
+++ b/gnu/packages/linux.scm
@@ -4100,6 +4100,23 @@ (define-public fuse-2
(("-DFUSERMOUNT_DIR=[[:graph:]]+")
"-DFUSERMOUNT_DIR=\\\"/var/empty\\\"")))))))))
+(define-public fuse-for-appimage
+ (package
+ (inherit fuse)
+ (name "fuse")
+ (version "3.16.2")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/libfuse/libfuse/releases/"
+ "download/fuse-" version "/fuse-" version ".tar.gz"))
+ (sha256
+ (base32 "11yfl2w2a445hllyzlakq97n32g06972vxpmh7lpbclnj9fhb5zp"))))
+ (arguments
+ (substitute-keyword-arguments (package-arguments fuse)
+ ((#:configure-flags original-flags #~(list))
+ #~(append #$original-flags '("--default-library=static")))))))
+
(define-public unionfs-fuse
(package
(name "unionfs-fuse")

base-commit: ba9466481d10992d35f09d010166d616fdb6a637
--
2.46.0
N
N
Noé Lopez wrote on 7 Nov 17:50 +0100
[PATCH v5 2/3] pack: Add support for AppImage pack format.
(address . 73842@debbugs.gnu.org)
7507b2c56ffbbd8bc051937c5c88bb8c0e3ae8f3.1730998051.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* guix/scripts/pack.scm: Add Appimage format.
* doc/guix.texi: Document AppImage pack.
* tests/pack.scm: New AppImage tests.

Co-authored-by: Noé Lopez <noelopez@free.fr>
Change-Id: I33ebfec623cff1cfcd6f029d2d3054c23ab1949a
---
doc/guix.texi | 59 ++++++++++++++++++++++--
guix/scripts/pack.scm | 104 +++++++++++++++++++++++++++++++++++++++++-
tests/pack.scm | 40 +++++++++++++++-
3 files changed, 197 insertions(+), 6 deletions(-)

Toggle diff (316 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 151fcd89ac..a1435ceece 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -6949,6 +6949,16 @@ Invoking guix pack
environment}, using commands like @command{singularity shell} or
@command{singularity exec}.
+@cindex AppImage, create an AppImage file with @command{guix pack}
+Another format internally based on SquashFS is
+@uref{https://appimage.org/, AppImage}. An AppImage file can be created
+and executed without any special privileges:
+
+@example
+file=$(guix pack -f appimage --entry-point=bin/guile guile)
+$file --help
+@end example
+
Several command-line options allow you to customize your pack:
@table @code
@@ -7065,6 +7075,47 @@ Invoking guix pack
installation or other, non-rpm packs.
@end quotation
+@item appimage
+@cindex AppImage, create an AppImage file with @command{guix pack}
+This produces an @uref{https://appimage.org/, AppImage file} with the
+@samp{.AppImage} extension. AppImage is a SquashFS volume prefixed with
+a runtime that mounts the SquashFS file system and executes the binary
+provided with @option{--entry-point}. This results in a self-contained
+archive that bundles the software and all its requirements into a single
+file. When the file is made executable it runs the packaged software.
+
+@example
+guix pack -f appimage --entry-point=bin/vlc vlc
+@end example
+
+The runtime used by AppImages makes use of libfuse to mount the image
+quickly. If libfuse is not available, the AppImage can still be started
+using the @option{--appimage-extract-and-run} flag.
+
+@quotation Warning
+ When building an AppImage, always @emph{pass} the
+@option{--relocatable} option (or @option{-R}, or @option{-RR}) to make
+sure the image can be used on systems where Guix is not installed. A
+warning is printed when this option is not used.
+@end quotation
+
+@example
+guix pack -f appimage --entry-point=bin/hello --relocatable hello
+@end example
+
+@quotation Note
+The resulting AppImage does not conform to the complete standard as it
+currently does not contain a @file{.DirIcon} file. This does not impact
+functionality of the AppImage itself, but possibly that of software used
+to manage AppImages.
+@end quotation
+
+@quotation Note
+As the generated AppImage packages the complete dependency graph, it
+will be larger than comparable AppImage files found online, which depend
+on host system libraries.
+@end quotation
+
@end table
@cindex relocatable binaries
@@ -7154,10 +7205,10 @@ Invoking guix pack
@cindex entry point, for Docker and Singularity images
@item --entry-point=@var{command}
-Use @var{command} as the @dfn{entry point} of the resulting pack, if the pack
-format supports it---currently @code{docker} and @code{squashfs} (Singularity)
-support it. @var{command} must be relative to the profile contained in the
-pack.
+Use @var{command} as the @dfn{entry point} of the resulting pack, if the
+pack format supports it---currently @code{docker}, @code{appimage}, and
+@code{squashfs} (Singularity) support it. @var{command} must be
+relative to the profile contained in the pack.
The entry point specifies the command that tools like @code{docker run} or
@code{singularity run} automatically start by default. For example, you can
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 7c5fe76fe0..26ba80b80d 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -10,6 +10,8 @@
;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -56,6 +58,7 @@ (define-module (guix scripts pack)
#:use-module ((gnu packages compression) #:hide (zip))
#:use-module (gnu packages guile)
#:use-module (gnu packages base)
+ #:autoload (gnu packages appimage) (appimage-type2-runtime)
#:autoload (gnu packages gnupg) (guile-gcrypt)
#:autoload (gnu packages guile) (guile2.0-json guile-json)
#:use-module (srfi srfi-1)
@@ -64,6 +67,7 @@ (define-module (guix scripts pack)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
#:use-module (ice-9 match)
+ #:use-module (ice-9 optargs)
#:export (symlink-spec-option-parser
self-contained-tarball
@@ -71,6 +75,7 @@ (define-module (guix scripts pack)
rpm-archive
docker-image
squashfs-image
+ self-contained-appimage
%formats
guix-pack))
@@ -974,8 +979,100 @@ (define* (rpm-archive name profile
(gexp->derivation (string-append name ".rpm") build
#:target target
#:references-graphs `(("profile" ,profile))))
+
+;;;
+;;; AppImage format
+;;;
+(define* (self-contained-appimage name profile
+ #:key target
+ (profile-name "guix-profile")
+ entry-point
+ (compressor (lookup-compressor "zstd"))
+ localstatedir?
+ (symlinks '())
+ (archiver tar)
+ (extra-options '()))
+ "Return a self-contained AppImage containing a store initialized with the
+closure of PROFILE, a derivation. The AppImage contains /gnu/store unless
+RELOCATABLE option is used; if LOCALSTATEDIR? is true, it also contains
+/var/guix, including /var/guix/db with a properly initialized store database.
+
+SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
+added to the pack."
+ (unless entry-point
+ (leave (G_ "entry-point must be provided in the '~a' format~%")
+ 'appimage))
+ (let-keywords extra-options #f ((relocatable? #f))
+ (unless relocatable?
+ (warning (G_ "AppImages should be built with the --relocatable flag~%"))))
+
+ (define runtime-package appimage-type2-runtime)
+ (define runtime-path "bin/runtime-fuse3")
+ (define %valid-compressors '("gzip" "zstd"))
+
+ (let ((compressor-name (compressor-name compressor)))
+ (unless (member compressor-name %valid-compressors)
+ (leave (G_ "~a is not a valid squashfs archive compressor used in
+generating the AppImage. Valid compressors are: ~a~%")
+ compressor-name
+ %valid-compressors)))
-
+ (define builder
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules (source-module-closure
+ '((guix build store-copy)
+ (guix build utils))
+ #:select? not-config?)
+ #~(begin
+ (use-modules (guix build utils)
+ (guix build store-copy)
+ (rnrs io ports)
+ (srfi srfi-1)
+ (srfi srfi-26))
+
+ (define (concatenate-files result file1 file2)
+ "Creates a new file RESULT containing FILE1 followed by FILE2."
+ (call-with-output-file result
+ (lambda (output)
+ (call-with-input-file file1
+ (lambda (input)
+ (dump-port input output)))
+ (call-with-input-file file2
+ (lambda (input)
+ (dump-port input output))))))
+
+ (let* ((appdir "AppDir")
+ (squashfs "squashfs")
+ (profile-items (map store-info-item
+ (call-with-input-file "profile" read-reference-graph)))
+ (profile (find (lambda (item)
+ (string-suffix? "-profile" item))
+ profile-items)))
+ (mkdir-p appdir)
+ ;; Copy all store items from the profile to the AppDir.
+ (populate-store '("profile") appdir)
+ ;; Symlink the provided entry-point to AppDir/AppRun.
+ (symlink (string-append "." profile "/" #$entry-point)
+ (string-append appdir "/AppRun"))
+ ;; Create .desktop file as required by the spec.
+ (make-desktop-entry-file
+ (string-append appdir "/" #$name ".desktop")
+ #:name #$name
+ #:exec #$entry-point)
+ ;; Compress the AppDir.
+ (invoke #+(file-append squashfs-tools "/bin/mksquashfs") appdir
+ squashfs "-root-owned" "-noappend"
+ "-comp" #+(compressor-name compressor))
+ ;; Append runtime and squashFS into file AppImage.
+ (concatenate-files #$output
+ #$(file-append runtime-package "/" runtime-path)
+ squashfs)
+ ;; Add execution permission.
+ (chmod #$output #o555))))))
+ (gexp->derivation (string-append name ".AppImage") builder
+ #:target target
+ #:references-graphs `(("profile" ,profile))))
+
;;;
;;; Compiling C programs.
;;;
@@ -1311,6 +1408,7 @@ (define %formats
(squashfs . ,squashfs-image)
(docker . ,docker-image)
(deb . ,debian-archive)
+ (appimage . ,self-contained-appimage)
(rpm . ,rpm-archive)))
(define (show-formats)
@@ -1327,6 +1425,8 @@ (define (show-formats)
deb Debian archive installable via dpkg/apt"))
(display (G_ "
rpm RPM archive installable via rpm/yum"))
+ (display (G_ "
+ appimage AppImage self-contained and executable format"))
(newline))
(define (required-option symbol)
@@ -1694,6 +1794,8 @@ (define-command (guix-pack . args)
(process-file-arg opts 'preun-file)
#:postun-file
(process-file-arg opts 'postun-file)))
+ ('appimage
+ (list #:relocatable? relocatable?))
(_ '())))
(target (assoc-ref opts 'target))
(bootstrap? (assoc-ref opts 'bootstrap?))
diff --git a/tests/pack.scm b/tests/pack.scm
index f8a9e09c28..1c1e312557 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -3,6 +3,7 @@
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -32,7 +33,8 @@ (define-module (test-pack)
#:use-module (guix utils)
#:use-module ((guix build utils) #:select (%store-directory))
#:use-module (gnu packages)
- #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target))
+ #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target
+ hello))
#:use-module (gnu packages bootstrap)
#:use-module ((gnu packages package-management) #:select (rpm))
#:use-module ((gnu packages compression) #:select (squashfs-tools))
@@ -340,6 +342,42 @@ (define rpm-for-tests
(mkdir #$output))))))))
(built-derivations (list check))))
+ (unless store (test-skip 1))
+ (test-assertm "appimage"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(invoke #$image))))
+ (built-derivations (list check))))
+
+ (unless store (test-skip 1))
+ (test-assertm "appimage + localstatedir"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:localstatedir? #t
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
(unless store (test-skip 1))
(test-assertm "deb archive with symlinks and control files"
(mlet* %store-monad
--
2.46.0
N
N
Noé Lopez wrote on 7 Nov 17:50 +0100
[PATCH v5 3/3] news: Add entry for guix pack ’s AppImage format
(address . 73842@debbugs.gnu.org)(name . Sebastian Dümcke)(address . code@sam-d.com)
f88c6785a6f6ec52f3139e75aac710b4beae7463.1730998052.git.noelopez@free.fr
From: Sebastian Dümcke <code@sam-d.com>

* etc/news.scm: Add entry.

Change-Id: If37b3b578517c15c2868239998975f7718f7d689
---
etc/news.scm | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

Toggle diff (58 lines)
diff --git a/etc/news.scm b/etc/news.scm
index 4929fa2a9a..4911ee65ee 100644
--- a/etc/news.scm
+++ b/etc/news.scm
@@ -25,6 +25,7 @@
;; Copyright © 2024 Vivien Kraus <vivien@planete-kraus.eu>
;; Copyright © 2024 Guillaume Le Vaillant <glv@posteo.net>
;; Copyright © 2024 Zheng Junjie <873216071@qq.com>
+;; Copyright © 2024 Sebastian Dümcke <code@sam-d.com>
;;
;; Copying and distribution of this file, with or without modification, are
;; permitted in any medium without royalty provided the copyright notice and
@@ -32,6 +33,43 @@
(channel-news
(version 0)
+ (entry (commit "9e96056bf40591cc9439d60c4c5bae5aa0f0b2f8")
+ (title
+ (de "Neues Format @samp{appimage} für den Befehl @command{guix pack}")
+ (en "New @samp{AppImage} format for the @command{guix pack} command")
+ (fr "Nouveau format @samp{AppImage} pour la commande @command{guix pack}"))
+ (body
+ (de "@command{guix pack} kann nun AppImage-Dateien erstellen. Das
+AppImage-Dateiformat erlaubt es, in einer einzelnen Datei Software zu verteilen. Die
+AppImage-Datei lässt sich ohne besondere Benutzerrechte ausführen. Hier ist
+ein Beispiel:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Siehe @command{info \"(guix.de) Aufruf von guix pack\"} für mehr
+Informationen.")
+ (en "@command{guix pack} can now produce AppImage a single file,
+self-contained software archive. AppImage files are easily distributed and can
+be run with user privileges. Here is an example for the @code{hello} package:
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+See @command{info \"(guix) Invoking guix pack\"} for more information.")
+ (fr "@command{guix pack} peut désormais produire un fichier
+AppImage. AppImage est une manière de distribuer les logiciels en un seul
+fichier, qui peut être executé avec des droits d’utilisateur. Voici un
+exemple pour le paquet @code{hello} :
+
+@example
+guix pack --format=appimage --entry-point=bin/hello hello
+@end example
+
+Consultez @command{info \"(guix.fr) Invoquer guix pack\"} pour plus
+d’informations.")))
(entry (commit "5966e0fdc78771c562e0f484a22f381a77908be0")
(title
--
2.46.0
O
O
outlook user wrote on 9 Nov 18:33 +0100
RE: bug#21142: Sshfs-fuse requires fuse
AS8P251MB08540FEAB2CBBE1C3EC883C6975E2@AS8P251MB0854.EURP251.PROD.OUTLOOK.COM
That workaround worked in 2014, but now "libfuse" seems to search under "/usr/bin" and not "/run/setuid-programs". The `DFUSERMOUNT_DIR` modification doesn't seem to work anymore. I think to reproduce all what is needed is `strace` with follow forks to see the software seeking "/usr/bin." And personally, I use an "AppImage" to invoke fuse that will seek for `fusermount3`

The author of libfuse will push an update in the future for libfuse to give up upon searching binaries by itself and only relying on $PATH simply, but that's not for now...
Attachment: file
L
L
Ludovic Courtès wrote on 15 Nov 18:02 +0100
Re: [bug#73842] [PATCH v5 2/3] pack: Add support for AppImage pack format.
(name . Noé Lopez)(address . noe@xn--no-cja.eu)
87h688l9bk.fsf@gnu.org
Hello Noé,

The patch series LGTM, and tests pass: we’re all set!

But… one thing I noticed when trying it out is that the resulting
AppImage would fail to run on relatively bare-bones non-Guix systems:

Toggle snippet (11 lines)
$ ./2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage
fuse: failed to exec fusermount3: No such file or directory

Cannot mount AppImage, please check your FUSE setup.
You might still be able to extract the contents of this AppImage
if you run it with the --appimage-extract option.
See https://github.com/AppImage/AppImageKit/wiki/FUSE
for more information
open dir error: No such file or directory

I wonder if there’s something that can be done on our side about it or
if it’s a limitation of the approach (I expect the latter: ‘fusermount’
has to be available and setuid root), in which case we could just add a
warning in the manual.

WDYT?

Interestingly, it works on my Guix System laptop, except if I strace it:

Toggle snippet (17 lines)
$ /gnu/store/2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage
Hello, world!
$ strace -f -o /tmp/log.strace -s 500 /gnu/store/2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage
fusermount3: mount failed: Operation not permitted

Cannot mount AppImage, please check your FUSE setup.
You might still be able to extract the contents of this AppImage
if you run it with the --appimage-extract option.
See https://github.com/AppImage/AppImageKit/wiki/FUSE
for more information
open dir error: No such file or directory
$ grep 'mount(' /tmp/log.strace
17569 mount("2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", "/tmp/.mount_2i2l6iLZ8WYZ", "fuse.2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", MS_RDONLY|MS_NOSUID|MS_NODEV, "fd=5,rootmode=40000,user_id=1000,group_id=998") = -1 EPERM (Operation not permitted)
17570 mount("2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", ".", "fuse.2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", MS_RDONLY|MS_NOSUID|MS_NODEV, "fd=6,rootmode=40000,user_id=1000,group_id=998") = -1 EPERM (Operation not permitted)
17571 mount("/dev/fuse", ".", "fuse", MS_RDONLY|MS_NOSUID|MS_NODEV, "fd=6,rootmode=40000,user_id=1000,group_id=998") = -1 EPERM (Operation not permitted)

Ludo’.
N
N
Noé Lopez wrote on 17 Nov 17:25 +0100
(address . 73842@debbugs.gnu.org)
87mshxq12m.fsf@xn--no-cja.eu
Forwarding here since I replied instead of replied to all :/
-------------------- Start of forwarded message --------------------
From: Noé Lopez <noe@xn--no-cja.eu>
To: Ludovic Courtès <ludo@gnu.org>
Subject: Re: [bug#73842] [PATCH v5 2/3] pack: Add support for AppImage pack
format.
Date: Sun, 17 Nov 2024 17:20:48 +0100

Hi Ludovic,

Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (5 lines)
> Hello Noé,
>
> The patch series LGTM, and tests pass: we’re all set!
>

Great! Thanks a lot for the time you spent reviewing.

Toggle quote (22 lines)
> But… one thing I noticed when trying it out is that the resulting
> AppImage would fail to run on relatively bare-bones non-Guix systems:
>
> --8<---------------cut here---------------start------------->8---
> $ ./2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage
> fuse: failed to exec fusermount3: No such file or directory
>
> Cannot mount AppImage, please check your FUSE setup.
> You might still be able to extract the contents of this AppImage
> if you run it with the --appimage-extract option.
> See https://github.com/AppImage/AppImageKit/wiki/FUSE
> for more information
> open dir error: No such file or directory
> --8<---------------cut here---------------end--------------->8---
>
> I wonder if there’s something that can be done on our side about it or
> if it’s a limitation of the approach (I expect the latter: ‘fusermount’
> has to be available and setuid root), in which case we could just add a
> warning in the manual.
>
> WDYT?

This is an expected error from the AppImage runtime, as said in the
output the runtime depends on FUSE 3 to mount the AppImage.

The AppImage can still be ran using --appimage-extract-and-run, which I
believe we noted in the documentation:

Toggle quote (4 lines)
>The runtime used by AppImages makes use of libfuse to mount the image
>quickly. If libfuse is not available, the AppImage can still be started
>using the @option{--appimage-extract-and-run} flag.

As for things that can be done from our side, we could change runtime to
one that uses other technologies just like supported by “-RR”.

Toggle quote (23 lines)
>
> Interestingly, it works on my Guix System laptop, except if I strace it:
>
> --8<---------------cut here---------------start------------->8---
> $ /gnu/store/2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage
> Hello, world!
> $ strace -f -o /tmp/log.strace -s 500 /gnu/store/2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage
> fusermount3: mount failed: Operation not permitted
>
> Cannot mount AppImage, please check your FUSE setup.
> You might still be able to extract the contents of this AppImage
> if you run it with the --appimage-extract option.
> See https://github.com/AppImage/AppImageKit/wiki/FUSE
> for more information
> open dir error: No such file or directory
> $ grep 'mount(' /tmp/log.strace
> 17569 mount("2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", "/tmp/.mount_2i2l6iLZ8WYZ", "fuse.2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", MS_RDONLY|MS_NOSUID|MS_NODEV, "fd=5,rootmode=40000,user_id=1000,group_id=998") = -1 EPERM (Operation not permitted)
> 17570 mount("2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", ".", "fuse.2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage", MS_RDONLY|MS_NOSUID|MS_NODEV, "fd=6,rootmode=40000,user_id=1000,group_id=998") = -1 EPERM (Operation not permitted)
> 17571 mount("/dev/fuse", ".", "fuse", MS_RDONLY|MS_NOSUID|MS_NODEV, "fd=6,rootmode=40000,user_id=1000,group_id=998") = -1 EPERM (Operation not permitted)
> --8<---------------cut here---------------end--------------->8---
>
> Ludo’.

I’m pretty sure a process attached with ptrace can’t exec SUID programs,
for security reasons (ptrace allows you to change the code of the
attached program so its a privilege escalation).

I think you can run it with “sudo -E strace -f -o /tmp/log.strace -s 500
/gnu/store/2i2l6irl2n8q24aimfmidvlglllc4s8z-hello-appimage-pack.AppImage”.

Have a nice day,
Noé
-------------------- End of forwarded message --------------------
L
L
Ludovic Courtès wrote on 20 Nov 13:05 +0100
Re: [bug#73842] [PATCH v5 2/3] pack: Add support for AppImage pack format.
(name . Noé Lopez)(address . noe@xn--no-cja.eu)(address . 73842@debbugs.gnu.org)
87ed36xg90.fsf@gnu.org
Hi,

(+Cc: issue.)

Noé Lopez <noe@noé.eu> skribis:

Toggle quote (10 lines)
> This is an expected error from the AppImage runtime, as said in the
> output the runtime depends on FUSE 3 to mount the AppImage.
>
> The AppImage can still be ran using --appimage-extract-and-run, which I
> believe we noted in the documentation:
>
>>The runtime used by AppImages makes use of libfuse to mount the image
>>quickly. If libfuse is not available, the AppImage can still be started
>>using the @option{--appimage-extract-and-run} flag.

Oh right. Perhaps we can tweak it to explicitly mention that
‘fusermount3’ must be in $PATH (as opposed to referring to “libfuse”).
WDYT?

(If you prefer I can make this change and push the whole thing; it’s
just a fine point.)

Toggle quote (3 lines)
> As for things that can be done from our side, we could change runtime to
> one that uses other technologies just like supported by “-RR”.

Yup. The two techniques are definitely close to one another!

Toggle quote (4 lines)
> I’m pretty sure a process attached with ptrace can’t exec SUID programs,
> for security reasons (ptrace allows you to change the code of the
> attached program so its a privilege escalation).

Yes, that makes sense.

Thank you!

Ludo’.
N
N
Noé Lopez wrote on 23 Nov 14:40 +0100
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 73842@debbugs.gnu.org)
871pz2kqze.fsf@xn--no-cja.eu
Hi,

Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (24 lines)
> Hi,
>
> (+Cc: issue.)
>
> Noé Lopez <noe@noé.eu> skribis:
>
>> This is an expected error from the AppImage runtime, as said in the
>> output the runtime depends on FUSE 3 to mount the AppImage.
>>
>> The AppImage can still be ran using --appimage-extract-and-run, which I
>> believe we noted in the documentation:
>>
>>>The runtime used by AppImages makes use of libfuse to mount the image
>>>quickly. If libfuse is not available, the AppImage can still be started
>>>using the @option{--appimage-extract-and-run} flag.
>
> Oh right. Perhaps we can tweak it to explicitly mention that
> ‘fusermount3’ must be in $PATH (as opposed to referring to “libfuse”).
> WDYT?
>
> (If you prefer I can make this change and push the whole thing; it’s
> just a fine point.)
>

That would be the easiest, thanks!

Toggle quote (15 lines)
>> As for things that can be done from our side, we could change runtime to
>> one that uses other technologies just like supported by “-RR”.
>
> Yup. The two techniques are definitely close to one another!
>
>> I’m pretty sure a process attached with ptrace can’t exec SUID programs,
>> for security reasons (ptrace allows you to change the code of the
>> attached program so its a privilege escalation).
>
> Yes, that makes sense.
>
> Thank you!
>
> Ludo’.

Thank you :)
Noé
L
L
Ludovic Courtès wrote on 23 Nov 17:01 +0100
(name . Noé Lopez)(address . noe@xn--no-cja.eu)(address . 73842@debbugs.gnu.org)
87ttbyos5n.fsf@gnu.org
Hey Noé,

Pushed as 8d6389b866d91a72569ecf50ddf313c17430d34b!

Thank you, and thanks for your patience!

Of course, only after pushing and thinking through the FUSE issue did I
realize that something must be wrong with the tests because we cannot
rely on ‘fusermount3’ or FUSE in general in the build environment.
Indeed, those tests were not building anything at all because the
‘check’ derivation has zero outputs.

So I tried the change below, but that leads to a failing test:

Toggle snippet (4 lines)
Failed to run /tmp/guix-build-check-appimage.drv-0/appimage_extracted_b74cb93b3ec3113d87e0f1da08dcd973/AppRun: No such file or directory
builder for `/gnu/store/jxvrfa81m60zwwdrj55x0npmfbgblfv6-check-appimage.drv' failed with exit code 127

Could you or Sebastian take a look? No rush, but it’s best to have
working tests for features like this. (Please open a new issue to
follow up on this.)

Thanks,
Ludo’.
Toggle diff (40 lines)
diff --git a/tests/pack.scm b/tests/pack.scm
index 1c1e312557..c395f3935f 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2017-2021, 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2017-2021, 2023, 2024 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
@@ -356,8 +356,18 @@ (define rpm-for-tests
(list #:relocatable? #t)))
(check (gexp->derivation
"check-appimage"
- #~(invoke #$image))))
- (built-derivations (list check))))
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (ice-9 popen)
+ (guix build utils))
+
+ (let ((pipe (open-pipe* OPEN_READ
+ #$image "--appimage-extract-and-run")))
+ (call-with-output-file #$output
+ (lambda (port)
+ (dump-port pipe port)))
+ (exit (status:exit-val (close-pipe pipe)))))))))
+ (built-derivations (list (pk 'APPIMAGE-drv check)))))
(unless store (test-skip 1))
(test-assertm "appimage + localstatedir"
@@ -374,6 +384,7 @@ (define rpm-for-tests
(list #:relocatable? #t)))
(check (gexp->derivation
"check-appimage"
+ ;; FIXME: Do something as above.
#~(begin
(invoke #$image)))))
(built-derivations (list check))))
L
L
Ludovic Courtès wrote on 23 Nov 17:02 +0100
control message for bug #73842
(address . control@debbugs.gnu.org)
87serios5e.fsf@gnu.org
close 73842
quit
?
Your comment

This issue is archived.

To comment on this conversation send an email to 73842@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 73842
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch