Jérôme Belleman
Home  •  Tools  •  Posts  •  Talks  •  Travels  •  Graphics  •  About Me

Three Ways to Make DEB Packages

17 Aug 2016

Three different approaches to making DEB packages: the official one with debhelper, the simple one with dpkg-deb and the brutal one with CheckInstall.

Why in the name of everything that's holy does building packages have to be such a tedious process – often even more so than writing the packaged software itself? This is true of RPM packages as it is of DEB packages, and in the latter case the official Debian New Maintainers' Guide won't claim otherwise. Luckily though, there are more transparent, straightforward – and I'm sure some would call sacrilegious – ways to make DEB packages.

I've been trying to get an overview of three approaches here, considering the interesting case of building mutt.

1 The Official Way

Not unlike building RPMs, the official way to build DEBs involves using tools trying to guess what you want. As is most of the time the case in computing, they all fail miserably and, to make matters worse, they behave obscurely.

It all starts with running dh_make, which will create basic debian/{control,rules} files and a wealth of other ones you won't even want to see. In spite of the fact dh_make should be run from the source directory, it expects an original tarball named after a specific template, unless you specify one with -f:

tar xf mutt-1.6.2.tar.gz
cd mutt-1.6.2
dh_make -f ../mutt-1.6.2.tar.gz

At the prompt, answer that you want a single package (s) and that all details are correct. You'll find that dh_make will deem it wise to create another orig tarball, regardless of whether we already have one or not, for some reason. You may then edit the debian/control file:

Source: mutt
Section: mail
Priority: optional
Maintainer: John Doe <john.doe@example.com>
Build-Depends: debhelper (>=9),autotools-dev
Standards-Version: 3.9.6

Package: mutt
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: text-based mailreader supporting MIME, GPG, PGP and threading
 Mutt is a sophisticated text-based Mail User Agent.

... and the debian/rules file:

%:
    dh $@  --with autotools_dev

override_dh_auto_configure:
    ./configure --prefix=/usr --enable-imap --enable-pop --enable-smtp \
        --with-ssl --enable-hcache --with-gss

The override_dh_auto_configure target lets you override the way the build is configured. There other targets you can guess by typing dh_<Tab> in a shell, e.g. override_dh_auto_install, override_dh_clean, ...

Finally:

debuild

What's unpleasant about this approach is all those generated files you don't want, all those obscure commands, command wrappers, command wrapper wrappers you don't remember which one to run. I'm sure many of these commands mean good and try to guess what you need, or prevent you from making mistakes, but all they do in practice is get in the way.

It happens sometimes that debuild fails to run because something has somehow changed in the source directory. This is the gist of the problem: you haven't the foggiest why this should stop you building a package and how files get changed in the first place.

All this is just too obscure.

2 The Simple Way

From the official way, I tried to identify what was really done and stripped it all down to the bare minimum. Setting export DH_VERBOSE = 1 in debian/rules teaches you a lot. And the How to make a "Basic" .deb thread got me started.

Build the software as you normally would do:

tar xf mutt-1.6.2.tar.gz
cd mutt-1.6.2
./configure --prefix /usr \
    --enable-imap --enable-pop --enable-smtp \
    --with-ssl --enable-hcache --with-gss
make
fakeroot make DESTDIR=$PWD/../mutt-1.6.2-1 install
cd ..

The configure options are those we normally use to build mutt. Once we get to make install, we use a DESTDIR to divert the installation. Why not then set the prefix to the diverted directory instead and be done with it? Because some program builds such as mutt's expect from the prefix the location of some configuration files, which is why the prefix must be set to the final install location. Be sure to use absolute paths when specifying DESTDIR. Using relative paths may lead to files being installed into unexpected locations.

Installing into a diverted directory isn't what fakeroot does; fakeroot lets you perform file manipulations which would normally require root privileges. Again, that's useful when installing mutt which involves running chgrp.

Write a control file, to be kept in a DEBIAN directory:

mkdir mutt-1.6.2-1/DEBIAN
vim mutt-1.6.2-1/DEBIAN/control

This is a canonical control file, inspired from a Ubuntu package:

Package: mutt
Version: 1.6.2-1
Architecture: amd64
Maintainer: John Doe <john.doe@example.com>
Description: text-based mailreader supporting MIME, GPG, PGP and threading
 Mutt is a sophisticated text-based Mail User Agent.

Finally:

dpkg-deb -b mutt-1.6.2-1

It's transparent, efficient, flexible, straightforward.

3 The CheckInstall Way

This must be the wrappers of wrappers, which I would normally call evil. But as it turns out, it's a very convenient and versatile way of building packages. CheckInstall doesn't only do DEBs but also RPMs and more. What it does is to wrap around make install – or whichever means you use to install software, even just cp – and tracks where files go. From this it creates a package which you can use out of the box for future installations.

Build the package as usual:

tar xf mutt-1.6.2.tar.gz
cd mutt-1.6.2
./configure --prefix /usr \
    --enable-imap --enable-pop --enable-smtp \
    --with-ssl --enable-hcache --with-gss
make

Then:

sudo checkinstall -y \
    --pkgversion 1.6.2 \
    --maintainer 'John Doe \<john.doe@example.com\>' \
    --backup=no \
    make install

You need to be root, here, since the files will effectively be copied system-wide so CheckInstall has got something to track. There is an --install=no option which means the package won't be installed, but the files will be regardless, so you had better not use it to avoid confusion. The -y option is probably a good idea if you agree CheckInstall's defaults are reasonable and you don't want to be interactively asked about them.

Options such as --pkgversion and --maintainer let you set what you would normally have defined in a control file. Note how you need to escape some characters when specifying a maintainer. Setting --backup=no disables making backups of previous builds – why indeed would you need them?

You need to write the description in a description-pak file in CheckInstall's working directory, which is created with some defaults the first time you run it. The first line is used as the short description, the lines following immediately the longer one. The way the description in description-pak is parsed is a little odd in that if the second line contains too much of the first line, it will be ignored.

The fact that you need to be root to effectively install files for the sole purpose of building a package leaves me uncomfortable. True, you end up assuming privileges anyway when installing the package. But could it happen that you find yourself in a situation where files installed system-wide aren't tracked anymore?

4 Conclusion

We've established that the The Official Way is a right bind. The Simple Way is probably the way to go, a way that puritans would undoubtedly call outrageous, but it does work wonders. The CheckInstall Way I would call outrageous, but in fairness, it does work beautifully too in practice.

5 References