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?

Palette

By futurile

Palette

  • 313