wiki / linux
pkgbuild from zero
How an Arch package is described, built, checked and rebuilt in a clean chroot, with a worked example for a Rust CLI.
updated · 4 min read
A PKGBUILD is a bash file that makepkg sources. Its variables describe the package and its functions say how to turn the source into files on disk. makepkg runs the functions in order, prepare(), build(), check(), package(), with sources extracted under $srcdir. package() installs into $pkgdir, a fake root, and whatever lands there becomes the package.
the variables
pkgname=hyperfine
pkgver=2.0.0
pkgrel=1
pkgdesc='Command-line benchmarking tool'
arch=('x86_64')
url='https://github.com/sharkdp/hyperfine'
license=('MIT OR Apache-2.0')
depends=('glibc' 'libgcc')
makedepends=('cargo')
source=("$pkgname-$pkgver.tar.gz::https://static.crates.io/crates/$pkgname/$pkgname-$pkgver.crate")
b2sums=('cb4e16c2bf5a7870126ddc3beb8ae786ba05f16ddb2a0a7ec124b253838e443c1680df42384d2c726da4fd9ce6e02c9637eb979ba49a6842d85c1b2bfc229dad')
pkgname: lowercase letters and digits plus@._+-. For a Rust tool, use the binary’s name.pkgver: the upstream version. Letters, digits, periods and underscores, no hyphens. An upstream2.0.0-alpha.2becomes2.0.0_alpha.2, and${pkgver//_/-}turns it back where a URL needs the original.pkgrel: starts at 1. Bump it when a PKGBUILD change alters the built package, and reset it to 1 on every new upstream version.arch: the architectures it builds for, or('any')for packages with nothing compiled in them.license: an SPDX expression as a single quoted string. MIT and BSD are license families rather than single texts, so each package installs its own copy (seepackage()below).depends: everything needed at runtime. List every direct dependency, even ones another dependency already pulls in. Most Rust packages need only glibc and libgcc here.makedepends: needed only to build.base-develis assumed present, so leave out its members.checkdependsis the same idea for the test suite.source: URLs or local files.name::urlsaves the download under a name you choose, which helps here because crates.io serves.cratefiles.- Checksums: one array entry per source. Prefer the strongest type upstream publishes, in the order b2, sha512, sha384, sha256.
SKIPturns off the check for one entry, which is what VCS sources such as git need.
the functions
The Rust package guidelines give a template. Here it is for hyperfine:
prepare() {
cd "$pkgname-$pkgver"
export RUSTUP_TOOLCHAIN=stable
cargo fetch --locked --target host-tuple
}
build() {
cd "$pkgname-$pkgver"
export RUSTUP_TOOLCHAIN=stable
export CARGO_TARGET_DIR=target
cargo build --frozen --release
}
check() {
cd "$pkgname-$pkgver"
export RUSTUP_TOOLCHAIN=stable
cargo test --frozen
}
package() {
cd "$pkgname-$pkgver"
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/$pkgname"
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname/" LICENSE-MIT
}
Why each piece is there:
cargo fetch --lockedinprepare()downloads every dependency at the versions inCargo.lock, so the later steps can run offline.--target host-tuplefetches only what this platform needs.--frozeninbuild()means locked and offline: no network and no dependency changes, which keeps the build reproducible.RUSTUP_TOOLCHAINandCARGO_TARGET_DIRprotect against a builder’s personal cargo or rustup settings. Official packages build in a chroot and don’t need them. AUR users often build on their own machines, where they help.- The guideline template passes
--all-features. hyperfine’s only feature is Windows-specific, so this build leaves it off. check()runs tests without--release, because release builds turn off overflow checks anddebug_assert!.
The crate ships Cargo.lock, LICENSE-MIT and LICENSE-APACHE, so all of the above works against the crates.io tarball. Many crates leave license files out of the .crate. When that happens, use the GitHub release tarball as the source instead.
build, check, publish
updpkgsums # recompute checksums after changing pkgver or source
makepkg -si # build; -s installs missing deps, -i installs the result
makepkg --printsrcinfo > .SRCINFO # metadata file the AUR reads
namcap PKGBUILD
namcap hyperfine-2.0.0-1-x86_64.pkg.tar.zst
updpkgsums comes from pacman-contrib and rewrites the checksum array in place. It uses whichever type the PKGBUILD already has and falls back to sha256. Other makepkg flags worth knowing: -c cleans up work files after a successful build, -C deletes $srcdir before building, and -f overwrites a package that was already built.
namcap reads either the PKGBUILD or the built package. Lines starting with E: are errors to fix, W: are warnings you can override if you know why, and I: lines only show with -i.
clean chroot builds
Your own machine has hundreds of packages installed that the PKGBUILD never declared. A build that passes there can fail on a clean system. A chroot that contains only base-devel plus the declared dependencies catches missing entries in depends and makedepends.
The devtools package provides the tools. Official packagers use pkgctl build, which keeps its chroots in /var/lib/archbuild/. By hand it looks like this:
mkdir ~/chroot
mkarchroot ~/chroot/root base-devel
makechrootpkg -c -r ~/chroot
-r points at the chroot directory and -c cleans the chroot before building. Arguments after -- go to makepkg.
before pushing to the AUR
pkgrelreset to 1 ifpkgverchanged- checksums updated with
updpkgsums .SRCINFOregenerated and committed- namcap shows no
E:lines - the package built in a clean chroot
sources
- PKGBUILD, ArchWiki
- Rust package guidelines, Arch developer manual
- makepkg(8), updpkgsums(8), namcap(1)
- pkgctl-build(1), makechrootpkg(1)
- hyperfine on crates.io