Creating Custom RPMs, Part Two

Last month's column looked at the basics of generating RPMs, including the format of the all-important .spec file. In theory, those principles should be enough to let you create .spec files and RPMs for a number of purposes. In practice, however, RPM generation is complex enough that some examples are sure to help. So, this month's column presents two examples: creating a non-program RPM and creating a program RPM.

Last month’s column looked at the basics of generating RPMs, including the format of the all-important .spec file. In theory, those principles should be enough to let you create .spec files and RPMs for a number of purposes. In practice, however, RPM generation is complex enough that some examples are sure to help. So, this month’s column presents two examples: creating a non-program RPM and creating a program RPM.

Creating a Non-Program RPM

A non-program RPM holds architecture-independent, non-program data. For instance, you might want to create a non-program RPM for fonts that don’t come with your distribution. These might be open source fonts that you’ve downloaded from the Internet, fonts from a commercial source, or fonts you’ve created yourself. In any event, packaging the fonts in an RPM file allows you to easily reinstall the fonts if you need to reinstall the operating system, or to install the fonts on multiple computers. (Of course, you should check license terms before installing commercial fonts on more than one computer.)

To begin the process, you should package the fonts into a carrier file, such as a tarball. The tarball should contain all of the files you want to appear in the final RPM, in their appropriate directories relative to the system root directory.

For example, suppose you want to create a package called myfonts version 1.2.0 that installs files in /usr/share/fonts/myTT/ and /usr/share/fonts/myType1/. The tarball should contain the directories usr/share/fonts/myTT/, usr/share/fonts/myType1/, and all of the files and subdirectories within those two directories. You should copy the files over to an appropriate scratch directory and create the tarball from that directory. The tarball should omit the leading slash, as it will if you create the tarball with default options, as in tar cvfz usr/share/fonts. The final tarball should be named after the package you’re creating — myfonts-1.2.0.tgz in this case.

In the case of a font RPM, chances are you’ll want the subdirectories to have appropriate font definition files (fonts.scale and fonts.dir), so be sure they’re present. If you’re not sure how to create them, consult appropriate documentation, such as the February 2000 “Guru Guidance” column in Linux Magazine (available online at http://www.linux-mag.com/2000-02/guru_01.html).

Once the tarball has been created, copy it to /usr/src/redhat/SOURCES/. On distributions other than Red Hat or Fedora, the redhat directory is likely to be something different. For instance, on SuSE, it’s called packages.

Now, it’s time to create a .spec file. Call it whatever you’d like, but it’s recommended to base its name on the package you’re creating, such as myfonts-1.2.0.spec. Listing One shows a sample file.

Listing One‘s preamble is fairly straightforward, including a package name and version, a description, and so on. Because the package is one you’ve created yourself, the Source: tag specifies a filename instead of a complete URL.

The Prefix tag specifies how much of the leading part of the installed packages’ filenames may be changed. In this RPM, all of the files are intended to reside in /usr/share/fonts/, but font files will work just as well if installed in /usr/local/fonts/, /opt/ fonts/, and so on. Specifying a Prefix enables users to relocate the package at install time by using the –relocate parameter to rpm. Many packages lack this feature, though.

Listing One‘s %prep section consists of a single macro: %setup -c -q. This macro automatically expands the tarball into an appropriate scratch directory. The -c option tells it to create a subdirectory; if you omit it, your tarball’s directories should be subdirectories of one named after the package, as in myfonts- 1.2.0/usr/share/fonts.

Because this example builds a font RPM, there’s no need to build program files. Thus, there’s no %build section. The %install section merely copies the files you’ve extracted to the virtual root directory, which is specified using the $RPM_BUILD_ROOT variable. (Alternatively, you could extract the tarball directly to this directory in the %prep section, but that would require several lines to create the directory and extract files to it.) The %clean section merely tells the package-building tools to clean up the temporary directory used for building the RPM.

The %files section is critically important for any .spec file. Listing One uses the %defattr macro to set the default ownership and mode of files in the RPM — in this case, they’re set to 0755, owned by root and the root group. This particular file doesn’t contain any documentation or configuration files, so no macros specify them. The %files section does include a list of files — or more precisely, directories. Only two appear in this example. You can list individual files if you like, but listing directories is usually much simpler.

Listing One concludes with the %changelog section that lists the changes to the package. This section is trickier to write than it appears; the RPM utilities are particularly fussy about the date format, which should consist of a three-letter day-of-the-week abbreviation, a three-letter month name abbreviation, the numbered day within the month, and the full year. This date code appears on a line that begins with an asterisk (*) and ends with contact information for the package maintainer. The next line is more free-form: it begins with a dash (-) and can hold any text describing changes to the file. If you make multiple entries (typically, for changes when you update the RPM), they must appear with the most recent date at the top of the list.

After you create the .spec file, you presumably want to create a package. From the directory in which the myfonts-1.2.0.spec file resides, type the following command:

$ rpmbuild -bb –buildarch noarch \ myfonts-1.2.0.spec

Older versions of RPM used rpm rather than rpmbuild to do this task, so you may need to use rpm instead. Distributions that use rpmbuild tend to put it in a separate package (rpm-build), so you may need to install it.

The -bb parameter tells the program to build a binary package from the specified .spec file. Because this is an architecture-independent package, you can add the –buildarch noarch parameter, which tells the system to build a package that installs on any architecture. If you omit this parameter, your package will still work, but it will only install on the architecture you built it on — for instance, on x86 computers if you built it on an x86 system.

After typing the command, you should see a series of lines summarizing rpmbuild‘s actions, concluding with a line that reads + exit 0. This signifies a successful build. The RPM should appear in /usr/src/redhat/RPMS/noarch/ as myfonts-1.2.0-1.noarch.rpm.

You may want to check the file out with rpm -qpi myfonts-1.2.0-1.noarch.rpm and rpm -qpl myfonts -1.2.0-1.noarch.rpm to be sure the description is OK and that it contains the files that it should contain. After verifying the information, you can install the RPM by typing rpm -Uvh myfonts-1.2.0-1.noarch.rpm.

A procedure like this one can be used for any number of package types, including fonts, audio files, clip art, and even binary-only programs.

Creating a Program RPM

Most RPMs on your system are architecture-dependent, binary RPMs. Listing Two shows a .spec file for creating an RPM from dnsmasq (http://thekelleys.org.uk/dnsmasq), a lightweight, combination DHCP and DNS server intended for small networks that reside behind broadband routers. (dnsmasq actually ships with its own .spec file. Listing Two is a simplified file for presentation purposes, but it’s still functional.) dnsmasq makes a good example in part because it’s small and builds quickly — you don’t want to use a big package like Samba or OpenOffice.org to test your RPM-building skills, since you can wait for hours before discovering a problem late in the process!

As with the fonts RPM .spec file in Listing One, Listing Two‘s preamble is fairly succinct. To avoid confusion with RPMs built from the .spec file provided by the program’s author, you can include your initials in the Release line. Here, the finished package is dnsmasq-2.7-1rws.i386.rpm, at least on an x86 system configured to build with 80386 optimizations. The Requires: line specifies a dependency that RPM can’t figure out for itself: the post-install script calls chkconfig, so that package must be present.

Listing Two‘s %prep stage calls the same %setup script as Listing One, but because the source tarball unpacks its files into a directory named after itself, the -c option isn’t necessary. Unlike Listing One, Listing Two also includes a %build section. This section is quite short, though: it’s simply a call to make. Many other packages also include ./configure, make config, or some other similar command prior to the call to make.

Listing Two‘s %install section is unusually long because dnsmasq doesn’t include an installation script. Instead, this section copies files individually, creating necessary directories first. Files are installed in subdirectories of $RPM_BUILD_ ROOT. Many packages can use a script that’s called as %makeinstall, which automatically installs packages in such a way that files are installed to the correct subdirectory (namely, $RPM_BUILD_ROOT).

You might notice that Listing Two copies rpm/dnsmasq.rh to etc/rc.d/init.d/dnsmasq in the temporary directory. This file is a SysV startup script designed for Red Hat or Fedora and may not work on other distributions. You may want to omit it if you’re using another distribution.

The %post section includes the call to chkconfig. This tool isn’t present on all RPM-based distributions, so if it gives you trouble, you may want to omit this section and the Requires line in the preamble that refers to the utility.

The %files section includes a %defattr option that sets the default attributes for the files, as described last month. The %doc line identifies documentation files, which are flagged as such for future RPM reference. Likewise, the %config line identifies configuration files that will be backed-up should you subsequently upgrade the package. The remaining %attr lines identify the remaining files and ensure that their permissions are set correctly.

To build the dnsmasq package, you’d copy the source package to the SOURCES subdirectory in the RPM build directory and then type rpmbuild -ba dnsmasq-2.7.spec. The -ba option tells the system to create a source package (stored in /usr/src/redhat/SRPMS/ and a binary package (stored in /usr /src/redhat/RPMS/i386/ or possibly another subdirectory named after your system’s architecture). On any but the very oldest systems, the build process should complete in just a few seconds. You can then install the binary package as you would any other.

Troubleshooting RPM Creation

Unfortunately, creating RPMs can be a tricky proposition. The build process can fail at any stage along the way, so watch for error messages and try to fix the problem.

One common problem is caused by confusion over directories. Most .spec file sections specify directories using the $RPM_BUILD _ROOT variable, but the %files section omits it. The %prep, %build, and %install sections use the build directory (which is distinct from the $RPM_BUILD_ROOT directory) as their working directory. Sometimes you must issue a cd command to move into a subdirectory to begin the build process. Whenever possible, you should use macros, such as %makeinstall, because they automatically work with the correct directories.

For complex packages, you might want to test the process by completing a build and perhaps even a full install and a test of the software before putting together a .spec file. In particular, if you need to tweak compile options, finding the correct options can be much quicker if you do it manually rather than from a .spec file.