Skip to content

Instantly share code, notes, and snippets.

@Earnestly
Last active March 22, 2024 03:48
Show Gist options
  • Star 43 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save Earnestly/bebad057f40a662b5cc3 to your computer and use it in GitHub Desktop.
Save Earnestly/bebad057f40a662b5cc3 to your computer and use it in GitHub Desktop.
A brief overview of the process involved in creating a pacman package.

A Brief Tour of the Makepkg Process: What Makes a Pacman Package

Introduction

This is a terse document covering the anatomy of a package built for the pacman package manager.

The following example commands can mostly run verbatim to manually create a package without makepkg.

Building

build()

This is used for the creation and generation of needful resources and files. This function will create the src directory which is referred to via the $srcdir variable.

A typical procedure for most projects might look like this:

mkdir src
# Extract the source into src
cd src
./configure --prefix=/usr
make

Installation

package()

The separation of build and installation happens here. Fakeroot is used to maintain appropriate permissions while not actually running as root. That is, the facade of root permission is maintained so long real root privilege is not needed.

mkdir pkg
cd src/program
fakeroot -- make DESTDIR=../../pkg install

Metadata

The meat of the data pacman depends on is now generated, this includes a simple key value pair file called .PKGINFO and an "mtree", the .MTREE.

A .PKGINFO is just a simple collection of what one would express in the PKGBUILD, almost exactly. Keys with multiple entries are simply repeated.

cd pkg
cat << EOF > .PKGINFO
pkgname = $pkgname
pkgver = $pkgver-$pkgrel
pkgdesc = $pkgdesc

url = $url
license = $license

builddate = $(date -u '+%s')
size = $(du -sb --apparent-size | awk '{print $1}')

arch = $(uname -m)

depend = libfoo
depend = libbar
depend = libbaz

makedepend = buildlibfoo
makedepend = buildlibbar
makedepend = buildlibbaz
EOF

An mtree is essentially a way to generate a map of a directory structure with all kinds of attributes included, such as permissions, uids, etc. This allows pacman to easily know what the attributes should be so that any issues can be cross-checked when using the -Qk option.

Bsdtar is then used to generate the .MTREE file. This disables !all of the attributes and then enables a selected few.

When creating the mtree, the .PKGINFO file needs to be first in the archive.

cd pkg
fakeroot -- env LANG=C bsdtar -czf .MTREE --format=mtree --options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' .PKGINFO *

Package

All that remains is to generate a tarball of our package. We use fakeroot again as with everything during the package phase.

cd pkg
fakeroot -- env LANG=C bsdtar -cf - .MTREE .PKGINFO * | xz -c -z - > $pkgname-$pkgver-$pkgrel-$arch.tar.xz

Example

#!/bin/sh --
# Basic makepkg which just creates a package for antimicro, built from git.

# Because I don't do any error handling, just bail if any command fails for
# any reason.
set -o errexit

startdir=$PWD
srcdir=$startdir/src
pkgdir=$startdir/pkg

# The pkgrel just indicates the version of the build itself, independent of
# the pkgver, although a pkgver bump resets the pkgrel to 1.
pkgrel=1
arch=$(uname -m)

# Check for all dependencies.  This command will return any which are
# missing, each one on a newline.
pacman -T cmake qt5-tools libxtst qt5-base sdl2 libxkbcommon-x11

# Build.
mkdir -p "$srcdir"
cd "$srcdir"
git clone https://github.com/AntiMicro/antimicro

cd antimicro

# No hyphens allowed in the version.
pkgver=$(git describe --long --tags | sed 's/^v//; s/\([^-]*-g\)/r\1/; s/-/./g')
cmake -DMAKE_INSTALL_PREFIX=/usr -DUSE_SDL2=ON
make

# Installation.
mkdir -p "$pkgdir"
fakeroot -- make DESTDIR="$pkgdir" install

# Package.
cd "$pkgdir"
cat <<! > .PKGINFO
pkgname = antimicro-git
pkgver = $pkgver-$pkgrel
pkgdesc = map keyboard and mouse actions to gamepad buttons, inspired by qjoypad
url = https://github.com/AntiMicro/antimicro
builddate = $(date -u +%s)
packager = Unknown Packager
size = $(du -sb --apparent-size "$pkgdir" | awk '{print $1}')
arch = $arch
license = GPL
conflict = antimicro
provides = antimicro
depend = libxtst
depend = qt5-base
depend = sdl2
depend = libxkbcommon-x11
makedepend = cmake
makedepend = qt5-tools
!

fakeroot -- env LANG=C bsdtar -czf .MTREE --format=mtree --options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' .PKGINFO *
fakeroot -- env LANG=C bsdtar -cf - .MTREE .PKGINFO * | xz -c -z - > "$startdir"/antimicro-git-"$pkgver"-"$pkgrel"-"$arch".pkg.tar.xz

# Test.
cd "$startdir"
namcap -m antimicro-git-"$pkgver"-"$pkgrel"-"$arch".pkg.tar.xz
@stigok
Copy link

stigok commented Jun 3, 2017

This document is outdated.
Read the manpage for MAKEPKG for an up-to-date overview. https://www.archlinux.org/pacman/PKGBUILD.5.html

@Earnestly
Copy link
Author

@stigok I assume you're aware that this is an example to demonstrate how a simple pacman compatible package is produced without the use of makepkg? Although I'm not going to bother including the new .BUILDINFO files just for this demonstration.

With that in mind, what do you find is outdated?

@ajaym62
Copy link

ajaym62 commented Dec 19, 2018

I thought this was pretty useful. My app is cross-built on Ubuntu for archlinux (don't ask ...). What I need to do is build the package for install/upg/remove only, so this was quite instructive.

One thing I can't get working is the .INSTALL file containing pre/post bash functions for remove/install/upg. How is that handled by makepkg?

Thanks!

@Earnestly
Copy link
Author

Earnestly commented Dec 31, 2018

@ajaym62 Hm, I don't seem to get notifications from gist and so seeing these comments are pure chance. isaacs/github#21

As for .INSTALL, it should work if included as the other files such as .PKGINFO. makepkg itself doesn't transform the foo.install script (which can be bash or sh, depending on how pacman is compiled) so the PKGBUILD(5) manual should be appropriate.

Note that this example needs to be updated for SOURCE_DATE_EPOCH usage and perhaps .BUILDINFO. I should also use a more complex example which includes things like install scripts and patches.

@leap0x7b
Copy link

Can you parse .PKGINFO in Bash?

@Earnestly
Copy link
Author

Sure, it's just key = value with duplicate keys representing multiple values per key. Just about the easiest format to parse.

@greg-minshall
Copy link

@leapofazzam123 you might want to parse the .SRCINFO instead of the PKGINFO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment