Building Your Own Package

This section will lead you through an example of package creation, using the GNU hello program. Since this program is increidbly well-behaved, it won't require many of the features listed in the previous chapter.

The Template

In the base gar directory is a file called pkg-template.mk. This contains a quick form to fill out in order to get started. Make a directory for your package and copy pkg-template.mk to Makefile in your package's directory.

$ mkdir utils/hello
$ cp pkg-template.mk utils/hello/Makefile
$ cd utils/hello

Begin by taking responsibility for your package, editing the Makefile and filling out the MAINTAINER variable with your e-mail address.

MAINTAINER = J. Random Hacker <haesslich@zork.net>

For aesthetic purposes, get rid of the pre-install rule at the bottom of the file, and set the CATEGORIES.

CATEGORIES = utils

Fetch

Our first step is to make sure the source archives can be fetched successfully. The key elements here are the MASTER_SITES and DISTFILES. The first step is to hunt around and find the upstream source archive.

GNU hello is, like all GNU projects, released officially on the ftp://ftp.gnu.org/gnu/ site. A quick search finds the ftp://ftp.gnu.org/gnu/hello/ directory, and the most recent version up at the ftp://ftp.gnu.org/gnu/hello/hello-2.1.1.tar.gz URL.

Looking at this URL we must dissect it into two parts, the filename and the directory URL part. The directory URL (including the trailing slash) goes into MASTER_SITES, and the filename goes into DISTFILES.

MASTER_SITES = ftp://ftp.gnu.org/gnu/hello/
DISTFILES = $(GARNAME)-$(GARVERSION).tar.gz

Now that DISTFILES looks like it fits the pattern of the filename, so it's now up to us to fill in those two portions.

GARNAME = hello
GARVERSION = 2.1.1

To test this, we now run "make fetch".

$ make fetch
[===== NOW BUILDING:	hello-2.1.1	=====]
install -d download
 ==> Grabbing download/hello-2.1.1.tar.gz
 	==> Trying file//files/hello-2.1.1.tar.gz
