RPMs can be a great way to manage the packages you install on your system. Unfortunately, not everything you might want to install is available in RPM form. Perhaps you need a more recent version of a program than the one that ships with your distribution; or maybe it's a program you wrote yourself; or perhaps it's just something that's very obscure. Similar dilemmas can occur with non-program packages, such as font or clip art collections.
RPMs can be a great way to manage the packages you install on your system. Unfortunately, not everything you might want to install is available in RPM form. Perhaps you need a more recent version of a program than the one that ships with your distribution; or maybe it’s a program you wrote yourself; or perhaps it’s just something that’s very obscure. Similar dilemmas can occur with non-program packages, such as font or clip art collections.
Whatever the case, if you’re installing a program for a single computer, chances are you’ll just install it the old-fashioned way, by typing make && make install (or some similar set of commands) in the source directory. make gets the job done, but it means you’re giving up the benefits of the RPM system. What’s more, if you’re maintaining several computers, running around installing the same package on all of them can be tedious.
Life would be much easier if you could create your own RPM, giving you the ability to easily install the package on many systems and the benefits of its package tracking. Fortunately, RPM includes tools that enable you to do just that. Creating an RPM isn’t as easy as creating a tarball, but the task isn’t insurmountable.
This month’s column covers the files and file formats involved in creating RPMs. Next month’s column will present practical examples, including how to build both binary and architecture-independent RPMs.
RPMs in Theory and Practice
The idea behind RPMs can be stated succinctly: provide an archive format that both embeds dependency information and enables the tracking of installed files. Given those features, RPM can be used to install a package only if prerequisite packages already exist, to install a package only if package files don’t conflict with existing files, to easily uninstall packages, and so on.
For flexibility, the RPM format also supports several broad classes of RPMs: binary RPMs (the common, executable program packages); source RPMs (RPMs containing source code, which can be automatically recompiled on multiple platforms); and architecture-independent RPMs (those containing scripts, fonts, and other architecture-independent files). However, none of these types of RPMs spring into existence fully-formed, like Athena from Zeus’s forehead. They must all be built, based on descriptions provided by the package maintainer.
These descriptions are stored in a file that ends in .spec, such as mypackage.spec. Creating an RPM file from scratch is a matter of obtaining a non-RPM source package (and, optionally, patches to it), writing a .spec file, and running a command to convert these files into an RPM. Depending on the type of package you want to build, you can create a binary RPM, a source RPM, an architecture-independent RPM, or a combination of RPM types.
The .spec file relies on a pristine source file, which is the original source code or other package files, along with an optional set of patch files, which modify the pristine source file in some way. The patch files might change installation directories in a make file, apply bug fixes, add System V startup scripts for a server, or do just about anything else. This ability to use pristine sources along with patch files is particularly handy for distribution maintainers, but it’s something you don’t always need to use, particularly when you’re building an RPM for files you’ve created yourself (such as a custom clip art collection). A source RPM, if you build one, is actually a combination of the pristine sources, the patches, and the .spec file into a single file.
The .spec File: An Overview
A .spec file is an ordinary plain-text file, so you can create one using any text editor. It’s broken into several distinct sections:
* THE PREAMBLE. This section contains overview information for the package, most of which will be visible to users who use the rpm command’s -i option.
* THE PREP SECTION. You tell RPM how to prepare files to be built or packaged in this section. Usually this involves unpacking a source tarball and applying any patches you need.
* THE BUILD SECTION. This section compiles the software. While some binary packages can get by with a simple make command in this section, others require an elaborate series of commands. In the case of non-binary packages, this section can be left completely blank.
* THE INSTALL SECTION. RPM requires that files be installed (typically to a dummy directory) before they can be packaged. This section does this job by calling an install target to make, by using an install script, or by copying files one-by-one.
* INSTALL AND UNINSTALL SCRIPTS. These scripts aren’t part of the build process. Instead, they’re run when the package is installed on an end-user’s system. For instance, these scripts might call ldconfig after installing libraries to make the libraries available.
* THE CLEAN SCRIPT. This script helps clean up temporary files after building a package.
* THE FILE LIST. You must tell RPM what files to include in a package. In addition, you can flag certain files as being special in various ways, such as being configuration or documentation files. RPM may then treat these files differently when verifying their integrity or when upgrading a package.
* THE CHANGE LOG. The change log can record changes you make to a package over its history.
Most of these sections are identified by the percent symbol (%) before a keyword, as in %prep or %install. Several sections support RPM macros, which are identified in the same way, such as %makeinstall. This similarity of notation can make .spec files tricky to read if you’re not familiar with them.
Let’s look at several important .spec file sections.
The preamble, unlike most .spec file sections, isn’t identified by a keyword preceded by a percent symbol. Rather, it begins at the start of the .spec file. The preamble consists of a series of tags — a tag name, a colon, and tag data. Most of these tags provide information about the package when you use the -i option to rpm, as in rpm -qpi foo.rpm.
A typical preamble looks something like this:
Summary: A lightweight DHCP/DNS server
Group: System Environment/Daemons
A lightweight DHCP server, DNS forwarder and DNS server for a local network.
[The Source line in this example has been split across two lines because of the column width of the magazine. Source should be a single line in the real file.]
Most of these fields should be familiar from their like-named counterparts in the output of rpm -qpi. A few, such as the BuildRoot and Requires options, are not displayed by this command, though. The BuildRoot tag sets the name of a temporary directory that’s used for building the package — this directory is a stand-in for the root (/) directory of the computer when preparing a package’s contents. RPM can determine most dependencies based on the libraries called by executable programs in the package, but you can use the Requires tag to specify other packages by name.
Arguably the most critical tag is the Source tag. This specifies the pristine source tarball — either the source code or a tarball that holds the files you want to convert into RPM format. Typically, the Source tag points to a complete URL. This is largely for the benefit of humans, though; the RPM utilities strip away everything but the final element (dnsmasq-2.7.tar.gz in this example) and look for that filename in the /usr/src/redhat/SOURCES directory. (Distributions other than Red Hat or Fedora typically use something else in place of redhat in this path. For instance, SuSE uses packages.)
The Patch0 and Patch1 tags point to patch files, which contain modifications to the pristine sources. You can list as many patch files as you like, but you should ensure that the patches apply cleanly and work before you try building an RPM with them.
The %description line begins a multi-line description of the package. The description ends with the next section, which is typically %prep.
The Build Instructions
To build a package, RPM must usually extract the tarball’s contents, apply patches, and compile the source code. (One or more of these steps may be omitted for some packages.) These tasks are handled with the %prep and %build sections.
The %prep section typically uses the %setup macro. This macro is fairly intelligent: it can identify several common tarball forms (such as those that use gzip or bzip2 compression), and extracts files into a temporary build directory (typically a subdirectory of /usr/src/redhat/BUILD. It supports several parameters, such as -q, which causes a “quieter” extraction process. Alternatively, you can provide ordinary commands, much as you would in a shell script, to extract the file if it’s in an unusual format.
The %prep section also patches the source code files, if you’re patching the pristine sources. This task is accomplished with the %patch macro. You must typically include one %patch macro per patch file. You can either append the patch number directly (as in %patch0 to apply Patch0) or specify the patch number using the -P parameter (as in %patch -P 0). You can also use a lowercase -p option to tell the macro how many levels to strip from filenames in the patch file, just as you can with the patch command in a shell prompt.
RPM looks for patch files in the same directory as the source package (/usr/src/redhat/SOURCES or its equivalent), so be sure you store them there.
The %build section actually compiles the program. This section typically consists of the commands you’d type, starting from the package’s source directory, to build the program. For instance, it might contain the following lines:
Consult the program’s build instructions for details of what you should use. Some programs support compile-time options, so you might add options to one of these commands to add or remove support for particular features. In the case of non-program packages, there may be no %build section at all.
The Installation and File Specifications
Today, most programs enable you to type make install to install the program. You should not, though, use that command in the %install section of the .spec file! Doing so will install the program in your real system, not in the build root directory specified by the BuildRoot tag.
In most cases, you can use the %makeinstall macro to do the job. This macro automatically installs the package to the appropriate directory. If this doesn’t work (say, because a program has no install target in its makefile), you must copy files to appropriate directories in the build root, which you can specify using the $RPM_BUILD_ROOT variable.
For instance, to copy the source/bigprog file to a location that will ultimately become /usr/bin in the final RPM, you’d include the following line in the %install section:
cp source/bigprog $RPM_BUILD_ROOT/usr/bin
Once you’ve installed the files in the build root directory, you must tell RPM what files to include in the final RPM. This task is done with the %files section. At its simplest, this section consists of a list of files or directories. (In the case of directories, RPM automatically includes all the files and subdirectories of the directory that you specify.) A few special macros enable you to set attributes or flag files as being special:
* The %defattr macro sets the permissions and ownership for files. Typically, this is set to %defattr(-,root,root), which uses the files’ existing permissions (the dash) and sets ownership and group ownership to root. If you prefer, you can explicitly set permissions by using an octal code, such as 644 instead of a dash.
* The %attr macro sets ownership on individual files. It leads the line that specifies a particular file or directory.
* The %config macro specifies configuration files. These files are given special treatment during package deletions and upgrades to prevent configuration files from being deleted or overwritten by generic configuration files. Only one file may follow the %config directive, so it’s always followed by the complete path to a single file.
An Overview of the RPM Build Process
Once you’ve created a .spec file, it’s time to begin building your package. One important preliminary step is to copy your source files, including both the package’s pristine source tarball and any patch files you’re using, to /usr/src/redhat/SOURCES or its equivalent. Alternatively, you can create symbolic links there that point to the actual files elsewhere on your system.
Some operations may require root privileges. By default, /usr /src and its subdirectories can only be written by root, so you’ll need to be root to copy files there and to build the package. If you change the ownership or permissions of this directory tree, though, you can build packages as a non-root user.
Recent versions of RPM use a program called rpmbuild to build RPMs. This program is typically installed as part of a package called rpm-build. Older versions of RPM folded these features into the main rpm binary, though. Whether you’re using rpmbuild or rpm to build your packages, you’ll pass a two-letter build command to the utility:-ba builds binary and source packages; -bb builds only a binary package; and -bs builds only a source package. You might use it if you want to prepare a package on one system but compile it on another with a different architecture.
One other option you might well want to use is the –target parameter. You pass a code for the target architecture to this parameter. Another use is to specify architecture-independent (–target noarch) RPMs. You’d use this option for non-program packages, such as fonts, clip art, and so on. Packages that consist entirely of architecture-independent scripts, such as shell or Perl scripts, also use this option.
Tune In Next Month
Next month’s column presents examples of .spec files for building actual packages. If you want to try your hand before then, though, try locating a source RPM file (one whose name ends in .src.rpm and type rpm2cpio package.src.rpm | cpio -i substituting the package filename for package. src.rpm. This command extracts the .spec file, pristine source file, and any patch files from the source RPM. You can then experiment with the package’s .spec file, making changes and building the package.
Roderick W. Smith is the author or co-author of twelve books, including
Linux Power Tools and
Advanced Linux Networking. He can be reached at email@example.com.