Guix Alchemy

Customisation with package transformations

Mastering Guix Packaging

  • Rather than trying to learn it all pick a path
  • The main scenarios are
    • ​Using an older package (downgrading)
    • Rebuilding an existing package
    • Modifying an existing package
    • Creating a new package (with and without source)
    • Contributing a package to the distribution

Package variant:  the local build is different from the Guix archives

Learning Path

Profiles

Package

Commands

Shell

Command

Basic Build

Command

Manifests

Transform

Command

Manifests

Transforms in Manifests

Guix Build Overview

  • Guix build is used to build a package (or a derivation)
  • Receives:
    • package definition
    • Guix build options
  • It outputs:
    • build logs
    • build derivation 
    • package
  • Recommendation: use with guix shell for a clean build environment
$ guix build hello --no-substitutes --no-grafts
The following derivation will be built:
     /gnu/store/xn12lv3ckihkai4044p28h5im78lvad5-hello-2.12.1.drv
    
    [... lots of output as it builds the package ...]
    
    successfully built /gnu/store/xn12lv3ckihkai4044p28h5im78lvad5-hello-2.12.1.drv
	/gnu/store/5mqwac3zshjjn1ig82s12rbi7whqm4n8-hello-2.12.1
Option Description
--dry-run Give output but don't take the action
--no-substitutes Don't use binary substitutes
--no-grafts Do not put security grafts onto packages
--verbosity=<lvl> 0=no output; 1=quiet; 2=show urls; 3=build log
--cores=n Use N CPU cores for each build job
--source Build a source package
--max-jobs=N Allow the Guix Daemon to run at most N build jobs. Each build can be limited to use --cores=N
--check

Common Build Options

1

Create build environment with guix shell

3

Create a test environment with guix shell and test the new build

2

Build the current package to test dependencies

Package build steps

$ guix shell --container --nesting --development hello \
  --network --no-grafts nss-certs
The following derivation will be built:
  /gnu/store/jaz9fabc7l9iwkxf648iq0ajafzgs049-profile.drv
building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 23 packages...

Create a build environment

- nesting - run further package commands in the shell environment

- add nss-certs so that commands can use SSL

1

Rebuild the Package

$ guix shell --container --nesting --development hello \
  --network --no-grafts nss-certs
The following derivation will be built:
  /gnu/store/jaz9fabc7l9iwkxf648iq0ajafzgs049-profile.drv
building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 23 packages...

[env]$ guix build --no-substitutes --no-grafts --source hello
The following derivation will be built:
  /gnu/store/2p9bkv91gr64nm17qq0byavwg5q6w2lg-hello-2.12.1.tar.gz.drv
building /gnu/store/2p9bkv91gr64nm17qq0byavwg5q6w2lg-hello-2.12.1.tar.gz.drv...

