A short Debian packaging case: GUI apps, GPL, PyInstaller

This is generally about Debian packaging.

It is specialized for a program that is:

  • an app (not a utility or a server)
  • a Python app frozen with PyInstaller
  • an app that creates documents with its own mimetype
  • a GUI app that uses the desktop metaphor
  • uses some open source software (GPL), but is itself proprietary

The goal is a Debian package which:

  • passes the lintian test and will install on most Linux distributions.
  • conforms to requirements for the desktop metaphor
  • protects your copyright and fulfils some of your obligation under GPL and LGPL

The goal is NOT a package that the Ubuntu or Debian organizations will necessarily accept for distribution.  In particular:

  • it will be missing  the changelog.
  • those organizations might prefer a traditional Linux packaging not using PyInstaller (and therefore smaller in size.)
  • those organizations prefer free software

Flaws in this post

This post is a work in progress… these things don’t work:

  • the copyright file in the debian input directory doesn’t work using these methods
  • When you click on the .deb package the installer opens and offers to install, or reinstall, and does succeed in installing.  However, later the installer (e.g. Ubuntu Software Center) fails to recognize the installed app IS installed (fails to offer to remove it).

Similar tutorials

How to create a basic .deb  (too simple, for a command line app)

Ubuntu Packaging Guide (too complex, for an app to be distributed in Ubuntu channels.)

Debian New Maintainers Guide (too complex, for packaging specialists.)

Deploying Proprietary Java Software on Ubuntu Linux most similar to this post (but it omits discussion of copyright and licensing, by a company that sells licensing software!)

Tools

We will use these command-line tools:

  • dpkg-deb  (the ‘packager.’)
  • dpkg  (the installer)
  • lintian (checks packages)
  • fakeroot

They are available free.  Just try to use them: if they are not installed, you will get a message that tells you how to install them.  Use your package manager (e.g. Ubuntu Software Center, or apt-get) to install them.

What the Debian packager does

The packager reads a directory of files ( the ‘input directory’) and produces a single package file ( .deb) which an package manger (installer) will read to install your app.

So the larger ‘packaging process’ is mostly creating the input directory.

The structure of the input directory

The input directory contains:

  • a subdirectory (DEBIAN) that controls the packager (also called metadata: data about your app)
  • a subdirectory (e.g. usr/share) that is a template of the installation on the target computer

The root of the directory is named the same as the package.

Details of the debian directory.

Naming a package

Debian package naming conventions (see section 7.3)

Example input directory

helloworld_1.0-1/
helloworld_1.0-1/DEBIAN
helloworld_1.0-1/DEBIAN/control
helloworld_1.0-1/usr/share/bin/helloworld.exe 
helloworld_1.0-1/usr/share/applications/helloworld.desktop
helloworld_1.0-1/usr/share/mime/packages/helloworld.xml
helloworld_1.0-1/usr/share/icons/hicolor/48x48/apps/helloworld.png
helloworld_1.0-1/usr/share/icons/hicolor/scalable/apps/helloworld.svg
helloworld_1.0-1/usr/share/icons/hicolor/scalable/mimetypes/application-hlw.svg
helloworld_1.0-1/usr/share/pixmaps/helloworld.png ** I think this is unnecessary, a fallback for older versions helloworld_1.0-1/DEBIAN/copyright ** Currently I can't get this to be recognized by dpkg-deb

Explanation of the example input directory

The DEBIAN subdirectory controls the packager.

Everything else below the root is a template.  (After your app is installed, all those files will exist on the target computer.)

In this example, there is only one executable file (…/bin/helloword.exe) since PyInstaller has hidden (bundled, or put in a self-extracting archive) the dependencies.

The control file

Package: helloworld
Version: 1.0-1
Section: Applications/Graphics
Priority: optional
Architecture: i386
Depends: 
Maintainer: John Doe <jdoe@example.com>
Installed-Size: 10000
Description: Classic example program doing nothing important! (synopsis)
 (Extended description) No dependencies or significant conflicts.
 Each line starting with a blank is part of a paragraph.
 This variation of helloworld creates a file with mimetype foo.

Note, ‘Depends’ is empty because PyInstaller has bundled the dependencies (subverted the usual Linux install strategy.)

Installed-Size is just an estimate, in kb, so a user can decide whether to install.

I think the architecture is i386 since PyInstaller includes a bootstrap loader compiled for this architecture?  The architecture ‘any’ would be for pure Python and Java apps, packaged in a different, non-shortcut way.

Checklists

There is no concise document describing a Debian package checklist, but the lintian utility effectively defines the checklist.

It requires a copyright (license) file.

Lintian does not check for desktop integration.

The Gnome checklist for desktop integration.

The desktop file

A desktop file ‘registers’ your app with the OS.  Its the glue that makes your app work with the desktop metaphor.

The Gnome guide to desktop files, for developers explains it, but the same also works for other desktops (e.g. KDE.)

