Writing RPM packages is easier than you might think

By on March 10, 2007 12:38 am

Hello,

Writing RPM packages seems to intimidate some, but it can be easier than you might guess. Below, I will:

  • point out the online reference RPM documentation
  • describe one method of configuring a build environment
  • outline a simple specfile (sysreport package)
  • describe the process of building the package from the specfile
  • introduce a few convenient RPM macros

Here are a few terms:

  • specfile a file containing the rules for building an RPM package
  • RPM the distributable package intended for the target
  • SRPM a special package containing all the source code and the specfile for a given package
  • rpm the command-line program used for installing packages
  • rpmbuild the command-line program used for building packages


The best place to get detailed documentation is the Maximum RPM Book, which is a bit old, but has documentation on most RPM building questions.

In order to build RPMs, you need a handful of things in addition to a normal build environment (gcc, make, etc.). You will need a package which includes the rpmbuild command. Some RPM-based distributions package that in a package called “rpm-build” and others include it in the main “rpm” package itself.

Once you have the rpmbuild utility, you’ll need to configure your (preferably non-root) build user’s directory. It’s possible to build software as root, but much safer to build packages as non-root.

You will need to create a set of directories to contain the types of files on which rpmbuild operates. I almost always put these files into a directory called ./packages/ just off my home directory. That leaves you with something that looks like the below tree.


$ pwd
/home/mabrown
$ tree -d -L 1 packages/
packages/
|-- BUILD
|-- RPMS
|-- SOURCES
|-- SPECS
|-- SRPMS
`-- repodata

Not that you could get clever and symlink the RPMS and SRPMS directories to a developer-writable system-wide repository. This is not necessary, just my particular predilection. Here’s what you’ll find in each of these directories.

  • SPECS This directory contains a single spec file for each package you want to build.
  • SOURCES This directory contains tarballs, patch files or any other files used in the actual building of the package.
  • BUILD When rpmbuild is untarring the source tarballs, it will create temporary its directories here. If a particular build fails, then, you’ll find the evidence under this directory.
  • RPMS Successfully built packages will end up here. There will be a subdirectory for each architecture (e.g., x86_64, i386, i586, noarch).
  • SRPMS Under usual invocations (rpmbuild -ba "$SPECFILE"), an SRPM file will be built and left here, as well. This SRPM file contains all of the required SOURCES (patches, tarballs, etc.) and the specfile itself.

To make all of these directories, fairly simply (I assume bash is your shell):


cd && mkdir packages/{BUILD,SRPMS,SPECS,SOURCES,RPMS}

Naturally, there is a configuration file to tell rpmbuild where to find all of the above. The default is a $HOME/.rpmmacros file. I have included one example below. There are dozens of things you can configure and set in this file, but the most important one is the %_topdir macro.


%_topdir %(echo $HOME)/packages
%_smp_mflags -j3
%debug_package %{nil}

The %_topdir macro should expand to the directory name which contains all of your BUILD, RPMS, SRPMS, SPECS and SOURCES directories.

Now, let’s take a look at a simple package and specfile and prepare to build the package. For example, the sysreport package contains a simple example of a specfile. So, how do you extract the specfile itself? You simply install the SRPM (sysreport-1.3.15-6.src.rpm), which install the source files AND the specfile into your build environment.


rpm -Uvh sysreport-1.3.15-6.src.rpm

Presuming you have configured your build environment correctly with %_topdir, $HOME/packages/SPECS/sysreport.spec should now be available to you. Additionally, this will put the tarball(s), patch files and other required files into the SOURCES directory.

There is not much inside this package, but let’s look at the three most import phases of the build process before we actually try to build the package. The three most important phases of the build process should be terribly familiar to anybody who has installed GNU software before. These three steps (%setup, %build and %install) are simply the familiar triumvirate.

  • The %setup phase untars the software “tar -xvzf $TARBALL”
  • The %build phase is the analog to “./configure && make”
  • The %install phase performs “make install”

Most modern specfiles define an attribute called Buildroot. This attribute should properly be called install root, but it is hard to rewrite history. Almost all specfiles (in 2007) use the buildroot (also known as $RPM_BUILD_ROOT) and will specify it as an argument to any “make install” commands. In the sysreport.spec, the “make install DESTDIR=$RPM_BUILD_ROOT” command shows exactly where we are going to install our freshly built software.

The remaining sections and attributes of the specfile are largely metadata for end users (%changelog and %description) or important metadata and rules to take the results of the build process and turn that into the desired software package.

The %files section is a complete list of all of the files that should comprise the package. Everything (except the documentation files) is sought under the $RPM_BUILD_ROOT.

Now, you have the specfile itself, and you can modify it to your heart’s content. In order to build the package completely and build the SRPM (so that you can distribute the source, the specfile and any patches), you simply run rpmbuild:


rpmbuild -ba $HOME/packages/sysreport.spec

If everything went well, the rpmbuild process should exit zero, and it should also report to you that it has written out two package files (at least). One of them will be the package for the target platform, and the other should be the SRPM.

Here are some additional, interesting and/or useful macros or sections.

  • different systems have slightly different choices about filesystem locations. Most converge on FHS, but even so, the following macros insulate the packager from needing to know what the convention is on a particular platform. Here’s a brief chart of common macros (and a common path value for that macro):
    • %{_sysconfdir} /etc
    • %{_var} /var
    • %{_bindir} /usr/bin
    • %{_sbindir} /usr/sbin
    • %{_mandir} /usr/share/man
    • %{_infodir} /usr/share/info
  • %patch0 is a simple macro for applying a patch defined as “Patch0” at the top of the specfile. %patch1 would apply Patch1, and so forth. Arguments to this macro are handed through to the patch binary (e.g. %patch0 -p0 = patch -p0 < the-file-defined-as-Patch0).
  • %configure, a generic and simple macro chosen by the systems integrator for your platform.
  • %if, %else and %endif. RPM provides conditional logic, too. This can be useful, in case you are building packages which might have different needs on one platform or distribution (e.g., CentOS init script vs. SuSE init script).
  • The %pre and %post script hooks allow for custom script actions before a package is installed and just after it is installed.
  • The %preun and %postun script hooks allow for custom script actions before and after a package is uninstalled.

Good luck and happy trails,

-Martin