If you want to skip the backstory and get to the material, click here. Or if you are looking for a simple checklist to tick off before releasing a new package version, take a look at this tool created by me.

How it all came to be OR Unnecessary banter

I recently adopted two orphaned packages from the [Arch User Repository][aw:AUR]. An orphaned package is one whose maintainer or original creator is no longer updating the package. I had recently installed Arch Linux on a new machine and wanted to set up the famous fortune | cowsay command.

fortune | cowsay

The best thing to greet you on a new terminal.

While looking for fortune-mod fortune files, I came across a package called fortune-mod-all-en which was a metapackage to install all english language fortune files at once. It seemed that it hadn’t been updated in a long time and so I flagged the package out of date. This caused the maintainer to orphan the package and I adopted it. (Sounds so cheesy!). There were a lot of packages within this collection which were broken and I got a few of them fixed and gained the ownership of fortune-mod-firefly in a similar way.

Scrambling to learn

Now I had maintainership of two packages and had never maintained a package before. Talk about being thrown into the battlefield with a gun but never having used one. So as always, I headed over to the Arch Wiki. False. I was stupid and excited and decided that I could figure it all out on myself. That was a bad move. I did a lot of mistakes and broke some things. But eventually after reading the Arch Wiki I was able to fix it. This post is meant to serve as a guide to people who want to package software for Arch Linux in the AUR. This is only meant as a personal reference and hence should be cross-referenced with the Arch Wiki before taking any actions.

Arch Linux Package Management

Packages in Arch Linux are built using the makepkg utility and the information stored in a PKGBUILD file. When makepkg runs, it searches for a PKGBUILD in the current directory and follows the instructions in it to acquire the required files and/or compile them to be packed within a package file (pkgname.pkg.tar.xz). The resulting package contains binary files and installation instructions ready to be installed by pacman.

A PKGBUILD is a shell script containing the build information required by Arch Linux packages.

An Arch package is no more than a tar archive, or ‘tarball’, compressed using xz, which contains the following files generated by makepkg:

  • The binary files to install.
  • .PKGINFO: contains all the metadata needed by pacman to deal with packages, dependencies, etc.
  • .MTREE: contains hashes and timestamps of the files, which are included in the local database so that pacman can verify the integrity of the package.
  • .INSTALL: an optional file used to execute commands after the install/upgrade/remove stage. (This file is present only if specified in the PKGBUILD.)
  • .Changelog: an optional file kept by the package maintainer documenting the changes of the package. (It is not present in all packages.)

Package Managemt Checklist

An example PKGBUILD prototype looks like this:

# Maintainer: Your Name <youremail@domain.com>
# Contributor: Previous Maintainer <email>
pkgname=NAME
pkgver=VERSION
pkgrel=1
epoch=
pkgdesc=""
arch=()
url=""
license=('GPL')
groups=()
depends=()
makedepends=()
checkdepends=()
optdepends=()
provides=()
conflicts=()
replaces=()
backup=()
options=()
install=
changelog=
source=("$pkgname-$pkgver.tar.gz"
        "$pkgname-$pkgver.patch")
noextract=()
md5sums=()
validpgpkeys=()

prepare() {
	cd "$pkgname-$pkgver"
	patch -p1 -i "$srcdir/$pkgname-$pkgver.patch"
}

build() {
	cd "$pkgname-$pkgver"
	./configure --prefix=/usr
	make
}

check() {
	cd "$pkgname-$pkgver"
	make -k check
}

package() {
	cd "$pkgname-$pkgver"
	make DESTDIR="$pkgdir/" install
}

What you should check when making or updating a PKGBUILD:

  • NEVER install packages to /usr/local/bin.
  • AVOID adding new variables to PKGBUILD. If absolutely needed prefix the variable names with underscores.
  • To print a message during installation use the .install file. (See /usr/share/pacman/proto.install)
  • Ensure ALL dependencies required to RUN the program are present in the depends array. Use ldd, namcap to check.
  • Add optional dependencies (programs that add features or make it easier) to the optdepends array. Do keep in mind that this list will be printed during package installation.
  • Make sure the optdepends array is annotated. eg. optdepends=('cups: printing support' 'sane: scanner support').
  • The package description should not be self-referential and ideally should be less than 80 characters.
  • Keep line length under 100 characters.
  • Minimize empty lines in the PKGBUILD where possible.
  • QUOTE the variables which MAY contain spaces. Also try to use the ${variable} syntax over $variable syntax to avoid ambiguities.
  • Make sure that the integrity variables are updated. You can use updpkgsums tool for that.

Package Naming

  • Use only alphanumeric characters and lowercase names.
  • DO NOT suffix package names with version numbers. Some exceptions are discussed on the Arch Wiki though.
  • pkgver should match the upstream version.
  • pkgrel should be incremented when the PKGBUILD is changed etc. They start at 1 and are incremented in single steps. Reset this value when incrementing pkgver.

