guix-packaging-tutorial/guix-packaging-tutorial.org

15 KiB
Raw Permalink Blame History

Guix Packaging Tutorial

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:

  1. Automake >= 1.11.1
  2. Autoconf >= 2.65
  3. Gettext >= 0.18.1
  4. help2man >= 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:

  1. use the M-x guix-edit from Emacs (assuming you've installed the "guix-edit" package; for example using guix install emacs-guix),
  2. clone guix (git clone git://git.sv.gnu.org/guix.git) and grep the package sources in /gnu/packages, or
  3. use the guix edit command 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.

Footnotes