Packaging From Scratch

[Important]

Requirements: build-essential, automake, gnupg, lintian, fakeroot and pbuilder.

In this example we will be using the GNU hello program as our example. You can download the source tarball from ftp.gnu.org. For the purposes of this example, we will be using the ~/hello/ directory.

mkdir ~/hello
cd ~/hello
wget http://ftp.gnu.org/gnu/hello/hello-2.1.1.tar.gz

We will also compare our package to one that is already packaged in the Ubuntu repository. For now, we will place it in the ubuntu directory so we can look at it later. To get the source package, make sure you have a "deb-src" line in your /etc/apt/sources.list file for the Main repository. Then, simply execute:

mkdir ubuntu
cd ubuntu
apt-get source hello
cd ..
[Note]

Unlike most apt-get commands, you do not need to have root privileges to get the source package, because it is downloaded to the current directory. In fact, it is recommended that you only use apt-get source as a regular user, because then you can edit files in the source package without needing root privileges.

What the apt-get source command does is:

  1. Download the source package. A source package commonly contains a .dsc file describing the package and giving md5sums for the source package, an .orig.tar.gz file containing the source code from the author(s), and a .diff.gz file containing patches applied against the source code with the packaging information.

  2. Untar the .orig.tar.gz file into the current directory.

  3. Apply the gunzipped .diff.gz to the unpacked source directory.

If you manually download the source package (.dsc, .orig.tar.gz, and .diff.gz files), you can unpack them in the same way apt-get source does by using dpkg-source as follows:

dpkg-source -x *.dsc

The first thing you will need to do is make a copy of the original (sometimes called "upstream") tarball in the following format: <packagename>_<version>.orig.tar.gz. This step does two things. First, it creates two copies of the source code. If you accidentally change or delete the working copy you can use the one you downloaded. Second, it is considered poor packaging practice to change the original source tarball unless absolutely necessary. See the section called “Common Mistakes” for reasons.

cp hello-2.1.1.tar.gz hello_2.1.1.orig.tar.gz
tar -xzvf hello_2.1.1.orig.tar.gz
[Warning]

The underscore, "_", between the package name (hello) and the version (2.1.1), as opposed to a hyphen, "-", is very important. Your source package will incorrectly be built as a Debian native package.

We now have a hello-2.1.1 directory containing the source files. Now we need to create the customary debian directory where all the packaging information is stored, allowing us to separate the packaging files from the application source files.

mkdir hello-2.1.1/debian
cd hello-2.1.1/debian/

We now need to create the essential files for any Ubuntu source package: changelog, control, copyright, and rules. These are the files needed to create the binary packages (.deb files) from the original (upstream) source code. Let us look at each one in turn.

changelog

The changelog file is, as its name implies, a listing of the changes made in each version. It has a specific format that gives the package name, version, distribution, changes, and who made the changes at a given time. If you have a GPG key, make sure to use the same name and email address in changelog as you have in your key. The following is a template changelog:

package (version) distribution; urgency=urgency

  * change details
    more change details
  * even more change details

-- maintainer name <email address>[two spaces]  date

The format (especially of the date) is important. The date should be in RFC822 format, which can be obtained from the 822-date program.

Here is a sample changelog file for hello:

hello (2.1.1-1) dapper; urgency=low

   * New upstream release with lots of bug fixes.

-- Captain Packager <[email protected]>  Wed,  5 Apr 2006 22:38:49 -0700

Notice that the version has a -1 appended to it, or what is called the Debian revision, which is used so that the packaging can be updated (to fix bugs for example) with new uploads within the same source release version.

[Note]

Ubuntu and Debian have slightly different package versioning schemes to avoid conflicting packages with the same source version. If a Debian package has been changed in Ubuntu, it has ubuntuX (where X is the Ubuntu revision number) appended to the end of the Debian version. So if the Debian hello package was changed by Ubuntu, the version string would be 2.1.1-1ubuntu1. If a package for the application does not exist in Debian, then the Ubuntu version is 0 (e.g., 2.1.1-0ubuntu1).

Now look at the changelog for the Ubuntu source package that we downloaded earlier:

less ../../ubuntu/hello-2.1.1/debian/changelog

Notice that in this case the distribution is unstable (a Debian branch), because the Debian package has not been changed by Ubuntu. Remember to set the distribution to your target distribution release.

At this point create a changelog file in the debian directory where you should still be.


control

The control file contains the information that the package manager (such as apt-get, synaptic, and aptitude) uses, build-time dependencies, maintainer information, and much more.

For the Ubuntu hello package, the control file looks something like:

Source: hello
Section: devel
Priority: optional
Maintainer: Captain Packager <[email protected]> 
Standards-Version: 3.6.1

Package: hello
Architecture: any
Depends: ${shlibs:Depends}
Description: The classic greeting, and a good example
 The GNU hello program produces a familiar, friendly greeting.  It
 allows non-programmers to use a classic computer science tool which
 would otherwise be unavailable to them.
 .
 Seriously, though: this is an example of how to do a Debian
 package.
 It is the Debian version of the GNU Project's `hello world' program
 (which is itself an example for the GNU Project).

Create control using the information above (making sure to provide your information for the Maintainer field).

The first paragraph gives information about the source package. Let us go through each line:

  • Source: This is the name of the source package, in this case, hello.

  • Section: The apt repositories are split up into sections for ease of browsing and categorization of software. In this case, hello belongs in the devel section.

  • Priority: This sets the importance of the package to users. It should be one of the following:

    • Required - packages that are essential for the system to work properly. If they are removed it is highly likely that your system will break in an unrecoverable way.

    • Important - minimal set of packages for a usable system. Removing these packages will not produce an unrecoverable breakage of your system, but they are generally considered important tools without which any Linux installation would be incomplete. Note: This does not include things like Emacs or even the X Window System.

    • Standard - Somewhat self explanatory.

    • Optional - in essence this category is for non-required packages, or the bulk of packages. However, these packages should not conflict with each other.

    • Extra - packages that may conflict with packages in one of the above categories. Also used for specialized packages that would only be useful to people who already know the purpose of the package.

  • Maintainer: The package maintainer with email address.

  • Standards-Version: The version of the Debian Policy to which the package adheres (in this case, version 3.6.1). An easy way to find the current version is apt-cache show debian-policy | grep Version.

  • Build-Depends: One of the most important fields and often the source of bugs, this line lists the binary packages (with versions if necessary) that need to be installed in order to create the binary package(s) from the source package. Packages that are essential are required by build-essential and do not need to be included in the Build-Depends line. In the case of hello, all the needed packages are a part of build-essential, so a Build-Depends line is not needed. The list of build-essential packages can be found at /usr/share/doc/build-essential/list.

The second paragraph is for the binary package that will be built from the source. If multiple binary packages are built from the source package, there should be one section for each one. Again, let us go through each line:

  • Package: The name for the binary package. Many times for simple programs (such as hello), the source and binary packages' names are identical.

  • Architecture: The architectures for which the binary package(s) will be built. Examples are:

    • all - The source is not architecture-dependent. Programs that use Python or other interpreted languages would use this. The resulting binary package would end with _all.deb.

    • any - The source is architecture-dependent and should compile on all the supported architectures. There will be a .deb file for each architecture ( _i386.deb for instance)

    • A subset of architectures (i386, amd64, ppc, etc.) can be listed to indicate that the source is architecture-dependent and does not work for all architectures supported by Ubuntu.

  • Depends: The list of packages that the binary package depends on for functionality. For hello, we see ${shlibs:Depends}, which is a variable that substitutes in the needed shared libraries. See the dpkg-source man page for more information.

  • Recommends: Used for packages that are highly recommended and usually are installed with the package. Some package managers, most notably aptitude, automatically install Recommended packages.

  • Suggests: Used for packages that are similar or useful when this package is installed.

  • Conflicts: Used for packages that will conflict with this package. Both cannot be installed at the same time. If one is being installed, the other will be removed.

  • Description: Both short and long descriptions are used by package managers. The format is:

    Description: <single line synopsis>
     <extended description over several lines>

    Note that there is one space at the beginning of each line in the long description. More information on how to make a good description can be found at http://people.debian.org/~walters/descriptions.html.

