How to Build an Alpine Linux Package
The Alpine Linux wiki alĀready has a deĀcent exĀplaĀnaĀtion about how to build packĀages; howĀever, I wanted throw someĀthing toĀgether a litĀtle more conĀverĀsaĀtional. This writeup should get you buildĀing a packĀage quickly. The only reĀquireĀment to folĀlow along is a workĀing Alpine Linux inĀstalĀlaĀtion. Iāll strongly imĀply usĀing Docker, cause thatās what Iāve used. Regardless of how your setup, the genĀeral steps will be same. Thereās just less setup with Docker.
APKBUILD
The main thing weāll need is an APKBUILD ļ¬le.
For my own adĀvenĀture, I didĀnāt need to make an APKBUILD ļ¬le from scratch. So, Iāll have less to say about that. Thus, Iām not goĀing to atĀtempt to cover all the deĀtails of the APKBUILD ļ¬le here. You can glance over the ofĀļ¬Ācial docĀuĀmenĀtaĀtion and there are plenty of exĀamĀples in the ofĀļ¬Ācial reposĀiĀtory.
Instead, Iāll go over a high level overview and show how to genĀerĀate a patch ļ¬le. Patch ļ¬les alĀlow us to make modĀiĀļ¬ĀcaĀtions to the source ļ¬les, withĀout havĀing those changes live in a repo. This conĀcept is useĀful if we need to make some speĀciļ¬c change to our Alpine build that wouldĀnāt be useĀful anyĀwhere elseāor maybe you canāt get the repo ownĀers to make the change for some reaĀson. Regardless, we can still tweak the build or the tests if needed.
Hereās an exĀamĀple of an APKBUILD ļ¬le. This one has most of the stuff goĀing on, so it should be useĀful for learnĀing a thing or two.
# Contributor: prspkt <prspkt@protonmail.com>
# Maintainer: prspkt <prspkt@protonmail.com>
pkgname=brotli
pkgver=1.0.9
pkgrel=1
pkgdesc="Generic lossless compressor"
url="https://github.com/google/brotli"
arch="all"
license="MIT"
makedepends="cmake python3-dev"
subpackages="$pkgname-doc $pkgname-static $pkgname-dev $pkgname-libs py3-$pkgname:py3"
source="$pkgname-$pkgver.tar.gz::https://github.com/google/brotli/archive/v$pkgver.tar.gz
optimize-mips-s390x.patch
build-tool-against-shared-lib.patch
838.patch
"
# secfixes:
# 1.0.9-r0:
# - CVE-2020-8927
prepare() {
default_prepare
sed -i 's,/usr/bin/env bash,/bin/sh,' tests/*.sh
}
build() {
if [ "$CBUILD" != "$CHOST" ]; then
CMAKE_CROSSOPTS="-DCMAKE_SYSTEM_NAME=Linux -DCMAKE_HOST_SYSTEM_NAME=Linux"
fi
cmake -B build \
-DCMAKE_BUILD_TYPE=None \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INSTALL_LIBDIR=lib \
-DBUILD_SHARED_LIBS=True \
-DCMAKE_C_FLAGS="$CFLAGS" \
$CMAKE_CROSSOPTS
make -C build
}
check() {
make -C build test
}
package() {
make -C build DESTDIR="$pkgdir" install
local man; for man in docs/*.?; do
install -D -m644 $man "$pkgdir"/usr/share/man/man${man##*.}/${man##*/}
done
}
py3() {
cd "$builddir"
python3 setup.py install --prefix=/usr --root="$subpkgdir"
}
sha512sums="b8e2df955e8796ac1f022eb4ebad29532cb7e3aa6a4b6aee91dbd2c7d637eee84d9a144d3e878895bb5e62800875c2c01c8f737a1261020c54feacf9f676b5f5 brotli-1.0.9.tar.gz
59e934578ce23b703f8f25f56578f8e9fd1466a9844b6f67b542acd6496df352548e3762697dc3851cfd0f8e1bad170bfdaa3e8a87c901fe81e2f3042e3aee84 optimize-mips-s390x.patch
f4a7653a0f7ef69f059d7f744a48c7731c8e66f977ce2e66cd106f697e82aa1f005923898d216a3d8be143b2dc8db1927c09daedb981818e752640a333d75fbc build-tool-against-shared-lib.patch
58ef677595f0db80b7d1353e42603cc30ef9b0b9530927f731ee31ac60ad9a3b2aac960a5cd100f8b10e547c9534e1ebf78c53550b52eed6fb3b7fb853317d20 838.patch"
Youāll need to deĀclare the packĀage name, verĀsion, varĀiĀous sorts of deĀpenĀdenĀcies, and the loĀcaĀtion of the source ļ¬les. There some funcĀtions for startĀing the build, runĀning the tests, and genĀerĀatĀing any needed subĀpackĀages. A subĀpackĀage is baĀsiĀcally a way of splitĀting a sinĀgle build into mulĀtiĀple packĀages. A comĀmon exĀamĀple may be to split the docĀuĀmenĀtaĀtion into its own packĀage, if itās quite large.
As menĀtioned, in the same diĀrecĀtory as the APKBUILD ļ¬le, you may see some patch ļ¬les. These are baĀsiĀcally diffs shoved into a text ļ¬le. In fact, thatās reĀally all that they are. Assuming youāre usĀing git, we can litĀerĀally use the git diff
comĀmand here.
# Using a POSIX shell here
git diff something...somethang > my-reasonably-named-patch-file.patch
Iām sure thereās sevĀeral ways to genĀerĀate these things. But itās only a diff, nothĀing fancy. We end up someĀthing that looks like this:
Upstream: Yes
Reason: Fixes #11948
From 092446fafb4bfb81738853b7c7f76b293cd92a80 Mon Sep 17 00:00:00 2001
From: Evgenii Kliuchnikov <eustas.ru@gmail.com>
Date: Wed, 2 Sep 2020 10:49:49 +0200
Subject: [PATCH] Revert "Add runtime linker path to pkg-config files (#740)"
This reverts commit 31754d4ffce14153b5c2addf7a11019ec23f51c1.
---
scripts/libbrotlicommon.pc.in | 2 +-
scripts/libbrotlidec.pc.in | 2 +-
scripts/libbrotlienc.pc.in | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/scripts/libbrotlicommon.pc.in b/scripts/libbrotlicommon.pc.in
index 10ca969e..2a8cf7a3 100644
--- a/scripts/libbrotlicommon.pc.in
+++ b/scripts/libbrotlicommon.pc.in
@@ -7,5 +7,5 @@ Name: libbrotlicommon
URL: https://github.com/google/brotli
Description: Brotli common dictionary library
Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -R${libdir} -lbrotlicommon
+Libs: -L${libdir} -lbrotlicommon
Cflags: -I${includedir}
diff --git a/scripts/libbrotlidec.pc.in b/scripts/libbrotlidec.pc.in
index e7c3124f..6f8ef2e4 100644
--- a/scripts/libbrotlidec.pc.in
+++ b/scripts/libbrotlidec.pc.in
@@ -7,6 +7,6 @@ Name: libbrotlidec
URL: https://github.com/google/brotli
Description: Brotli decoder library
Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -R${libdir} -lbrotlidec
+Libs: -L${libdir} -lbrotlidec
Requires.private: libbrotlicommon >= 1.0.2
Cflags: -I${includedir}
diff --git a/scripts/libbrotlienc.pc.in b/scripts/libbrotlienc.pc.in
index 4dd0811b..2098afe2 100644
--- a/scripts/libbrotlienc.pc.in
+++ b/scripts/libbrotlienc.pc.in
@@ -7,6 +7,6 @@ Name: libbrotlienc
URL: https://github.com/google/brotli
Description: Brotli encoder library
Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -R${libdir} -lbrotlienc
+Libs: -L${libdir} -lbrotlienc
Requires.private: libbrotlicommon >= 1.0.2
Cflags: -I${includedir}
Optionally, you can put a note at the top to tell everyĀone why the h*ck your usĀing a patch ļ¬le. The exĀamĀple above did this. Documentation is fun and someĀtimes helpĀful? So maybe do the note thing?
Generate the Checksums
Once we have our APKBUILD ļ¬le setup, weāll need to genĀerĀate checkĀsums for the source ļ¬les.
This process is a simĀple comĀmand: abuild checksum
, which needs to be run from the same diĀrecĀtory as the APKBUILD ļ¬le. There is no need to downĀload the source ļ¬les manĀuĀally as the abuild tool will hanĀdle that for us. Executing this via Docker isĀnāt too difĀferĀent. Once again, usĀing this imĀage, we can ļ¬re off the comĀmand like so:
docker run --rm -it -v "$PWD":/home/builder/package andyshinn/alpine-abuild:v12 checksum
The proĀgram will modĀify your APKBUILD ļ¬le, if needed. Obviously, if youāve alĀready genĀerĀated a checkĀsum beĀforeāand no source ļ¬les have changed since the last inĀvoĀcaĀtionāthen thereās nothĀing to upĀdate here.
To note, you canĀnot skip this step. The build will fail later on if the checkĀsums are not up to date with the source ļ¬les.
We Need Keys
In Alpine Linux, reposĀiĀtoĀries and packĀages are signed usĀing asymĀmetĀric crypĀtogĀraĀphy. Assuming you want downĀstream users to be able to seamĀlessly inĀstall packĀages youāve built, youāll need a way to manĀage keys. The alĀterĀnaĀtive is to use this ļ¬ag: apk add --allow-untrusted package-name
. However, for our purĀposes, Iāll asĀsume we need to manĀage keys.
Luckily, Alpine makes this easy. First, we should set the idenĀtity of the packĀager, which is us (or your comĀpany, dogāwhoĀever reĀally). You can do this by eiĀther editĀing your /etc/abuild.conf ļ¬le, or you can set the PACKAGER enĀviĀronĀment variĀable. There is a genĀeral forĀmat for the digĀiĀtal nametag. It will end up lookĀing someĀthing like: PACKAGER="Matthew Parris <notmyrealemail@website.com>"
.
Next, all we to need to do is run this comĀmand: abuild-keygen -n
. Then, the pubĀlic and priĀvate keys will both be genĀerĀated in the ~/.abuild
diĀrecĀtory for the curĀrent user. The Docker verĀsion isĀnāt much difĀferĀent:
docker run --rm -it \
--entrypoint abuild-keygen \
-v "$PWD":/home/builder/.abuild \
-e PACKAGER="Matthew Parris <notmyrealemail@website.com>" \
andyshinn/alpine-abuild:v12 -n
The keys will then be placed in your curĀrent workĀing diĀrecĀtory.
In reĀalĀity, weāre genĀerĀatĀing RSA 256 bit keysāwhich you can creĀate manĀuĀally usĀing openssl. The abuild-keyĀgen proĀgram is only a conĀveĀnient script wrapĀping this beĀhavĀior.
So how do we tell apk to trust our signed packĀages and reposĀiĀtoĀries? We need to copy our pubĀlic key into the /etc/apk/keys diĀrecĀtory. Make sure you donāt change the ļ¬leĀname of the key. Now, we should be able to run the comĀmand apk add package-name
, with no --allow-untrusted
ļ¬ag.
Build It!
We have our APKBUILD ļ¬le setup and our signĀing key ready. Letās exĀeĀcute the build!
For the build process, you should only need your priĀvate key in place. We can manĀuĀally set the loĀcaĀtion of our priĀvate key with an enĀviĀronĀment variĀable PACKAGER_PRIVKEY. This variĀable will be set to a path like so: PACKAGER_PRIVKEY=/path/to/private/key
. Like the other enĀviĀronĀment variĀables weāve seen so far, you can also set this in your /etc/abuild.conf ļ¬le. The enĀviĀronĀment variĀable techĀnique is conĀveĀnient when usĀing Docker, which weāll set later.
Assuming everyĀthing is setup, we are about ready to build. Youāll probĀaĀbly want to run the comĀmand abuild-apk
to upĀdate the packĀage cacheāelse abuild may not be able to downĀload the packĀages. Next, ļ¬re off the comĀmand abuild -r
. The abuild proĀgram should downĀload the needed packĀages for the build, start the build, run the tests, and ļ¬Ānally packĀage the source code up into one or more apk ļ¬les.
By deĀfault, the apk ļ¬les will be placed in the ~/packages diĀrecĀtory for the curĀrent user. This deĀfault can be changed by setĀting the enĀviĀronĀment variĀable REPODEST (or by modĀiĀfyĀing the /etc/abuild.conf ļ¬le).
Letās go through what this would look like on Docker. Youāll end up runĀning a comĀmand simĀiĀlar to this:
docker run --rm -it \
-e RSA_PRIVATE_KEY="$(cat /path/to/private/key.rsa)" \
-e RSA_PRIVATE_KEY_NAME="key.rsa" \
-v "$PWD:/home/builder/package" \
-v "/where/you/want/to/put/the/packages:/packages" \
andyshinn/alpine-abuild:v12
To note, this Dockerļ¬le does change some of the enĀviĀronĀment variĀables aroundālike REPODEST is set to /packages. Also, make sure you run this from the diĀrecĀtory that conĀtains the APKBUILD ļ¬le (or adĀjust the bind mount). I recĀomĀmend lookĀing at the Dockerļ¬le itĀself and playĀing around with this. For exĀamĀple, youāll likely want to adĀjust the bind mounts or the key loĀcaĀtion.
And thatās pretty much it. Anyways, for more deĀtail than what Iāve given here, check out the Alpine wiki. Thanks for readĀing.