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:
- debian-binary - deb format version number
- control.tar.gz - all package metainformation
- data.tar.gz - the actual installable 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:
- deb -- Debian binary package format (man page)
- Creating Debian Packages
- dpkgdeb -- Debian package archive (.deb) manipulation tool (man page)
- Debian Binary Package Building HOWTO
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:
This will produce a directory tree along with a couple of pre-created files, like so:$ mkdeb-init.sh my-test-package Creating my-test-package... Done.
$ 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:
(You could obviously use a Python setup.py script to do this for you like the Python Maemo tutorial suggests.)<dir>/data/usr/bin/foo
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 ()