How to patch and rebuild an RPM package

The following describes how to derive a patch for a modification you need to make to the source code of a program, and apply that patch during the RPM building process.

Setup

By default, at least on a Red Hat box, rpm uses /usr/src/redhat as the location of the %_topdir macro, which specifies where most of the work involved in building an RPM takes place. You can and should change this; it is a good idea to make this a directory that you can write to with a non-privileged account, to avoid compiling and building packages as root. Why? A lot of commands get executed when building a package. Sometimes things go wrong. If you're root, important things may be damaged. A big mess may be made. I once (foolishly) rebuilt a proftpd package as root, and the "make install" stage blew up and left newly compiled files all over the place, whereas if I'd been a regular user, I'd have simply gotten a bunch of "permission denied" messages. Anyway, the macro is easily changed by adding something like the following to ~/.rpmmacros:

# Path to top of build area
%_topdir    /home/you/src/rpm

If you have never worked on RPMs in this manner, you will need to create a few directories in which to work. I use a subdirectory in my homedir:

linmiri% mkdir -p ~/src/rpm
linmiri% cd ~/src/rpm
linmiri% mkdir BUILD RPMS SOURCES SPECS SRPMS
linmiri% mkdir RPMS/{i386,i486,i586,i686,noarch,athlon}

The BUILD directory is where sources will be extracted and compiled, and as such is more or less a temporary working directory. SOURCES is for source tarballs, patches, and additional files, which are extracted from SRPM packages you install, and used and included in packages you build. The RPMS and SRPMS directories are where packages you build will end up, so don't use them as a storage area, as you may end up overwriting something unintentionally. The SPECS directory is where the specfile included with an SRPM will be placed. Again, be careful leaving work in this directory; if you later install another SRPM of the same package, it will overwrite your modified specfile!

Rebuilding an RPM from SRPM

If you just want to recompile, without making any changes to the source, all you have to do is run:

linmiri% rpmbuild --rebuild package-1.0.src.rpm

You'll still need the writeable %_topdir as described above, but otherwise, that's it. Your new package will be in %_topdir/RPMS/<arch>.

Installing the SRPM

The first thing you will need to do is install the source RPM (SRPM). If you have set your %_topdir as described, you will be able to install the SRPM as yourself:

linmiri% rpm -ivh package-1.0.src.rpm

That will put a specfile in %_topdir/SPECS, and a source tarball (plus any other included patches or additional files) in %_topdir/SOURCES.

Changing `configure` flags

If all you need to do is change the configure flags, just edit the specfile. You don't need to touch the source at all. There's usually a %configure section, where it should be fairly self-evident what to do. Then skip to "Rebuilding the package."

Patching the source

Since the rpmbuild process will delete the original extracted source and start from scratch when building packages, you'll want to do your work on a copy of the source with a different name. So if extracting package-1.0.tar.gz creates a directory named package-1.0/, make a copy named something like package-1.0p/ and make your changes there.

To create a patch containing your changes, cd to %_topdir/BUILD -- one directory above the source tree -- and use diff to create a patchfile in the SOURCES directory. There may be other patches to apply to this source, so give your patchfile a unique name to quickly describe the purpose of the patch. Run diff with "-u" to get the unified diff format that is standard for patches, "-N" to include any new files from your modified source, and "-r" to operate recursively, listing the original first, then your version:

linmiri% diff -uNr package-1.0/ package-1.0p/ > ../SOURCES/package-1.0-my.patch

The top of the patchfile will look something like this:

--- package-1.0/file Thu Aug 14 16:24:49 2003
+++ package-1.0p/file Mon Aug 18 01:24:17 2003

For another approach at making diffs, `man gendiff`. That tool lets you change individual files, leaving the originals with a unique filename suffix, and generates a diff based on those, all from a single source directory. I usually prefer keeping my changes in a separate directory, to prevent accidental deletion of my work by rpmbuild.

Adding the patch to the package

Next, add the patch to the specfile, so it will be applied when building the package. There may be other patches already, and they are applied in order of their number in the specfile, so number yours appropriately. Add to %_topdir/SPECS/package.spec, in the top section where the name, version, and source lines are:

Patch0: package-1.0-my.patch

Farther down, there will be a section that deals with preparing for the build. Add a patch command that corresponds with the patch line above, typically right after %setup:

%prep
%setup ...
%patch0 -p1

The "-p1" in the patch command above is how many directories to strip off the beginning of the filenames listed in the patch. Since, at this stage of building an RPM, the current directory is the top level of the source tree -- one level lower than where the patch was made -- we need to strip off one directory level.

Again, there may be other patches already being applied to this package, so give yours a unique number in the specfile, and be careful that your patch doesn't break others, and that others don't break yours. Usually the next available number will work fine, and your patch will be applied after all others. If the package is already installed, make the version number of your package higher so it will be considered newer when you go to upgrade it, and update the changelog with a quick note on what you did.

Rebuilding the package

You should be able to rebuild tha package with your changes now:

linmiri% rpmbuild -ba SPECS/package.spec

That will patch, configure, and compile the source, build a binary RPM package in %_topdir/RPMS/<arch>/, and an SRPM package in %_topdir/SRPMS containing the original source (checksums for which can still be used), plus your additional patch and new specfile, so that the package can be rebuilt again on top of the work you've already done. Use "-bb" to make just a binary RPM, or "-bs" to make only an SRPM.

The easy way

"Jesus tapdancing christ, Brad," you might be saying, "that's a lot of work." Yes, it is. If you were hoping you could just go into the source after doing a "make" and fiddle with a couple of things before rolling the RPM, sorry, you're shit outta luck. The rpmbuild command won't let you do it. You can "short-circuit" to skip various parts of the overall process, but not all the way to the "create an RPM out of this" stage.

Part of the philosophy behind RPM is that the build process should be repeatable, as explained here. It may be small consolation when you just want make a simple change on your own system, where building once is good enough, but that's the way it is. Hopefully you now know enough to get by.

More information

Additional RPM Resources

More notes