My RPM Build Environment

When building RPM’s I have some requirements that I like to adhere to which makes building packages easier for me. I create my own build environment and tools around the RPM build process that makes this easy.

I want to do all my building as a non-root user. There are a few reasons for this. First, in most cases as a developer I shouldn’t have root access and I can take one excuse for having local admin away by forcing development as non root here. Second, most automated build systems won’t run as root. If my build needs root to do something then I can’t leverage most automated systems or forcing me to shoehorn something in. I should be able to keep it simple.

There should be nothing left after a build that could impact a future build run. I don’t want to leave artifacts around on a build server after the build is complete. Those artifacts may impact how future builds run changing the outcome and leading to inconsistent packages.

Build outcome should be reproducible at any point in time. If I take the exact same inputs I should expect the exact same output (except where things get date stamped). Therefore I’m going to ensure that my builds use the same inputs every time.

So, I meet the requirements using these rules:

  • Download all external source code and keep it local. Upstream providers may update their source files and you may not be aware of changes.
  • Use a temporary working directory to do all builds. I will create a set of build directories and point rpmbuild to those directories. At the end of the build I wipe those directories out.
  • In each project create a src directory that has all the necessary files needed to build the package. I download all external code into that directory. Any patches I add or other files go in here as well. This allows me to keep the main project directory cleaner
  • While not mentioned in the requirements, I always use source control to track all changes. This allows us to audit everything that changed, why it was changed and most importantly who did it.

In order to make things easier I have a script which automated the package building. It will create a temporary work space for the build, copy the source files into it, build the RPMs, and copy the results out of the work space. Speeds up what I do tremendously:


#!/bin/bash

tmp=`mktemp -d ~/tmp/rpm-XXXXX`
trap "rm -rf $tmp" EXIT

mkdir $tmp/BUILD $tmp/BUILDROOT $tmp/SOURCES $tmp/RPMS $tmp/SRPMS
cp -v src/* $tmp/SOURCES
rpmbuild -ba --buildroot=$tmp/BUILDROOT --define "_topdir $tmp" myproject.spec
mv $tmp/RPMS/*/*rpm .

You’ll notice that I make a temporary directory and then immediate set a trap to clean it up. This is so that if I ctrl-c the build my temporary directory gets removed. I don’t want to leave junk lying around and this is a good idea. In a normal build it will remove the directory at the end of the script. The last line will take the RPMs that get produced and copy them to your current directory.

One other thing I like to add is a

set -x

command. That command will output exactly what is being run in the script for error checking. Adding it to the rpm spec file is good as well for identifying failures.