simple debian packaging

Preamble

When I started to become smart enough with coding that I felt ready for this next step, diving into debian packaging was a rather horrible experience as I recall. Maybe I followed the wrong tutorials or didn't search enough. Or maybe there weren't any easy ones for beginners back then that suited my needs and state of knowledge. Anyway, this is intended to be the tutorial I would have needed when I wanted to start distributing my software in debian packages.

The following is my current typical workflow when writing and packaging "serious" software. Others might find it wrong, I don't. :-)

software distribution

When you're writing open source software as a hobby (like me) and like the idea of sharing it, you will probably come to the point where you ask yourself how to distribute it properly. You might want your software to be

  1. easily distributed by you
  2. easily accessible by others
  3. easily installed
  4. easily removed (!) as well
  5. maybe even able to receive updates

The first three points might seem the most important to you:

» I want that I can hand people some file or archive that they can execute or extract and my software just works like in my development environment! «

Now depending on what software you write there are various methods for acquiring this goal. Most of the big scripting languages like Python, Perl or R for example have an own package system. So If you just write modules for such languages, you're probably good to go with that. But if you're on a software project that uses various parts of the whole operating system - e.g. multiple languages in combination, systemd, dbus, per-user configuration files, etc... - you will need something else.

You could for example write a script or a Makefile that does the build process and installation for you. Which should not be too hard, because installation is actuallly nothing else than copying files to the right place. Maybe with a bit of preprocessing depending on the target environment.

So you give your friends this hacky script you wrote that they probably have to execute with super user privileges and they got your software running. A couple of weeks later, you release the next version with new folder structure and a bunch of new features and again hand it to your friends. They, in turn, execute your new installation script which now unfortunately installs the new version next to the old version. Your friends now have your files all over the place on their machines because the old version was not removed which is why - what a pity - the new version does not work anymore. One of them calls you for help, you tell them to remove the old version's files - with super user privileges of course - which results in quite a mess. Eventually, you have to come over and fix their system by hand. Meh..., not optimal!

This summarizes - in a nutshell - problems people had before there were sophisticated package management systems like dpkg or rpm. But now that we have them, you can spare yourself the pain and enjoy the ease of use!

Since this is an article on debian packaging and I don't know rpm so well, we're focussing on debian packages here 8-].

debian packages

A proper debian package knows:

So with a simple file hosting service where you upload your debian package, we have all points of our list from the beginning checked. You may even make it more comfortable with hosting an own apt repository, enabling you to serve automatic updates.

creating a debian package

Now let's get to creating a debian package - aka .deb-file.

sorting your files

First, take your software into a folder and create a structure of directories according to the filesystem hierarchiy standard where you sort your files into. For example:

purpose directory
executables usr/bin
libraries/modules usr/lib/PKGNAME
documentation/manpages usr/share/doc/PKGNAME
configuration files etc/PKGNAME

This structure along with the files will later be installed to the end-user's system. Starting directly with this structure keeps things clean. Sometimes it's not clear where to put a specific file. If you have trouble choosing the right location, look at similar packages/programs and check where they install comparable files to.

Let's assume we wanted to package a simple shell script called insult that just insults some pre-configured people. We're going to name the package insult same as the executable. So we have the following directory structure:

> tree
. # the folder where you work in
├── etc
│   └── insult
│       ├── insult.txt # configuration file
│       └── people.txt # configuration file
└── usr
    ├── bin
    │   └── insult # executable
    ├── lib
    │   └── insult
    │       └── functions.sh # library
    └── share
        └── doc
            └── insult
                └── insult.1 # manpage

9 directories, 5 files

We have the executable usr/bin/insult:

#!/bin/sh
SANDBOX=$(dirname $0)/../..

# source the functions
. $SANDBOX/usr/lib/insult/functions.sh

# insult every configured person
for person in $(cat $SANDBOX/etc/insult/people.txt);do
    insult_someone $person
done

All it does is source the shell function library usr/lib/insult/functions.sh and then call the insulting function on all pre-configured people.

The library usr/lib/insult/functions.sh:

#!/bin/sh
# function to insult someone
# expects name as first argument
# the insulting text from the configuration file
# /etc/insult/insult.txt
# is appended to the name and printed to the console
insult_someone () {
echo "$1 $(cat $SANDBOX/etc/insult/insult.txt)"
}