Starting download of /gnu/store/3dq55rw99wdc4g4wblz7xikc8a2jy7a3-hello-2.12.1.tar.gz
From https://ftpmirror.gnu.org/gnu/hello/hello-2.12.1.tar.gz...
following redirection to `https://mirrors.tripadvisor.com/gnu/hello/hello-2.12.1.tar.gz'...
downloading from https://ftpmirror.gnu.org/gnu/hello/hello-2.12.1.tar.gz ...
 hello-2.12.1.tar.gz  1009KiB                                                               1.6MiB/s 00:01 [##################] 100.0%
successfully built /gnu/store/2p9bkv91gr64nm17qq0byavwg5q6w2lg-hello-2.12.1.tar.gz.drv
/gnu/store/3dq55rw99wdc4g4wblz7xikc8a2jy7a3-hello-2.12.1.tar.gz

download the source and put it into the store.

2

Rebuild the Package












[env]$ guix build --no-substitutes --no-grafts --source hello
The following derivation will be built:
  /gnu/store/2p9bkv91gr64nm17qq0byavwg5q6w2lg-hello-2.12.1.tar.gz.drv
building /gnu/store/2p9bkv91gr64nm17qq0byavwg5q6w2lg-hello-2.12.1.tar.gz.drv...

Starting download of /gnu/store/3dq55rw99wdc4g4wblz7xikc8a2jy7a3-hello-2.12.1.tar.gz
From https://ftpmirror.gnu.org/gnu/hello/hello-2.12.1.tar.gz...
following redirection to `https://mirrors.tripadvisor.com/gnu/hello/hello-2.12.1.tar.gz'...
downloading from https://ftpmirror.gnu.org/gnu/hello/hello-2.12.1.tar.gz ...
 hello-2.12.1.tar.gz  1009KiB                                                               1.6MiB/s 00:01 [##################] 100.0%
successfully built /gnu/store/2p9bkv91gr64nm17qq0byavwg5q6w2lg-hello-2.12.1.tar.gz.drv
/gnu/store/3dq55rw99wdc4g4wblz7xikc8a2jy7a3-hello-2.12.1.tar.gz

[env]$ guix build --no-substitutes --no-grafts --verbosity=1 hello
The following derivation will be built:
  /gnu/store/3zh2qpi897s2x229s93iakji86b08a20-hello-2.12.1.drv

building /gnu/store/3zh2qpi897s2x229s93iakji86b08a20-hello-2.12.1.drv...
/gnu/store/5mqwac3zshjjn1ig82s12rbi7whqm4n8-hello-2.12.1

2

$ guix shell --container --nesting --no-grafts
guix shell: warning: no packages specified; creating an empty environment
The following derivation will be built:
  /gnu/store/jv8hws211gw8vbv9rcjnigl5xik1k8vq-profile.drv

building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 1 package...

Test the Package

- no network as we're installing locally

- no packages as we're just going to test it

- add testing packages through a manifest (for example coreutils)

  • Install the package into a test shell - use it!
  • 🏆 TIP: by avoiding installing it into a standard profile we can delete it:

$ guix gc --delete /gnu/store/<blah>

                                              OR use --check

3

$ guix shell --container --nesting --no-grafts
guix shell: warning: no packages specified; creating an empty environment
The following derivation will be built:
  /gnu/store/jv8hws211gw8vbv9rcjnigl5xik1k8vq-profile.drv

building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 1 package...

[env]$ guix shell --install  /gnu/store/3zh2qpi897s2x229s93iakji86b08a20-hello-2.12.1.drv
The following package will be installed:
   hello 2.12.1

The following derivation will be built:
  /gnu/store/kkf3dwfjy1mkll3m4wbcxx76v2p2yjyc-profile.drv

building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 1 package...
hint: Consider setting the necessary environment variables by running:

     GUIX_PROFILE="/home/steve/.guix-profile"
     . "$GUIX_PROFILE/etc/profile"

Alternately, see `guix package --search-paths -p "/home/steve/.guix-profile"'.

$ /home/steve/.guix-profile/bin/hello
Hello,world!

Test the Package

Success!

3

VIDEO

Guix Build

2:40 minutes

1

2

3

Package Transformations

  • Modify an existing package
  • Create a package variant by alterating
  • Elements we can change
    • Source
      • ​latest available
      • specific version
      • git repository
    • Inputs
    • Build process
    • Source alterations - patches
    • Tuning for specific hardware
  • The package transformation can take place:
    • ​using the cli tools
    • using a package definition
    • using a manifest

CLI Package Transformations

$ guix build calcurse --with-source=https://calcurse.org/files/calcurse-4.8.1.tar.gz \
      --no-substitutes --no-grafts

package to build

source URL

$ guix build calcurse --with-source=calcurse@4.8.1.01=https://calcurse.org/files/calcurse-4.8.1.tar.gz \
      --no-substitutes --no-grafts

normal build options

version to use

Option Description
with-source Build the package using a specified source. URL or file.
Can be either source for the build, or an input.
with-input Use a specified package as an input to the build.
with-git-url The same as with-source but use a specified git URL. 
Builds from HEAD, unless --with-commit is used.
with-commit Use the a set commit
with-branch Build a particular branch, default is master
​with-latest Build a package using the latest upstream release of the source that the importer can detect.
with-version Use a specific release of the upstream source release.
with-configure-flags Customise by adding configure flags
with-patch Customise the build's source with a patch

Transformation Options

1

Create build environment with guix shell

3

Create a test environment with guix shell and test the new build

2

Build the current package to test dependencies

Package build steps

1

Create build environment with guix shell

3

Build the package using a transformation

2

Build the current package to test dependencies

4

Install the new package into a new test environment

Package build steps

$ guix shell --development fzf --container --nesting \
    --network --no-grafts nss-certs
The following derivation will be built:
  /gnu/store/fgc04pns05wp0yxwmm7xnvkpcybs3k1c-profile.drv

2.0 MB will be downloaded
 go-github-com-mattn-go-isatty-0.0.11  4KiB          201KiB/s 00:00 ▕██████████████████▏ 100.0%
 go-golang-org-x-crypto-0.4.0  1.4MiB                817KiB/s 00:02 ▕██████████████████▏ 100.0%
 go-golang-org-x-sys-0.8.0-0.ca59eda  569KiB          90KiB/s 00:06 ▕██████████████████▏ 100.0%
 go-golang-org-x-term-0.3.0  13KiB                    74KiB/s 00:00 ▕██████████████████▏ 100.0%
building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 37 packages...

Git Package Transformation

- development: all the pkgs needed to build

- nesting: so we can run commands

- nss-certs pkg added for downloads

[env]$ guix build fzf --with-git-url=fzf=https://github.com/junegunn/fzf \
  --with-commit=fzf=0.44.1 --no-substitutes --no-grafts
updating checkout of 'https://github.com/junegunn/fzf'...
retrieved commit d7d2ac39513f2068f70fbe717d212c9bce8750ed
The following derivation will be built:
  /gnu/store/ix845m79aw8l08h7d7737x2gl0pn25mn-fzf-0.44.1.drv
building /gnu/store/ix845m79aw8l08h7d7737x2gl0pn25mn-fzf-0.44.1.drv...

[... lots of build output ...]

successfully built /gnu/store/ix845m79aw8l08h7d7737x2gl0pn25mn-fzf-0.44.1.drv
/gnu/store/ihlh8nip211l2lm6p3h0502jwbpzrps1-fzf-0.44.1

Package to build

Package to build from git

- the package itself

- input to package

Commit to use

- commit hash

- tag

Git repository URL

VIDEO

Guix Transformation

~ minutes
TBD

Transformations in Manifests

  • A manifest contains a collection of packages
  • Often used to declaratively install packages into a profile
  • Actually manifests are code
    • GNU Guile
    • Uses lots Guix provided functions
  • ​Guile is in the Scheme family
    • ​A lot of parentheses
    • Be a rebel and space them vertically if it helps!
  • ​For the most part consider it a DSL




(specifications->manifest (list "calcurse"))

Package Transformations

(specifications->manifest 
	(list "calcurse" "cbonsai@1.3.1" "git:send-email")
)

Lists hold multiple things

Different package specs using their names

$ guix shell --container --nesting --manifest=basic-manifest.scm
The following derivation will be built:
  /gnu/store/4hr96x21bs1w23gfp6ynlghkfiav1zh6-profile.drv

applying 4 grafts for calcurse-4.5.1 ...
applying 3 grafts for cbonsai-1.3.1 ...
building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
generating GLib schema cache...
building directory of Info manuals...
building XDG desktop file cache...
building XDG MIME database...
building profile with 4 packages...

[env]$ guix package --list-installed --profile=$GUIX_ENVIRONMENT
guix      31e736...  out         /gnu/store/caqz00i1b1qwhlsb7v5j6lgr5l9x78n8-profile
git       2.41.0     send-email  /gnu/store/gdh0p...p14n6fsj1gz04mx-git-2.41.0-send-email
cbonsai   1.3.1      out         /gnu/store/zbjphbd9v7rbadv2y8jb7k9qqvzf5s4q-cbonsai-1.3.1
calcurse  4.5.1      out         /gnu/store/1bgjd0l2dlpjj29x8faik724xz2jzivn-calcurse-4.5.1

A function that's called with the provided list

The Lisperati will hunt you down for this!!! It is evil OMG!!!!!

Create a manifest called basic-manifest.scm:

works with guix shell and guix package

  • Reading from inside to the outside often helps
  • Lines 1-3:  Guile code can contain modules
    • ​The profile and transformations provide the Guix functions
    • We need (gnu packages calcurse) so we can call the calcurse package
  • Lines 10: calcurse-transform the calcurse variable
    • Next it's formed into manifest by package->manifest
  • Line 7-8: calcurse-transform
    • ​calcurse-src is evaluated
    • calcurse-src is transformed with options->transformation
  • Line 5: calcurse-src is a Guile pair consisting of the 'with-source' transformation and a location for the code

Basic manifest transformation

(use-modules (guix profile)
             (guix transformations)
             (gnu packages calcurse))
             
(define calcurse-src '(with-source . "https://calcurse.org/files/calcurse-4.8.1.tar.gz"))

(define calcurse-transform
	(options->transformation (list calcurse-src)))
    
(package->manifest (list (calcurse-transform calcurse)))

1

Create build environment with guix shell

3

Build the package using the transformation

2

Build the current package to test dependencies

4

Install the new package into a new test environment

Package build steps

1

Create build environment with guix shell

3

Create a manifest

Install the new package into a new test environment

2

Build the current package to test dependencies

4

Build the package using the transformation

Package build steps

5

Git transformation

  • Conceptually the same as previously:
    • we create a transformation and apply it to the package
    • we convert from the transformed package to a manifest
  • A bit more condensed as we're now familiar with the format
  • Uses a list of git transformations
(use-modules (guix profiles)
             (guix transformations)
             (gnu packages terminals))
             
(define fzf-transform (options->transformation
							(list '(with-git-url . "fzf=https://github.com/junegunn/fzf")
                                  '(with-commit . "fzf=2024010"))))

(packages->manifest (list (fzf-transform fzf)))

Advanced: multiple packages

Multiple packages and multiple transformations ...

  • 4 and 8: two transforms
  • 14-15: two transformations, converted into a list
  • 13: a separate list of packages
  • 12: add the transforms to the first list creating a big list
(use-modules (guix profiles)
             (guix transformations))
 
(define fzf-transform (options->transformation
                        (list '(with-git-url . "fzf=https://github.com/junegunn/fzf")
                              '(with-commit . "fzf=2024010"))))
 
 (define calcurse-transform (options->transformation
       '((with-source . "calcurse@20231207=https://calcurse.org/files/calcurse-4.8.1.tar.gz"))))
 
 (packages->manifest
  (append
    (specifications->packages (list "hello" "cbonsai" "gnupg"))
    (list (fzf-transform (specification->package "fzf"))
          (calcurse-transform (specification->package "calcurse")))))

The equivalent of: guix shell --development mutt vim git

  • 8-9: normal specification of packages
  • 6-7: converts to a development manifest
  • 5: create a list
  • 4: turn into a manifest

Advanced: multiple manifests

(use-modules (guix profiles)
             (guix transformations))
 
(concatenate-manifests
  (list
    (package->development-manifest
      (specification->package "mutt"))
    (packages->manifest
      (specifications->packages (list "vim" "git")))))

Development manifest to install all required libraries ...

Advanced: package name

  • What we did so far was create the same package but with some different inputs
  • This time we're doing a package inheritance
    • inheritance uses Scheme records - bottom line we can change some fields
    • We run the transform which outputs a package
    • We then use package/inherit to change some fields: technically this is a new package
(use-modules (guix profiles)
             (guix transformations)
             (guix packages)
             (gnu packages calcurse))

(define calcurse-transform
    ((options->transformation
        (list '(with-source . "calcurse=https://calcurse.org/files/calcurse-4.5.0.tar.gz")))
      calcurse))

(define calcurse-local (package/inherit
                         calcurse-transform
                         (name "calcurse-local")
                         (version "4.5.0")))

(packages->manifest (list calcurse-local))

Give the package variant a different name ...

What we've seen

Package transformations give us sophisticated capabilities to build package variants

1

Transforms

Manifests are a declarative way to manage an environment

2

Manifests

Transforms and manifests together give us a lot of flexibility and repeatability

3

Alchemy

Package Transformation Alchemy

  • Package transformations let us create locally build package variants
  • Implications
    • ​They use the package definition (recipe) that's in the archive
    • Only used to alter packages, not to create new ones
    • The source location needs to be processable by the package definition
    • The source can't have changed in some way the package definition isn't expecting
  • ​Uncomfortable tracking which packages have been altered

Taking it further:

  • Gentoo Use flags are a more flexible system 
  • Flags have to be exposed by the packager
  • Examples: https://www.gentoo.org/support/use-flags/
  • GSoC 2023: https://blog.lispy.tech/2023/05/parameterized-packages.html

Steps from here

Profiles

Package

Commands

Shell

Command

Basic Build

Command

Manifests

Transform

Command

Manifests

Transforms in Manifests

Package Definitions

Creating new packages

Thank You!

Questions?

1

Create build environment with guix shell

3

Build the package using a transformation

2

Build the current package to test dependencies

4

Install the new package into a new test environment

Package build steps

  • Reading from inside to the outside often helps
  • Lines 1-3:  Guile code can contain modules
    • ​The profile and transformations provide the Guix functions
    • We need (gnu packages calcurse) so we can call the calcurse package
  • Lines 10: calcurse-transform the calcurse variable
    • Next it's formed into manifest by package->manifest
  • Line 7-8: calcurse-transform
    • ​calcurse-src is evaluated
    • calcurse-src is transformed with options->transformation
  • Line 5: calcurse-src is a Guile pair consisting of the 'with-source' transformation and a location for the code

Basic manifest transformation

(use-modules (guix profile)
             (guix transformations)
             (gnu packages calcurse))
             
(define calcurse-src '(with-source . "https://calcurse.org/files/calcurse-4.8.1.tar.gz"))

(define calcurse-transform
	(options->transformation (list calcurse-src)))
    
(package->manifest (list (calcurse-transform calcurse)))

Palette

By futurile

Palette

  • 29