Releasing Free/Libre/Open Source Software (FLOSS) for Source Installation
David A. Wheeler
2009-04-13 (revised 2010-03-03)
If you’ve written (or started to write) some
Free/Libre/Open Source Software (FLOSS), please follow
the time-tested community standards for releasing FLOSS software
when you want people to be able to install it from source code.
This article will briefly explain why you should follow
standard release conventions, and what they are.
Why Follow Standard Release Conventions?
First, the why.
The quick answer is, “because it makes it easy to install your software -
including creating packages to do so”.
Modern Linux and BSD systems (as well as MacOS and MS Windows) normally install
programs via “packages”; on Linux and BSD systems, this automatically
installs all the necessary dependencies too.
These packages are much easier to
create and maintain if you follow standard release practices.
I’ve written a number of FLOSS programs (such as
flawfinder and
SLOCCount), and participated
in other development, but I didn’t really understand the importance of the
release standards until I started work on
open proofs.
I quickly learned that the packaging concept was simple, but
some programs were hideously painful to package because they hadn’t
followed the usual release standards.
I knew some of these practices before, but I sometimes skimped on them
before I understood the nightmare I was (unintentionally) inflicting on others.
I intend to modify my future releases to better meet usual release practices,
now that I’ve “learned better”... and
I hope to spread the word so that others will do the same.
What are the Standard Release Conventions?
Now, the what. These documents in particular describe
good release practices:
- The GNU Coding Standards,
especially the material on the
release process.
-
Software Release Practice HOWTO by Eric Steven Raymond.
- Filesystem Hierarchy Standard (FHS) — this describes where files are supposed to go.
If you’re releasing FLOSS, you want it to be easy to package, so it
might be a very good idea to know about typical packaging guidelines.
You can look at:
At least look over the first set (the
GNU coding standards’ release process,
Software Release Practice HOWTO, and the
Filesystem Hierarchy Standard (FHS)).
A Quick Checklist
I know that the above is a lot information, so here is a
quick checklist of some of the most important guidelines
(in my opinion), based on problems that I’ve had:
- Pick a good, simple, and unique Google-able name for the package
and its executables.
Limit your package name to the lower case letters
a-z, digits 0-9, and the four punctuation characters “-._+”.
The
Software Release Practice HOWTO’s chapter on
good project and archive naming practice and the
Fedora naming guidelines may help.
Make sure the uppercase/lowercase distinction is irrelevant; some filesystems
can’t distinguish them, and package names tend to be all lowercase anyway.
Try to pick a name that’s unique —
at least unique among packages, and
preferably, one that doesn’t have any Google hits at all
(so that people can find your package easily).
A good example of a bad name is
Why, because it is
completely un-Googleable.
If you don’t pick a good name, then in the best possible case,
people will rename your program, often leading to confusion.
At worst, they will never find your program at all.
Similarly, try to make to make the name of the executable(s) unique;
they will probably be installed in /usr/bin with all other user programs.
- Identify the version in MAJOR.MINOR... format
or by using dates in YYYYMMDD format.
People and software must be able to easily determine if a version
is older or newer than another one.
Using version numbers (e.g., 2.2.2) or date values in YYYYMMDD format
(e.g., 20090413) makes that clear.
The filenames of major releases should have the form
NAME-VERSION.FORMAT, e.g.,
“myprogram-1.1.tar.gz”, so that even after downloading there
are no questions.
Please use a version control system,
but please make occasional “real” releases that
are single-file archives; that way, people can easily know what version
is ready to use, and how to get it.
- Use a standard, widely-used, GPL-compatible FLOSS license — and say so.
One of the worst mistakes a FLOSS project can make is to use
a non-standard license (especially a home-grown one) or fail to
state a license at all.
“Home-grown” licenses are often not even FLOSS, impede reuse
of other code, can delay packaging or use by years,
and will dissuade potential co-developers.
Many organizations and distributions explicitly forbid using software
that fails to include license statement; failing to state the license
makes the software useless to many.
Use one of the standard FLOSS licenses (e.g., MIT, BSD-new,
LGPL, or GPL), and
be sure your license is GPL-compatible.
Fedora’s licensing
page has more info.
You do not need to use the GPL itself, but using a GPL-incompatible
license is a mistake, in part because most FLOSS is GPL’ed.
Do not write your own FLOSS license.
Clearly state in your documents - and code header files - what the license is.
- Follow good distribution-making practice, in particular,
make sure tarballs
always unpack into a single new directory named NAME-VERSION.
The
Software Release Practice HOWTO’s “Good distribution-making practice”
goes into this.
Make sure that tarballs unpack into a subdirectory, or unwary users will
end up with unwanted junk in directories they use.
By doing this, you’ll fulfill people’s
expectation that the first two steps to do are
(1) unpack (untar) the file, and
(2) “cd” into its directory.
- Support the standard invocation to configure, build, and install it:
./configure --prefix=/usr ; make; make install.
Many people expect that, after the unpack-and-cd steps,
you’ll perform the normal configure, make, make install process.
Please implement configuring, building, and installing this way,
because this presumption is widespread (e.g.,
here,
here,
here,
and
here).
You do not need to use the autoconf/automake tools to implement this
(though you can) - just conform to the usual external interface.
See the
GNU Coding Standards’ “Managing the Release Process” for more.
Don't interleave the configuring, building, and installation steps;
in particular, the build ("make") step should create all the files to be
installed, and the install step should do nothing but install the files
created by the build step.
The first two steps should not require root privileges; the latter
one may require privileges depending on where files are being
installed.
The next three bullets discuss the assumptions of each of these
three steps.
-
Support the standard ./configure options like --prefix,
--exec-prefix, --bindir, --libdir, and so on.
By supporting the standard “configure” options you make it easy to
install the program wherever the user wants it.
Supporting “prefix” is especially important;
“prefix” is prepended to all filenames to be installed, and
should default to “/usr/local”
(for a system-wide installation that’s not part of the system).
When the program is packaged for a given system, the prefix is typically
changed to “/usr” instead.
You should support installation by a single unprivileged user, in which
case the prefix would typically be “$HOME”.
There is a reason that people need control over these values,
for example, never assume that “libdir” is the same as “$(prefix)/lib” —
on many 64-bit systems it is “$(prefix)/lib64”.
Here’s how configuration should work.
It’s nice to support --srcdir, but I wouldn’t worry about that;
--srcdir can be awkward to support, and is far less important
(storage space is plentiful nowadays,
so you can just make a copy and then build).
But options like --prefix and --libdir are vital.
-
Create a makefile that can rebuild everything and
uses makefile variables (including applicable standard
standard makefile variable names and targets).
By default, invoking “make” should rebuild/recompile everything
(by tradition this target is named “all”).
Makefiles should be able to rebuild everything;
many distributions forbid using pre-built binaries or libraries in packages
(since they can’t then fix problems).
It’s okay to include pre-built binaries/libraries in a source
distribution, as long as “make clean”
(or “make maintainer-clean” or
“make really-clean”) can get rid of them.
Makefiles should maximally use makefile variables so they can be easily
overridden where necessary, e.g., “make CC=mygcc”.
Use standard makefile variable names where they apply
(e.g., prefix, exec_prefix, bindir, libdir, etc.).
GNU publishes useful makefile conventions.
At the very least, a default “make” should build everything, and
“make install” should install the results, but it’s often helpful to
support other targets like “clean” and “uninstall”.
You don’t need to create a makefile directly; many programs
use makefile generators, such as the one in
Cmake
and
autoconf/automake.
Just make sure that the generated makefile(s) support these conventions.
Note: be sure to mark phony targets (like “all”, “clean”, and “install”)
with the .PHONY mark, like this:
.PHONY : clean
This is particularly important for “install”; without it,
“make install” will probably do nothing on filesystems that
ignore case distinctions (because the
make will see that the “install” file already exists).
In general,
marking phony targets (targets that aren’t really files)
will eliminate mysterious errors and document more clearly what is what.
Even if you use a completely different build system that does not use
make, at least include a tiny makefile that invokes the
“real” make system.
There’s no reason you have to use make directly
if you don’t want to.
For example, today’s FLOSS Java applications tend to use
Ant or
Maven.
Cmake is
a very useful cross-platform build system, with a lot of users
(Cmake generates makefiles, so it automatically meets the requirement
to provide a makefile).
However, make sure you include a small makefile that can invoke your
build and installation system.
That way, people don’t need to figure out how to invoke your make system,
and programs can be automatically built and installed regardless
of your preferred build system.
-
“make install” should not rebuild anything; simply copy files to locations as specified by DESTDIR, prefix, and so on.
Most packaging systems, including Ubuntu, Debian, and Fedora’s,
expect programs to be completely built by the steps above, and then split
“installation” into two steps: An “install” to an intermediate directory
(where the files will then be collected), and a final “install” done
on the user’s actual system.
This is easily done if your installation program is invoked using
“make install” and
supports a few standard conventions.
The install rule should
“not modify anything in the directory where the program was built,
provided ‘make all’ has just been done...
The commands should create all the directories in which files are
to be installed, if they don’t already exist.”
In most cases using “cat” in “make install” is a mistake;
“make install” should simply make directories and copy already-built
files into the appropriate directories, so think carefully before
adding any command other than mkdir, install, cp, and ln.
Please support the
DESTDIR convention, that is, they should always install into
the directory $(DESTDIR)$(prefix) where DESTDIR can be set by the user.
(This implies that you should always create directories before installing
into them; $(DESTDIR)/usr/bin might not exist!)
Packaging can be very painful if you don’t support DESTDIR.
Automating
DESTDIR can be a pain, so it’s best if the program supports it
to start with.
If your package doesn’t support DESTDIR, at least make sure that
the “make install” target only uses a few simple commands to place the files
in their final positions (e.g., mkdir, install, cp, and ln), so that
the package
Auto-DESTDIR can
automatically support DESTDIR.
The GNU documents recommend not depending on mkdir supporting “-p”,
but I think this is obsolete advice; “mkdir -p” has become very widespread,
and it is even included in the Single Unix Standard version 3,
so I think it’s okay to depend on it now.
-
Do not require web access or interactivity for building, testing,
or installing the software.
Often these aren't permitted or possible.
If you must interact with a user before installation can complete,
install all the files without interaction,
and detect when a user tries to start the program for the first time.
Then display information on how to complete the installation, or
interactively complete the installation.
- Document the external tools/libraries needed for building and running,
and make it easy to remove them and install them separately.
Document in your README or INSTALL file the external tools/libraries you
need for building and running your software, including a URL
for each uncommon one.
Most importantly, make it easy for users to separately install and
update those tools/libraries!
It’s okay if you include a copy of an externally-developed
tool or library, as a convenience for people who don’t want to download it
separately, but do not require people to use these
embedded versions.
Over time, separate tools/libraries get updated, and whatever version
is embedded in your release will be obsolete.
Even if you sent the latest version at the time, external programs
will be upgraded later.
Try to ensure that users can use dynamic libraries to load the latest versions,
if that’s practical.
Unfortunately, many Java programs are especially bad at this.
Many distribution guidelines forbid using
bundled libraries in their packages, for example, see
Fedora's "No bundled libraries" rule
and
Debian's policy on convenience copies.
- If you patch an external library/tool, get the patch upstream.
If you don’t, your “local” version will stagnate while the original tool
gets many improvements.
- Use pkg-config when finding or installing libraries.
Freedesktop.org's pkg-config
is relatively new but has swept the world of POSIX systems because
it's an easily and simple solution to a more complicated problem.
If you're compiling an application, it can tell you which flags to use.
If you're creating a library, please create and install the pkg-config .pc
files, so others can use pkg-config.
- Use standard user interfaces.
For command line tools, use “-” single-letter options, “--” long-name
options, and “--” by itself to signal “no more options”.
For GUI tools, provide a .desktop file.
Make it easy for people to start up your application.
GNU standards for command line interfaces
notes standard option conventions for command line interfaces, including
some standard long names like “--version” and “--help”.
Once installed, GUI programs should be runnable from the main menu,
which requires that a .desktop entry file be created and correctly installed;
see
freedesktop.org’s desktop entry specification.
- Where sensible, make the application useable as a library
or via the command line.
This makes it possible to build a larger program
(using libraries or scripts) that uses your program/library.
- Include tests (as “make check”).
Users and packagers want to know if the program is working as intended,
and anyone who makes an improvement to it will want to know if the program
is still working.
So, include easily-invoked test cases,
where you send in inputs, produce outputs, and check
that the outputs are correct.
Tests that cover a significant amount of the program’s functionality
and implementation are best, but even a cursory test
(a “smoke test”) is better than nothing.
Set up your test suite so that it’s easy to add new tests.
Make sure the test will work on the program built even if another
version of the software is installed.
- Include documentation.
Typically you’ll have a brief README (what is this program?) and INSTALL
(how to install it and any requirements for tools).
It’s a good idea to have a man page and additional separate documentation,
sufficient to answer the questions:
What does your program do (sufficient so the user can determine if they
want it)?
What are some sample invocations?
What are its options?
What is its input format or API?
What is output format or result?
What are its strengths or weaknesses?
What is the project’s URL?
- Make it portable.
If your software can port to different architectures and platforms,
then it’s probably flexible enough to handle likely future changes.
Follow formal and informal standards, such as the
Single Unix Specification.
Consider using checking tools like the
Linux Foundation's AppChecker.
- Support internationalization/locationalization (i18n/L10n).
Use tools (like gettext) for any command-line or GUI interface
so that people can use their native language in such interfaces.
Do not assume that characters all fit in one or two bytes (they do not).
I recommend using UTF-8 to encode characters in most cases,
but in some cases (e.g., Java, Windows APIs)
it may be easier to use a different encoding;
if you do, beware of their gotchas
(some glyphs take more than 2 bytes in UTF-16, and you have to worry
about byte ordering and the byte-order mark).
Related pages
Here are a few related pages:
-
Getting Packaged (Debian)
-
”Distribution-friendly projects” (LWN) by Diego Pettenò -
part 1,
part 2,
part 3.
-
How you know your
Free or Open Source Software Project is doomed to FAIL
by Tom Callaway.
-
Packaging
Unix software by Adam Sampson
-
“Recursive Make Considered Harmful” by Peter Miller
has some interesting comments about using make (I am not a fan
of using VPATH, as he describes, but you can learn a lot about
using make well from this paper).
-
Cmake is a very useful cross-platform build system.
-
Quagmire
is intended to replace automake and libtool, and eventually some of
autoconf. Unlike these tools it requires GNU make and is
written solely using GNU make features -- no preprocessing is required.
"The GNU Configure and Build System" by Ian Lance Taylor
explains the goals of quagmire, by an author of a book about
the GNU configure and build system (autoconf, automake, and libtool).
-
The GNU build system (aka "autotools") is a mature but baroque
set of tools for building and installing software.
-
Automating DESTDIR for Packaging discusses how supporting DESTDIR can
(in some cases) be automated; the
Auto-DESTDIR package
automates DESTDIR using some of the approaches it describes.
-
How to destroy your community (Josh Berkus, PostgreSQL developer)
-
How to get your code into an open source project (by Richard Jones)
and
Contributing to Open Source Projects HOWTO (by Dan Kegel)
explain how to contribute to existing projects, which is essentially
the perspective from the other side.
-
The Art of Unix Programming
(Raymond) provides a lot of lessons learned about how to develop
software on Unix-like systems.
There are efforts to help create packages for many different systems
simultaneously.
For example, the Linux Foundation announced in April 2009 that it would host a
build service to build packages for multiple systems simultaneously
(based on the OpenSUSE build service).
But these are much easier if you follow the guidelines above.
2009: software installation in GNU/Linux is still broken -- and a path to fixing it argues that GNU/Linux distributions
should change how application software is installed.
That's worth discussing, but I suspect that if there are any such changes,
they will primarily work only for software that follows guidelines
like the ones above.
Feel free to see my home page at
http://www.dwheeler.com.
You may also want to look at my paper
Why OSS/FS? Look at
the Numbers! and my book on
how to develop
secure programs.
(C) Copyright 2009 David A. Wheeler.