Three Ways to Make DEB Packages
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.