The configuration file etc/insult/people.txt:

Emma
Peter
Joe

The configuration file etc/insult/insult.txt:

is very not intelligent!

The manpage usr/share/doc/insult/insult.1:

.\" Manpage for insult


.TH insult 1 "October 11, 2016" "0.0.1" "insult"


.SH NAME

insult \- script to output insulting text for pre-configured people.

.SH DESCRIPTION

.B insult
is a script to output insulting text for pre-configured people.

This is a plain
.B shell
implementation.

.SH AUTHOR
.B insult
was written by some author <some.author@internet.de>.

If we run the executable usr/bin/insult, we get the following output:

Emma is very not intelligent!
Peter is very not intelligent!
Joe is very not intelligent!

So now we have a working piece of software that we could install as-is to the end-user's system root directory /.

the debian folder

Now that our software is functional and sorted into the right directories, we can begin with debianizing. This means we have to provide instructions for creating the debian package from our files. This is done in a folder that has to be called debian for obvious reasons. So we add this folder:

> mkdir debian
> tree
.
├── debian # new debian folder
├── etc
│   └── insult
│       ├── insult.txt
│       └── people.txt
└── usr
    ├── bin
    │   └── insult
    ├── lib
    │   └── insult
    │       └── functions.sh
    └── share
        └── doc
            └── insult
                └── insult.1

10 directories, 5 files

There is now a bunch of files that need to be placed in there. For a detailed description, see Other files under the debian/ directory.

First, the small ones:

debian/compat :

8

Yeah, just put an 8 in there... It has to do with the compatibility level.


debian/insult.manpages :

usr/share/doc/insult/insult.1

This file contains paths to the manpages. For the paths you always have to think relative to the same folder the debian folder is in.


debian/insult.install :