[Desktop Entry]
Type=Application
Encoding=UTF-8
Name=helloworld
GenericName=a sample application
Comment=display greeting
MimeType=application/hlw
Exec=helloworld %f
Icon=helloworld
Terminal=false
Categories=Graphics;Application

Name: brand name e.g. Mozilla

GenericName: a noun, what kind or category of application e.g. web browser

Comment: a verb, a tooltip describing what app does for user e.g. surf web

MimeType: a string identifying a mimetype the app recognizes (reads) (so when a user clicks on a file, the app is a candidate for launching, i.e. ‘Open with.. ‘

The value for Exec and Icon are not absolute path names, so an OS uses a well-defined algorithm to search places for matching stuff. The package template puts that stuff in those places.

The %f in the Exec means the application takes a single argument which is a filename (necessary for click to open.)

Terminal: whether the app is launched directly, or launched from a terminal (console, which might display messages of interest to programmers.)

Files that define the mimetype

In the debian input directory, these files help to define a new mimetype (custom to your app):

  • helloworld_1.0-1/usr/share/mime/packages/helloworld.xml
  • helloworld_1.0-1/usr/share/icons/hicolor/scalable/mimetypes/application-hlw.svg

See Creating New Mimetypes in a Shortcut Debian Packaging

Also, the ‘MimeType’ field in the .desktop file associates your app with the mimetype.

The mimetype is only ‘defined’ on the installed on computer, not permanently defined in the sense that most of the world understands it.

The Icons

See a forthcoming post: App icons for Linux apps.

The copyright file.

See a forthcoming post: a Debian copyright file for a proprietary app using LGPL components

Building the package

>fakeroot dpkg-deb --build helloworld_1.0-1

That creates the file helloworld_1.0-1.deb, the package.

Checking the package

On a command line:

>lintian helloworld_1.0-1.deb

Pay particular attention to errors (lines beginning with ‘E’).

Common errors:

  • wrong-file-owner-uid-or-gid : you didn’t use fakeroot
  • unstripped-binary-or-object: you should have used the -s option to PyInstaller *
  • no-copyright-file: TODO
  • description-starts-with-package-name: you don’t need your apps name leading the description
  • backup-file-in-package: your editor left a hidden backup file that you should delete else it will be installed.
  • non-standard-dir-perm usr/ 0775 != 0755: I don’t understand, they look equal to me?

* using strip on the PyInstaller output is NOT correct.

Lintian does not check conformance to the Gnome checklist for desktop integration.  It will gladly assume that your app is a command line app.

Some documents say the Debian organization requires a man page, but lintian calls it a warning, not an error.

Note that errors are not necessarily fatal to an install.  For example, I have an unstripped binary, but it does install.

Iterating

Repeat:

  • futz with the input directory (structure or content files)
  • build the package
  • check the package

Until lintian reports no errors (warnings are OK.)

Actually installing

In a file browser, double click on the .deb file.  A package manager will start, for example Ubuntu Software Center.

Since this example is all about user-friendliness, you should do it this way (graphically) since that is what many users will do.  It might reveal other errors.

However, alternatively, on a command line:

>sudo dpkg -i helloworld_1.0-1.deb

The installer (dpkg) installs your app (moves the components of your app from the package to their place in the file system, does other initialization of the installation.)

Errors in installation

In one of my tests, Ubuntu Software Center showed a warning dialog saying “The package is of bad quality” and giving details ‘The package doesn’t provide a valid Installed-Size control field. See Debian Policy 5.6.20.’   This for a package that had passed lintian (except for the copyright) and which dpkg invoked from the command line did not complain about.

Testing the installation

At a minimum (for this example) your program’s icon should appear in your launcher (e.g. Ubuntu Dash.)

Complete testing is beyond the scope of this tutorial but would follow the checklists cited above.

Testing uninstall

Graphically:

  • Start the Ubuntu Software Center
  • Open the ‘All Software’ tab.  (It might not appear under the ‘Installed’ tab?  A problem in the package shown above? )
  • Enter your app name e.g. ‘helloworld’ in the search widget.  Your app should appear in the main view (a list of one item.) **
  • Select your app.  A ‘Remove’ button should appear.
  • Click the Remove button.  After a short wait, the ‘Remove’ button dissappears.

Note that Ubuntu now has forgotten the location of the package that you originally gave to install.

** I can’t get this to behave consistently.  For some reason, USC doesn’t show my app as installed, even though USC installed it without complaint and the app shows in the Dash.

However, alternatively, on a command line:

>sudo dpkg -r helloworld

Here you use the name of the app, not the name of the package file with suffix .deb .

Advertisements

One thought on “A short Debian packaging case: GUI apps, GPL, PyInstaller

  1. Nice tutorial. As an add on, you can include tutorial to convert created deb package to rpm using alien. This is specifically useful as rpmbuild does not allows inclusion of binary file in the source directory.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s