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).
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!)
We will use these command-line tools:
- dpkg-deb (the ‘packager.’)
- dpkg (the installer)
- lintian (checks packages)
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.
Naming a package
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 <email@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.
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 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):
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.
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:
Pay particular attention to errors (lines beginning with ‘E’).
- 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.
- futz with the input directory (structure or content files)
- build the package
- check the package
Until lintian reports no errors (warnings are OK.)
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.
- 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 .