Maintainer Monologue – Creating your first Package

The Archlinux package creation system is one of the key features that make Archlinux (and arch-based distros) superior to much of the existing Linux paradigm.

makepkg

https://wiki.archlinux.org/index.php/Makepkg

https://www.archlinux.org/packages/core/x86_64/pacman/

The archlinux package creation tool is makepkg. You can build a package from the Arch User Repos simply by git cloning the desired package repo and running makepkg -si as in sync dependancies and install.

There are some small caveats to this working. For example, yay does the heavy lifting of handling and dependancies from the AUR which are listed in the PKGBUILD. Without it, for each AUR dependancy you will need to build and install that package first or makepkg will fail.

yay

https://aur.archlinux.org/packages/yay-git/

https://github.com/Jguer/yay

yay is one of my personal favorite AUR packages and is considered by many to be a critical component of a familiar Archlinux installation, at least in terms of one’s reliance on it. yay is, more often than not, among the first packages you would want to install on a base or unmodified archlinux system. I cannot speak highly enough about yay, take it for granted or understate it’s importance or significance. yay makes the AUR with thousands upon thousands of packages easily accessible to you. The AUR provides a venue of sorts for users and developers to share their software with Archlinux users around the world. If this is not a staggering concept on its own then I don’t know what is.

There is some debate about including AUR wrappers (such as yay or pamac) in a package repository, such as Endeavour does. For one, it can be tedious to keep them up to date, yet it is critical that this should happen for any potential security flaws or other bugs which may be identified. For any package that you really desire the latest commits for, you should build it from source. yay will not build an AUR package if it’s included in one of the repositories you have listed in pacman.conf.

It is highly desireable to use yay if only because any needed pgp keys for checking signatures will be fetched automatically and added to the keyring. This can be a tedious manual process if the package you want to build has many signatures on its sources which you don’t have the pubkeys for in your keyring.

building packages with makepkg

https://wiki.archlinux.org/index.php/Makepkg#Usage

Everyone who uses archlinux and packages from the AUR should at least be familiar with the following process for building and installing yay-git from source, but it stands as a useful example of manually building a package. (Note, you’ll need the packages in the base-devel group installed to make full use of makepkg.)

git clone https://aur.archlinux.org/yay-git
cd yay-git
makepkg -scif

The flags on makepkg are -s sync dependancies, -c clean up after, -i install, and -f force overwrite any previously created package with the same name in that directory.

The PKGBUILD

https://wiki.archlinux.org/index.php/PKGBUILD

https://aur.archlinux.org/packages/?O=0&K=

Building a package manually is a necessary primer to creating your own package. For package creation, you’ll simply want to edit the PKGBUILD or write one from scratch. I typically use an existing PKGBUILD from other packages I maintain as a template or reference because many of them are at least similar, but any PKGBUILD will do. If the software you are packaging is very large or not of your own creation, you might seek out any similar packages in the AUR, or even look at PKGBUILDs of some of the built packages which are in the official Archlinux repos.

Here’s a very simple example. You might be familiar with the read-only cache technique of sharing updates between computers on the same local network. If you aren’t, it’s very useful and can be found here:

https://wiki.archlinux.org/index.php/Pacman/Tips_and_tricks#Read-only_cache

There are, of course, AUR package which do what is described in the next section, but I never figured out the configuration for those and a while back I had implemented my own method for doing this in some bootstrapping scripts for archlinuxARM installations.

The method I devised uses a very simple golang http server, and just a few very short scripts otherwise as well as a systemd service. Also, I wanted to create a local repository of AUR packages from yay’s cache and provide these as a local repo that can be configured in pacman.conf on other machines on the LAN.

It’s by no means perfect at this point but it is simple and works within the context which I’m using it.

So here is the PKGBUILD I’ve come up with, and what follows will be a line-by-line breakdown. Note that there is nothing requiring you to arrange your PKGBUILDs this way but for the sake of other users and maintainers it’s best to follow this general outline

