Debian Packaging by Example

John Tucker
7 min readSep 30, 2024

--

It seems odd to be writing an article about Debian packaging in 2024 when it dates back to 1993; some 30 years ago. But… I tried to follow the the official tutorial on the topic, Packaging Intro, and found myself horribly confused.

So, here I write a tutorial based on the official tutorial, but elaborate on topics as necessary.

What is a Package?

A Debian package is a collection of files that allow for applications or libraries to be distributed via the package management system. The aim of packaging is to allow the automation of installing, upgrading, configuring, and removing computer programs for Debian in a consistent manner. A package consists of one source package, and one or more binary packages.

The first thing that confused me was the distinction between a source and binary package. From Packaging BinaryPackage:

A Debian package is a file that ends in .deb and contains software for your Debian system.

A .deb is also known as a binary package. This means that the program inside the package is ready to run on your system.

and from Packaging SourcePackage:

Source packages provide you with all of the necessary files to compile or otherwise, build the desired piece of software.

The important bit of context that helps illustrate this difference is that Debian packaging is historically focused on software written in the C programming language. Here have a simple source file, hithere.c:

#include <stdio.h>

int main()
{
printf("Hi there!\n");
return 0;
}

In order to run this software, one must first compile it into an architecture-specific (e.g., amd64 for the machine I was using for this tutorial) binary file, named hithere, using:

cc -o hithere hithere.c

When creating a Debian package for this software, the binary package would contain the hithere binary file and the source package would contain the hithere.c source file.

Requirements

To follow along, you will need a machine running a Debian-based operating system, e.g., the popular Ubuntu. This tutorial was written using a Google Cloud GCE instance based on the Canonical, Ubuntu, 24.04 LTS Minimal, amd64 noble minimal image built on 2024–09–26 image.

The machine need a number of Debian packages and can be installed using:

sudo apt update
sudo apt install build-essential -y
sudo apt install devscripts -y
sudo apt install debhelper -y

Upstream Tarball

The starting point of creating a Debian package is an upstream tarball.

A tarball is the .tar.gz or .tgz file upstream makes (can also be in other compression formats like .tar.bz2,.tb2 or .tar.xz).

Contains the software upstream developer has written.

But, it is not clear what the requirements are for this tarbar; even the UpstreamGuide is not overly helpful. I ended up reverse-engineering the official tutorial’s upstream tarball to guess at them.

The package, named hello-world, we are going to create primarily consists of a Bash script; hello.

#!/usr/bin/env bash

echo 'Hello World!'

Installing this package should copy this file into the /usr/bin folder and make it executable.

note: One rabbit hole that I went down was trying to copy this file into the /usr/local/bin folder which is not allowed; the UpstreamGuide has guidance on the appropriate folders to use.

Next, following the pattern of the official tutorial, we start by creating a folder, hello-world-1.0, and copy the hello script into it.

The less obvious requirement is having a Makefile in this folder:

all:
echo 'no op build rule'

install:
install hello $(DESTDIR)/usr/bin

Google’s Search Labs AI describes makefiles; I also found the tutorial Learn Makefiles useful.

A makefile is a text file that contains instructions for building a program by compiling and linking source code files. It’s a way to automate the process of building software and other complex tasks.

Some observations:

  • The indents are a single tab; not spaces
  • The first (default) rule is used to build the software; while not needed for Bash scripts, we need to supply a no op rule
  • The second, install target, rule is used to install the software
  • It turns out that the use of make’s DESTDIR (described below) is required

Prepending the variable DESTDIR to each target in this way provides for staged installs, where the installed files are not placed directly into their expected location but are instead copied into a temporary location (DESTDIR). However, installed files maintain their relative directory structure and any embedded file names will not be modified.

At this point, we could install the software by running:

sudo make install

note: If you run this command, you will want to remove the file /usr/bin/hello before continuing.

We then create the upstream tarball, hello-world_1.0.orig.tar.gz, from the parent folder of the hello-world-1.0 folder:

