Unix/Linux Software Development: be Friendly to Your Packagers

On this page, 'Best Practices' for Unix / GNU/Linux software development are collected that make the packager's life easier. Software authors are not necessarily aware of the issues of software packaging, since the two are quite distinct activities - communication between the two group is therefore necessary.

User-editable vs. constant files

Everything that should be modified by the local sysadmin goes into /etc, everything else goes into /usr - so much is obvious (but see below). But what with scripts that need to be edited by the user in some cases (resolvconf package), or (HTML/mail) templates that are often edited by the user (request-tracker, mailman)?

request-tracker clearly has the best approach to this: put these files into /usr/share by default, but try to load them also from /etc/FOO. In this way, I don't have to deal with ucf prompts on every package upgrade, but still don't have to wait for the much longer installation times for conffiles in /etc (mailman shows that this can take quite a while - though admittedly it's much better now than in earlier mailman versions.)

Make system directories configurable

Autconf supports a number of flags to control the location of special directories, --sysconfdir for /etc is one well-known example. Please support these flags, and add your own if appropriate, instead of hard-coding them

Likewise, please use TMPDIR if it is set, instead of hard-coding /tmp or /var/tmp.

Compile time options

Every setting that has to be picked at compile time forces the packager to make a choice that rightfully should belong to the user (in most cases.) Making as much of the program behaviour as possible configurable at run time makes the packager's life easier since he doesn't have all the users telling him that he should be picking different compile time switches.

This is especially true for compile time options that cause library dependencies: for instance, instead of linking to a database library, use a database abstraction layer that can be configured at run time. Obviously, writing modular code in that sense is not trivial, and is overkill in many cases, so it's not always an obvious choice.

Installation targets / DESTDIR mechanism

While most Makefiles today are generated using autoconf/automake (or other more or less common build tools, such as ant for Java applications), there are various projects which use hand-crafted build systems. These often exhibit a problem which makes packaging pretty hard for whoever is doing the packaging.

Namely they lack support for the DESTDIR variable which is prepended to the intended installation location. If a file should finally reside if /usr/bin for example, the Makefile (in whichever way it was created) should provide a mechanism to install the file into ${DESTDIR}/usr/bin instead. A file intended for /etc is likewise installed to ${DESTDIR}/etc instead. The important part is that the program should still look for its configuration in /etc, the DESTDIR mechanism is only used so that the packager can simply bundle up anything below ${DESTDIR}  instead of locating any installed file and picking it out of the fully installed system.

Of course it is not important whether the makefile actually uses DESTDIR to achieve the goal or some other easily available mechanism, it is only important that it provides some means to have any file which needs to be installed to make the package work copied to a mirror of the final directory layout below some specified location.

Please don't change anything that's not within your build tree in your 'build' target. Your 'install' target, on the other hand, should not change anything within your build tree; otherwise there'll be some root-owned files lying around.

Be careful with autoconf/automake

If you package use autoconf/automake for configuration, there are few tips you should know:

Use Sensible Version Numbers

Ideally, sorting the version numbers alphanumerically should result in the correct ordering of the versions; that way, packaging tools can easily compare versions when a package is to be up- or downgraded.

Using a consistent numbering schema that makes it easy to distinguish between snapshot, beta, release candidates and final releases, and using version numbers in a way that makes it easy to distinguish major releases from minor (bugfix) releases is a good idea, too.

"make clean" and generated files

The "make clean" and "make distclean" commands should work as intended: make clean should delete all files that are generated by "make all", and after "make distclean" everything should be like it was when the tarball was originally unpacked.

Generated files - be they object files, or generated source files - don't belong in a distributed tarball. Instead, it is vital that the Makefile includes rules to build them. Usually the only exception to this are the configure script and related files: users should be able to build the software without having the autotools installed, especially since the autotools are extremely version sensitive. Another reason for exceptions of this rule is if the tools to generate the files are either complex to set up correctly or if it is extremely time-consuming to regenerate those files.

Please use 'rm -f' in your cleanup targets so that it may proceed without user interaction, in case some files are owned by different users, or are read-only.

Communication issues

A public mailing list is a big bonus as soon as the software has more than a few users, or as soon as more than one person is developing the software - and for many small programs, when somebody decides to package it for Debian (or whatever OS), this is the first time that a second developer joins the original author. Further mailing list that could be created when the project grows: translators, announcements, packagers (the latter as a closed list with hand-picked subscribers, so that security issues can be discussed before public disclosure.) For smaller projects, it usually suffices for this purpose when a README in the software project includes an email address of the software author (keeping in mind that not only the packager may need to contact the author about security problems, but maybe also the security team of the software distribution that the packager targets.)

Use a public version control system

There are a number of free version control systems to choose from (cvs, arch, svn, ...) as well as some not-so-free ones (bitkeeper). You can get free hosting for all of them.

Version control is important because:

If you do use a version control system, please do not package their metadata (SCCS subdirectories, .cvsignore files, ...) in the tarball.

Version control systems generally don't deal with links well. For this and other reasons, if you do need to have links in your sources when compiling, generate them early and delete them in your "clean" target.

External libraries

Many programs make use of external libraries - after all, there's no need to reinvent the wheel every time. The distribution tar ball should not include those libraries (source code or object code), or at least should also allow an external copy of the library to be used. On one hand, including dozens if not hundreds of copies of the same library wastes a lot of space, and on the other hand, if this library has a security flaw, each copy needs to be fixed (the classical example was a security flaw in the zlib library.)

For the same reasons, dynamic linking should be preferred over static linking whenever possible: less wasted disk and memory, and a dynamic library can just be replaced on a security upgrade, while applications with statically linked libraries must be recompiled.

Persistent files

When adding a file, don't remove it with the next release only to add another one.

For example, man-pages have the bad habit of including man-pages-?version.lsm and man-pages-?version.Announce files that are removed and recreated for each and every release.