Due to my stubbornness fondness for testing the latest and greatest software releases, I often run into problems, and then I need to apply patches on top of Arch Linux's packages to fix them.
When the number of patched packages grows large enough, this may devolve into chaos, so this is a rough sketch of the method I have used over the years to keep this under control.
Identifying patched packages
The first challenge is marking the packages that I have patched, so both pacman and I can understand what is installed on my system.
I do this by simply appending a snippet like this at the end of each of my patched PKGBUILDs:
custompkgrel=1
pkgrel="${custompkgrel}00${pkgrel}"
This will turn a package version like helloworld-1.23-4
into helloworld-1.23-1004
.
This both makes it identifiable (usually, packages have a small pkgrel
), and makes pacman detect the patched packages as updates of the unpatched ones.
When I patch the package further, I bump custompkgrel
, so that each patched package has a unique version.
Applying .patch files
The straightforward way to apply .patch files is to put them inline with the rest of the PKGBUILD, like this:
# Maintainer: John Smith <john.smith@example.com>
pkgname=helloworld
pkgver=1.23
pkgrel=4
pkgdesc="A simple Hello World program"
url='https://www.example.com'
arch=('any')
license=('MIT')
source=("hello.tar.gz"
+ "helloworld-i18n-support.patch"
"helloworld-fix-typo.patch")
sha256sums=('d267ac47beecb944222bc2e8f7d6dd5e3ec9602cf4a4f3ed70b1e424c8213400'
+ '87d2f5b9365fbd0b2342f6632fc202f56d7237097596aa30e3aaf92aa2d19b86'
'5635261bac29f1c0ed8cfbb4044e56bda9e85e9f50898fc0e6d1ca00f205f891')
prepare() {
+ patch -Np1 -i helloworld-i18n-support.patch
patch -Np1 -i helloworld-fix-typo.patch
}
build() {
gcc -o hello hello.c
}
package() {
install -Dm755 "$srcdir/hello" "$pkgdir/usr/bin/hello"
}
+
+custompkgrel=1
+pkgrel="${custompkgrel}00${pkgrel}"
However, my experience is that it often takes a long time for a patch to be polished, submitted, accepted and released upstream; and during that time, keeping the package synchronized with the Arch Linux repositories or the AUR (via git pull
) turns into a chore of resolving frequent spurious merge conflicts.
Instead, I have settled on a hack to keep all my patches at the end of the PKGBUILD. Behold:
# [...the original, unchanged PKGBUILD...]
custompkgrel=1
pkgrel="${custompkgrel}00${pkgrel}"
# This monkey-patches "prepare" to append a line of code at the beginning
# Note that this can be called multiple times, each appending a further line of code
before_prepare() {
if [ -z "${before_prepare_script+x}" ]; then
before_prepare_script=""
eval "$(echo 'prepare() { eval "$before_prepare_script";'; declare -f prepare | head -n -1 | tail +3; echo '}')"
fi
before_prepare_script="$before_prepare_script$(printf "%q " "$@");"$'\n'
}
source+=("helloworld-i18n-support.patch")
sha256sums+=('87d2f5b9365fbd0b2342f6632fc202f56d7237097596aa30e3aaf92aa2d19b86')
before_prepare patch -Np1 -i helloworld-i18n-support.patch
With this method, all my changes are an appendage to the PKGBUILD, so they don't conflict. You might object that this is contrary to version control best practices, where a merge conflict is a strong hint that you must look at whatever has changed and confirm that it is correct.
However, my experience is that for this very particular case of a personal patch repository, this greatly improves the UX, while not sacrificing much: The patch
command will fail when the code targeted by the .patch file changes, so there is still a conflict detection method in place.
Putting it all together
Usually, one of my patched PKGBUILD files looks like this:
# [...the original, unchanged PKGBUILD...]
custompkgrel=1
source "../custompkg.sh" || exit 1
# ZEALCHARM START Add i18n support
# See https://example.com/helloworld-org/helloworld/pull/1234
source+=("helloworld-i18n-support.patch")
sha256sums+=('87d2f5b9365fbd0b2342f6632fc202f56d7237097596aa30e3aaf92aa2d19b86')
before_prepare patch -Np1 -i helloworld-i18n-support.patch
# ZEALCHARM END Add i18n support
Here, I have refactored the previous utility into a custompkg.sh
script, so I can share the common parts across all my patched packages.
The overall flow
When I decide to patch a PKGBUILD, what I do is:
- Clone the PKGBUILD git repository, e.g.
git clone https://gitlab.archlinux.org/archlinux/packaging/packages/helloworld.git
- Apply and commit my changes to the PKGBUILD, as explained above.
- Build the package and add it to a custom local repository.
- Run
pacman -Syu
. Pacman will detect the package as an update, since thepkgrel
has changed (e.g.1.23-4 -> 1.23-1004
).
To keep the package up to date, the process is similar:
- Pull the PKGBUILD git repository, i.e.
cd helloworld && git pull
. - Rebuild the package and add it to the custom local repository.
- Run
pacman -Syu
. Pacman will detect the package as an update, since thepkgver
orpkgrel
will have changed (e.g.1.23-1004 -> 1.23-1005
).
I have some scripts that automate most of this process, so the overall amount of work to keep the patched packages up to date is pretty small.