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.
The example e-mail address is a spam trap, and will list any mails sent to it with the vipul's razor spam registry. One of the BBC developers, Seth Schoen, notes that his last name is German for "beautiful", and so set up a spam trap using the German word for "ugly".
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!