Generated using Claude.ai (Opus 4.5 model)

Complete Guide to Creating Arch Linux Packages

A comprehensive guide consolidating official Arch Linux documentation on creating, building, and submitting packages to the Arch User Repository (AUR).


Table of Contents

  1. Overview
  2. Prerequisites and Setup
  3. Understanding the PKGBUILD File
  4. PKGBUILD Variables Reference
  5. PKGBUILD Functions
  6. Building Packages with makepkg
  7. VCS Package Guidelines
  8. Testing and Quality Assurance
  9. Submitting to the AUR
  10. Package Maintenance
  11. Best Practices and Common Mistakes
  12. Quick Reference

1. Overview

What is an Arch Linux Package?

An Arch package is a tar archive compressed with zstd (.pkg.tar.zst) containing:

The Arch Build System

The Arch Build System (ABS) is a ports-like system for building packages from source:

Component Description
PKGBUILD Bash script containing build instructions and metadata
makepkg Tool that reads PKGBUILDs and creates packages
pacman Package manager for installing/removing packages
devtools Tools for building in clean chroots (official packages)

Package Repositories


2. Prerequisites and Setup

Install Required Tools

# Install base development tools (required)
sudo pacman -S base-devel

# Install additional useful tools
sudo pacman -S namcap devtools git

The base-devel group includes: autoconf, automake, binutils, bison, fakeroot, file, findutils, flex, gawk, gcc, gettext, grep, groff, gzip, libtool, m4, make, pacman, patch, pkgconf, sed, sudo, texinfo, which.

Configure makepkg (Optional)

Edit /etc/makepkg.conf or ~/.makepkg.conf:

# Use multiple cores for compilation
MAKEFLAGS="-j$(nproc)"

# Use multiple cores for compression
COMPRESSZST=(zstd -c -z -q -T0 -)

# Enable ccache for faster rebuilds (optional)
# BUILDENV=(... ccache ...)

Set Up a Working Directory

mkdir -p ~/packages
cd ~/packages

3. Understanding the PKGBUILD File

What is a PKGBUILD?

A PKGBUILD is a Bash script that defines how to build a package. It contains:

  1. Metadata variables - Package name, version, description, etc.
  2. Source information - Where to download source files
  3. Functions - How to build and package the software

PKGBUILD Template

Use the official prototype as a starting point:

cp /usr/share/pacman/PKGBUILD.proto ~/packages/mypackage/PKGBUILD

Minimal PKGBUILD Example

# Maintainer: Your Name <your.email@example.com>
pkgname=hello-world
pkgver=1.0.0
pkgrel=1
pkgdesc="A simple hello world program"
arch=('x86_64')
url="https://example.com/hello-world"
license=('MIT')
depends=('glibc')
source=("https://example.com/${pkgname}-${pkgver}.tar.gz")
sha256sums=('abc123...')

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

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

4. PKGBUILD Variables Reference

Mandatory Variables

Variable Description
pkgname Package name (lowercase alphanumerics, @._+-). Cannot start with - or .
pkgver Upstream version. No hyphens allowed (replace with _)
pkgrel Arch-specific release number. Starts at 1, increment for PKGBUILD fixes
arch Supported architectures: ('x86_64') or ('any') for arch-independent
Variable Description
pkgdesc Brief description (≤80 chars). Don’t include package name
url Upstream project URL
license SPDX license identifier(s): ('GPL-3.0-or-later'), ('MIT'), etc.

Dependency Arrays

Variable Description
depends Runtime dependencies (required to run the software)
makedepends Build-time only dependencies (not needed at runtime)
checkdepends Dependencies for running tests (only needed for check())
optdepends Optional dependencies with descriptions: ('python: for scripting support')

Important: Always list all direct dependencies, even if they’re pulled in transitively. Transitive dependencies can change.

Source Arrays

