2006-07-18 · in Tech Notes · 1267 words

This page lists some of the things you should think about when distributing programs for Unix-like operating systems as source code — in particular, from the point of view of someone trying to produce binary packages for distribution.

You should also look at:

I've tried to make the advice here as general as possible, but there's an inevitable slant towards the things I've discovered while packaging software for GARStow. If you're aware of anything I've missed, please let me know.

General advice

The most important thing I've got to say here: please, please, don't roll your own custom configuration system. Use GNU autoconf and automake instead. Yes, they're awkward for some things, but they make life very easy for packagers when used correctly. If you're not using automake, then it's very tricky to get everything below correct unless your program is absolutely trivial (and if it's trivial, then it'd be very easy to write an automake config for).

The exception is for add-on modules for programming languages where there's an existing packaging system that's widely-used: for Perl use CPAN, for Python use distutils, and so on.

Be very cautious about systems that have been designed to replace automake; all that I've encountered have been very convenient for developers, but cause problems for end users and packagers (for example, assuming standard locations for files, or not allowing environment variables to be passed to compilers, or not supporting installation staging). If you really have to use something other than automake, then make certain that it's actually possible to use it to install the software and produce packages.

Don't interleave building and installation steps; construct your build system so that the entire package is configured and built, and then the entire package is installed. Installation should not touch the source directory at all, nor should configuration or building touch the destination directories; it's recommended practice to compile software as a non-privileged user and only install as root.

If you're installing a shared library, also install a pkg-config file; this makes detecting and using the library in other packages easier and more reliable. (The uptake of pkg-config has been surprisingly rapid among libraries; even X uses it these days.)

Configuration

Do not do interactive configuration by default; it's important that package builds should be automatable. If you want an interactive configuration system, then do as the Linux kernel and busybox do, and make it generate a config file that can be machine-edited and stored by packagers.

Check the output from ./configure --help to make sure that all the options are listed, and that the defaults are sensible and accurate.

Support cross-building the package for different architectures. automake will take care of most of this for you, but you need to be careful about things like internal tools used during the build process.

Source code

Don't hardcode paths into your source code; make them an option at configuration time. If your program automatically detects where its binary is, make sure it doesn't break in the presence of something like stow (i.e. that it isn't overly enthusiastic about following symlinks — Python has this problem).

Have a test suite for your program. Even just checking that your program's executable can run and print a help message will catch several kinds of build problems. Make the test suite available via make check, in order to reassure users that the package basically works. The tests should run to completion and exit 0 if they succeed; if any of them fail, make check must exit non-zero. Don't rely on users inspecting the output to check the tests passed!

Installation

Follow the GNU standards for how prefix and the related variables work. In particular, note that bindir and similar variables should default to depending on prefix at install time; don't set bindir to a fixed path at configure time (unless the user's explicitly specified one). Don't make up your own names for the variables (some packages use PREFIX or, worse, DESTDIR to mean the same thing as prefix).

Support DESTDIR. This is much more convenient for some packaging systems, so people building RPMs or Debian packages will find their jobs rather easier if you support it.

Don't assume that installation paths exist; use mkdir -p to make bindir etc. before you try installing files into them. (This applies even if your build depends on a directory existing — remember that the prefix you install into may be different from the one you had at build time.)

Don't assume you can edit or replace existing files and directories in your installation prefix, since at install time it might just be a staging directory. This means that each file should only be provided by one source package. If you want several packages to provide data for another program, have them write to different files in a shared directory (which is the approach taken by pkg-config, among others).

Similarly, don't assume that you can check for the existence of files in your installation prefix at install time.

Don't install things in a directory that you haven't been told about. I don't necessarily want config files installed in /etc, particularly if I'm not root.

Don't explicitly set ownership of installed files unless they're suid — you don't know what the administrator's conventions for file ownership are. If you're installing suid stuff, add a configure-time option not to do so, for people who're installing your software without being root.

Distribution

If your program is named gnomovision and it's at version 3.4, then your distribution tarball should be named gnomovision-3.4.tar.gz (or .bz2) and it should contain a single directory called gnomovision-3.4. If you release a bug-fix version, make sure the directory name is changed to match the tarball name. If you release patch files, generate them with diff -Naur old-version new-version, so they can be applied with patch -p1.

Use GPG to sign your distribution files. Provide a detached signature file, named the same as the matching distribution file plus .asc or .sig (invoking gpg --detach-sign -a your-file will do the right thing).

Document your package's dependencies somewhere — such as the download web page for your project, or the INSTALL file in the distribution. If you're depending on another package that's uncommon or otherwise hard to search for, give the URL where it can be found.

Make certain that your package actually contains all the files necessary to build it. It's easy to leave out a source file by accident, or change configure.ac before release and forget to regenerate configure (which will produce interesting errors for users who don't have the same version of autoconf as you), or to forget to clean out version control or object files from your tree.

Make your version control repository public (or at least provide daily snapshots), and make sure you check changes into it before you release a tarball. If a user finds a bug in your package, it's useful to be able to check the repository first to see if it's already been fixed.

Finally, make sure that you build, test and install your package, from the exact distribution file you ship, on a different machine from the one you developed it on, with only the package's documented dependencies installed. (Ideally, on several machines with different operating systems and architectures.) It's depressingly common to find packages where the test suite clearly wasn't run before release!