Writing A SlackBuild Script

From SlackWiki
Jump to: navigation, search


Introduction

Originally written by Florian Mueller jjdm@jjdm.org
Substantial cleanup and enhancement by Robby Workman (rworkman)

If you use slackware as your main operating system, you have probably wanted to install quite a few applications which are not available in the official slackware.com or even third-party repositories like linuxpackages.net, or perhaps you just don't like using third-party packages. In this situation, you have several options on how to install the application:

* ./configure && make && make install
* use checkinstall
* use installwatch
* compile and use makepkg by hand
* write a SlackBuild script

I will go through the last option: writing SlackBuild Scripts (which combines the best qualities of all the other aforementioned methods). With a SlackBuild script, you have the build process automated, which will allow you to easily do later upgrades or patches to the package. SlackBuild scripts are also the method by which Patrick Volkerding builds all of the official packages for Slackware. If you look at the various scripts from different sources, you will notice that there is generally an application-independent portion of a script and an application-specific portion of the script.

I cannot teach you how to build the "perfect" package, as reaching that goal requires fairly in-depth knowledge of the Slackware operating system. You must consider the interactions of your proposed package with all of the other packages within the distribution; they must be integrated seamlessly. What I can teach you is how to build a package that works and which stays true to the "Slackware Way."

"But it takes so much time!"

It will take approximately thirty minutes to go through this tutorial and about fifteen minutes to create each package (actual compile process not included), but the time you save in the future (you want to create a newer version of the package) makes the initial time expenditure worth it.

The Slackware package structure

See Packages#Slackware Package Layout

Setting up your build environment

See Build_Environment for examples of how various users do this.

Getting Started

Hopefully, everything is now clear about Slackware package structure, and you have set up a clean build environment, so we'll begin the process of building a package with a SlackBuild script.

For this example, we'll create a package of latex2html - I made my homepage with that tool.

First, you have to create a directory named <build_environment>/latex2html/. Get the most recent source code release of latex2html place it in this directory. Note that use of wget below to obtain the most recent source code is optional - you can just as well use your favorite web browser to download it, and then move it into the correct directory.

$ cd <build_environment>
$ mkdir latex2html
$ cd latex2html
$ wget http://www.latex2html.org/~latex2ht/current/latex2html-2002-2-1.tar.gz # 03.27.2012

Next, we'll create some other needed files with touch. If you're not familiar with touch, see:

man touch

Note that the *.SlackBuild file will always contain the name of the application for which it's written; for example, gaim would have gaim.SlackBuild.

$ touch latex2html.SlackBuild
$ touch slack-desc

Extract the source code of the application, because we'll need to look at the configure script later on to determine what options we need to pass to it.

$ tar -xzf latex2html-2002-2-1.tar.gz || exit 1

Writing the slack-desc file

See this Slack-desc page on SlackWiki.org for instructions on how to write a proper slack-desc file.

Writing the SlackBuild script

This is the section which takes the most time, and I'll go through it with you step by step. When you build more packages, you'll probably be able to just copy an existing SlackBuild script and customize it. First, you need to understand that you can write your SlackBuild script in any manner you choose so long as it creates a working package; the method described here is more or less the way Pat Volkerding [[1]] does it, but even Pat has several different styles for writing the official SlackBuild scripts. Therefore, if you see something you would do a different way, feel free to do it that way - it's okay.

Initial Setup

Open the file latex2html.SlackBuild with your favourite editor. What follows below is a piece by piece walk-through of a working SlackBuild script. You may certainly paste the exact contents of those pieces, but in the author's opinion, you have a better chance of understanding it if you write everything yourself.

First, you'll need to set your shell interpreter. This should be /bin/sh, as *every* Slackware system is guaranteed to have this shell installed, and you want maximum portability. For this same reason, be careful not to use any extensions and/or syntax that is customized for your particular shell (bash, zsh, or whatever), as it won't be interpreted correctly. The '-e' flag tells the shell to exit on any error; this helps with both debugging your script as well as ensuring your script does not proceed in an unknown state.

#!/bin/sh -e

