Packaging for dummies (1)

Packaging is one of the things I was trying to master several times. As always lack of condensed quick-start guides was putting me off. I do realize strict packaging rules exist for a reason but they're too complex to be thrown at beginners in introductory documents. That said I've finally shaved the yak and here's what you need to start packaging for Debian.

Case study

Packaging is important for several reasons I hopefully don't have to explain:

We'll focus on building package for Mongrel 2, which at the moment of writing is officially distributed only as a tarball. The process of manual building is rather simple thus well suited as an example.

Introduction to DEB packages

There are two types of packages:

Consists of a single file with .deb extension and specific format that contains our software binaries, docs, etc. along with some metadata and install hooks.

Consists of three files: package metadata in .dsc, .orig.tar.gz tarball with original software source and .diff.gz that contains Debian-specific changes to original source

In the following process will also see some metadata and control files:

One short note on package versioning rules:

package_version-revision_arch

This means our upstream mongrel2-1.7.5 becomes mongrel2_1.7.5-1_amd64 on 64-bit system. Given it's our first package revision, which clearly makes sense for the first package.

Building package

To assists with building, Debian community released several tools referred as packaging helpers. If you're starting a new package like here, just use dh. I assume you have a Debian or Ubuntu box, if not grab one with Vagrant.

Let's start with initial environment setup:

apt-get install build-essential debhelper dh-make

cat >> ~/.bashrc <<EOF
DEBEMAIL="your.email.address@example.org"
DEBFULLNAME="Firstname Lastname"
export DEBEMAIL DEBFULLNAME
EOF

. ~/.bashrc

Get a source tarball and convert it to Debian format, BSD licensed, single binary (single .deb) using helper:

mkdir mongrel2
cd mongrel2/
wget http://mongrel2.org/static/downloads/mongrel2-1.7.5.tar.bz2
tar -jxvf mongrel2-1.7.5.tar.bz2 
cd mongrel2-1.7.5/
dh_make -c bsd -s -f ../mongrel2-1.7.5.tar.bz2 

This produces ../mongrel2_1.7.5.orig.tar.bz2 (slightly renamed original source) and debian/ directory with metadata and control files mentioned above. Let's remove stuff we're not interested much in scope of this tutorial:

rm debian/*{.ex,.EX} debian/README.*

Let's start working with generated debian/control:

#!control
Source: mongrel2
Section: unknown
Priority: extra
Maintainer: Firstname Lastname <your.email.address@example.org>
Build-Depends: debhelper (>= 8.0.0)
Standards-Version: 3.9.2
Homepage: <insert the upstream URL, if relevant>

Package: mongrel2
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: <insert up to 60 chars description>
 <insert long description, indented with spaces>

You can find explanations of each of the these field in policy manual, but for now just fill in Homepage, Description. Most importantly we'll have to specify build and runtime dependencies (names of required packages from Debian repositories, optionally with versions). Finally we can edit Section to assign our package to one of the sections and change Priority to optional.

Target debian/control may look like this:

#!control
Source: mongrel2
Section: httpd
Priority: optional
Maintainer: Firstname Lastname <your.email.address@example.org>
Build-Depends: debhelper (>= 8.0.0), sqlite3, libzmq1, libzmq-dev, libsqlite3-dev
Standards-Version: 3.9.2
Homepage: http://mongrel2.org

Package: mongrel2
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, sqlite3, libzmq1
Description: Application, language and network architecture agnostic web server.
 Mongrel2 is an application, language, and network architecture agnostic web 
 server that focuses on web applications using modern browser technologies.
 It supports 17 languages and platforms, HTTP, Flash sockets, WebSockets,
 Long Polling and many ways to deploy and hack on it.

Next we have to adjust debian/rules which is responsible for building our software. By default it has following contents:

#!makefile
#!/usr/bin/make -f

%:
        dh $@

If we wanted to build software that used autotools we could have left it untouched. This tarball however ships with simpler build procedure that can be expressed in the following commands:

make all
make install

Fortunately dh allows us to override it's hooks. Resulting debian/rules looks like this:

#!makefile
#!/usr/bin/make -f

%:
        dh $@ 

override_dh_auto_configure:

override_dh_auto_build:
        $(MAKE) all

override_dh_auto_clean:
        $(MAKE) clean

The process of building has some more restrictions though:

Mongrel's Makefile violates second rule by installing it's binaries into /usr/local. We have to fix this by recording our first patch. First fix the offending line then call another helper which compares modifications to original source:

sed -e 's/PREFIX?=\/usr\/local/PREFIX?=\/usr/' -i Makefile
dpkg-source --commit

Name a patch and provide relevant description of changes. In my case patchfile debian/patches/fhs_prefix.patch was created with following contents:

#!diff

--- mongrel2-1.7.5.orig/Makefile
+++ mongrel2-1.7.5/Makefile
@@ -1,6 +1,6 @@ CFLAGS=-g -O2 -Wall -Wextra -Isrc -pthread -rdynamic -DNDEBUG $(OPTFLAGS) -D_FILE_OFFSET_BITS=64 LIBS=-lzmq -ldl -lsqlite3 $(OPTLIBS)
-PREFIX?=/usr/local
+PREFIX?=/usr

get_objs = $(addsuffix .o,$(basename $(wildcard $(1))))

You can verify that this patch is present in debian/patches/series as a candidate for application.

Rest of files is easy to modify therefore I'll present only the final versions.

Final debian/changelog:

mongrel2 (1.7.5-1) unstable; urgency=low

  * Initial release

 -- Firstname Lastname <your.email.address@example.org>  Fri, 03 Aug 2012 12:52:05 +0000

Final debian/copyright:

#!control
Format: http://dep.debian.net/deps/dep5
Upstream-Name: mongrel2
Source: <url://mongrel2.org>

Files: *
Copyright: 2010 Zed A. Shaw and Mongrel2 Project Contributors
License: BSD-3-Clause

Files: debian/*
Copyright: 2012 Firstname Lastname <your.email.address@example.org>
License: BSD-3-Clause

License: BSD-3-Clause 
  Redistribution and use in source and binary forms, with or without (...)

Now it's time to build that damn (unsigned) package!

sudo apt-get install libsqlite3-dev libzmq-dev libsqlite3
dpkg-buildpackage -b -uc -us -rfakeroot

Congratulations! You can grab your package from ../mongrel2_1.7.5-1_amd64.deb and happily start using it.

Updating package

Let's say after some time you find out that your package is missing important dependency. For example someone uninstalled uuid-runtime used by m2sh for generating UUIDs and package manager didn't protest.

In such situation you just fix package dependencies or whatever the mistake is and update changelog entry with revision bump. Next just restart build:

dch -i "Added missing uuid-runtime dependency"
dpkg-buildpackage -b -uc -us -rfakeroot

Your updated package is now at ../mongrel2_1.7.5-2_amd64.deb.

Further reading

This was just a starter. It gives you a working package but it's just a tip of the iceberg. I urge you to at least skim through maintainer's guide. If you'd like to learn more on the topic of Debian packaging follow also the links below:

Packaging for Ubuntu doesn't differ on basic level from Debian. They have though a slightly different workflow built around Launchpad and Bazaar. You can read on this topic in their guide.

What's next?

In next episode we'll go through RPM build process. Happy packaging!