make[1]: *** [file//files/hello-2.1.1.tar.gz] Error 1
 	==> Trying
file///var/www/garchive/hello-2.1.1/hello-2.1.1.tar.gz
make[1]: *** [file///var/www/garchive/hello-2.1.1/hello-2.1.1.tar.gz]
Error 1
 	==> Trying ftp//ftp.gnu.org/gnu/hello/hello-2.1.1.tar.gz
--21:58:16--  ftp://ftp.gnu.org/gnu/hello/hello-2.1.1.tar.gz
           => `download/hello-2.1.1.tar.gz'
Resolving ftp.gnu.org... done.
Connecting to ftp.gnu.org[199.232.41.7]:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD /gnu/hello ... done.
==> PASV ... done.    ==> RETR hello-2.1.1.tar.gz ... done.
Length: 389,363 (unauthoritative)

100%[====================================>] 389,363       34.99K/s ETA
00:00

21:58:29 (34.99 KB/s) - `download/hello-2.1.1.tar.gz' saved [389363]

	[fetch] complete for hello.

Don't worry about those Error 1 lines. They're saying that GAR didn't find the file to be fetched in any already-downloaded locations. The system will try local dirs first, then on to the upstream site, and finally to our haphazardly-maintained archive of packages we've grabbed.

We now check the tarball that's in the download/ dir to make sure it's what we wanted, and move on to the next step.

Checksum

Once we're certain that we have our file locations worked out, we can make a set of md5 signatures. To do so, just run "make makesum".

$ make makesum
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
Checksums made for download/hello-2.1.1.tar.gz 

Now you'll notice a file called "checksums" containing md5 signatures for all the DISTFILES.

$ ls
Makefile  checksums  download/
$ cat checksums 
70c9ccf9fac07f762c24f2df2290784d  download/hello-2.1.1.tar.gz

Now when we run "make checksum", we see that the checksums match and the file validates.

$ make checksum
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
install -d cookies
 ==> Running checksum on hello-2.1.1.tar.gz
70c9ccf9fac07f762c24f2df2290784d  download/hello-2.1.1.tar.gz
file hello-2.1.1.tar.gz passes checksum test!
	[checksum] complete for hello.

Extract

Since hello-2.1.1.tar.gz is a gzipped tarball, we can just let GAR figure out how to handle extraction. We run "make extract", and let the system unpack.

$ make extract
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
	[checksum] complete for hello.
install -d work
 ==> Extracting download/hello-2.1.1.tar.gz
	[extract] complete for hello.

Now our tarball has been unpacked into the work/ subdirectory.

$ ls
Makefile  checksums  cookies/  download/  work/
$ ls work
hello-2.1.1/
$ ls work/hello-2.1.1/
ABOUT-NLS   INSTALL        TODO        configure*  intl/
src/        AUTHORS        Makefile.am aclocal.m4  configure.ac
m4/         tests/         BUGS        Makefile.in config.guess*
contrib/    man/           COPYING     NEWS        config.h.in
depcomp*    missing*       ChangeLog   README      config.rpath*
doc/        mkinstalldirs* ChangeLog.O THANKS      config.sub*
install-sh* po/

Patch

Patching is a last-resort technique, as far as GAR packaging is concerned. It's finnicky, causes problems when upstream upgrades occur, and generally is kept around as a last resort. Of all the situations where a developer feels the need to patch, perhaps one in ten is a legitimate reason to patch. The Gar Tips file contains a list of techniques for avoiding patching, but here are a few in the context of how to avoid patching:

Override Make Vars
Perhaps you're patching a Makefile or Makefile.in to set some variable. Look up the Override Make Vars Gar Tip for a better way to set that variable that won't involve a patch.

Concatenation
Most programming or scripting languages are procedural, and events that come later in a file will adjust the state set up by commands that came earlier. If you're looking to adjust some state, consider using "echo command >> $(WORKSRC)/some-source-file" in a pre- or post-configure rule to add a command that comes LAST in the source file or script that does what you want. Most often this has the effect of overriding the existing behavior, and it is resistant to change in upstream source files.

That said, if you're still interested in patches, look at the "make makepatch" command, and check out the PATCHFILES variable.

Configure

The key to configuration is the CONFIGURE_SCRIPTS variable. As this is a GNU package, it uses GNU Autoconf to configure the source tree for compilation. This is the most common format that we find source code packages, and it is nearly a Unix-wide standard now.

CONFIGURE_SCRIPTS = $(WORKSRC)/configure
CONFIGURE_ARGS = $(DIRPATHS)

These defaults (which came with the pkg-template.mk) should work just fine. $(DIRPATHS) contains a set of configure arguments that set the directories where we want the package's files to ultimately live. We run them, and get the following:

$ make configure
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
	[checksum] complete for hello.
	[extract] complete for hello.
	[patch] complete for hello.
 ==> Running configure in work/hello-2.1.1
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets ${MAKE}... yes
checking for gcc... gcc

...lots of checks follow, leading ultimately to...

config.status: creating src/Makefile
config.status: creating tests/Makefile
config.status: creating config.h
config.status: executing depfiles commands
config.status: executing default-1 commands
config.status: creating po/POTFILES
config.status: creating po/Makefile
config.status: executing default commands
	[configure] complete for hello.

Build

Since GNU hello uses Autoconf and Automake, we get a standard Makefile for building and installing. Again, the default is good enough for us.

BUILD_SCRIPTS = $(WORKSRC)/Makefile

So we run "make build" and get a lot of compilation output

$ make build
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
	[checksum] complete for hello.
	[extract] complete for hello.
	[patch] complete for hello.
	[configure] complete for hello.
 ==> Running make in work/hello-2.1.1
make[1]: Entering directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1'
make  all-recursive
make[2]: Entering directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1'
Making all in contrib

culminating in the magic message

make[1]: Leaving directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1'
	[build] complete for hello.

Install

Since the GNU hello Makefile also handles installation just the way we like, using $(DESTDIR) as an installation superprefix for the standard dirs, we can also accept the default:

INSTALL_SCRIPTS = $(WORKSRC)/Makefile

Thus, running "make install" gives us more voluminous output.

$ make install
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
	[checksum] complete for hello.
	[extract] complete for hello.
	[patch] complete for hello.
	[configure] complete for hello.
	[build] complete for hello.
 ==> Running make install in work/hello-2.1.1
make[1]: Entering directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1'
Making install in contrib
make[2]: Entering directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1/contrib'
make[3]: Entering directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1/contrib'

...ending with the magical message...

make[3]: Leaving directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1'
make[2]: Leaving directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1'
make[1]: Leaving directory
`/home/nick/bbc/gar/utils/hello/work/hello-2.1.1'
	[install] complete for hello.

Now is the time to check that everything was installed properly in your $(DESTDIR). The true test of this is to see if your "make tarball" works right.

$ make tarball
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
	[checksum] complete for hello.
	[extract] complete for hello.
	[patch] complete for hello.
	[configure] complete for hello.
	[build] complete for hello.
rm -rf cookies/install*
make DESTDIR=/home/nick/bbc/gar/utils/hello/tmp
BUILD_PREFIX=/../../../../../../..//../../../home/nick/built/ install
make[1]: Entering directory `/home/nick/bbc/gar/utils/hello'
[===== NOW BUILDING:	hello-2.1.1	=====]
	[fetch] complete for hello.
	[checksum] complete for hello.
	[extract] complete for hello.
	[patch] complete for hello.
	[configure] complete for hello.
	[build] complete for hello.

As you can see, the tarball rule does some feints before actually building a tarball for your package. First it does a normal build, but then it plays a few games with cookie dirs, and that's when things can get wonky. Eventually it does its install into a tmp dir, and then builds the tarball:

	[install] complete for hello.
make[1]: Leaving directory `/home/nick/bbc/gar/utils/hello'
find tmp -depth -type d | while read i; do rmdir $i > /dev/null 2>&1
|| true; done
tar czvf
/home/nick/bbc/gar/utils/hello/work/hello-2.1.1-install.tar.gz -C tmp
../
./bin/
./bin/hello
./share/

This goes on for every file it has installed, ending up with

./share/locale/uk/LC_MESSAGES/
./share/locale/uk/LC_MESSAGES/hello.mo
mkdir -p cookies/. && date >> cookies/tarball

Now we should see a tarball in work/ with the word "install" in the filename.

$ ls
Makefile  checksums  cookies/  download/  tmp/  work/
$ ls work/
hello-2.1.1/  hello-2.1.1-install.tar.gz

Now we can check our tarball to see if it has what we want

$ tar tvzf work/hello-2.1.1-install.tar.gz | head -30
drwxr-xr-x nick/nick         0 2002-09-15 23:23:14 ./
drwxr-xr-x nick/nick         0 2002-09-15 23:23:13 ./bin/
-rwxr-xr-x nick/nick     11095 2002-09-15 23:23:13 ./bin/hello
drwxr-xr-x nick/nick         0 2002-09-15 23:23:13 ./share/
drwxrwxr-x nick/nick         0 2002-09-15 23:23:12 ./share/locale/
drwxrwxr-x nick/nick         0 2002-09-15 23:23:10 ./share/locale/ca/

(lots more output follows)

Or we can inspect the tmp/ dir to see what was installed by the package.

$ tree tmp/
tmp/
|-- bin
|   `-- hello
`-- share
    `-- locale
        |-- ca
        |   `-- LC_MESSAGES
        |       `-- hello.mo
        |-- da
        |   `-- LC_MESSAGES
        |       `-- hello.mo

(lots more follows)

        |-- tr
        |   `-- LC_MESSAGES
        |       `-- hello.mo
        `-- uk
            `-- LC_MESSAGES
                `-- hello.mo

65 directories, 32 files

Our final Makefile

Our final utils/hello/Makefile looks like so

GARNAME = hello
GARVERSION = 2.1.1
CATEGORIES = utils
MASTER_SITES = ftp://ftp.gnu.org/gnu/hello/
DISTFILES = $(GARNAME)-$(GARVERSION).tar.gz
MAINTAINER = J. Random Hacker <haesslich@zork.net>

DESCRIPTION = The classic greeting, and a good example 
define BLURB
  The GNU hello program produces a familiar, friendly greeting.
  It allows nonprogrammers to use a classic computer science tool
  which would otherwise be unavailable to them.

  In all seriousness, this is an example of how to do a GAR
  package.  It is the GAR version of the GNU Project's `hello
  world' program (which is itself an example for the GNU 
  Project).
endef

CONFIGURE_SCRIPTS = $(WORKSRC)/configure
BUILD_SCRIPTS = $(WORKSRC)/Makefile
INSTALL_SCRIPTS = $(WORKSRC)/Makefile

CONFIGURE_ARGS = $(DIRPATHS)

include ../../gar.mk

We then add the Makefile and checksums files to CVS, check our work in, and we are done packaging GNU hello!