You might want to include a license of some sort with your SlackBuild script (preferably a GPL or BSD-style license), but at a minimum, you'll want something like this:

#<your name> revision date yyyy/mm/dd

With the next few lines, we set some variables that will be used throughout the script. First is the "CWD" variable; in our case, CWD will be <build_environment>/latex2html/. We also test if the TMP variable is set, and if not, we set it to /tmp.

#Set initial variables:	

CWD=$(pwd)
if [ "$TMP" = "" ]; then
  TMP=/tmp
fi

Some people like to build in a subdirectory of /tmp (such as /tmp/build), but that's up to you.

# The version which appears in the application's filename
VERSION=2002-2-1 	

# If the version conflicts with the Slackware package standard
# The dash character ("-") is not allowed in the VERSION string
# You can set the PKG_VERSION to something else than VERSION
PKG_VERSION=2002.2.1 # the version which appears in the package name. 

ARCH=${ARCH:-i486} # the architecture on which you want to build your package	

# First digit is the build number, which specifies how many times it has been built.	
# Second string is the short form of the authors name, typical three initials:w
BUILD=${BUILD:-1_rlw}

# The application's name
APP=latex2html

# The installation directory of the package (where its actual directory
# structure will be created) 
PKG=$TMP/package-$APP

Set SLKCFLAGS (which will be used for both CFLAGS and CXXFLAGS). If you are building on a system with an earlier version of gcc than 3.4.x, then you'll need to use "-mcpu" instead of "-mtune" below.

if [ "$ARCH" = "i486" ]; then
  SLKCFLAGS="-O2 -march=i486 -mtune=i686"
 elif [ "$ARCH" = "x86_64" ]; then
  SLKCFLAGS="-O2 -fPIC"
fi

The section just finished sets up a few application-specific variables. When you want to create a package of some other application, you can usually just change the variables, and most of the further steps will work automatically.

Extract Sources

# Delete the leftover directories if they exist (due to a previous build)
# and (re)create the packaging directory
rm -rf $PKG 
mkdir -p $TMP $PKG
rm -rf $TMP/$APP-$VERSION

# Change to the TMP directory
cd $TMP || exit 1
 
# Extract the application source in TMP
# Note: if your application comes as a tar.bz2, you need tar -jxvf
tar -zxvf $CWD/$APP-$VERSION.tar.gz || exit 1

# Change to the application source directory
cd $APP-$VERSION || exit 1
 
# Change ownership and permissions if necessary
# This may not be needed in some source tarballs, but it never hurts
chown -R root:root .
chmod -R u+w,go+r-w,a-s .

Configure and Compile Sources

# Set configure options
# If your app is written in C++, you'll also need to add a line for CXXFLAGS
CFLAGS="$SLKCFLAGS" \
  ./configure \
  --prefix=/usr \
  --sysconfdir=/etc \
  --localstatedir=/var \
  --with-perl=/usr/bin/perl \
  --enable-eps \
  --enable-gif \
  --enable-png \
  --build=$ARCH-slackware-linux \
  --host=$ARCH-slackware-linux 

# compile the source, but exit if anything goes wrong
make || exit
 
# Install everything into the package directory, but exit if anything goes wrong
make install DESTDIR=$PKG || exit

There are three configure options I always set:

  • --prefix=/usr
  • --sysconfdir=/etc
  • --localstatedir=/var

This makes configuration files go to /etc, state files (such as log files) go to /var, and the rest goes to /usr. That's the usual Slackware way, but it's your system, so you can certainly install everything in /usr/local or some other location. See the Unix Filesystem Hierarchy Standard [[2]] for more information on "correct" locations of various filetypes.

You notice that there were several other options passed to the configure script, and for each application you compile, you have to figure those out for yourself - that's why you were told to extract the sources earlier in this process. You simply cd into the source directory and run:

./configure --help

This will produce a page or two (sometimes more, though) of information about various options that are specific to the application. Read through this information and figure out what you need (I like to pipe that command through lpr to get a printed copy, but you can certainly use some sort of pager as well:

./configure --help | lpr
./configure --help | less

The DESTDIR variable is very important in this script because it specifies the directory in which the files should be installed. This should always be our package directory ($PKG). Unfortunately, some applications' Makefiles will not support the DESTDIR variable, so you can't use it for those apps. A simple line like this:

grep DESTDIR Makefile*

while inside the source directory should tell you whether it supports DESTDIR or not. If you get some lines of output with $DESTDIR in them, you're in good shape. If the command returns no output, then the Makefile does not support the DESTDIR variable.

Here's a piece of advice: ALWAYS go through the ./configure && make && make install DESTDIR=/somedir process manually and as a NORMAL USER account BEFORE you run your SlackBuild script. There are quite a few applications out there which try to do "funny stuff" during the installation phase.

For example, apcupsd will attempt to patch your /etc/rc.d/rc.6 init script
Yes, it's possible to avoid this with a configure option, but it's not obvious that you would need
to do so until you look at all of the Makefiles for apcupsd (or watch the install process)

Anyway, if you go through the process as a normal user, you will get "Permission Denied" errors and such if the install process tries to write anywhere it's not allowed to do so.

Install Documentation

# Create a directory for documentation
mkdir -p $PKG/usr/doc/$APP-$VERSION

# Copy documentation to the docs directory and fix permissions
cp -a BUGS Changes FAQ INSTALL LICENSE MANIFEST README TODO docs/ $PKG/usr/doc/$APP-$VERSION
find $PKG/usr/doc/$APP-$VERSION -type f -exec chmod 644 {} \;

I (rworkman) also like to place a copy of my SlackBuild script in this directory

cat $CWD/$APP.SlackBuild > $PKG/usr/doc/$APP-$VERSION/$APP.SlackBuild

Make sure you look inside the actual source archive of the application, because some applications won't have all of the documentation files specified above, and some applications will have additional files. In other words, don't just copy/paste what you see above into your SlackBuild script - you *must* customize this section for each individual application.

Final Touches

# Create the ./install directory and copy the slack-desc into it
mkdir -p $PKG/install
cat $CWD/slack-desc > $PKG/install/slack-desc

NOTE: In some cases, you will have some sort of command or setup that needs to run after the package contents are installed - for this, you would add a file to $CWD called "doinst.sh" which contains the needed commands, and then compress that file with gzip. The SlackBuild script will zcat (which means to gunzip and cat its contents) that file and write the output to a doinst.sh file in the $PKG/install directory.

# Add doinst.sh to package (if it exists)
if [ -e $CWD/doinst.sh.gz ]; then
  zcat $CWD/doinst.sh.gz > $PKG/install/doinst.sh
fi

Let's conserve space if we can; strip libraries and binaries and compress man pages with gzip Note that you might be able to use "make install-strip" instead of "make install" above instead to accomplish the same purpose

# Strip some libraries and binaries
( cd $PKG
   find . | xargs file | grep "executable" | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null
   find . | xargs file | grep "shared object" | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null
)

# Compress man pages if they exist
if [ -d $PKG/usr/man ]; then
  ( cd $PKG/usr/man
  find . -type f -exec gzip -9 {} \;
  for i in $(find . -type l) ; do ln -s $(readlink $i).gz $i.gz ; rm $i ; done
  ) 
fi

# Compress info pages if they exist (and remove the dir file)
if [ -d $PKG/usr/info ]; then
  gzip -9 $PKG/usr/info/*.info
  rm -f $PKG/usr/info/dir
fi

Build the Package

# Build the package
cd $PKG
/sbin/makepkg -l y -c n $TMP/$APP-$PKG_VERSION-$ARCH-$BUILD.tgz

Other Concerns

DESTDIR Option Not Available

As mentioned above, there are quite a few applications whose Makefiles do not support the DESTDIR option for make install. On some applications the DESTDIR Makefile variable has another name. For example, some Qt applications use the variable INSTALL_ROOT for the same purpose. If you can understand Makefiles, it is probably worth your time to take a look at its contents and try to find out which actions are performed in the install rule. Sometimes there will be no DESTDIR equivalent at all. The best thing you can do in this situation is write a patch for the Makefile.in or equivalent, and submit it to the developer(s) for inclusion in the source, but I realize that everyone doesn't have the ability to do that. The second best thing you can do it write to the developer(s) and ask them to include that functionality in future releases. In the meantime, here are some thoughts on the subject...

Example 1:

Configure the build with:

./configure --prefix=$PKG/usr

along with your other configure options. This will install *all* of the package contents in that directory. If the package creates $PKG/usr/etc and $PKG/usr/var directories (or any other directories that should be elsewhere), you can probably just move them to their correct location within the package directory tree and everything will be fine. You might also try this along with your other configure options.

./configure --prefix=$PKG/usr \
   --sysconfdir=$PKG/etc \
   --localstatedir=$PKG/var 

There are some applications, however, which "hard-code" configuration files based on configure/Makefile parameters. In those cases, you'll have to figure out a way to patch the config file prior to packaging it, or in worst-case scenario, include instructions for the end user on how to make the necessary changes.

Example 2:

This example makes use of the ability to override any Makefile variable, which is called a macro in the Makefile terminology, on the command line and not have to worry about patching the Makefile to include a DESTDIR macro in the Makefile. This approach makes it a bit easier for those not familiar with Makefiles.

If the Makefile does not honor DESTDIR for the 'make install' command, you can change the prefix macro, instead:

make prefix=$PKG/usr install

This will override the $(prefix) variable inside the Makefile and install to the location you supplied on the command line. Therefore, you can configure with the standard ./configure --prefix=/usr (or ./configure --prefix=/usr/local) syntax, yet install to a different location as if you supplied a DESTDIR=$PKG/usr for the 'make install' command.

IMPORTANT NOTE: Macro names are case-sensitive (at least for GNU make). Some Makefiles may use a "PREFIX =" macro instead of the usual "prefix =", so the 'make install' command would look like this:

make PREFIX=$PKG/usr install

Therefore, it's necessary to look inside the Makefile to be sure which form was used for the prefix. For large, complex makefiles, the easiest way is to 'grep' the Makefile, like so:

grep -i '^prefix \?=' Makefile{,.in}

The -i option makes the search case-insensitive and the {,.in} part at the end will search "Makefile" or "Makefile.in" files, the second one being a template for the "configure" script. Grep's search term is a basic regular expression, so the escaped question mark after the space (\?) means there may or may not be a space in between when searching.

Once in a while, there may be a "PREFIX =" in a Makefile that is not defined which you are to edit and supply the location. Use the usual /usr (or /usr/local), in this case, and then use 'make PREFIX=$PKG/usr install' to install to the package's build location.

Patching the Sources

Sooner or later, there will be some reason to patch the source code prior to building a package, and you'll want to be able to do this automatically.

Obtaining the Patch

In most cases, the patch will be provided by the author of the source code, so we're not going to discuss patch *creation* here. Download the patch and place it in the same directory as the SlackBuild script, slack-desc file, and other related files (in $CWD from above).

$ wget http://someapplication.org/files/patches/bigsecuritypatch.diff

It's not necessary to do the next step, but because developers generally want to conserve space if possible, it's conventional to do this:

$ gzip -9 bigsecuritypatch.diff

This will result in a new file called bigsecuritypatch.diff.gz -- we'll use that in the SlackBuild script in just a moment.

Applying the Patch

You will now need to edit your <application>.SlackBuild script so that it applies the patch before it runs configure, make, and make install. To do this, you'll want something like this to run before the configure script, but after extracting the sources:

zcat $CWD/bigsecuritypatch.diff.gz | patch -p1 || exit

Depending on how the patch was created, you might use a different patchlevel on that line, as in:

zcat $CWD/bigsecuritypatch.diff.gz | patch -p0 || exit

It's a bit beyond the scope of this HOWTO, but essentially, the -p# specifies the number of trailing directories to skip when looking for the file to patch. You'll often need to skip the top-level directory, but not always (hence the -p0).

See Also

SlackBuild Script Repositories