15 KiB
Guix Packaging Tutorial
- COMMENT Note to authors
- COMMENT Review checklist
- Introduction
- Preliminaries and conventions
- Packaging GNU Hello
- COMMENT Prose detention
- Footnotes
COMMENT Note to authors
COMMENT Emacs Org mode
This document was written in GNU Emacs 29.1 using Org mode version 9.7-pre. Reading and editing the source is possible using any text editor. Org functionality and Lisp functions (likely) require Emacs version >= 26. Get Emacs at https://www.gnu.org/software/emacs/.
See Emphasis and Monospace for basic Org markup syntax.
Cycle header visiblity using S-TAB (M-x org-shifttab). Navigate
between headers using C-c C-n (M-x org-next-visible-heading) and
C-c C-p (M-x org-previous-visible-heading). Hide all text but the
current subtree using C-x n s (M-x org-narrow-to-subtree). Unhide
all hidden text with C-x n w (M-x widen). See Narrowing.
Org mode allows executing embeded source code. Press C-c C-c or
call M-x org-execute-source-block when the cursor is in a source
block to execute the code. See Working with Source Code.
Write source code contained in blocks marked with the :tangle header
argument to disk as standalone files by "tangling" (see section
below).
Export this document to Texinfo using M-x
org-texinfo-export-to-texinfo (see below) and info using M-x
org-texinfo-export-to-info (see below). When using the Emacs info
reader, load an info manual from source using C-u C-h i
<guix-packaging-tutorial.info>. Read the generated info file with
the info application using
info ./guix-packaging-tutorial.info
COMMENT Tangle
Generate standalone source code files from code blocks.
COMMENT Export to Texinfo format
Convert this document to Texinfo.
COMMENT Export to info format
Convert this document to info.
COMMENT Convenience functions
COMMENT link-to-current-info-node
(defun xc/link-to-current-info-node (&optional arg)
"Create link to current Info node in a variety of formats.
With a negative prefix, place Org link to info node with
description of the current node into the kill ring.
With no prefix, place the url corresponding to the current Info
node into the kill ring.
With universal prefix, visit url with default web browser and do
not put url into the kill ring.
With numeric prefix, place Org link with node name as description
into the kill ring."
(interactive "p")
(unless Info-current-node
(user-error "No current Info node"))
(let* ((info-file (if (stringp Info-current-file)
(file-name-sans-extension
(file-name-nondirectory Info-current-file))))
(node Info-current-node)
(url (cond
((or (string= info-file "emacs") (string= info-file "org") (string= info-file "elisp"))
(concat "https://www.gnu.org/software/emacs/manual/html_node/"
info-file "/"
(if (string= node "Top") ""
(concat (replace-regexp-in-string " " "-" node t) ".html"))))
((string= info-file "guile")
(concat "https://www.gnu.org/software/guile/manual/html_node/"
(if (string= node "Top") ""
(concat (replace-regexp-in-string " " "-" node t) ".html"))))
((string= info-file "guix")
(concat "https://guix.gnu.org/en/manual/devel/en/html_node/"
(if (string= node "Top") ""
(concat (replace-regexp-in-string " " "-" node t) ".html"))))
)))
(cond
((eq arg -1) ; -1 prefix
(kill-new (format "[[info:%s#%s][%s]]" info-file node node))
(message "%s" (car kill-ring)))
((eq arg 1) ; no prefix
(kill-new url)
(message "%s" (car kill-ring)))
((eq arg 4) ; universal prefix
(browse-url-default-browser url))
(t ; any other prefix
(kill-new (format "[[%s][%s]]" url node))
(message "%s" (car kill-ring))))))
COMMENT buffer-local-abbrevs
An "abbrev" is Emacs jargon for a sequence of characters that expands (is text replaced) with different text. For example, rather than write out "GUIX_PACKAGING_TUTORIAL_DIR" every time, we define a buffer-local abbrev "gd" which expands to "GUIX_PACKAGING_TUTORIAL_DIR".
(defun my-define-local-abbrev-table (definitions)
"Define abbrev table with DEFINITIONS local to the current buffer.
Abbrev DEFINITIONS is a list of elements of the form (ABBREVNAME
EXPANSION ...) that are passed to `define-abbrev'.
The local abbrev table has name of the current buffer appended
with \"-abbrev-table\".
Use `my-clear-local-abbrev-table' to remove local abbrev
definitions."
(let* ((table-symbol (intern (concat (buffer-name) "-abbrev-table")))) ; inserts in obarray
(define-abbrev-table table-symbol definitions)
(setq-local local-abbrev-table (symbol-value table-symbol))
(message "Created local abbrev table '%s'" table-symbol)))
(defun my-clear-local-abbrev-table ()
"Clear buffer-local abbrevs.
See `my-define-local-abbrev-table'."
(let* ((buffer-table-string (concat (buffer-name) "-abbrev-table"))
(buffer-table-symbol (intern-soft buffer-table-string)) ; nil if not in obarray
(buffer-table (if buffer-table-symbol
(symbol-value buffer-table-symbol))))
(cond ((abbrev-table-p buffer-table)
(clear-abbrev-table buffer-table)
(if (intern-soft buffer-table-string)
(unintern buffer-table-string))
(message "Cleared local abbrev table '%s'" buffer-table-string)))))
(my-clear-local-abbrev-table)
(my-define-local-abbrev-table
'(;("abbrev" "expansion" nil :case-fixed t)
("gd" "GUIX_PACKAGING_TUTORIAL_DIR")
("$gd" "$GUIX_PACKAGING_TUTORIAL_DIR")
))
COMMENT "Make" clean
Ensure a clean working environment when executing the code in this document.
export GUIX_PACKAGING_TUTORIAL_DIR="/tmp/guix-packaging-tutorial"
if [ -d "$GUIX_PACKAGING_TUTORIAL_DIR" ]
then
rm -rf "$GUIX_PACKAGING_TUTORIAL_DIR" && echo "Removed \"$GUIX_PACKAGING_TUTORIAL_DIR\""
fi
if [ ! -d "$GUIX_PACKAGING_TUTORIAL_DIR" ]
then
echo "Creating tutorial directory..."
mkdir "$GUIX_PACKAGING_TUTORIAL_DIR"
echo "Created \"$GUIX_PACKAGING_TUTORIAL_DIR\""
echo "Changing to \"$GUIX_PACKAGING_TUTORIAL_DIR\"..."
cd "$GUIX_PACKAGING_TUTORIAL_DIR"
echo "Current directory: $(pwd)"
fi
Removed "/tmp/guix-packaging-tutorial" Creating tutorial directory... Created "/tmp/guix-packaging-tutorial" Changing to "/tmp/guix-packaging-tutorial"... Current directory: /tmp/guix-packaging-tutorial
COMMENT Review checklist
COMMENT Introduction
- Intended audience is clearly defined
- Direction on how to contribute to this document
-
Assumptions and requires
- Basic knowledge of Guix
- A Guix installation
-
Bash
- environment variables
- variable assignment
- how to use documentation such as man, –help, or info
- Git
- Guile?
- Emacs?
- Geiser?
- Conventions
COMMENT Preliminaries
Introduction
This document details the process of packaging software for Guix.
The intended audience is…
It assumes you have the read the Guix Introduction and have either installed Guix on a foreign distro or use the Guix System.
Guix packaging requires using a shell, such as Bash. It will help to have a basic understanding of…
Preliminaries and conventions
Before we begin, let's find someplace to store our work. The
following creates an environment variable referencing our working
directory. We use /tmp which is removed each time the computer
reboots. Make sure to change it to something else if you want to keep
your work!
export GUIX_PACKAGING_TUTORIAL_DIR=/tmp/guix-packaging-tutorial
Packaging GNU Hello
In software development, a first program often prints "hello world". This is a basic task which introduces core concepts and demonstrates that the system works. GNU Hello1 is a small C program created by the GNU project which demonstrates processing an argument list and models coding standards. It's a simple application yet provides enough complexity to introduce us to some Guix concepts and prove that we can create a package.
Building a package requires source code. Every free software project distributes its source code differently. Some use a version control system, others host compressed or archived files. You may need to search for the latest version.
The GNU Hello project page2 says its source code is stored with
both the Git3 and CVS4 version control systems. There is
also a download area for source code bundles, given as (tar.gz
files). Looking at the NEWS file5, the latest version of the
hello application is 2.12.1.
Once we have the source code, we need to build the application with
it. Hopefully the project authors left a note about how to do that!
We find build instructions for hello in README-dev6. It
reads,
Autotools
This distribution uses whatever versions of Automake, Autoconf and Gettext are listed in NEWS; usually the latest ones released. If you are getting the sources from git (or change configure.ac), you'll need to have these tools installed to (re)build. You'll also need help2man. All of these programs are available from ftp://ftp.gnu.org/gnu.
Building
After getting the git sources, and installing the tools above, you can run
./bootstrap
to do a fresh build. After that first time, running make should suffice.
We've gathered a lot of information. We have an application called "hello" which requires at least four programs to build:
Automake>= 1.11.1Autoconf>= 2.65Gettext>= 0.18.1help2man>= 1.39.2
The latest version is 2.12.1.
Building requires running ./bootstrap.
Guix summarizes this information in a package definition:
(package
(name "hello")
(version "2.12.1")
(source (origin
(method url-fetch)
(uri (string-append "mirror://gnu/hello/hello-" version
".tar.gz"))
(sha256
(base32
"banana"))))
(build-system gnu-build-system)
(synopsis "Hello, GNU world: An example GNU package")
(description
"GNU Hello prints the message \"Hello, world!\" and then exits. It
serves as an example of standard GNU coding practices. As such, it supports
command-line arguments, multiple languages, and so on.")
(home-page "https://www.gnu.org/software/hello/")
(license gpl3+)))
Guix package definitions are written in GNU Guile, a dialect of Scheme.
COMMENT Prose detention
This section is for things that don't quite behave and need a little love.
COMMENT Stealing package structure from an existing package
Rather than create a package definition from scratch, we start with an existing one. There are several ways to find package definitions:
- use the
M-x guix-editfrom Emacs (assuming you've installed the "guix-edit" package; for example usingguix install emacs-guix), - clone guix (
git clone git://git.sv.gnu.org/guix.git) and grep the package sources in/gnu/packages, or - use the
guix editcommand from the command-line.
Using M-x guix-edit from Emacs provides autocomplete on package
names and let's you easily navigate package definitions (since
everything happens in Emacs). Grepping the sources gives you
filenames and maybe some context but requires you to manually open the
definitions (doing this from Emacs makes this easier). Running guix
edit from the command-line requires knowing the package name and
isn't much help here.
The raspberrypi-userland package within the raspberry-pi.scm
module looks like it might be a good reference. This required some
research. Using M-x guix-edit, I tried "rasp<TAB>" and "rpi<TAB>".
These brought up the raspberrypi-userland and rpi-imager packages.
I looked at the definitions and saw that both projects share the
home-page of pico-sdk, "https://github.com/raspberrypi". However,
rpi-imager uses the qt-build-system whereas raspberrypi-userland
and pico-sdk both use CMake. In retrospect, it might help to
first grep for the home-page of project we aim to package. There's no
consistent way to find an extant package to model your new one after.
It requires some exploration.
COMMENT Packaging a binary
My understanding is that we don't actually need source code to create a package. Might packaging a binary be a better starting point than finding the source code? I suppose it depends on the audience. If we're writing to future Guix contributors, then we should start from source code. If we're speaking to people looking to switch to Guix from a different package manager, then being able to distribute binaries is an important aspect of transitioning systems
I've updated the "Intended audience" to include something about future Guix contributions. Packaging binaries is probably an important topic to include if Guix is to spread, especially to industry.