I got to work on a fun project yesterday, I needed to build a script that could generate a Debian package from a python module.
Yes, that does sound easy enough… Except that it’s not!
First of all, how does one build a .deb package? There’s lots of documentation on that, probably way too much, even. The main idea is that you will have to create a
debian folder and add a few files in there that will define your package and how to build it, and then by running
debuild, it will run a somewhat typical
./configure; make clean; make; make install
One of the problems with that is that you generally don’t have nor want to write a
Makefile for your Python project… I mean, what would it even do?
The other issue I had was that Debian doesn’t want us to install just eggs through a deb package, which is understandable, you don’t want to install a package of a package.
Let’s get started!
I will assume you’re working on a project named
ninja, and that it has this structure:
ninja/ core/ ninjalib.py something.py ninja.py readme.txt
Now, the first things you’ll need to do is to create (directly in the
ninja folder) the
Makefile and the
debian folder, which will contain at least the following files:
Makefile debian/ changelog control dirs docs postinst rules
Each of these files needs to follow a strict format, and contains very specific information:
Since python doesn’t require us to actually build anything, we’re going to use the
Makefile only to put our files in place.
LIBDIR = $(DESTDIR)/usr/share/pyshared/ninja BINDIR = $(DESTDIR)/usr/bin clean: rm -f *.py[co] */*.py[co] install: mkdir -p $(LIBDIR) mkdir -p $(BINDIR) cp -r core $(LIBDIR)/ cp ninja.py $(BINDIR)/ninja uninstall: rm -rf $(LIBDIR) rm -f $(BINDIR)/ninja
A few things to note here:
$(DESTDIR)is an environment variable, defined in your
rulesfile, which represents the root of the build environment (think
cleanremoves the dirty python bytecode.
installshould put in place all required code, and
uninstallshould remove it.
Makefiles allow only tabs, be careful.
This file should be your project’s changelog, but it must comply with the debian format, see example:
ninja (0.1) unstable; urgency=low * Initial version -- Cyril Ninja <ninja@...> Wed, 20 Jan 2010 18:47:11 -0500
The entire file should consist of these blocks, but be careful:
(0.1)is the version number for the package you’re building.
debuildwill always use the top-most version number.
- You should have as many lines starting with
*as there are changes in this version.
- It is an actual tab before the
*, no spaces are allowed.
- The last line starts with a space, and there are 2 spaces between the email and the date.
In this file, you have to define what your packages actually are, and what they depend on (also what other packages they suggest, if you wish to do that).
Let’s see an example:
Source: ninja Section: python Priority: extra Maintainer: Cyril Ninja
Build-Depends: debhelper (>= 7), python-support Standards-Version: 3.8.0 Homepage: http://ninja Package: ninja Architecture: all Depends: python (>= 2.6), python-ninja-foundation Description: This is powerful ninja stuff
This example defines two packages: a source one, and a binary one. This is pretty much straightforward, so I’ll leave it like that.
Makefile earlier, we defined a couple of directories (
BINDIR) where our code will be installed, and this file should contain the directory names we want to include in our package.
For this project, it should look like this:
You should define in this file all documentation that should be included in the package.
Let’s have a look at the awesome ninja
It will be installed in
postinst file is used to run commands when the package is installed / upgraded / removed. In our case, we only need to add a symlink so that
python can find our module.
#!/bin/sh set -e case "$1" in configure) ln -s /usr/share/pyshared/ninja /usr/lib/python2.6/dist-packages/ ;; abort-upgrade|abort-remove|abort-deconfigure) rm -f /usr/lib/python2.6/dist-packages/ninja ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac exit 0
Feel free to add any command you wish in that file.
If you’re anything like me, you’re going to hate that file… Fortunately for us, there is virtually nothing to change from the example below:
#!/usr/bin/make -f # -*- makefile -*- configure: configure-stamp configure-stamp: dh_testdir touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir $(MAKE) touch $@ clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp dh_clean install: build dh_testdir dh_testroot dh_prep dh_installdirs $(MAKE) DESTDIR=$(CURDIR)/debian/ninja install binary-arch: build install # emptyness binary-indep: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples dh_pysupport dh_link dh_fixperms dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep .PHONY: build clean binary-indep binary install configure
- Simply replace ninja with your project name, and you should be all set.
- Same here: tabs only.
Now that everything seems to be in place, it’s time to build our magnificent Debian package!
ninja folder, run
At some point, if you do have a gpg key mathing the author email address you defined, it should ask for your password twice, to sign both packages.
When it stops, look in the parent folder, you should see
ninja_0.1_all.deb ninja_0.1.orig.tar.gz ninja_0.1.dsc
After that, I hope you know what you have to do