tar -czvf hello-world_1.0.orig.tar.gz hello-world-1.0/

Some observations:

  • The naming of this tarball is very particular [package-name]_[version].orig.tar.gz (notice the underscore)

At this point, the top level folder has the folder with its files we created and the tarball of it in it.

Add the Debian Packaging Files

Next we are going to create a number of folders and files in the folder, hello-world-1.0, that we used to create the upstream tarball.

First we a debian folder with a file named control with the following contents; changing the Maintainer to be your name and email.

Source: hello-world
Maintainer: First Last <someone@somewhere.com>
Section: misc
Priority: optional
Standards-Version: 4.7.0
Build-Depends: debhelper-compat (= 13)

Package: hello-world
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: greet user
hithere greets the user, or the world.

From Control files and their fields:

The debian/control file contains the most vital (and version-independent) information about the source package and about the binary packages it creates.

note: The details of the contents of this file, and the other files in the debian folder, are outside the scope of this article.

Next we create a file changelog using the following command (changing the email); the version 1.0–1 is interpreted as version 1.0 of the upstream and 1 of the Debian packaging files for that upstream version.

export EMAIL=someone@somewhere.com; dch --create -v 1.0-1 --package hello-world

From Control files and their fields:

Every source package must include the Debian changelog file, debian/changelog. Changes in the Debian version of the package should be briefly explained in this file. 3 This includes modifications made in the Debian package compared to the upstream one as well as other changes and updates to the package

We create an empty copyright file.

We create a rules file with the following content:

#!/usr/bin/make -f
%:
dh $@

note: The indents are a single tab; not spaces.

From Control files and their fields:

The recommended way to implement the build process of a Debian package, in the absence of a good reason to use a different approach, is the dh tool. This includes the contents of the debian/rules building script. dh is the most common packaging helper tool in Debian. Using it will usually save effort in complying with the rules in this document, because dh will automatically implement many of them without requiring explicit instructions.

We create a source folder with a file, format, with the following content:

3.0 (quilt)

Google’s Search Labs AI describes the source/format file:

The purpose of the source format file in Debian is to specify the format of a package and how it should be built.

Next we create the file hello-world.dirs with the content:

usr/bin

From Chapter 5. Other files under the debian directory:

This file specifies any directories which we need but which are not created by the normal installation procedure

Here we need to create a relative folder usr/bin as part of the binary package; the folder /usr/bin which the package installs into, however, is alway expected to exist on a Debian machine.

At this point we have the following folders and files:

If we got all this right, we can successfully run the command to build the Debian package.

debuild -us -uc

The build generates a number of additional files; some more important than others:

 hello-world-1.0/debian/.debhelper/
hello-world-1.0/debian/debhelper-build-stamp
hello-world-1.0/debian/files
hello-world-1.0/debian/hello-world.substvars
hello-world-1.0/debian/hello-world/
hello-world_1.0-1.debian.tar.xz
hello-world_1.0-1.dsc
hello-world_1.0-1_amd64.build
hello-world_1.0-1_amd64.buildinfo
hello-world_1.0-1_amd64.changes
hello-world_1.0-1_amd64.deb

The Binary Package

The file, hello-world_1.0–1_amd64.deb, is the binary package (here for the amd64 architecture). We can install it onto our machine using:

sudo dpkg -i ./hello-world_1.0-1_amd64.deb

If we got things right, we can run the command hello and get the output Hello World!.

As we are essentially done, you can remove the package using the following command.

sudo apt remove hello-world

The Source Package

In this example, the source package consists of three files:

  • hello-world_1.0.orig.tar.gz: The upstream tarball
  • hello-world_1.0–1.dsc: A package description file; contains much of the metadata in the control file
  • hello-world_1.0–1.debian.tar.xz: A tarball of the files we created in the debian folder

Wrap Up

Wow; that was more than I expected and we even glazed over the contents of the Debian packaging files.

--

--

John Tucker
John Tucker

Written by John Tucker

Broad infrastructure, development, and soft-skill background

No responses yet