+
+
ember
+
+
xgboost
<-
prettier
hapi
+
sqlite
!=
+
qwik
argocd
+
+
deno
symfony
cobol
λ
ocaml
+
+
vim
+
adonis
hack
numpy
git
+
+
junit
ractive
+
npm
+
+
wsl
+
+
+
+
+
gatsby
abap
+
+
+
+
+
+
qwik
puppet
gcp
scala
+
crystal
html
+
+
!==
+
+
+
+
pycharm
java
fiber
>=
bundler
oauth
+
+
+
+
!==
+
argocd
+
+
+
+
+
+
+
+
+
Back to Blog
Creating Alpine Linux APKBUILD Files: Master Package Development
Alpine Linux APKBUILD Package Development

Creating Alpine Linux APKBUILD Files: Master Package Development

Published Jun 11, 2024

Learn to write proper APKBUILD files for Alpine Linux packages. This guide covers syntax, functions, dependencies, and best practices for package maintainers.

16 min read
0 views
Table of Contents

Creating Alpine Linux APKBUILD Files: Master Package Development

I’ll show you how to write proper APKBUILD files for Alpine Linux packages. After maintaining dozens of packages for Alpine, I’ve learned the ins and outs of creating clean, maintainable build scripts that work reliably.

Introduction

APKBUILD files are the heart of Alpine’s package system. They’re shell scripts that tell the package manager how to download, compile, and install software. Think of them as recipes - they need to be precise, complete, and reliable.

I’ve been writing APKBUILD files for years, and the format is actually pretty elegant once you understand the structure. Alpine’s approach keeps things simple while being incredibly powerful.

Why You Need This

  • Create custom packages for your organization
  • Contribute packages to Alpine Linux repositories
  • Maintain internal software distributions
  • Understand Alpine’s packaging philosophy

Prerequisites

You’ll need these things first:

  • Alpine Linux development environment
  • Basic understanding of shell scripting
  • Familiarity with build systems (make, cmake, etc.)
  • Knowledge of the software you’re packaging

Step 1: Understanding APKBUILD Structure

Basic APKBUILD Anatomy

Let’s start with the fundamental structure every APKBUILD needs.

What we’re doing: Creating a minimal but complete APKBUILD template.

# Create a new package directory
mkdir -p ~/aports/testing/example-app
cd ~/aports/testing/example-app

# Create basic APKBUILD
cat > APKBUILD << 'EOF'
# Contributor: Your Name <[email protected]>
# Maintainer: Your Name <[email protected]>
pkgname=example-app
pkgver=1.0.0
pkgrel=0
pkgdesc="Example application for demonstration"
url="https://github.com/example/example-app"
arch="all"
license="MIT"
depends=""
makedepends="gcc libc-dev make"
install=""
subpackages="$pkgname-dev $pkgname-doc"
source="$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz"
builddir="$srcdir/$pkgname-$pkgver"

build() {
    cd "$builddir"
    make
}

check() {
    cd "$builddir"
    make test
}

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

sha512sums="
abcd1234...  example-app-1.0.0.tar.gz
"
EOF

Code explanation:

  • pkgname: Package name (must match directory name)
  • pkgver: Upstream version number
  • pkgrel: Alpine-specific package release number
  • arch: Supported architectures (“all”, “x86_64”, “aarch64”, etc.)
  • depends: Runtime dependencies
  • makedepends: Build-time dependencies only

Variable Definitions

What we’re doing: Understanding key APKBUILD variables and their purposes.

# Package metadata
pkgname=myapp               # Package name
pkgver=2.1.0               # Version from upstream
pkgrel=1                   # Increment for Alpine changes
pkgdesc="Brief description" # One-line package description
url="https://example.com"   # Project homepage

# Architecture support
arch="all"                 # Builds on all architectures
arch="x86_64 aarch64"     # Specific architectures only
arch="noarch"              # Architecture-independent

# Dependencies
depends="python3 py3-requests"              # Runtime dependencies
makedepends="python3-dev py3-setuptools"    # Build dependencies
checkdepends="py3-pytest"                   # Test dependencies

Variable explanation:

  • pkgrel: Reset to 0 for new upstream versions, increment for Alpine-specific changes
  • arch="all": Package builds and works on all Alpine architectures
  • Dependencies use Alpine package names, not upstream names

Tip: I always check what other similar packages use for dependencies. The Alpine package database is your friend here.

Step 2: Writing Build Functions

The build() Function

What we’re doing: Writing a proper build function that compiles source code.

build() {
    cd "$builddir"
    
    # Configure build (autotools example)
    ./configure \
        --prefix=/usr \
        --sysconfdir=/etc \
        --mandir=/usr/share/man \
        --localstatedir=/var \
        --disable-static \
        --enable-shared
    
    # Build the software
    make
}

For CMake projects:

build() {
    cd "$builddir"
    
    cmake -B build \
        -DCMAKE_INSTALL_PREFIX=/usr \
        -DCMAKE_INSTALL_LIBDIR=lib \
        -DBUILD_SHARED_LIBS=True \
        -DCMAKE_BUILD_TYPE=RelWithDebInfo
    
    cmake --build build
}

For Python projects:

build() {
    cd "$builddir"
    python3 setup.py build
}

Build function explanation:

  • Always use cd "$builddir" first
  • Use standard Alpine paths (/usr, /etc, /var)
  • Disable static libraries unless specifically needed
  • Use RelWithDebInfo for debugging symbols

The check() Function

What we’re doing: Adding test execution to ensure package quality.

check() {
    cd "$builddir"
    
    # Run test suite
    make check
    
    # For Python packages
    python3 -m pytest
    
    # For CMake projects
    cmake --build build --target test
    
    # Custom test commands
    ./build/bin/myapp --version
    ./build/bin/myapp --self-test
}

Code explanation:

  • check() function is optional but highly recommended
  • Tests should run quickly and not require network access
  • Test failures should cause package build to fail
  • Include basic functionality tests when possible

The package() Function

What we’re doing: Installing files into the package directory structure.

package() {
    cd "$builddir"
    
    # Standard make install
    make DESTDIR="$pkgdir" install
    
    # For CMake projects
    DESTDIR="$pkgdir" cmake --install build
    
    # For Python projects
    python3 setup.py install --prefix=/usr --root="$pkgdir"
    
    # Manual file installation
    install -Dm755 myapp "$pkgdir/usr/bin/myapp"
    install -Dm644 myapp.conf "$pkgdir/etc/myapp/myapp.conf"
    install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md"
}

Installation explanation:

  • DESTDIR="$pkgdir": Redirects installation to package directory
  • install -Dm755: Sets file permissions (755 for executables)
  • install -Dm644: Sets file permissions (644 for regular files)
  • Always use absolute paths starting with $pkgdir

Step 3: Advanced APKBUILD Features

Subpackages

What we’re doing: Creating multiple packages from one APKBUILD.

# Define subpackages
subpackages="$pkgname-dev $pkgname-doc $pkgname-lang $pkgname-openrc"

# Development package function
dev() {
    pkgdesc="$pkgdesc (development files)"
    depends="$pkgname=$pkgver-r$pkgrel"
    
    amove usr/include
    amove usr/lib/pkgconfig
    amove usr/lib/*.so
}

# Documentation package function
doc() {
    pkgdesc="$pkgdesc (documentation)"
    install_if="$pkgname=$pkgver-r$pkgrel docs"
    
    amove usr/share/doc
    amove usr/share/man
}

# Language package function
lang() {
    pkgdesc="$pkgdesc (translations)"
    install_if="$pkgname=$pkgver-r$pkgrel lang"
    
    amove usr/share/locale
}

# OpenRC service scripts
openrc() {
    pkgdesc="$pkgdesc (OpenRC init scripts)"
    install_if="$pkgname=$pkgver-r$pkgrel openrc"
    
    amove etc/init.d
}

Subpackage explanation:

  • amove: Moves files from main package to subpackage
  • install_if: Automatically installs subpackage when conditions are met
  • depends: Ensures subpackage depends on main package

Source Handling

What we’re doing: Managing source files, patches, and checksums.

# Multiple source files
source="
    $pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz
    fix-compilation.patch
    myapp.initd
    myapp.confd
    "

# Different source types
source="
    https://example.com/releases/$pkgname-$pkgver.tar.gz
    $pkgname.git::git+https://github.com/user/repo.git?tag=v$pkgver
    fix-musl.patch
    "

# Checksum verification
sha512sums="
abc123...  example-app-1.0.0.tar.gz
def456...  fix-compilation.patch
789ghi...  myapp.initd
jkl012...  myapp.confd
"

Source handling tips:

  • Use :: to rename downloaded files
  • Git sources use special syntax with branch/tag specification
  • Always run abuild checksum to generate checksums
  • Keep patches small and well-documented

Conditional Logic

What we’re doing: Adding architecture and option-specific behavior.

# Architecture-specific dependencies
case "$CARCH" in
    x86_64) makedepends="$makedepends nasm" ;;
    aarch64) makedepends="$makedepends libffi-dev" ;;
esac

# Optional features
_with_systemd=no
[ "$_with_systemd" = yes ] && makedepends="$makedepends systemd-dev"

# Version-specific patches
case "$pkgver" in
    1.*) source="$source old-version.patch" ;;
    2.*) source="$source new-version.patch" ;;
esac

build() {
    cd "$builddir"
    
    local _configure_args="
        --prefix=/usr
        --sysconfdir=/etc
        "
    
    # Conditional build options
    if [ "$_with_systemd" = yes ]; then
        _configure_args="$_configure_args --enable-systemd"
    fi
    
    ./configure $_configure_args
    make
}

Practical Examples

Example 1: C Application with Autotools

What we’re doing: Creating an APKBUILD for a traditional C application.

# Contributor: Your Name <[email protected]>
# Maintainer: Your Name <[email protected]>
pkgname=htop
pkgver=3.2.1
pkgrel=0
pkgdesc="Interactive process viewer"
url="https://htop.dev/"
arch="all"
license="GPL-2.0-or-later"
makedepends="autoconf automake libtool ncurses-dev"
subpackages="$pkgname-doc"
source="https://github.com/htop-dev/htop/archive/$pkgver.tar.gz"
builddir="$srcdir/$pkgname-$pkgver"

prepare() {
    default_prepare
    cd "$builddir"
    autoreconf -fiv
}

build() {
    cd "$builddir"
    ./configure \
        --prefix=/usr \
        --sysconfdir=/etc \
        --mandir=/usr/share/man \
        --enable-unicode
    make
}

check() {
    cd "$builddir"
    make check
}

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

sha512sums="
abc123def456...  3.2.1.tar.gz
"

Example 2: Python Package

What we’re doing: Packaging a Python application with proper Alpine conventions.

# Contributor: Your Name <[email protected]>
# Maintainer: Your Name <[email protected]>
pkgname=py3-requests
_pkgname=requests
pkgver=2.28.1
pkgrel=0
pkgdesc="Python HTTP library"
url="https://requests.readthedocs.io/"
arch="noarch"
license="Apache-2.0"
depends="python3 py3-certifi py3-charset-normalizer py3-idna py3-urllib3"
makedepends="py3-setuptools"
checkdepends="py3-pytest py3-pytest-httpbin"
source="https://files.pythonhosted.org/packages/source/${_pkgname:0:1}/$_pkgname/$_pkgname-$pkgver.tar.gz"
builddir="$srcdir/$_pkgname-$pkgver"

build() {
    cd "$builddir"
    python3 setup.py build
}

check() {
    cd "$builddir"
    # Skip tests requiring network
    python3 -m pytest -k "not test_https_warnings"
}

package() {
    cd "$builddir"
    python3 setup.py install --prefix=/usr --root="$pkgdir"
}

sha512sums="
def789ghi012...  requests-2.28.1.tar.gz
"

Troubleshooting

Common Build Failures

Problem: Package fails to build with missing dependencies Solution: Check and add required makedepends

# Check what files are missing
abuild -r 2>&1 | grep -i "not found"

# Find which package provides missing files
apk search cmd:gcc
apk search so:libssl.so

# Add to makedepends
makedepends="gcc libc-dev openssl-dev"

Checksum Mismatches

Problem: Source file checksums don’t match Solution: Regenerate checksums

# Update checksums automatically
abuild checksum

# Manually verify source files
sha512sum source-file.tar.gz

# Check if source file changed upstream
wget -O - URL | sha512sum

Installation Path Issues

Problem: Files installed in wrong locations Solution: Fix configure options and install commands

# Check where files are being installed
make -n DESTDIR=/tmp/test install

# Use proper Alpine paths
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var

# Fix manual installations
install -Dm755 binary "$pkgdir/usr/bin/binary"

Best Practices

  1. Package Naming:

    # Use consistent naming
    pkgname=myapp              # C/C++ applications
    pkgname=py3-mylib          # Python 3 libraries
    pkgname=go-myapp           # Go applications
    pkgname=perl-my-module     # Perl modules
  2. Version Management:

    • Follow upstream versioning exactly
    • Use pkgrel for Alpine-specific changes
    • Document version changes in commit messages
  3. Security Practices:

    • Always verify source checksums
    • Keep dependencies minimal
    • Use secure download URLs (HTTPS)
    • Review patches carefully

Verification

To verify your APKBUILD is working correctly:

# Check APKBUILD syntax
abuild sanitycheck

# Test build process
abuild -r

# Check package contents
tar -tzf ~/packages/testing/x86_64/myapp-1.0.0-r0.apk

# Install and test package
sudo apk add ~/packages/testing/x86_64/myapp-1.0.0-r0.apk

Wrapping Up

You just learned how to create proper APKBUILD files:

  • Understood the basic structure and required variables
  • Mastered build, check, and package functions
  • Learned advanced features like subpackages and conditionals
  • Practiced with real-world examples
  • Troubleshot common build issues

Writing good APKBUILD files takes practice, but following these patterns will give you a solid foundation. The Alpine packaging system is really well designed - once you get the hang of it, creating packages becomes straightforward and reliable.