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
Issues
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:
Makefile
Since python doesn’t require us to actually build anything, we’re going to use the Makefile only to put our files in place.
Example
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 yourrulesfile, which represents the root of the build environment (thinkchroot).cleanremoves the dirty python bytecode.installshould put in place all required code, anduninstallshould remove it.Makefiles allow only tabs, be careful.
changelog
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.
control
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 NinjaBuild-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.
dirs
In the Makefile earlier, we defined a couple of directories (LIBDIR, 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:
usr/bin/ usr/share/pyshared/ninja
docs
You should define in this file all documentation that should be included in the package.
Let’s have a look at the awesome ninja docs file:
readme.txt
It will be installed in /usr/share/doc/ninja
postinst
The 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.
Example file
#!/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.
rules
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.
Ready?
Now that everything seems to be in place, it’s time to build our magnificent Debian package!
From your ninja folder, run
debuild
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
Subscribe
There are only two things I want to say to you
1) THANK YOU
2) I LOVE YOU
I’ve been trying to create a deb package out of a minimal python application I’ve written but have failed again and again until I found your tutorial!!
Thank you, it really helped me!!
I love you too!
Your guide really did save my life, thanks so much, I love you!
Some suggestions:
* Under “binary-indep:”, please add “dh_compress”. This option ensures that stuff life changelog.Debian and manpages are compressed. Otherwise you’ll have lintian (Debian package sanity tester) errors, even though your package will work without fault.
* Please add a manpage in the project as well.
* changelog does allow spaces before ‘*’