Variable Description
source Array of source files (URLs or local filenames)
sha256sums SHA-256 checksums for each source (or 'SKIP' to skip verification)
validpgpkeys GPG key fingerprints for signature verification
noextract Files to not auto-extract

Source URL best practices:

# Use variables for maintainability
source=("https://github.com/user/repo/archive/v${pkgver}.tar.gz")

# Rename downloaded files
source=("${pkgname}-${pkgver}.tar.gz::https://example.com/download/${pkgver}.tar.gz")

# Local files (must be in same directory as PKGBUILD)
source=("fix-build.patch"
        "myconfig.conf")

Other Useful Variables

Variable Description
provides Virtual packages this provides: ('libfoo.so=1-64')
conflicts Packages that conflict with this one
replaces Packages this replaces (use sparingly, mostly for renames)
backup Config files to backup on upgrade: ('etc/myapp.conf')
options Build options: ('!strip' '!buildflags' 'staticlibs')
install Post-install script filename: install=${pkgname}.install
changelog Changelog filename
epoch Force package to be seen as newer (use sparingly)
groups Package groups this belongs to

Architecture-Specific Variables

Append _<arch> to create architecture-specific overrides:

source_x86_64=("https://example.com/binary-x86_64.tar.gz")
sha256sums_x86_64=('...')
depends_x86_64=('lib32-glibc')

5. PKGBUILD Functions

Available Variables in Functions

Variable Description
$srcdir Directory where sources are extracted
$pkgdir Fake root directory for installation (becomes package contents)
$startdir Directory containing the PKGBUILD (deprecated, avoid using)

prepare() - Source Preparation

Optional. Runs after source extraction. Used for patching and source modifications.

prepare() {
    cd "$srcdir/$pkgname-$pkgver"
    
    # Apply patches
    patch -Np1 -i "$srcdir/fix-build.patch"
    
    # Generate build files
    autoreconf -fiv
}

pkgver() - Dynamic Version (VCS Packages)

Optional. Used to update pkgver from VCS sources. See VCS Package Guidelines.

build() - Compilation

Optional (but usually needed). Compiles the source code.

build() {
    cd "$srcdir/$pkgname-$pkgver"
    
    # Standard autotools
    ./configure --prefix=/usr
    make
    
    # Or CMake
    cmake -B build -S . \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX=/usr
    cmake --build build
    
    # Or Meson
    arch-meson build
    meson compile -C build
}

Important: Always use --prefix=/usr. Arch packages should never install to /usr/local.

check() - Testing

Optional but recommended. Runs the test suite.

check() {
    cd "$srcdir/$pkgname-$pkgver"
    make check
    # or: make test
    # or: ctest --test-dir build
}

package() - Installation

Required. Installs files into $pkgdir.

package() {
    cd "$srcdir/$pkgname-$pkgver"
    
    # Standard make install
    make DESTDIR="$pkgdir" install
    
    # Install license
    install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
    
    # Install documentation
    install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md"
    
    # Install config file (add to backup array)
    install -Dm644 myapp.conf "$pkgdir/etc/myapp.conf"
}

Note: Everything in $pkgdir will be packaged. The directory structure under $pkgdir becomes the root filesystem structure.

Split Packages

Build multiple packages from one PKGBUILD:

pkgbase=myproject
pkgname=('myproject' 'myproject-docs')
# ... other variables ...

package_myproject() {
    pkgdesc="The main application"
    depends=('glibc')
    
    cd "$srcdir/$pkgbase-$pkgver"
    make DESTDIR="$pkgdir" install
}

package_myproject-docs() {
    pkgdesc="Documentation for myproject"
    arch=('any')
    
    cd "$srcdir/$pkgbase-$pkgver"
    install -Dm644 docs/* -t "$pkgdir/usr/share/doc/$pkgbase/"
}

6. Building Packages with makepkg

Basic Usage

cd ~/packages/mypackage

# Build the package
makepkg

# Build and install dependencies automatically
makepkg -s

# Build, install deps, and install the package
makepkg -si

# Build and remove makedepends afterward
makepkg -sr

# Clean build directory after successful build
makepkg -c

Useful makepkg Options

Option Description
-s, --syncdeps Install missing dependencies with pacman
-r, --rmdeps Remove makedepends after build
-i, --install Install package after building
-c, --clean Clean up work files after build
-f, --force Overwrite existing package
-C, --cleanbuild Remove $srcdir before building
-o, --nobuild Download and extract only (no build)
-e, --noextract Don’t extract sources (use existing $srcdir)
-R, --repackage Repackage without rebuilding
-g, --geninteg Generate integrity checksums
--nocheck Skip the check() function
--skippgpcheck Skip PGP signature verification
--skipchecksums Skip checksum verification
-L, --log Log build output to file

Generate Checksums

# Generate and append to PKGBUILD
makepkg -g >> PKGBUILD

# Or use updpkgsums (from pacman-contrib)
updpkgsums

Install the Built Package

# Using pacman
sudo pacman -U mypackage-1.0.0-1-x86_64.pkg.tar.zst

# Or using makepkg -i
makepkg -si

7. VCS Package Guidelines

Naming Convention

VCS packages must be suffixed with the VCS type:

VCS Source Format

# Git (most common)
source=("git+https://github.com/user/repo.git")

# Git with specific branch
source=("git+https://github.com/user/repo.git#branch=develop")

# Git with specific tag
source=("git+https://github.com/user/repo.git#tag=v1.0.0")

# Git with specific commit
source=("git+https://github.com/user/repo.git#commit=abc123")

# SSH URL
source=("git+ssh://git@github.com/user/repo.git")

For VCS sources, use SKIP for checksums:

sha256sums=('SKIP')

The pkgver() Function

The pkgver() function dynamically generates version numbers from VCS sources.

Recommended format: RELEASE.rREVISION or RELEASE.rREVISION.gHASH

Git with tags (preferred):

pkgver() {
    cd "$srcdir/$pkgname"
    git describe --long --tags --abbrev=7 | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
}
# Output: 1.2.3.r5.gabc1234 (5 commits after tag v1.2.3)

Git without tags:

pkgver() {
    cd "$srcdir/$pkgname"
    printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short=7 HEAD)"
}
# Output: r150.abc1234 (150 commits, hash abc1234)

Mercurial:

pkgver() {
    cd "$srcdir/$pkgname"
    printf "r%s.%s" "$(hg identify -n)" "$(hg identify -i)"
}

Subversion:

pkgver() {
    cd "$srcdir/$pkgname"
    printf "r%s" "$(svnversion | tr -d 'A-z')"
}

Complete VCS PKGBUILD Example

# Maintainer: Your Name <email@example.com>
pkgname=myapp-git
_pkgname=myapp
pkgver=1.2.3.r5.gabc1234
pkgrel=1
pkgdesc="My application (development version)"
arch=('x86_64')
url="https://github.com/user/myapp"
license=('MIT')
depends=('glibc')
makedepends=('git' 'cmake')
provides=("${_pkgname}")
conflicts=("${_pkgname}")
source=("git+${url}.git")
sha256sums=('SKIP')

pkgver() {
    cd "$srcdir/$_pkgname"
    git describe --long --tags --abbrev=7 | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
}

build() {
    cmake -B build -S "$_pkgname" \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX=/usr
    cmake --build build
}

package() {
    DESTDIR="$pkgdir" cmake --install build
    install -Dm644 "$srcdir/$_pkgname/LICENSE" \
        "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}

VCS Package Rules

  1. Don’t commit pkgver bumps alone - Only commit when PKGBUILD changes
  2. Use provides/conflicts - Link to the stable package name
  3. Add VCS tool to makedepends - git, subversion, mercurial, etc.
  4. pkgrel stays at 1 - Reset when pkgver changes, increment only for PKGBUILD fixes

8. Testing and Quality Assurance

Using namcap

namcap analyzes PKGBUILDs and packages for common errors:

# Check PKGBUILD
namcap PKGBUILD

# Check built package
namcap mypackage-1.0.0-1-x86_64.pkg.tar.zst

# Check installed package
namcap -i mypackage

Common namcap warnings:

Warning Meaning
dependency X detected and target Y depends on it Missing dependency
dependency X included but already satisfied Redundant dependency
ELF file has no RELRO Security issue (missing hardening)
Dependency included, but may not be needed Possible unnecessary dependency

Note: namcap can produce false positives. Investigate warnings but don’t blindly follow all suggestions.

Building in a Clean Chroot

Building in a clean chroot ensures: - All dependencies are correctly declared - No accidental linking against packages on your system - Reproducible builds

Using devtools (recommended for AUR maintainers):

# Install devtools
sudo pacman -S devtools

# Build in clean chroot
pkgctl build

# Or use the repository-specific script
extra-x86_64-build

Using clean-chroot-manager (alternative):

# Install from AUR
yay -S clean-chroot-manager

# Create chroot
sudo ccm c

# Build package
sudo ccm s

Testing the Package

# List package contents
pacman -Qlp mypackage-1.0.0-1-x86_64.pkg.tar.zst

# View package info
pacman -Qip mypackage-1.0.0-1-x86_64.pkg.tar.zst

# Install and test
sudo pacman -U mypackage-1.0.0-1-x86_64.pkg.tar.zst

# Verify installation
pacman -Ql mypackage
pacman -Qi mypackage

# Test the software actually works
mypackage --version

Checking Dependencies

# Find libraries linked by an executable
ldd /usr/bin/myapp

# Better alternative
lddtree /usr/bin/myapp

# Find which package provides a library
pacman -Qo /usr/lib/libfoo.so

9. Submitting to the AUR

Prerequisites

  1. Read the guidelines - Familiarize yourself with Arch package guidelines
  2. Create an AUR account - Register at https://aur.archlinux.org
  3. Set up SSH keys - Required for pushing packages

Setting Up SSH Authentication

# Generate a dedicated SSH key for AUR
ssh-keygen -t ed25519 -f ~/.ssh/aur

# Configure SSH
cat >> ~/.ssh/config << EOF
Host aur.archlinux.org
    IdentityFile ~/.ssh/aur
    User aur
EOF

# Copy public key to AUR profile
cat ~/.ssh/aur.pub
# Paste this into "My Account" > "SSH Public Key" on AUR website

Creating a New AUR Package

# Clone the (empty) AUR repository
git -c init.defaultBranch=master clone ssh://aur@aur.archlinux.org/pkgname.git
cd pkgname

# You'll see: "warning: You appear to have cloned an empty repository."
# This is expected for new packages

# Add your PKGBUILD
cp ~/packages/mypackage/PKGBUILD .

# Generate .SRCINFO (REQUIRED)
makepkg --printsrcinfo > .SRCINFO

# Add files
git add PKGBUILD .SRCINFO

# Commit
git commit -m "Initial upload"

# Push
git push

The .SRCINFO File

The .SRCINFO file is a machine-readable representation of PKGBUILD metadata. It must be regenerated and committed whenever PKGBUILD changes.

# Generate .SRCINFO
makepkg --printsrcinfo > .SRCINFO

# Always commit both files together
git add PKGBUILD .SRCINFO
git commit -m "Update to version X.Y.Z"
git push

AUR Submission Rules

  1. No duplicates - Don’t submit packages already in official repos
  2. Must be useful - Packages should benefit the community
  3. Must be legal - Comply with licensing terms
  4. x86_64 only - Packages must support x86_64
  5. No replaces for AUR - Don’t use replaces unless renaming a package
  6. Maintain your packages - Respond to comments and keep packages updated

Updating an AUR Package

cd ~/aur/mypackage

# Update PKGBUILD
# ... edit PKGBUILD ...

# Regenerate .SRCINFO
makepkg --printsrcinfo > .SRCINFO

# Commit and push
git add PKGBUILD .SRCINFO
git commit -m "Update to version X.Y.Z"
git push

10. Package Maintenance

Responding to Feedback

Flagging Out-of-Date Packages

If you find an unmaintained package:

  1. Flag it as out-of-date with details
  2. Email the maintainer if possible
  3. After 2 weeks with no response, file an orphan request

Adopting Orphaned Packages

Orphaned packages can be adopted by any registered AUR user:

# Clone the existing repository
git clone ssh://aur@aur.archlinux.org/pkgname.git

# Make your changes and push

Deletion Requests

Submit deletion requests for: - Duplicate packages - Packages moved to official repos - Abandoned/broken packages with no maintainer


11. Best Practices and Common Mistakes

Do’s

Use HTTPS sources when available
Verify sources with checksums and PGP signatures
Use $pkgname and $pkgver variables in source URLs
List all direct dependencies even if transitively satisfied
Test in a clean chroot before submitting
Use --prefix=/usr for configure scripts
Include licenses in /usr/share/licenses/$pkgname/
Use install command instead of cp for proper permissions
Add backup array for config files in /etc

Don’ts

Never install to /usr/local - Arch packages use /usr
Never modify $srcdir outside of prepare()
Don’t use sudo in functions - makepkg handles privileges
Avoid custom variables without _ prefix - May conflict with makepkg
Don’t skip checksums without good reason
Don’t use replaces in AUR packages - Use conflicts instead
Don’t leave empty directories in packages
Don’t include .git directories in packages

Common Mistakes

Wrong: Installing with DESTDIR missing

package() {
    make install  # Files go to real system!
}

Right:

package() {
    make DESTDIR="$pkgdir" install
}

Wrong: Hardcoded paths

source=("https://example.com/myapp-1.0.0.tar.gz")

Right:

source=("https://example.com/${pkgname}-${pkgver}.tar.gz")

Wrong: Missing direct dependency

depends=('qt5-base')  # Missing qt5-svg even though app uses it

Right:

depends=('qt5-base' 'qt5-svg')  # Both are direct dependencies

Wrong: Version with hyphen

pkgver=1.0.0-beta1

Right:

pkgver=1.0.0_beta1

12. Quick Reference

Minimal PKGBUILD Checklist

☐ pkgname (lowercase, valid characters)
☐ pkgver (no hyphens)
☐ pkgrel=1
☐ pkgdesc (≤80 chars)
☐ arch=('x86_64') or arch=('any')
☐ url
☐ license (SPDX identifier)
☐ depends (all direct runtime deps)
☐ makedepends (build-only deps)
☐ source (with variables)
☐ sha256sums (or valid checksums)
☐ build() or package() function
☐ package() function (required)

Common Commands

# Build package
makepkg -s

# Build and install
makepkg -si

# Generate checksums
makepkg -g >> PKGBUILD
# or: updpkgsums

# Generate .SRCINFO
makepkg --printsrcinfo > .SRCINFO

# Check PKGBUILD with namcap
namcap PKGBUILD

# Check built package
namcap *.pkg.tar.zst

# Build in clean chroot
pkgctl build

# List package contents
pacman -Qlp *.pkg.tar.zst

# Install local package
sudo pacman -U *.pkg.tar.zst

File Locations

Location Purpose
/usr/bin/ Executables
/usr/lib/ Libraries
/usr/share/ Architecture-independent data
/usr/share/doc/$pkgname/ Documentation
/usr/share/licenses/$pkgname/ License files
/usr/share/man/ Man pages
/etc/ System configuration (add to backup array)

This guide consolidates information from the official Arch Linux Wiki and man pages. For the most up-to-date information, always refer to the official documentation.