# Maintainer: Moses Narrow <moe_narrow@use.startmail.com>
pkgname=readonly-cache
pkgname1=readonly-cache
pkgdesc="pacman readonly cache - serve pacman's cache on the local network"
pkgver=0.0.2
pkgrel=1
arch=('x86_64')
conflicts=(skyminer)
license=()
makedepends=(go)
source=(
"aur-local.sh"
"aur-remote-config.sh"
"readonly-cache.sh"
"readonly-cache.service"
"readonlycache.go"
)
sha256sums=('d476b95a400c9fc252f5402d9e911c36ff26675c9ee68e42f4b4696c65c9dd44'
'3c7a25664ecaaef35a1b48081f350cd50d571d81e514861aa54c8b84d432efd4'
'64153502b466979a987ba074bb3881095846d1af35de9b71eed029389ac085f8'
'ccfe41daa38a45b0498cc6dd9f6636db4a7e835ee5c48c3e952cac038006b5f4'
'8aff1be292e5102b3ff4418af4a3d758b3c68d5fca89c7ca6ba18631bb7b180b')
build() {
go build readonlycache.go
}
package() {
#mkdir -p ${pkgdir}/etc/systemd/system
mkdir -p ${pkgdir}/usr/lib/systemd/system
mkdir -p ${pkgdir}/usr/lib/${pkgname1}
mkdir -p ${pkgdir}/usr/bin
rmextension=".sh"
rcscripts=$(ls *.sh)
rcservices=$(ls *.service)
for i in $rcscripts ; do
install -Dm755 ${i} ${pkgdir}/usr/lib/${pkgname1}/${i}
ln -rTsf ${pkgdir}/usr/lib/${pkgname1}/${i} ${pkgdir}/usr/bin/${i//$rmextension}
chmod +x ${pkgdir}/usr/lib/${pkgname1}/${i}
done
for i in $rcservices ; do
install -Dm644 ${i} ${pkgdir}/usr/lib/systemd/system/${i}
done
install -Dm755 readonlycache ${pkgdir}/usr/lib/${pkgname1}/
install -Dm755 readonlycache.go ${pkgdir}/usr/lib/${pkgname1}/
ln -rTsf ${pkgdir}/usr/lib/${pkgname1}/readonlycache ${pkgdir}/usr/bin/readonlycache
}

The PKGBUILD – line by line

At the top goes the maintainer, commented out. This is only really needed if you are uploading the package to the AUR.

pkgname is the name of the package. It’s commonly used as a variable in the build and package functions.

My practice is to use pkgname1 in case I have two versions of the same package such as a versioned release and one that builds to the latest commits on github.

pkgdesc is just a short and sweet description of the package

pkgver can be whatever you want. When I have a package built to the latest github commits I use a func that fills in pkgver='autogenerated' like so:

pkgver() { 
cd ${srcdir}/${pkgname1} 
local date=$(git log -1 --format="%cd" --date=short | sed s/-//g)         local count=$(git rev-list --count HEAD) 
local commit=$(git rev-parse --short HEAD) 
echo "${date}.${count}_${commit}" }

pkgrel must increment whenever you update or change a PKGBUILD but the version does not change. If the version change, reset pkgrel to 1

arch=('x86_64')
conflicts=(skyminer)

in the other implementation of this same concept (in the skyminer bootstrapping package) I did not have the golang binary for the http server built in the created package, rather I had a script that would build this binary on the target system after it was booted. Having the binary built makes the package architecture-specific once it is built. So for sanity’s sake and my own personal immediate use I want the built package to reflect that it’s specific to x86_64 so i don’t mistakenly install it on aarch64, but this field could just as well say arch=(‘any’).

The skyminer bootstrapping package contains many scripts of the same names and most of this package was borrowed from elements of the other. It’s important that if you know of a potential conflict that you should list it.

Another caveat, some software I’ve packaged previously had very generic binary names, such as ‘node’ which conflicts with, nodejs. If you are not able to make such changes to the source, what I have done is to put these binaries in their own folder somewhere and symlink them to `/usr/bin` with a different name.

license=() I don’t typically bother with putting a license for my own scripts. The field I required but may be left blank

makedepends=(go) Since go build is called in the build func, we need to make sure golang is installed. You don’t need to list any packages which are included in the archlinux root filesystem by default or packages in base-devel as it is assumed these are already installed.

source=(
"aur-local.sh"
"aur-remote-config.sh"
"readonly-cache.sh"
"readonly-cache.service"
"readonlycache.go"
)

Sources listed like this are assumed to be local files. If you list them like this for an AUR package, be sure you push them to the git repo for the package. If you are doing that, I recommend compressing the sources into an archive of .tar.gz to easily differentiate it from the create package archive type which is .tar.xz.

sha256sums=(
'd476b95a400c9fc252f5402d9e911c36ff26675c9ee68e42f4b4696c65c9dd44'
'3c7a25664ecaaef35a1b48081f350cd50d571d81e514861aa54c8b84d432efd4'
'64153502b466979a987ba074bb3881095846d1af35de9b71eed029389ac085f8'
'ccfe41daa38a45b0498cc6dd9f6636db4a7e835ee5c48c3e952cac038006b5f4'
'8aff1be292e5102b3ff4418af4a3d758b3c68d5fca89c7ca6ba18631bb7b180b')

It’s always good practice to use checksums, though this may not be possible if your source is the latest github commits to a git repo. For anything you can’t easily add the checksums for, you can simply put ‘SKIP’

The checksums can be updated with updpkgsums which is provided by pacman-contib in the AUR.

Phew! Finally past the variables, now we can look at the functions.

build() {
go build readonlycache.go
}

This is fairly simple. If you wanted to do something more complex (in this contet) set your GOBIN to some directory inside the $srcdir. Also note that the sources are symlinked (or extracted) into the $srcdir, so this command actually happens in there.

Lastly, and most importantly, the package function

package() {
mkdir -p ${pkgdir}/usr/lib/systemd/system
mkdir -p ${pkgdir}/usr/lib/${pkgname1}
mkdir -p ${pkgdir}/usr/bin
rmextension=".sh"
rcscripts=$(ls *.sh)
rcservices=$(ls *.service)
for i in $rcscripts ; do
install -Dm755 ${i} ${pkgdir}/usr/lib/${pkgname1}/${i}
ln -rTsf ${pkgdir}/usr/lib/${pkgname1}/${i} ${pkgdir}/usr/bin/${i//$rmextension}
chmod +x ${pkgdir}/usr/lib/${pkgname1}/${i}
done
for i in $rcservices ; do
install -Dm644 ${i} ${pkgdir}/usr/lib/systemd/system/${i}
done
install -Dm755 readonlycache ${pkgdir}/usr/lib/${pkgname1}/
install -Dm755 readonlycache.go ${pkgdir}/usr/lib/${pkgname1}/
ln -rTsf ${pkgdir}/usr/lib/${pkgname1}/readonlycache ${pkgdir}/usr/bin/readonlycache
}

Some of this might be redundant. If you do install it will create the directories if they don’t exist and set the right permissions, etc. If you want to use a whole folder, you’ll have to use `cp -r` or `cp -a` depending on your circumstance.

The symlinks were tricky. It’s necessary to use ln -rTsf for the symlinks to work when the package is installed, else they might point inside the directory where the package was built or possibly yay’s cache.

I also changed the script names when I symlinked them to remove the .sh extension, which won’t let a script execute normally from /usr/bin if it’s kept that way.

Now you should test that your PKGBUILD can successfully be used by makepkg to create a package. Simply run makepkg in the directory containing your PKGBUILD and any sources and troubleshoot any errors which occur.

I hope this short example of packaging has been useful. If things are still confusing to you and the package you want to make is very small, such as this one, you can if you prefer, define a script or scripts in the PKGBUILD and echo them to files in build(). Then at least you’ll have everything in front of you in the same file.

Submitting a package to the AUR

https://wiki.archlinux.org/index.php/AUR_submission_guidelines#Submitting_packages

Now, let’s say that I want to upload this package to the AUR. You’ll need to do a bit of configuration for this which is outlined in the above citation

Read the rules first to ensure your package is in compliance and configure your ssh and git user as stated.

Then, clone the empty package repo (with ssh!)

git clone ssh://aur@aur.archlinux.org/readonly-cache.git
cd readonly-cache

Add your files into that directory you just cloned, and generate the .SRCINFO

makepkg --printsrcinfo > .SRCINFO

Add the necessary files to the package repo

git add -f PKGBUILD .SRCINFO aur-local.sh aur-remote-config.sh readonly-cache.sh readonly-cache.service readonlycache.go

add a commit message

git commit -m “added sources, PKGBUILD & .SRCINFO”

Push your commits

git push

Now, if you want to check and see if you can install the package with yay

yay -S readonly-cache

Viola!

Next time: Adopting AUR packages

Follow us:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: