Recipe Author Guide


Table of Contents

1. Legal Information
1.1. Copyright and License
1.2. Disclaimer
2. Prerequisites
3. Anatomy of a Package Recipe
3.1. Indicating the interpreter and general comments
3.2. Specifying legal information
3.3. Defining package and directory names
3.4. Checking the status of directories
3.5. Configuring the package
3.6. Building the package
3.7. Installing into the staging area
3.8. Tweaking the staging area
4. Creating Recipes the Easy Way

Abstract

This guide gives an overview of how to create recipe scripts to be used with the GNU/Linux System Architect Toolkit.

1. Legal Information

1.1. Copyright and License

This document is copyright (c)2003 by David Horton.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front Cover Texts, and with no Back Cover Texts. A copy of the license is available at http://www.gnu.org/copyleft/fdl.html.

1.2. Disclaimer

No liability for the contents of this document can be accepted. Use the concepts, examples and information at your own risk. There may be errors and inaccuracies, that could be damaging to your system. Proceed with caution, and although this is highly unlikely, the author(s) do not take any responsibility.

All copyrights are held by their by their respective owners, unless specifically noted otherwise. Use of a term in this document should not be regarded as affecting the validity of any trademark or service mark. Naming of particular products or brands should not be seen as endorsements.

2. Prerequisites

Readers of this guide should already be familiar with the concept of a package recipe and how it is used to automate building source code into binary packages. It is also necessary to understand the process of manually building and installing packages from source code.

3. Anatomy of a Package Recipe

This section gives a step-by-step description of the various parts in a package recipe.

3.1. Indicating the interpreter and general comments

Recipes are basically shell scripts so the first line needs to be the familiar #!/bin/bash interpreter declaration. Next there should be a brief description indicating the source package that the recipe is used for and also some very brief information about what the package is good for.

The following example is taken from the sysvinit-static recipe:

#!/bin/bash
#
# Recipe script to build sysvinit-2.85 with statically-linked init and
# sulogin utilities. 
#
# Static linking provides an extra degree of fault-tolerance by
# ensuring that init can still function even if libraries become
# corrupted or deleted.  The best fault-tolerance can be achieved
# when sysvinit-static is used with bash-static as a root shell.
#

3.2. Specifying legal information

Every recipe used in the Architect Toolkit must have a copyright notice, contact email address and GNU GPL license declaration. The copyright and GPL license ensure that any recipe created can be freely distributed and modified. The contact email address is for feedback and bug fixing. It is acceptable to obscure email addresses to avoid unsolicited commercial email (aka spam.)

Here is an example:

# Copyright (c)2003 David Horton
# dhorton@nospam.megsinet.net
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# http://www.gnu.org/licenses/gpl.txt
#
# A copy of the GNU GPL may be obtained on the web at:
#
#   http://www.gnu.org/licenses/gpl.txt
#
# or by writing to:
#
#   Free Software Foundation, Inc.
#   59 Temple Place - Suite 330
#   Boston, MA 02111-1307, USA
#

3.3. Defining package and directory names

Each recipe takes a source code package and builds it into a binary package. During this process the recipe needs to know the name of the package and the location of directories for source code, building and staging.

Here is an example that can be used verbatim in most recipies:

# Define the package name, source directory, build directory and
# staging directory paths.  Prefixes to directories may be overridden
# by setting shell variables SRC, BUILD and STAGING.  For example,
# doing "export SRC=/root/foo" before calling this script will override
# the default /usr/src and set SRC_DIR to /root/foo/$PKG_NAME instead.
# 
PKG_NAME=$(basename $0)
SRC_DIR=${SRC:-/usr/src}/$PKG_NAME
BUILD_DIR=${BUILD:-/var/tmp/build}/$PKG_NAME
STAGING_DIR=${STAGING:-/var/tmp/staging}/$PKG_NAME
#

The package name is derived from the name of the recipe script using the basename utility. For example, if the recipe is called bash-2.05a then the package name is also set to bash-2.05a. The basename utility is responsible for chopping off the path so if the recipe script happens to be run as ~/recipes/bash-2.05a or something similar the package name is still just bash-2.05a. See the basename man page for details

Once the package name is known it is used to derive the source, build and staging directory names. Each directory has a default prefix that is used to define the path. For source it is /usr/src. Build and staging use /var/tmp/build and /var/tmp/staging prefixes, respectively. The notation ${variable:-value} is used to define default values for path prefixes. For additional information on how this works, search the BASH man page for the string "parameter:-word". Recipe authors should not deviate from this method of defining paths except in the special case of modifying the source code path described below.

The source code directory is usually set to /usr/src/package-name, but there are times when it is necessary to alter the name. The most common case is when creating a recipe to build a package that has statically linked binaries.

The following example is taken from sysvinit-static-2.85:

PKG_NAME=$(basename $0)
SRC_DIR=${SRC:-/usr/src}/$PKG_NAME
# Remove "-static" from the source path using sed.
SRC_DIR=$(echo "$SRC_DIR" | sed s/-static//) 

The -static portion of the recipe name sysvinit-static-2.85 is used to differentiate the recipe and resulting binary package from the regular, shared library sysvinit-2.85. Although the recipes have different names, they both use the same source code and the source directory does not contain the string -static.

3.4. Checking the status of directories

Recipes should always check for the existence of source, build and staging directories before attempting to build a package. Specifically the source directory must exist and the build and staging directories must not exist. Checking for non-existent build and staging directories helps to avoid possibly overwriting important files.

The following example should be used in all recipes and should not need to be altered.

# Check that the source directory really exits and bail out if not.
if ! [ -d $SRC_DIR ]; then
  echo "Cannot find source code directory $SRC_DIR."
  exit 1
fi
#
# Do not overwrite existing build directory.
if [ -d $BUILD_DIR ]; then
  echo "Build directory $BUILD_DIR already exists."
  exit 2
else
  mkdir -p $BUILD_DIR
fi
#
# Do not overwrite existing staging directory.
if [ -d $STAGING_DIR ]; then
  echo "Staging directory $STAGING_DIR already exists."
  exit 4
else
  mkdir -p $STAGING_DIR
fi
#

3.5. Configuring the package

After the status of the directories has been verified, package configuration can begin. The package configuration step has two main goals.

  1. CPU compatibility

  2. FHS compliance

Configuring for CPU compatibility ensures the resulting binary package will run on a variety of x86 hardware. This is done by choosing an i386 as the target CPU. Since x86 processors are backward compatible any binaries built for an i386 will also run on 486, 586, and so on.

Configuring for FHS compliance means that the locations of files in the binary package will fit the criteria defined in the Filesystem Hierarchy Standard (FHS). Usually this involves setting the CC environmental variable and using command-line options with the ./configure script.

The following example from the findutils recipe shows the CC environmental variable being set and many of the common ./configure options being used.

# Configure the package for FHS compliance and i386 processor.
export CC="gcc -mcpu=i386"
cd $BUILD_DIR
$SRC_DIR/configure \
  --host=i386-pc-linux-gnu \
  --infodir=/usr/share/info \
  --libexecdir=/usr/sbin \
  --localstatedir=/var/cache \
  --mandir=/usr/share/man \
  --prefix=/usr
#

3.6. Building the package

Building is very easy for packages that feature a ./configure script and requires only the make command. Examples of this can be seen in the recipes for coreutils, e2fsprogs, findutils and several others.

Packges that do not have a ./configure script are usually built by specifying configuration options along with the make command. The following example taken from the sysvinit-static recipe shows the configuration option STATIC=-static being passed to make along with the CC option for generating i386 code.

make CC="gcc -mcpu=i386" STATIC="-static"

3.7. Installing into the staging area

Binary packages are not installed directly onto the target system. Instead they are installed into a staging area. This task is almost always accomplished by using the make command with a number of variables and the word install.

The following example is from the sed recipe:

# Install into staging area overriding make variables as needed.
make \
  exec_prefix=$STAGING_DIR \
  infodir=$STAGING_DIR/usr/share/info \
  mandir=$STAGING_DIR/usr/share/man \
  prefix=$STAGING_DIR/usr \
  install

All of the options used in the ./configure script for sed are included as variables passed to the make install command and their values are preceeded by $STAGING_DIR. This type of installation applies to every recipe that uses the ./configure script. Packages that do not use a configure script also need to have variables passed to make install. The varible names and usage will differ from package to package so it is wise to read the installation instructions.

Here is an example from the procps recipe:

make DESTDIR=$STAGING_DIR SHARED=0 install

In this case the DESTDIR variable is used to install the entire package into the staging directory.

3.8. Tweaking the staging area

Many times it may be difficult or impossible to acheive FHS compliance using available configuration options. In cases like this it is necessary to move files after installing into the staging area.

Here is an example from the coreutils recipe:

# Any utilities listed in FHS requirements for /bin get moved there.
# All others stay in /usr/bin.
FILES_TO_MOVE="cat chgrp chmod chown cp date dd df hostname ln ls mkdir mkfifo mknod mv rm rmdir stty su sync uname"
mkdir $STAGING_DIR/bin
for FILENAME in $FILES_TO_MOVE; do
  mv $STAGING_DIR/usr/bin/$FILENAME $STAGING_DIR/bin;
done
#

According to FHS some of the binaries from coreutils belong in the / hierarchy while others can go into the /usr hierarchy. The ./configure script features the prefix option or the exec-prefix option to control the location of binaries. Either way all of the binaries are installed into the same hierarchy. One way to acheive FHS compliance is to install into the /usr hierarchy using the prefix option and to later move any files that FHS specifies as being in the / hierarchy.

In addition to tweaking for FHS compliance it may be necessary to remove certain files or directories. Most often there is a dir file in the /usr/share/info directory that needs to be removed so that it will not overwrite the dir file on the target system. Examples of this can be seen in the coreutils, findutils and glibc recipes among others.

4. Creating Recipes the Easy Way

Most source code packages are built in much the same way and this is especially true with packages that use the ./configure script. Often times it is easier to modify an existing recipe rather than to create a new recipe completely from scratch. Take a look at the coreutils, findutils and sed recipes. The coreutils recipe was written first and findutils and sed were created by copying the coreutils recipe and then modifying a few lines. The bulk of the recipe is the same. So before spending hours typing up a brand new recipe, authors should take a look at existing recipes.