Mon, 11 Sep 2006

Packaging Python Maemo applications on a non-Debian platform (Part Two)

Continued from part one...

bdist_deb or not

After deciding to try a different approach I wondered if there was a Python distutils command for packaging a Debian .deb package. Turns out someone wrote a patch for a bdist_deb command but the bdist_deb patch is still sitting in the SourceForge patch tracker.

Unfortunately I soon discovered the bdist_deb command still requires the other Debian packaging scripts to be installed! :-( (I've since noticed the patch includes a "pythonic" version of dh_make that appears to only rely on one of the standard tools so potentially it could be modified slightly.)

Due to the dependency on the standard scripts--already given up on--this looked like another dead end.

What's in a .deb anyway?

By this time I was starting to think it might be quicker to reinvent the wheel so I decided to look into what made up a .deb package. Wikipedia has a summary on the implementation of .deb packages:

deb file is implemented as an ar archive. Canonical contents of this archive are 3 files:

Hmmm... that's starting to look straight-forward. Along the way I discovered some other useful descriptions of the file format and contents of individual files:

The last item in the above list states:
The internals of this Debian binary package format are described in the deb(5) manual page. Because this internal format is subject to change (between major releases of Debian), always use dpkg-deb(8) for manipulating .deb files.
This means any utility that doesn't use the official tools is "bad" but by this stage I don't care--I just want to make an install package for the Nokia 770!

Show me some code!

After getting a general overview of the file format and content I decided I'd write a couple of simple shell scripts to make my install package. (I considered a Python script but have bigger plans for that, more on that later.) It turns out you can create an installable package with little more than mkdir, ar, tar, gzip and a text editor.

Without further ado, the first of the tools in the mkdeb toolkit, mkdeb-init.sh:

#!/bin/sh
#
# Create directory structure to be used for a .deb file
#

TARGET_DIR=$1

echo Creating ${TARGET_DIR}...

mkdir ${TARGET_DIR}
mkdir ${TARGET_DIR}/control
mkdir ${TARGET_DIR}/data

echo "2.0" > ${TARGET_DIR}/debian-binary

touch ${TARGET_DIR}/control/control

echo Done.
The script is used like so:
$ mkdeb-init.sh my-test-package
Creating my-test-package...
Done.
This will produce a directory tree along with a couple of pre-created files, like so:
$ ls -R
my-test-package

./my-test-package:
control         data            debian-binary

./my-test-package/control:
control

./my-test-package/data:

You need to edit <dir>/control/control--for the OS2006 Application Installer this seems to be sufficient:

Package: [name]
Version: [version]
Section: user/other
Priority: optional
Architecture: all
Maintainer: [email]
Depends: python2.4, python2.4-hildon, python2.4-gtk2
Description: [short description]
 [indented long description]

Next, you need to copy your code into the final installation destination of your code in the <dir>/data/ directory. For example, if you have a script foo to be installed in /usr/bin/ on the Nokia 770 then you need to put it here:

<dir>/data/usr/bin/foo
(You could obviously use a Python setup.py script to do this for you like the Python Maemo tutorial suggests.)

By now you should have everything in place so you can now use the second half of the mkdeb toolkit, mkdeb-pack.sh:

#!/bin/sh
#
# Pack a directory structure into a .deb file
#

TARGET_DIR=$1
OUTPUT_FILEPATH=${TARGET_DIR}/$2

FILEPATH_CONTROL_TGZ=${TARGET_DIR}/control.tar.gz
FILEPATH_DATA_TGZ=${TARGET_DIR}/data.tar.gz
FILEPATH_DEBIAN_BINARY=${TARGET_DIR}/debian-binary

DIRPATH_CONTROL=${TARGET_DIR}/control
DIRPATH_DATA=${TARGET_DIR}/data

echo Using ${TARGET_DIR} to create ${OUTPUT_FILEPATH}...

tar czvf ${FILEPATH_CONTROL_TGZ} -C ${DIRPATH_CONTROL} --exclude=*~ .

tar czvf ${FILEPATH_DATA_TGZ} -C ${DIRPATH_DATA} .

ar -r ${OUTPUT_FILEPATH} ${FILEPATH_DEBIAN_BINARY} ${FILEPATH_CONTROL_TGZ} ${FILEPATH_DATA_TGZ}

echo Done.
The script is used like so:
$ mkdeb-pack.sh my-test-package my-test-package_0.1.0-1.deb
Using my-test-package to create my-test-package/my-test-package_0.1.0-1.deb...
./
./control
./
./usr/
./usr/bin/
./usr/bin/foo
Done.

You've almost got a usable installer--in fact if you have no executables it should work okay but you're going to run into some ownership and permissions problems in the package's current form.

Post-install Permission and Ownership fixes

In theory it's possible to set the correct ownership and permission when the files are tared but the version of tar on Mac OS X doesn't support the options. (If we use the Python tarfile library we can set them also--I'm looking at that option for the future.)

We'll need to re-pack our .deb but we can solve the permissions/ownership issue by creating a <dir>/control/postinst shell script. The script can be as simple as this (assuming the exec bit was previously set):

#!/bin/sh

chgrp users /usr/bin/foo
chown user /usr/bin/foo

Now, if you re-run the mkdeb-pack.sh script you should be good to go with a fresh Nokia 770-installable Python application.

The future

The most obvious enhancement is to turn all of this into a Python distutils bdist_ command extension and not rely on any external tools. This is almost do-able at present but I haven't found an ar archive format creation library for Python. The ar archive file format looks fairly straight-forward so it shouldn't be too much of a problem.

Anyway, all this is Works For Me, so I'd be interested to hear if it's useful for anyone else. Feel free to contact me at follower@rancidbacon.com.

Posted at: 02:25 | category: /maemo | Comments ()