copyright

This file gives the copyright information. Generally, copyright information is found in the COPYING file in the program's source directory. This file should include such information as the names of the author and the packager, the URL from which the source came, a Copyright line with the year and copyright holder, and the text of the copyright itself. An example template would be:

This package was debianized by {Your Name} <your email address>
{Date}

It was downloaded from: {URL of webpage} 

Upstream Author(s): {Name(s) and email address(es) of author(s)}

Copyright:
	Copyright (C) {Year(s)} by {Author(s)} {Email address(es)}

License:

As one can imagine, hello is released under the GPL license. In this case it is easiest to just copy the copyright file from the Ubuntu package:

cp ../../ubuntu/hello-2.1.1/debian/copyright .

You must include the complete copyright unless it is is GPL, LGPL, BSD, or Artistic License, in which case you can refer to the corresponding file in the /usr/share/common-licenses/ directory.

Notice that the Ubuntu package's copyright includes a license statement for the manual. It is important that all the files in the source be covered by a license statement.

rules

The rules file is an executable Makefile that has rules for building the binary package from the source packages. For hello, it will be easier to use the rules from the Ubuntu package:

#!/usr/bin/make -f
# Sample debian/rules file - for GNU Hello.
# Copyright 1994,1995 by Ian Jackson.
# I hereby give you perpetual unlimited permission to copy,
# modify and relicense this file, provided that you do not remove
# my name from the file itself.  (I assert my moral right of
# paternity under the Copyright, Designs and Patents Act 1988.)
# This file may have to be extensively modified

package = hello
docdir = debian/tmp/usr/share/doc/$(package)

CC = gcc
CFLAGS = -g -Wall
INSTALL_PROGRAM = install

ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
  CFLAGS += -O2
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
  INSTALL_PROGRAM += -s
endif

build:
        $(checkdir)
        ./configure --prefix=/usr
        $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)"
        touch build

clean:
        $(checkdir)
        rm -f build
        -$(MAKE) -i distclean
        rm -rf *~ debian/tmp debian/*~ debian/files* debian/substvars

binary-indep:   checkroot build
        $(checkdir)
# There are no architecture-independent files to be uploaded
# generated by this package.  If there were any they would be
# made here.

binary-arch:    checkroot build
        $(checkdir)
        rm -rf debian/tmp
        install -d debian/tmp/DEBIAN $(docdir)
        install -m 755 debian/postinst debian/prerm debian/tmp/DEBIAN
        $(MAKE) INSTALL_PROGRAM="$(INSTALL_PROGRAM)" \
                prefix=$$(pwd)/debian/tmp/usr install
        cd debian/tmp && mv usr/info usr/man usr/share
        cp -a NEWS debian/copyright $(docdir)
        cp -a debian/changelog $(docdir)/changelog.Debian
        cp -a ChangeLog $(docdir)/changelog
        cd $(docdir) && gzip -9 changelog changelog.Debian
        gzip -r9 debian/tmp/usr/share/man
        gzip -9 debian/tmp/usr/share/info/*
        dpkg-shlibdeps debian/tmp/usr/bin/hello
        dpkg-gencontrol -isp
        chown -R root:root debian/tmp
        chmod -R u+w,go=rX debian/tmp
        dpkg --build debian/tmp ..

define checkdir
        test -f src/$(package).c -a -f debian/rules
endef

binary: binary-indep binary-arch

checkroot:
        $(checkdir)
        test $$(id -u) = 0

.PHONY: binary binary-arch binary-indep clean checkroot

Let us go through this file in some detail. One of the first parts you will see is the declaration of some variables:

package = hello
docdir = debian/tmp/usr/share/doc/$(package)

CC = gcc
CFLAGS = -g -Wall
INSTALL_PROGRAM = install

ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
  CFLAGS += -O2
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
  INSTALL_PROGRAM += -s
endif

This section sets the CFLAGS for the compiler and also handles the noopt and nostrip DEB_BUILD_OPTIONS for debugging.

Next is the build rule:

build:
	$(checkdir)
	./configure --prefix=/usr
	$(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)"
	touch build

This rule runs ./configure with the proper prefix, runs make, and creates a build file that is a timestamp of the build to prevent erroneous multiple compilations.

The next rule is clean, which runs make -i distclean and removes the files that are made during the package building.

clean:
	$(checkdir)
	rm -f build
	-$(MAKE) -i distclean
	rm -rf *~ debian/tmp debian/*~ debian/files* debian/substvars

Next we see an empty binary-indep rule, because there are no architecture-independent files created in this package.

There are, however, many architecture-dependent files, so binary-arch is used:

binary-arch:    checkroot build
		$(checkdir)
		rm -rf debian/tmp
		install -d debian/tmp/DEBIAN $(docdir)
		install -m 755 debian/postinst debian/prerm debian/tmp/DEBIAN
		$(MAKE) INSTALL_PROGRAM="$(INSTALL_PROGRAM)" \
		prefix=$$(pwd)/debian/tmp/usr install
		cd debian/tmp && mv usr/info usr/man usr/share
		cp -a NEWS debian/copyright $(docdir)
		cp -a debian/changelog $(docdir)/changelog.Debian
		cp -a ChangeLog $(docdir)/changelog
		cd $(docdir) && gzip -9 changelog changelog.Debian
		gzip -r9 debian/tmp/usr/share/man
		gzip -9 debian/tmp/usr/share/info/*
		dpkg-shlibdeps debian/tmp/usr/bin/hello
		dpkg-gencontrol -isp
		chown -R root:root debian/tmp
		chmod -R u+w,go=rX debian/tmp
		dpkg --build debian/tmp ..

First, notice that this rule calls the checkroot rule to make sure the package is built as root and calls the build rule to compile the source. Then the debian/tmp/DEBIAN and debian/tmp/usr/share/doc/hello files are created, and the postinst and the prerm> scripts are installed to debian/tmp/DEBIAN. Then make install is run with a prefix that installs to the debian/tmp/usr directory. Afterward the documentation files (NEWS, ChangeLog, and the debian changelog) are gzipped and installed. dpkg-shlibdeps is invoked to find the shared library dependencies of the hello executable, and it stores the list in the debian/substvars file for the ${shlibs:Depends} variable in control. Then dpkg-gencontrol is run to create a control file for the binary package, and it makes the substitutions created by dpkg-shlibdeps. Finally, after the permissions of the debian/tmp have been set, dpkg --build is run to build the binary .deb package and place it in the parent directory.

postinst and prerm

The postinst and prerm files are examples of maintainer scripts. They are shell scripts that are executed after installation and before removal, respectively, of the package. In the case of the Ubuntu hello package, they are used to install (and remove) the info file. Go ahead and copy them into the current debian directory.

cp ../../ubuntu/hello-2.1.1/debian/postinst .
cp ../../ubuntu/hello-2.1.1/debian/prerm .

Building the Source Package

Now that we have gone through the files in the debian directory for hello in detail, we can build the source (and binary) packages. First let us move into the root of the extracted source:

cd ..

Now we build the source package using dpkg-buildpackage:

dpkg-buildpackage -S -rfakeroot

The -S flag tells dpkg-buildpackage to build a source package, and the -r flag tells it to use fakeroot to allow us to have fake root privileges when making the package. dpkg-buildpackage will take the .orig.tar.gz file and produce a .diff.gz (the difference between the original tarball from the author and the directory we have created, debian/ and its contents) and a .dsc file that has the description and md5sums for the source package. The .dsc and *_source.changes (used for uploading the source package) files are signed using your GPG key. If you do not have a GPG key, add the -us -uc flags to the invocation of dpkg-buildpackage. Your package will build but it cannot be uploaded until it is signed.

In addition to the source package, we can also build the binary package with pbuilder:

sudo pbuilder build ../*.dsc

Using pbuilder to build the binary packages is very important. It ensures that the build dependencies are correct, because pbuilder provides only a minimal environment, so all the build-time dependencies are determined by the control file.

We can check the source package for common mistakes using lintian:

cd ..
lintian -i *.dsc