You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.0 KiB

Distribute software with GNU Guix

-- mode: org; coding: utf-8; --


Here we discuss 'easy' tar-ball deployment of GNU Guix packages similar to the binary distribution of GNU Guix itself (see download).

How does Guix do it?

WARNING: this is an advanced topic, you may want to skip to the next section.

The source tree contains ./gnu/system/install.scm. This module provides an 'operating-system' definition for use on images for USB sticks etc., for the installation of the GNU system. It has the function 'self-contained-tarball'.

This in turn gets invoked from ./build-aux/make-binary-tarball.scm which is invoked from the command line with (see the main ./Makefile).

  # The self-contained tarball.
    $(top_builddir)/pre-inst-env "$(GUILE)"     \
      "$(top_srcdir)/build-aux/make-binary-tarball.scm" "$*" "$@"

The function 'self-contained-tarball' returns a compressed tar ball containing a store initialized with the closure of GUIX. The tarball contains gnu/store, /var/guix, and a profile under /root.guix-profile where GUIX is installed. It is a short function, so we can list it here with a few extra comments

(define* (self-contained-tarball #:key (guix guix))
  ;; fetch the derivation for the guix package:
  (mlet %store-monad ((profile (profile-derivation
                                 (list (package->manifest-entry guix))))))
    (define build
      ;; import build-time modules from the Guix source
      (with-imported-modules '((guix build utils)
                               (guix build store-copy)
                               (gnu build install))
            (use-modules (guix build utils)
                         (gnu build install))

            (define %root "root")

            ;; set the search path for binaries to find tar and xz
            (setenv "PATH"
                    (string-append #$guix "/sbin:" #$tar "/bin:" #$xz "/bin"))
            ;; create the root ~/.guix-profile/ for guix
            (populate-single-profile-directory %root
                                               #:profile #$profile
                                               #:closure "profile"
                                               #:deduplicate? #f)

            ;; Create the tarball.  Use GNU format so there's no file name
            ;; length limitation.
            (with-directory-excursion %root
              (zero? (system* "tar" "--xz" "--format=gnu"
                              ;; Avoid non-determinism in the archive.  Use
                              ;; mtime = 1, not zero, because that is what the
                              ;; daemon does for files in the store (see the
                              ;; 'mtimeStore' constant in
                              "--mtime=@1"        ;for files in /var/guix

                              "-cvf" #$output
                              ;; Avoid adding / and /var to the tarball, so
                              ;; that the ownership and permissions of those
                              ;; directories will not be overwritten when
                              ;; extracting the archive.  Do not include /root
                              ;; because the root account might have a
                              ;; different home directory.
                              (string-append "." (%store-directory))))))))

    ;; and build it using above build function
    (gexp->derivation "guix-tarball.tar.xz" build
                      #:references-graphs `(("profile" ,profile)))))

In short the derivation gets build from its derivation and the result gets packed into a tar ball. Let's try it:

make guix-binary.x86_64-linux.tar.xz

Runs the tests and creates the tarball. Pretty cool!

Rolling our own

Our users will want to deploy our tar ball on a fresh system. If they already have GNU Guix they can use the normal Guix installation path. The feature of a binary installation (here) is specifically for new users.

Guix also has the archive option which can create an archive of a software package with its dependencies. To unpack the archive you need GNU guix installed and update the access key. We can also provide that, but here we discuss creating a one-time binary installation.

We could include Guix itself in the tar ball - which would allow us to build a store that is guix 'ready'.

Anyway, there are a few options. The one thing I would like to try is to create an archive, install that in a container and tarball that up for distribution.

Creating the tarball

To package up the 'hello' package:

./pre-inst-env guix environment --container --ad-hoc hello tar gzip -- tar cvzf test.tgz /gnu/store

results in a minimalistic hello package with all dependencies - sized 28Mb.

That was one single command.

Next, unpack the software on a fresh Linux (e.g. using a VM or container):

cd /
tar xvzf test.tgz

Now the files are in /gnu and you should be able to run either directly

  Hello, world!

or by using the profile

  Hello, world!

This implies we can combine any number of software packages with dependencies and tar it all up for distribution. It will also unpack in a Docker container without special privileges.

Create relocatable installer

Starting from above tar ball we can make a relocatable binary installer using a few tricks. Unpack the tar ball somewhere

mkdir hello
cd hello
tar xvzf ../test.tgz

Now the files should be in ./gnu/store

ls ./gnu/store