include("site.inc"); $template = new Page; $template->initCommon(); $template->displayHeader(); ?>
Working through problems is one thing. It’s best, however, to set up an environment to help avoid problems all together. The following sections cover what are considered the best practices for creating RPMs.
Before you make an RPM, you should plan out what you intend to build and how it will be structured. As you build the RPM, you want to watch out for things that can go wrong, and work from a known clean environment.
Before you start to make an RPM, you need to follow a few steps to ensure you have everything ready.
Having a source RPM allows you to transfer all the sources for a package from one system to another, along with all the instructions coded in the spec file for actually building the binary package. This is very handy for keeping track of software, and it is also very important since you can regenerate the binary RPM at any time from the source RPM. In other words, make the generation of RPMs follow the RPM conventions and fit this into your normal software build process.
This means that for each RPM you want to build, you really need two: a source and a binary RPM. This isn’t that hard to do, since you can easily make a source RPM into a binary RPM with the rpmbuild command.
In addition to planning on making a source RPM, you should also start with pristine, unmodified sources for the application you plan to package as an RPM. Starting with pristine sources means you can reproduce the entire process and recreate the RPM from scratch if necessary. (Quality control and configuration management people really appreciate this.)
The pristine sources should be exactly the sources you got when you downloaded the application, or acquired it in house. This doesn’t mean that you won’t have to modify the sources eventually. For that, you create patches. The key is just to start the process with unmodified sources.
Some RPMs have nearly 100 patches that the rpmbuild command applies when building the RPM. That is a lot of patches, too many for most applications. Even so, the process is the same. Create a patch or patches for all the changes you need to make. You can easily specify patches in the spec file.
Cross Reference
Chapter 10 covers the spec file.
Keeping your patches separate from the original sources makes it easier to reproduce the RPM from scratch, and makes it easier to integrate a new version of the base software, since your code, in the form of patches, is separated from the base software code.
You don’t have to stuff all your software into one RPM. Instead, you can often simplify your RPM by dividing it into two or three separate (but likely dependent) RPMs.
For example, the RPM system itself has one RPM for the basic system, rpm, one for developers of the RPM system, rpm-devel, and one for those building RPMs, rpm-build. Yet another RPM provides the Python programming API, rpm-python.
Cross Reference
Chapter 17 covers Python programming.
This last division is important. The Python RPM draws in as a dependency the Python system itself. Adding this into, say, the core RPM package would needlessly complicate the dependencies for that package.
When dividing your software into RPMs, keep two main issues in mind:
*You want to divide the software into RPMs that fit the model for users of the system.
*You want to divide the software into RPMs such that the separate RPMs are simpler to create and manage.
The RPM system follows these guidelines, especially the first. Few users will extend the RPM system itself, which allows RPM team to shed this functionality from the core RPM and contain it in rpm-devel. Those who build RPMs fit into a different category than those who use RPMs since just about everybody needs to use RPMs to install packages, but few users actually build RPMs. Again, the separation works from a user’s perspective.
You also want your package divisions to make each package easier to specify. You can break particularly tough dependencies into smaller units and simplify things. If the package division doesn’t simplify things, then it may not be a good idea.
You don’t always have to work with the system RPM database. In fact, while developing RPMs, you probably don’t want to change the system database.
If you have a test RPM database, you can install your RPMs into this test database. To do so, use the --justdb, --dbpath, --prefix, and --badreloc options. These options allow you to install an RPM into just the database, using a different database, with a different root file location (into a test directory, for example) and handle all files that were not marked for relocation, respectively.
Note
The --test option when installing also allows you to just test the install, not actually perform it.
Combined, all these options mean you can use an RPM database just set up for testing and that problems won’t impact your working Linux systems. To make this work, though, you need a test RPM database.
To be rigorous, you should create the test RPM database from scratch from a known set of packages. This will allow you to exactly verify the behavior of your RPM under different system configurations. This is the best choice since you should install the packages under a known, and non-root, directory hierarchy to avoid having file problems with the working system.
If you want to cheat, you can copy your real RPM database to another directory and use that. Note that in this case, the file paths in the database will point to the real file locations on disk.
Regardless of how you create a test database, recreate the database each time you run a test, so that you are sure of a known starting state. Usually this is as simple as copying a master test RPM database into a directory you use for running tests.
Building RPMs isn’t as easy as it should be. You’ll often need to try again and again to get the rpmbuild command to create a working RPM. This section covers best practices to follow when performing the actual build of the RPM.
Using tools can help speed up the RPM-making process, as well as give you a head start in learning how RPMs work. RPM-building tools such as the Red Hat plugin for the Eclipse Integrated Development Environment have proven really helpful.
Cross Reference
Chapter 13 covers RPM-building tools. Appendix F covers the Eclipse Integrated Development Environment.
Even though so-called real Linux hackers can make a working virtual memory system with just the cat command, don’t scoff at tools. Your time is too valuable.
Another useful tool is the gendiff program that comes with the RPM release. The gendiff program makes it easier to create patches by avoiding the need to keep a separate directory of the original sources, The gendiff program also works on all changed files within a directory, making a patch for everything you modified.
To work with gendiff, you need to first save a backup copy of each file you intend to edit prior to editing. Use a consistent file-name extension for the saved copies of the files, such as .orig, short for original. After you edit some files, run the gendiff command as follows:
$ gendiff directory_name .saved_extension > patch_name.patch
For example, if you saved the original files to a .orig extension, you can create a patch in a directory named src (short for sources) with a command like the following:
$gendiff src .orig > mypatch.patch
The patch file mypatch.patch will contain all the differences detected for all files in the given directory.
Never, never, never build RPMs logged in as the root user. Always build your RPMS while logged in as a normal user. This is hard to remember since you must be logged in as root to install an RPM. And you’ll want to test each RPM you create to see if it can install cleanly.
Even so, never build RPMs logged in as the root user. The RPM spec file has a number of scripts and commands. An error in any of these could cause damage to your system. This includes modifying files, removing files, or copying new contents on top of system files. The root user has permission to perform all these operations.
To avoid all this, build your RPMs while logged in as a normal user. Any problematic scripts should generate errors.
RPM 4.1 and later revisions place more importance on signing your packages. The rpm command will, by default, verify signatures on each package it reads.
Therefore, you should create a digital signature for your packages, if only to meet user expectations. In addition, you should place a copy of your digital signature on your organization’s Web site and public key servers. Having multiple copies in multiple locations helps prevent malicious users from impersonating your keys.
Cross Reference
Chapter 12 covers signing packages.
Your Linux distribution probably includes more than one CD-ROM chock full of RPMs. Each of these RPMs has a spec file. You can examine these spec files and see how others choose to build their RPMs. Rather than starting from scratch, you can copy declarations from these spec files into your spec file.
Not all these packages were made smartly. Some spec files, as you will see, are a large mess. Obviously, don’t copy these. Look for clean spec files with clear directives.
A BuildRoot directive sets the location where your code will be built. The convention is for you to define a subdirectory beneath the _tmppath directory. For example:
BuildRoot: %{_tmppath}/%{name}-buildroot
Once set, rpmbuild defines the RPM_BUILD_ROOT environment variable to the value specified for the BuildRoot.
With the rpmbuild command, you can use the --buildroot option to specify a directory to use to override the BuildRoot directive in the spec file.
Using a BuildRoot set to a directory that normal users have write access to allows you to build the package logged in as a normal user. It also helps separate the contents of your package from those of other RPMs.
Always define a BuildRoot.
Each time you create a new version in RPM format, you should add an entry to the change log. This allows administrators to get a better idea about what changed from the previous version.
The change log can help people decide whether or not to upgrade a package. A log entry about a security fix, for example, provides useful information to users.
Packages are categorized into groups. These group names, while not always the best, appear in the graphical tools such as the Red Hat package manager. If your application is a Linux shell program, then users will expect to find it in the System Environment/Shells group and not the Development/Languages or System Environment/Daemons groups. This is a rather small detail, but it helps users find your package in the huge array of Linux RPMs.
The official list of RPM groups is located in /usr/share/doc/rpm-4.1/GROUPS for RPM 4.1, and similarly-named directories for other RPM versions.