Directories

  • Ensure your directory layout follows the guide below.
Directory Purpose
/etc System-essential configuration files
/usr/bin Binaries
/usr/lib Libraries
/usr/include Header files
/usr/lib/{pkg} Modules, plugins, etc.
/usr/share/doc/{pkg} Application documentation
/usr/share/info GNU Info system files
/usr/share/man Manpages
/usr/share/{pkg} Application data
/var/lib/{pkg} Persistent application storage
/etc/{pkg} Configuration files for {pkg}
/opt/{pkg} Large self-contained packages such as Java, etc.

Makepkg actions

Here’s what makepkg does and you don’t need to handle.

  1. Checks if package dependencies and makedepends are installed
  2. Downloads source files from servers
  3. Checks the integrity of source files
  4. Unpacks source files
  5. Does any necessary patching
  6. Builds the software and installs it in a fake root
  7. Strips symbols from binaries
  8. Strips debugging symbols from libraries
  9. Compresses manual and, or info pages
  10. Generates the package meta file which is included with each package
  11. Compresses the fake root into the package file
  12. Stores the package file in the configured destination directory (cwd by default)

How makepkg handles the source array

  • If you are using a VCS url (git://, svn:// or similar), make sure that the VCS itself is present in the makedepends array.
  • You can use architecture specific source and checksum arrays.
# Example section from a real PKGBUILD
source_x86_64=("https://github.com/maoserr/redshiftgui/releases/download/${pkgver}-Arch64/RedshiftGUI-${pkgver}-Linux-x86_64.tar.gz"
               'redshiftgui.sh')
sha256sums_x86_64=('1170e8d8eca1b7b936ffb5e70763259b3f15db810bf2d05b3b0221d8e35bbb27'
                   'df03e192fe32bda2ea546be85faf51c030ae8dd46c40aad229e39b6b296897b5')
source_i686=("https://github.com/maoserr/redshiftgui/releases/download/${pkgver}-Arch64/RedshiftGUI-${pkgver}-Linux-i686.tar.gz"
             'redshiftgui.sh')
sha256sums_i686=('4d34de7b9bf106569bd7b87f2cd96640e60f723cf27e37e89219c8ea5b14cb2d'
                 'df03e192fe32bda2ea546be85faf51c030ae8dd46c40aad229e39b6b296897b5')

  • The general format for the source array is:
source=('[folder::][vcs+]url[#fragment]')
- `folder`: (Optional) is used to change the name of the directory where the
  clone will be created.
- `vcs+`: is needed for URLs that do not reflect the type of VCS they are
  using. eg. `git+https://github.com`.
- `url`: is the URL to the repository. Can be local or on the internet.
- `#fragment`: (Optional) can be used to pull a specific branch or commit.
  See `man pkgbuild` for more information.

Generating version numbers from pkgver()

PKGBUILD files provide a variable called pkgver which can be populated using some command. The output of the pkgver() function (if any) in the PKGBUILD is assigned to the pkgver variable. Some useful pkgver() functions are:

# Most recent annotated tag reachable from last commit.
pkgver() {
  cd "$pkgname"
  git describe --long | sed 's/\([^-]*-g\)/r\1/;s/-/./g'
}
# Output: 2.0.r6.ga17a017
# Using the most recent un-annotated tag reachable from the last commit:
pkgver() {
  cd "$pkgname"
  git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g'
}
# Output: 0.71.r115.gd95ee07
# In case if the git tag does not contain dashes then one can use simpler sed
# expression sed 's/-/.r/;s/-/./'. 
# If tag contains a prefix, like v or project name then it should be cut off:
pkgver() {
  cd "$pkgname"
  # cutting off 'foo-' prefix that presents in the git tag
  git describe --long | sed 's/^foo-//;s/\([^-]*-g\)/r\1/;s/-/./g'
}
# Output: 6.1.r3.gd77e105
# If there are no tags then use number of revisions since beginning of the
# history:
pkgver() {
  cd "$pkgname"
  printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
# Output: r1142.a17a017
# Version and only commit/revision number (SHA1 omitted; however, without a SHA1
# quick referencing of an exact revision is lost if not mindful of versioning):
git describe --long | sed -r 's/-([0-9,a-g,A-G]{7}.*)//' | sed 's/-/./'
# Both methods can also be combined, to support repositories that start without
# a tag but get tagged later on (uses a bashism):
pkgver() {
  cd "$pkgname"
  ( set -o pipefail
    git describe --long 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' ||
    printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
  )
}
# Output: 0.9.9.r27.g2b039da  # if tags exist
#         r1581.2b039da       # else fallback
# In case no satisfactory pkgver can be extracted from the repository, the
# current date can be used:
pkgver() {
  date +%Y%m%d
}

#Output: 20170408