usr/*
etc/*

This file tells what files from your project to install where on the target system. Each line may be a pair of source target folders/files. If you leave the target out, the source will be copied as is - relative to the root directory /.

However, leaving out subdirectories or specific files is not possible with this method. You will have to specify each individual wanted file path for this and leave out the unwanted. More control over the installation process can be achieved by writing to the rules file, which we won't cover here.


debian/copyright :

Choose a license and specify it here. Let's pick the GPL-3 license.

Format: http://dep.debian.net/deps/dep5
Upstream-Name: insult

Files: *
Copyright: 2016 Some Author <some.author@nobodyinperson.de>
License: GPL-3.0+

Files: debian/*
Copyright: 2016 Some Author <some.author@nobodyinperson.de>
License: GPL-3.0+

License: GPL-3.0+
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 .
 This package is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 .
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>.
 .
 On Debian systems, the complete text of the GNU General
 Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

 


debian/rules :

#!/usr/bin/make -f

%:
    dh $@

The rules file is a Makefile that is supposed to do the hard work from preprocessing, building, installing, postprocessing and everything. And it is that short in this case because we use the dh (debhelper, that is) command which in turn evaluates the files mentioned earlier (e.g. insult.install, insult.manpages). Unless you have complicated building or stuff to do while packaging, you should not need to edit the rules file heavily or at all.


debian/changelog :

The changelog file is for information on what changed in every version of your software. There is the dch utility that helps you a lot creating this file with the right syntax. On a terminal:

# create a changelog file template
dch --create 
# open the file and adjust the information
vim debian/changelog # or any other editor you like

It could look like this:

insult (0.0.1) UNRELEASED; urgency=medium

  * Initial release.

 -- Some Author <some.author@nobodyinperson.de>  Thu, 13 Oct 2016 18:59:55 +0200

Every time you feel like documenting a change in your current development version, run dch and it should open an editor at the right place for you. As long as you are working on a specific version that is not yet ready, keep the UNRELEASED keyword in there. When you reach the point that you want to call the version releasable, replace the UNRELEASED word with something like unstable for debian or precise for ubuntu 12.04 or any other release keyword, you might know which one to choose.


debian/control :

Source: insult
Section: utils
Priority: extra
Maintainer: Some Author <some.author@nobodyinperson.de>
Build-Depends: debhelper (>= 8.0.0)
Standards-Version: 3.9.2

Package: insult
Architecture: all
Depends: ${misc:Depends}
Conflicts: insult
Replaces: insult
Description: produce insulting text for preconfigured people
 insult is a utility to produce insulting text for preconfigured names.
 You can configure the insulting text and the names of people.

The control file holds all the metadata for your package and is thus of vital importance. What's important is the Depends: ... part, where you can list other package names that your software depends on. The rest should be pretty much straight-forward.


build the package

We are now ready to build our package! From the same folder where the debian folder resides, run dpkg-buildpackage.

dpkg-buildpackage # build the package
# ...
# lots of output
# ...
# If everything went well, we should have a .deb file
# one folder layer above!
ls ../*.deb
# ../insult_0.0.1_all.deb

And that's it! We got our package. Now let's inspect it:

# to see the package metadata
dpkg-deb -I ../insult_0.0.1_all.deb
# neues Debian-Paket, Version 2.0.
# Größe 2728 Byte: control-Archiv= 628 Byte.
#      46 Byte,     2 Zeilen      conffiles
#     386 Byte,    12 Zeilen      control
#     373 Byte,     6 Zeilen      md5sums
# Package: insult
# Version: 0.0.1
# Architecture: all
# Maintainer: Some Author <some.author@nobodyinperson.de>
# Installed-Size: 23
# Conflicts: insult
# Replaces: insult
# Section: utils
# Priority: extra
# Description: produce insulting text for preconfigured people
#  insult is a utility to produce insulting text for preconfigured names.
#  You can configure the insulting text and the names of people.


# to see a list of files packed into the package
dpkg -c ../insult_0.0.1_all.deb
# drwxr-xr-x root/root         0 2016-10-13 19:03 ./
# drwxr-xr-x root/root         0 2016-10-13 19:03 ./etc/
# drwxr-xr-x root/root         0 2016-10-11 15:24 ./etc/insult/
# -rw-r--r-- root/root        15 2016-10-11 15:24 ./etc/insult/people.txt
# -rw-r--r-- root/root        25 2016-10-11 15:17 ./etc/insult/insult.txt
# drwxr-xr-x root/root         0 2016-10-13 19:03 ./usr/
# drwxr-xr-x root/root         0 2016-10-11 15:10 ./usr/lib/
# drwxr-xr-x root/root         0 2016-10-13 19:02 ./usr/lib/insult/
# -rwxr-xr-x root/root       269 2016-10-11 15:30 ./usr/lib/insult/functions.sh
# drwxr-xr-x root/root         0 2016-10-13 19:03 ./usr/share/
# drwxr-xr-x root/root         0 2016-10-13 19:03 ./usr/share/man/
# drwxr-xr-x root/root         0 2016-10-13 19:03 ./usr/share/man/man1/
# -rw-r--r-- root/root       236 2016-10-13 19:03 ./usr/share/man/man1/insult.1.gz
# drwxr-xr-x root/root         0 2016-10-11 15:09 ./usr/share/doc/
# drwxr-xr-x root/root         0 2016-10-13 19:03 ./usr/share/doc/insult/
# -rw-r--r-- root/root       152 2016-10-13 19:00
# ./usr/share/doc/insult/changelog.gz
# -rw-r--r-- root/root      1027 2016-10-13 18:55 ./usr/share/doc/insult/copyright
# -rw-r--r-- root/root       365 2016-10-11 15:35 ./usr/share/doc/insult/insult.1
# drwxr-xr-x root/root         0 2016-10-11 15:30 ./usr/bin/
# -rwxr-xr-x root/root       222 2016-10-11 15:30 ./usr/bin/insult

Test the your package with lintian:

# to scan the package for errors
lintian ../insult_0.0.1_all.deb

lintian is a very handy tool that scans your package for a huge amount of possible errors. In this case, there may be warnings like

W: insult: script-not-executable usr/lib/insult/functions.sh

You guess what should be done in this situation :-) A chmod +x usr/lib/insult/functions.sh should do the trick.

And that's pretty much it. You just built your own debian package!. And if lintian does not complain, a not-too-bad one.

You will very likely run into some major problems that may seem unresolvable to you, but try to relax and have a peek at StackOverflow or an irc channel for debian packaging or app development, people tend to be pretty great there as I recall.

Have fun with packaging!!

/nobodyinperson

Sources

I grasped this all over the net over the years and don't know anymore what information was coming from whom exactly, but here are some links that helped me: