head 1.1; branch 1.1.1; access ; symbols MAXIMUM_RPM_1_0:1.1.1.1 VENDOR:1.1.1; locks ; strict; comment @# @; 1.1 date 2001.08.28.12.07.09; author rse; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 2001.08.28.12.07.09; author rse; state Exp; branches ; next ; desc @@ 1.1 log @Initial revision @ text @
In this chapter, we're going to cover the spec file in detail. There are a number of different types of entries that comprise a spec file, and every one will be documented here. The different types of entries are:
Let's start by looking at comments.
Comments are a way to make RPM ignore a line in the spec file. The contents of a comment line are entirely up to the person writing the spec file.
To create a comment, enter an octothorp (#) at the start of the line. Any text following the comment character will be ignored by RPM. Here's an example comment:
# This is the spec file for playmidi 2.3...
Comments can be placed in any section of the spec file.
Looking at a spec file, the first thing you'll see are a number of lines, all following the same basic format:
<something>:<something-else>
The <something> is known as a ``tag'', because it is used by RPM to name or tag some data. The tag is separated from its associated data by a colon. The data is represented by the <something-else> above. Tags are grouped together at the top of the spec file, in a section known as the preamble. Here's an example of a tag and its data:
Vendor: White Socks Software, Inc.
In this example, the tag is ``Vendor''. Tags are not case-sensitive -- they may be all uppercase, all lowercase, or anything in-between. The Vendor tag is used to define the name of the organization producing the package. The data in this example is ``White Socks Software, Inc.''. Therefore, RPM will store White Socks Software, Inc. as the vendor of the package.
Note, also, that spacing between the tag, the colon, and the data is unimportant. Given this, and the case-insensitivity of the tag, each of the following lines are equivalent to the one above:
VeNdOr : White Socks Software, Inc.
vendor:White Socks Software, Inc.
VENDOR : White Socks Software, Inc.
The bottom line is that you can make tag lines as neat or as ugly as you like - RPM won't mind either way. Note, however, the tag's data may need to be formatted in a particular fashion. If there are any such restrictions, we'll mention them. Below, we've grouped tags of similar functions together for easier reference, starting with the tags that are used to create the package name.
The following tags are used by RPM to produce the package's final name. Since the name is always in the format:
<name>-<version>-<release>
it's only natural that the three tags are known as name, version, and release.
The name tag is used to define the name of the software being packaged. In most (if not all) cases, the name used for a package should be identical in spelling and case to the software being packaged. The name cannot contain any whitespace: If it does, RPM will only use the first part of the name (up to the first space). Therefore, if the name of the software being packaged is cdplayer, the name tag should be something like:
Name: cdplayer
The version tag defines the version of the software being packaged. The version specified should be as close as possible to the format of the original software's version. In most cases, there should be no problem specifying the version just as the software's original developer did. However, there is a restriction. There can be no dashes in the version. If you forget, RPM will remind you:
# rpm -ba cdplayer-1.0.spec
* Package: cdplayer
Illegal '-' char in version: 1.0-a
#
Spaces in the version will also cause problems, in that anything after the first space will be ignored by RPM. Bottom line: Stick with alphanumeric characters and periods, and you'll never have to worry about it. Here's a sample version tag:
Version: 1.2
The release tag can be thought of as the package's version. The release is traditionally an integer -- for example, when a specific piece of software at a particular version is first packaged, the release should be ``1''. If it is necessary to repackage that software at the same version, the release should be incremented. When a new version of the software becomes available, the release should drop back to ``1'' when it is first packaged.
Note that we used the word ``traditionally'', above. The only hard and fast restriction to the release format is that there can be no dashes in it. Be aware that if you buck tradition, your users may not understand what your release means.
It is up to the package builder to determine which build represents a new release and to update the release manually. Here is what a typical release tag might look like:
Release: 5
These tags provide information primarily for people who want to know a bit more about the package, and who produced it. They are part of the package file, and most of them can be seen by issuing an rpm -qi command.
The %description tag is used to provide an in-depth description of the packaged software. The description should be several sentences describing, to an uninformed user, what the software does.
The %description tag is a bit different than the other tags in the preamble. For one, it starts with a percent sign. The other difference is that the data specified by the %description tag can span more than one line. In addition, a primitive formatting capability exists. If a line starts with a space, that line will be displayed verbatim by RPM. Lines that do not start with a space are assumed to be part of a paragraph and will be formatted by RPM. It's even possible to mix and match formatted and unformatted lines. Here are some examples:
%description It slices! It dices! It's a CD player app that can't be beat. By using the resonant frequency of the CD itself, it is able to simulate 20X oversampling. This leads to sound quality that cannot be equaled with more mundane software...
The example above contains no explicit formatting. RPM will format the text as a single paragraph, breaking lines as needed.
%description It slices! It dices! It's a CD player app that can't be beat. By using the resonant frequency of the CD itself, it is able to simulate 20X oversampling. This leads to sound quality that cannot be equaled with more mundane software...
In this example, the first three lines will be displayed by RPM, verbatim. The remainder of the text will be formatted by RPM. The text will be formatted as one paragraph.
%description It slices! It dices! It's a CD player app that can't be beat. By using the resonant frequency of the CD itself, it is able to simulate 20X oversampling. This leads to sound quality that cannot be equaled with more mundane software...
Above, we have a similar situation to the previous example, in that part of the text is formatted and part is not. However, the blank line separates the text into two paragraphs.
The summary tag is used to define a one-line description of the packaged software. Unlike %description, summary is restricted to one line. RPM uses it when a succinct description of the package is needed. Here is an example of a summary line:
Summary: A CD player app that rocks!
The copyright tag is used to define the copyright terms applicable to the software being packaged. In many cases, this might be nothing more than ``GPL'', for software distributed under the terms of the GNU General Public License, or something similar. For example:
Copyright: GPL
The distribution tag is used to define a group of packages, of which this package is a part. Since Red Hat Software is in the business of producing a group of packages known as a Linux distribution, the name stuck. For example, if a suite of applications known as ``Doors '95'' were produced, each package that is part of the suite would define its distribution line like this:
Distribution: Doors '95
The icon tag is used to name a file containing an icon representing the packaged software. The file may be in either GIF or XPM format, although XPM is preferred. In either case, the background of the icon should be transparent. The file should be placed in RPM's SOURCES directory prior to performing a build, so no path is needed.
The icon is normally used by graphically-oriented front ends to RPM. RPM itself doesn't use the icon, but it's stored in the package file and retained in RPM's database after the package is installed. An example icon tag might look like:
Icon: foo.xpm
The vendor tag is used to define the name of the entity that is responsible for packaging the software. Normally, this would be the name of an organization. Here's an example:
Vendor: White Socks Software, Inc.
The url tag is used to define a Uniform Resource Locator that can be used to obtain additional information about the packaged software. At present, RPM doesn't actively make use of this tag. The data is stored in the package however, and will be written into RPM's database when the package is installed. It's only a matter of time before some web-based RPM adjunct makes use of this information, so make sure you include URLs! Something like this is all you'll need:
URL: http://www.gnomovision.com/cdplayer.html
The group tag is used to group packages together by the types of functionality they provide. The group specification looks like a path and is similar in function, in that it specifies more general groupings before more detailed ones. For example, a package containing a text editor might have the following group:
Group: Applications/Editors
In this example, the package is part of the Editors group, which is itself a part of the Applications group. Likewise, a spreadsheet package might have this group:
Group: Applications/Spreadsheets
This group tag indicates that under the Applications group, we would find Editors and Spreadsheets, and probably some other subgroups as well.
How is this information used? It's primarily meant to permit graphical front-ends to RPM, to display packages in a hierarchical fashion. Of course, in order for groups to be as effective as possible, it's necessary for all package builders to be consistent in their groupings. In the case of packages for Linux, Red Hat Software has the definitive list. Therefore, Linux package builders should give serious consideration to using Red Hat Software's groups. The current group hierarchy is installed with every copy of RPM, and is available in the RPM sources as well. Check out the file groups in RPM's documentation directory (normally /usr/doc/rpm-<version>), or in the top-level source directory.
The packager tag is used to hold the name and contact information for the person or persons who built the package. Normally, this would be the person that actually built the package, or in a larger organization, a public relations contact. In either case, contact information such as an e-mail address or phone number should be included, so customers can send either money or hate mail, depending on their satisfaction with the packaged software. Here's an example of a packager tag:
Packager: Fred Foonly <fred@@gnomovision.com>
One RPM feature that's been recently implemented is a means of ensuring that if a package is installed, the system environment has everything the package requires in order to operate properly. Likewise, when an installed package is erased RPM can make sure no other package relies on the package being erased. This dependency capability can be very helpful when endusers install and erase packages on their own. It makes it more difficult for them to paint themselves into a corner, package-wise.
However, in order for RPM to be able to take more than basic dependency information into account, the package builder must add the appropriate dependency information to the package. This is done by using the following tags. Note, however, that adding dependency information to a package requires some forethought. For additional information on RPM's dependency processing, please review chapter [*] on page [*].
The provides tag is used to specify a ``virtual package'' that the packaged software makes available when it is installed. Normally, this tag would be used when different packages provide equivalent services. For example, any package that allows a user to read mail might provide the mail-reader virtual package. Another package that depends on a mail reader of some sort, could require the mail-reader virtual package. It would then install without dependency problems, if any one of several mail programs were installed. Here's what a provides tag might look like:
Provides: mail-reader
The requires tag is used to alert RPM to the fact that the package needs to have certain capabilities available in order to operate properly. These capabilities refer to the name of another package, or to a virtual package provided by one or more packages that use the provides tag. When the requires tag references a package name, version comparisons may also be included by following the package name with <, >, =, >=, or <=, and a version specification. To get even more specific, a package's release may be included as well. Here's a requires tag in action, with a specific version requirement:
Requires: playmidi = 2.3
If the Requires tag needs to perform a comparison against a serial number defined with the serial tag (described below), then the proper format would be:
Requires: playmidi =S 4
The conflicts tag is the logical complement to the requires tag. The requires tag is used to specify what packages must be present in order for the current package to operate properly. The conflicts tag is used to specify what packages cannot be installed if the current package is to operate properly.
The conflicts tag has the same format as the requires tag -- namely, the tag is followed by a real or virtual package name. Like requires, the conflicts tag also accepts version and release specifications:
Conflicts: playmidi = 2.3-1
If the conflicts tag needs to perform a comparision against a serial number defined with the serial tag (described below), then the proper format would be:
Conflicts: playmidi =S 4
The serial tag is another part of RPM's dependency and upgrade processing. The need for it is somewhat obscure, but goes something like this:
Since RPM is unable to compare package A's version against the version specified by package B, there is no way to determine if package B's dependency requirements can be met. What to do?
The serial tag provides a way to get around this tricky problem. By specifying a simple integer serial number for each version, you are, in essence, directing how RPM interprets the relative age of the package. The key point to keep in mind is that in order for this to work, a unique serial number must be defined for each version of the software being packaged. In addition, the serial number must increment along with the version. Finally, the package that requires the serialized software needs to specify its version requirements in terms of the serial number.
Does it sound like a lot of trouble? You're right! If you find yourself in the position of needing to use this tag, take a deep breath and seriously consider changing the way you assign version numbers. If you're packaging someone else's software, perhaps you can convince them to make the change. Chances are, if RPM can't figure out the version number, most people can't, either! An example serial tag would look something like this:
Serial: 4
Note that RPM considers a package with a serial number as newer than a package without a serial number.
The autoreqprov tag is used to control the automatic dependency processing performed when the package is being built. Normally, as each package is built, the following steps are performed:
By doing this, RPM reduces the need for package builders to manually add dependency information to their packages. However, there are times when RPM's automatic dependency processing may not be desirable. In those cases the autoreqprov tag can be used to disable automatic dependency processing.
To disable automatic dependency processing, add the following line:
AutoReqProv: no
(The number zero may be used instead of no) Although RPM defaults to performing automatic dependency processing, the effect of the autoreqprov tag can be reversed by changing no to yes. (The number one may be used instead of yes)
As RPM gains in popularity, more people are putting it to work on different types of computer systems. While this would not normally be a problem, things start to get a little tricky when one of the following two situations becomes commonplace:
The real bind hits when RPM is used to package software for several of these different system environments. Without methods of controlling the build process based on architecture and operating system, package builders that develop software for more than one architecture or operating system will have a hard time indeed. The only alternative would be to maintain parallel RPM build environments and accept all the coordination headaches that would entail.
Fortunately, RPM makes it all easier than that. With the following tags, it's possible to support package building under multiple environments, all from a single set of sources, patches, and a single spec file. For a more complete discussion of multi-architecture package building, please see chapter [*].
The excludearch tag directs RPM to ensure that the package does not attempt to build on the excluded architecture(s). The reasons for preventing a package from building on a certain architecture might include:
An example of the first case might be that the software was designed based on the assumption that an integer is a 32-bit quantity. Obviously, this assumption is not valid on a 64-bit processor.
In the second case, software that depended on or manipulated low-level features of a given architecture, should be excluded from building on a different architecture. Assembly language programs would fall into this category.
One or more architectures may be specified after the excludearch tag, separated by either spaces or commas. Here is an example:
ExcludeArch: sparc alpha
In this example, RPM would not attempt to build the package on either the Sun SPARC or Digital Alpha/AXP architectures. The package would build on any other architectures, however. If a build is attempted on an excluded architecture, the following message will be displayed, and the build will fail:
# rpm -ba cdplayer-1.0.spec
Arch mismatch!
cdplayer-1.0.spec doesn't build on this architecture
#
Note that if your goal is to ensure that a package will only build on one architecture, then you should use the exclusivearch tag.
The exclusivearch tag is used to direct RPM to ensure the package is only built on the specified architecture(s). The reasons for this are similar to the those mentioned in the section on the excludearch tag above. However, the exclusivearch tag is useful when the package builder needs to ensure that only the specified architectures will build the package. This tag ensures that no future architectures will mistakenly attempt to build the package. This would not be the case if the excludearch tag were used to specify every architecture known at the time the package is built.
The syntax of the exclusivearch tag is identical to that of excludearch:
ExclusiveArch: sparc alpha
In this example, the package will only build on a Sun SPARC or Digital Alpha/AXP system.
Note that if your goal is to ensure that a package will not build on specific architectures, then you should use the excludearch tag.
The excludeos tag is used to direct RPM to ensure that the package does not attempt to build on the excluded operating system(s). This is usually necessary when a package is to be built on more than one operating system, but it is necessary to keep a particular operating system from attempting a build.
Note that if your goal is to ensure that a package will only build on one operating system, then you should use the exclusiveos tag. Here's a sample excludeos tag:
ExcludeOS: linux irix
The exclusiveos tag has the same syntax as excludeos, but it has the opposite logic. The exclusiveos tag is used to denote which operating system(s) should only be be permitted to build the package. Here's exclusiveos in action:
ExclusiveOS: linux
Note that if your goal is to ensure that a package will not build on a specific operating system, then you should use the excludeos tag.
A number of tags are used to specify directories and paths that are used in various phases of RPM's build and install processes. There's not much more to say collectively about these tags, so let's dive right in and look them over.
The prefix tag is used when a relocatable package is to be built. A relocatable package can be installed normally or can be installed in a user-specified directory, by using RPM's - -prefix install-time option. The data specified after the prefix tag should be the part of the package's path that may be changed during installation. For example, if the following prefix line was included in a spec file:
Prefix: /opt
and the following file was specified in the spec file's %files list:
/opt/blather/foonly
then the file foonly would be installed in /opt/blather if the package was installed normally. It would be installed in /usr/local/blather if the package was installed with the - -prefix /usr/local option.
For more information about creating relocatable packages, see chapter [*].
The buildroot tag is used to define an alternate build root. The name is a bit misleading, as the build root is actually used when the software is installed during the build process. In order for a build root to be defined and actually used, a number of issues must be taken into account. These issues are covered in chapter [*]. This is what a buildroot tag would look like:
BuildRoot: /tmp/cdplayer
The buildroot tag can be overridden at build-time by using the - -buildroot command-line option.
In order to build and package software, RPM needs to know where to find the original sources. But it's not quite that simple. There might be more than one set of sources that need to be part of a particular build. In some cases, it might be necessary to prevent some sources from being packaged.
And then there is the matter of patches. It's likely that changes will need to be made to the sources, so it's necessary to specify a patch file. But the same issues that apply to source specifications are also applicable to patches. There might be more than one set of patches required.
The tags that follow are crucial to RPM, so it pays to have a firm grasp of how they are used.
The source tag is central to nearly every spec file. Although it has only one piece of data associated with it, it actually performs two functions:
While there is no hard and fast rule, for the first function, it's generally considered best to put this information in the form of a Uniform Resource Locator (URL). The URL should point directly to the source file itself. This is due to the source tag's second function.
As mentioned above, the source tag also needs to direct RPM to the source file on the build system. How does it do this? There's only one requirement, and it is ironclad: The source filename must be at the end of the line as the final element in a path. Here's an example:
Source: ftp://ftp.gnomovision.com/pub/cdplayer-1.0.tgz
Given this source line, RPM will search its SOURCES directory for cdplayer-1.0.tgz. Everything prior to the filename is ignored by RPM. It's there strictly for any interested humans.
A spec file may contain more than one source tag. This is necessary for those cases where the software being packaged is contained in more than one source file. However, the source tags must be uniquely identified. This is done by appending a number to the end of the tag itself. In fact, RPM does this internally for the first source tag in a spec file, in essence turning it into source0. Therefore, if a package contains two source files, they may either be specified as:
Source: blather-4.5.tar.gz Source1: bother-1.2.tar.gz
or as:
Source0: blather-4.5.tar.gz Source1: bother-1.2.tar.gz
Either approach may be used. The choice is yours.
The nosource tag is used to direct RPM to omit one or more source files from the source package. Why would someone want to go to the trouble of specifying a source file, only to exclude it? The reasons for this can be varied, but let's look at one example: The software known as Pretty Good Privacy, or PGP.
PGP contains encryption routines of such high quality that the United States government restricts their export.1 While it would be nice to create a PGP package file, the resulting package could not legally be transferred between the U.S. and other countries, or vice-versa.
However, what if all files other than the original source, were packaged using RPM? Well, a binary package made without PGP would be of little use, but what about the source package? It would contain the spec file, maybe some patches, and perhaps even an icon file. Since the controversial PGP software was not a part of the source package, this sanitized source package could be downloaded legally in any country. The person that downloaded a copy could then go about legally obtaining the PGP sources themselves, place them in RPM's SOURCES directory, and create a binary package. They wouldn't even need to change the nosource tag. One rpm -ba command later, and the user would have a perfectly usable PGP binary package file.
Since there may be more than one source tag in a spec file, the format of the nosource tag is as follows:
nosource: <src-num>, <src-num>...<src-num>
The <src-num> represents the number following the source tag. If there is more than one number in the list, they may be separated by either commas or spaces. For example, consider a package containing the following source tags:
source: blather-4.5.tar.gz Source1: bother-1.2.tar.gz source2: blather-lib-4.5.tar.gz source3: bother-lib-1.2.tar.gz
If the source files for blather and blather-lib were not to be included in the package, the following nosource line could be added:
NoSource: 0, 3
What about that 0? Keep in mind that the first unnumbered source tag in a spec file is automatically numbered 0 by RPM.
The patch tag is used to identify which patches are associated with the software being packaged. The patch files are kept in RPM's SOURCES directory, so only the name of the patch file should be specified. Here is an example:
Patch: cdp-0.33-fsstnd.patch
There are no hard and fast requirements for naming the patch files, but traditionally the filename starts with the software name and version, separated by dashes. The next part of the patch file name usually includes one or more words indicating the reason for the patch. In our example above, the patch file contains changes necessary to bring the software into compliance with the Linux File System Standard, hence the fsstnd magic incantation.
RPM processes patch tags the same way it does source tags. Therefore, it's acceptable to use a Uniform Resource Locator (URL) on a patch line, too.
A spec file may contain more than one patch tag. This is necessary for those cases where the software being packaged requires more than one patch. However, the patch tags must be uniquely identified. This is done by appending a number to the end of the tag itself. In fact, RPM does this internally for the first patch tag in a spec file, in essence turning it into patch0. Therefore, if a package contains three patches, the following two methods of specifying them are equivalent:
Patch: blather-4.5-bugfix.patch Patch1: blather-4.5-config.patch Patch2: blather-4.5-somethingelse.patch
This is the same as:
Patch0: blather-4.5-bugfix.patch Patch1: blather-4.5-config.patch Patch2: blather-4.5-somethingelse.patch
Either approach may be used, but the second method looks nicer.
The nopatch tag is similar to the nosource tag discussed earlier. Just like the nosource tag, the nopatch tag is used to direct RPM to omit something from the source package. In the case of nosource, that ``something'' was one or more sources. For the nopatch tag, the ``something'' is one or more patches.
Since each patch tag in a spec file must be numbered, the nopatch tag uses those numbers to specify which patches are not to be included in the package. The nopatch tag is used in this manner:
NoPatch: 2 3
In this example, the source files specified on the source2 and source3 lines are not to be included in the build.
This concludes our study of RPM's tags. In the next section, we'll look at the various scripts that RPM uses to build, as well as to install, and erase, packages.
The scripts that RPM uses to control the build process are among the most varied and interesting parts of the spec file. Many spec files also contain scripts that perform a variety of tasks whenever the package is installed or erased.
The start of each script is denoted by a keyword. For example, the %build keyword marks the start of the script RPM will execute when building the software to be packaged. It should be noted that, in the strictest sense of the word, these parts of the spec file are not scripts. For example, they do not start with the traditional invocation of a shell. However, the contents of each script section are copied into a file and executed by RPM as a full-fledged script. This is part of the power of RPM: Anything that can be done in a script can be done by RPM.
Let's start by looking at the scripts used during the build process.
The scripts that RPM uses during the building of a package follow the steps known to every software developer:
Although each of the scripts perform a specific function in the build process, they share a common environment. Using RPM's - -test option2, we can see the common portion of each script. In the following example, we've taken the cdplayer package, issued an rpm -ba - -test cdplayer-1.0-1.spec, and viewed the script files left in RPM's temporary directory. This section (with the appropriate package-specific values) is present in every script RPM executes during a build:
#!/bin/sh -e # Script generated by rpm RPM_SOURCE_DIR="/usr/src/redhat/SOURCES" RPM_BUILD_DIR="/usr/src/redhat/BUILD" RPM_DOC_DIR="/usr/doc" RPM_OPT_FLAGS="-O2 -m486 -fno-strength-reduce" RPM_ARCH="i386" RPM_OS="Linux" RPM_ROOT_DIR="/tmp/cdplayer" RPM_BUILD_ROOT="/tmp/cdplayer" RPM_PACKAGE_NAME="cdplayer" RPM_PACKAGE_VERSION="1.0" RPM_PACKAGE_RELEASE="1" set -x umask 022
As we can see, the script starts with the usual invocation of a shell (in this case, the Bourne shell). There are no arguments passed to the script. Next, a number of environment variables are set. Here's a brief description of each one:
All of these environment variables are set for your use, to make it easier to write scripts that will do the right thing even if the build environment changes.
The script also sets an option that causes the shell to print out each command, complete with expanded arguments. Finally, the default permissions are set. Past this point, the scripts differ. Let's look at the scripts in the order they are executed.
The %prep script is the first script RPM executes during a build. Prior to the %prep script, RPM has performed preliminary consistency checks, such as whether the spec file's source tag points to files that actually exist. Just prior to passing control over to the %prep script's contents, RPM changes directory into RPM's build area, which, by default, is /usr/src/redhat/BUILD.
At that point, it is the responsibility of the %prep script to:
The first three items on this list are common to the vast majority of all software being packaged. Because of this, RPM has two macros that greatly simplify these routine functions. More information on RPM's %setup and %patch macros can be found in section [*] on page [*].
The last item on the list can include creating directories or anything else required to get the sources in a ready-to-build state. As a result, a %prep script can range from one line invoking a single %setup macro, to many lines of tricky shell programming.
The %build script picks up where the %prep script left off. Once the %prep script has gotten everything ready for the build, the %build script is usually somewhat anti-climactic -- normally invoking make, maybe a configuration script, and little else.
Like %prep before it, the %build script has the same assortment of environment variables to draw on. Also, like %prep, %build changes directory into the software's top-level build directory (located in RPM_BUILD_DIR, or usually called <name>-<version>).
Unlike %prep, there are no macros available for use in the %build script. The reason is simple: Either the commands required to build the software are simple (such as a single make command), or they are so unique that a macro wouldn't make it easier to write the script.
The environment in which the %install script executes is identical to the other scripts. Like the other scripts, the %install script's working directory is set to the software's top-level directory.
As the name implies, it is this script's responsibility to do whatever is necessary to actually install the newly built software. In most cases, this means a single make install command, or a few commands to copy files and create directories.
The %clean script, as the name implies, is used to clean up the software's build directory tree. RPM normally does this for you, but in certain cases (most notably in those packages that use a build root) you'll need to include a %clean script.
As usual, the %clean script has the same set of environment variables as the other scripts we've covered here. Since a %clean script is normally used when the package is built in a build root, the RPM_BUILD_ROOT environment variable is particularly useful. In many cases, a simple
rm -rf $RPM_BUILD_ROOT
will suffice.3
The other type of scripts that are present in the spec file are those that are only used when the package is either installed or erased. There are four scripts, each one meant to be executed at different times during the life of a package:
Unlike the build-time scripts, there is little in the way of environment variables for these scripts. The only environment variable available is RPM_INSTALL_PREFIX, and that is only set if the package uses an installation prefix.
Unlike the build-time scripts, there is an argument defined. The sole argument to these scripts, is a number representing the number of instances of the package currently installed on the system, after the current package has been installed or erased. Sound tricky? It really isn't. Here's an example:
Assume that a package, called blather-1.0, is being installed. No previous versions of blather have been installed. Since the software is being installed, only the %pre and %post scripts are executed. The argument passed to these scripts will be 1, since the the number of blather packages installed is 1.4
Continuing our example, a new version of the blather package, version 1.3, is now available. Clearly it's time to upgrade. What will the scripts' values be during the upgrade? As blather-1.3 is installing, its %pre and %post scripts will have an argument equal to 2 (1 for version 1.0 already installed, plus 1 for version 1.3 being installed). As the final part of the upgrade, it's then time to erase blather version 1.0. As the package is being removed, its %preun and %postun scripts are executed. Since there will be only one blather package (version 1.3) installed after version 1.0 is erased, the argument passed to version 1.0's scripts is 1.
To finally bring an end to this example, we've decided to erase blather 1.3. We just don't need it anymore. As the package is being erased, its %preun and %postun scripts will be executed. Since there will be no blather packages installed once the erase completes, the argument passed to the scripts is 0.
With all that said, of what possible use would this argument be? Well, it has two very interesting properties:
Based on these properties, it's trivial to write an install-time script that can take certain actions in specific circumstances. Usually, the argument is used in the %preun or %postun scripts to perform a special task when the last instance of a package is being erased.
What is normally done during these scripts? The exact tasks may vary, but in general, the tasks are any that need to be performed at these points in the package's existence. One very common task is to run ldconfig when shared libraries are installed or removed. But that's not the only use for these scripts. It's even possible to use the scripts to perform tests to ensure the package install/erasure should proceed.
Since each of these scripts will be executing on whatever system installs the package, it's necessary to choose the script's choice of tools carefully. Unless you're sure a given program is going to be available on all the systems that could possibly install your package, you should not use it in these scripts.
The %pre script executes just before the package is to be installed. It is the rare package that requires anything to be done prior to installation; none of the 350 packages that comprise Red Hat Linux 4.0 make use of it.
The %post script executes after the package has been installed. One of the most popular reasons a %post script is needed is to run ldconfig to update the list of available shared libraries after a new one has been installed. Of course, other functions can be performed in a %post script. For example, packages that install shells use the %post script to add the shell name to /etc/shells.
If a package uses a %post script to perform some function, quite often it will include a %postun script that performs the inverse of the %post script, after the package has been removed.
If there's a time when your package needs to have one last look around before the user erases it, the place to do it is in the %preun script. Anything that a package needs to do immediately prior to RPM taking any action to erase the package, can be done here.
The %postun script executes after the package has been removed. It is the last chance for a package to clean up after itself. Quite often, %postun scripts are used to run ldconfig to remove newly erased shared libraries from ld.so.cache.
The %verifyscript executes whenever the installed package is verified by RPM's verification command. The contents of this script is entirely up to the package builder, but in general the script should do whatever is necessary to verify the package's proper installation. Since RPM automatically verifies the existence of a package's files, along with other file attributes, the %verifyscript should concentrate on different aspects of the package's installation. For example, the script may ensure that certain configuration files contain the proper information for the package being verified:
for n in ash bsh; do echo -n "Looking for $n in /etc/shells... " if ! grep "^/bin/${n}\$" /etc/shells > /dev/null; then echo "missing" echo "${n} missing from /etc/shells" >&2 else echo "found" fi done
In this script, the config file /etc/shells, is checked to ensure that it has entries for the shells provided by this package.
It is worth noting that the script sends informational and error messages to stdout, and error messages only to stderr. Normally RPM will only display error output from a verification script; the output sent to stdout is only displayed when the verification is run in verbose mode.
RPM does not support macros in the sense of ad-hoc sequences of commands being defined as a macro and executed by simply referring to the macro name.
However, there are two parts of RPM's build process that are fairly constant from one package to another, and they are the unpacking and patching of sources. Because of this, RPM makes two macros available to simplify these tasks:
These macros are used exclusively in the %prep script; it wouldn't make sense to use them anywhere else. The use of these macros is not mandatory -- It is certainly possible to write a %prep script without them. But in the vast majority of cases they make life easier for the package builder.
As we mentioned above, the %setup macro is used to unpack the original sources, in preparation for the build. In its simplest form, the macro is used with no options and gets the name of the source archive from the source tag specified earlier in the spec file. Let's look at an example. The cdplayer package has the following source tag:
Source: ftp://ftp.gnomovision.com/pub/cdplayer/cdplayer-1.0.tgz
and the following %prep script:
%prep %setup
In this simple case, the %setup macro expands into the following commands:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
As we can see, the %setup macro starts by changing directory into RPM's build area and removing any cdplayer build trees from previous builds. It then uses gzip to uncompress the original source (whose name was taken from the source tag), and pipes the result to tar for unpacking. The return status of the unpacking is tested. If sucessful, the macro continues.
At this point, the original sources have been unpacked. The %setup macro continues by changing directory into cdplayer's top-level directory. The two cd commands are an artifact of %setup's macro expansion. Finally, %setup makes sure every file in the build tree is owned by root and has appropriate permissions set.
But that's just the simplest way that %setup can be used. There are a number of other options that can be added to accomodate different situations. Let's look at them.
In our example above, the %setup macro simply uncompressed and unpacked the sources. In this case, the tar file containing the original sources was created such that the top-level directory was included in the tar file. The name of the top-level directory was also identical to that of the tar file, which was in <name>-<version> format.
However, this is not always the case. Quite often, the original sources unpack into a directory whose name is different than the original tar file. Since RPM assumes the directory will be called <name>-<version>, when the directory is called something else, it's necessary to use %setup's -n option. Here's an example:
Assume, for a moment, that the cdplayer sources, when unpacked, create a top-level directory named cd-player. In this case, our %setup line would look like this:
%setup -n cd-player
and the resulting commands would look like this:
cd /usr/src/redhat/BUILD rm -rf cd-player gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cd-player cd /usr/src/redhat/BUILD/cd-player chown -R root.root . chmod -R a+rX,g-w,o-w .
The results are identical to using %setup with no options, except for the fact that %setup now does a recursive delete on the directory cd-player (instead of cdplayer-1.0), and changes directory into cd-player (instead of cdplayer-1.0).
Note that all subsequent build-time scripts will change directory into the directory specified by the -n option. This makes -n unsuitable as a means of unpacking sources in directories other than the top-level build directory. In the upcoming example on page [*], we'll show a way around this restriction.
A quick word of warning: If the name specified with the -n option doesn't match the name of the directory created when the sources are unpacked, the build will stop pretty quickly, so it pays to be careful when using this option.
How many times have you grabbed a tar file and unpacked it, only to find that it splattered files all over your current directory? Sometimes source archives are created without a top-level directory.
As you can see from the examples so far, %setup expects the archive to create its own top-level directory. If this isn't the case, you'll need to use the -c option.
This option simply creates the directory and changes directory into it before unpacking the sources. Here's what it looks like:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 mkdir -p cdplayer-1.0 cd cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
The only changes from using %setup with no options, are the mkdir and cd commands, prior to the commands that unpack the sources. Note that you can use the -n option along with -c, so something like %setup -c -n blather works as expected.
The -D option keeps the %setup macro from deleting the software's top-level directory. This option is handy when the sources being unpacked are to be added to an already-existing directory tree. This would be the case when more than one %setup macro is used. Here's what %setup does when the -D option is employed:
cd /usr/src/redhat/BUILD gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
As advertised, the rm prior to the tar command is gone.
The -T option disables %setup's normal unpacking of the archive file specified on the source0 line. Here's what the resulting commands look like:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
Doesn't make much sense, does it? There's a method to this madness. We'll see the -T in action in the next section.
The -b option is used in conjunction with the source tag. Specifically, it is used to identify which of the numbered source tags in the spec file are to be unpacked.
The -b option requires a numeric argument matching an existing source tag. If a numeric argument is not provided, the build will fail:
# rpm -ba cdplayer-1.0.spec
* Package: cdplayer
Need arg to %setup -b
Build failed.
#
Remembering that the first source tag is implicitly numbered 0, let's see what happens when the %setup line is changed to %setup -b 0:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
That's strange. The sources were unpacked twice. It doesn't make sense, until you realize that this is why there is a -T option. Since -T disables the default source file unpacking, and -b selects a particular source file to be unpacked, the two are meant to go together, like this:
%setup -T -b 0
Looking at the resulting commands, we find:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
That's more like it! Let's go on to the next option.
The -a option works similarly to the -b option, except that the sources are unpacked after changing directory into the top-level build directory. Like the -b option, -a requires -T in order to prevent two sets of unpacking commands. Here are the commands that a %setup -T -a 0 line would produce:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 cd cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
Note that there is no mkdir command to create the top-level directory prior to issuing a cd into it. In our example, adding the -c option will make things right:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 mkdir -p cdplayer-1.0 cd cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
The result is the proper sequence of commands for unpacking a tar file with no top-level directory.
If all these interrelated options seem like overkill for unpacking a single source file, you're right. The real reason for the various options is to make it easier to combine several separate source archives into a single, build-able entity. Let's see how they work in that type of environment.
For the purposes of this example, our spec file will have the following three source tags:5
source: source-zero.tar.gz source1: source-one.tar.gz source2: source-two.tar.gz
To unpack the first source is not hard; all that's required is to use %setup with no options:
%setup
This produces the following set of commands:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
If source-zero.tar.gz didn't include a top-level directory, we could have made one by adding the -c option:
%setup -c
which would result in:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 mkdir -p cdplayer-1.0 cd cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
Of course, if the top-level directory did not match the package name, the -n option could have been added:
%setup -n blather
which results in:
cd /usr/src/redhat/BUILD rm -rf blather gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd blather cd /usr/src/redhat/BUILD/blather chown -R root.root . chmod -R a+rX,g-w,o-w .
or
%setup -c -n blather
This results in:
cd /usr/src/redhat/BUILD rm -rf blather mkdir -p blather cd blather gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd /usr/src/redhat/BUILD/blather chown -R root.root . chmod -R a+rX,g-w,o-w .
Now let's add the second source file. Things get a bit more interesting here. First, we need to identify which source tag (and therefore, which source file) we're talking about. So we need to use either the -a or -b option, depending on the characteristics of the source archive. For this example, let's say that -a is the option we want. Adding that option, plus a ``1'' to point to the source file specified in the source1 tag, we have:
%setup -a 1
Since we've already seen that using the -a or -b option results in duplicate unpacking, we need to disable the default unpacking by adding the -T option:
%setup -T -a 1
Next, we need to make sure that the top-level directory isn't deleted. Otherwise, the first source file we just unpacked would be gone. That means we need to include the -D option to prevent that from happening. Adding this final option, and including the now complete macro in our %prep script, we now have:
%setup
%setup -T -D -a 1
This will result in the following commands:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w . cd /usr/src/redhat/BUILD cd cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/source-one.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w .
So far, so good. Let's include the last source file, but with this one, we'll say that it needs to be unpacked in a subdirectory of cdplayer-1.0 called database. Can we use %setup in this case?
We could, if source-two.tgz created the database subdirectory. If not, then it'll be necessary to do it by hand. For the purposes of our example, let's say that source-two.tgz wasn't created to include the database subdirectory, so we'll have to do it ourselves. Here's our %prep script now:
%setup
%setup -T -D -a 1
mkdir database
cd database
gzip -dc /usr/src/redhat/SOURCES/source-two.tar.gz | tar -xvvf -
Here's the resulting script:
cd /usr/src/redhat/BUILD rm -rf cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi cd cdplayer-1.0 cd /usr/src/redhat/BUILD/cdplayer-1.0 chown -R root.root . chmod -R a+rX,g-w,o-w . cd /usr/src/redhat/BUILD cd cdplayer-1.0 gzip -dc /usr/src/redhat/SOURCES/source-one.tar.gz | tar -xvvf - if [ $? -ne 0 ]; then exit $? fi mkdir database cd database gzip -dc /usr/src/redhat/SOURCES/source-two.tar.gz | tar -xvvf -
The three commands we added to unpack the last set of sources were added to the end of the %prep script.
The bottom line to using the %setup macro is that you can probably get it to do what you want, but don't be afraid to tinker. And even if %setup can't be used, it's easy enough to add the necessary commands to do the work manually. Above all, make sure you use the - -test option when testing your %setup macros, so you can see what commands they're translating to.
Next, let's look at RPM's second macro, %patch.
The %patch macro, as its name implies, is used to apply patches to the unpacked sources. In the following examples, our spec file has the following patch tag lines:
patch0: patch-zero patch1: patch-one patch2: patch-two
At its simplest, the %patch macro can be invoked without any options:
%patch
Here are the resulting commands:
echo "Patch #0:" patch -p0 -s < /usr/src/redhat/SOURCES/patch-zero
The %patch macro nicely displays a message showing that a patch is being applied, then invokes the patch command to actually do the dirty work. There are two options to the patch command:
How did the %patch macro know which patch to apply? Keep in mind that, like the source tag lines, every patch tag is numbered, starting at zero. The %patch macro, by default, applies the patch file named on the patch (or patch0) tag line.
The %patch macro actually has two different ways to specify the patch tag line it is to use. The first method is to simply append the number of the desired patch tag to the end of the %patch macro itself. For example, in order to apply the patch specified on the patch2 tag line, the following %patch macro could be used:
%patch2
The other approach is to use the -P option. This option is followed by the number of the patch tag line desired. Therefore, this line is identical in function to the previous one:
%patch -P 2
Note that the -P option will not apply the file specified on the patch0 line, by default. Therefore, if you choose to use the -P option to specify patch numbers, you'll need to use the following format when applying patch zero:
The -p (Note the lowercase ``p''!) option is sent directly to the patch command. It is followed by a number, which specifies the number of leading slashes (and the directories in between) to strip from any filenames present in the patch file. For more information on this option, please consult the patch man page.
When the patch command is used to apply a patch, unmodified copies of the files patched are renamed to end with the extension .orig. The -b option is used to change the extension used by patch. This is normally done when multiple patches are to be applied to a given file. By doing this, copies of the file as it existed prior to each patch, are readily available.
The -E option is passed directly to the patch program. When patch is run with the -E option, any output files that are empty after the patches have been applied, are removed.
Now let's take %patch on a test-drive, and put it through its paces.
Using the example patch tag lines we've used throughout this section, let's put together an example and look at the resulting commands. In our example, the first patch to be applied needs to have the root directory stripped. Its %patch macro will look like this:
%patch -p1
The next patch is to be applied to files in the software's lib subdirectory, so we'll need to add a cd command to get us there. We'll also need to strip an additional directory:
cd lib
%patch -P 1 -p2
Finally, the last patch is to be applied from the software's top-level directory, so we need to cd back up a level. In addition, this patch modifies some files that were also patched the first time, so we'll need to change the backup file extension:
cd ..
%patch -P 2 -p1 -b .last-patch
Here's what the %prep script (minus any %setup macros) looks like:
%patch -p1
cd lib
%patch -P 1 -p2
cd ..
%patch -P 2 -p1 -b .last-patch
And here's what the macros expand to:
echo "Patch #0:" patch -p1 -s < /usr/src/redhat/SOURCES/patch-zero cd lib echo "Patch #1:" patch -p2 -s < /usr/src/redhat/SOURCES/patch-one cd .. echo "Patch #2:" patch -p1 -b .last-patch -s < /usr/src/redhat/SOURCES/patch-two
No surprises here. Note that the %setup macro leaves the current working directory set to the software's top-level directory, so our cd commands with their relative paths will do the right thing. Of course, we have environment variables available that could be used here, too.
If a patch file is compressed with gzip, RPM will automatically decompress it before applying the patch. Here's a compressed patch file as specified in the spec file:
Patch: bother-3.5-hack.patch.gz
This is part of the script RPM will execute when the %prep section is executed:
echo Executing: %prep ... echo "Patch #0:" gzip -dc /usr/src/redhat/SOURCES/bother-3.5-hack.patch.gz | patch -p1 -s ...
First, the patch file is decompressed using gzip. The output from gzip is then piped into patch.
That's about it for RPM's macros. Next, let's take a look at the %files list.
The %files list indicates to RPM which files on the build system are to be packaged. The list consists of one file per line. The file may have one or more directives preceeding it. These directives give RPM additional information about the file and are discussed more fully below.
Normally, each file includes its full path. The path performs two functions. First, it specifies the file's location on the build system. Second, it denotes where the file should be placed when the package is to be installed.6
For packages that create directories containing hundreds of files, it can be quite cumbersome creating a list that contains every file. To make this situation a bit easier, if the %files list contains a path to a directory, RPM will automatically package every file in that directory, as well as every file in each subdirectory. Shell-style globbing can also be used in the %files list.
The %files list may contain a number of different directives. They are used to:
In the %files list, one or more directives may be placed on a line, separated by spaces, before one or more filenames. Therefore, if %foo and %bar are two %files list directives, they may be applied to a file baz in the following manner:
%foo %bar baz
Now it's time to take a look at the directives that inhabit the %files list.
RPM processes files differently according to their type. However, RPM does not have a method of automatically determining file types. Therefore, it is up to the package builder to appropriately mark files in the %files list. This is done using one of the directives below.
Keep in mind that not every file will need to be marked. As you read the following sections, you'll see that directives are only used in special circumstances. In most packages, the majority of files in the %files list will not need to be marked.
The %doc directive flags the filename(s) that follow, as being documentation. RPM keeps track of documentation files in its database, so that a user can easily find information about an installed package. In addition, RPM can create a package-specific documentation directory during installation and copy documentation into it. Whether or not this additional step is taken, is dependent on how a file is specified. Here is an example:
%doc README %doc /usr/local/foonly/README
The file README exists in the software's top-level directory during the build, and is included in the package file. When the package is installed, RPM creates a directory in the documentation directory named the same as the package (ie, <software>-<version>-<release>), and copies the README file there. The newly created directory and the README file are marked in the RPM database as being documentation. The default documentation directory is /usr/doc, and can be changed by setting the defaultdocdir rpmrc file entry. For more information on rpmrc files, please see chapter [*] on page [*].
The file /usr/local/foonly/README was installed into that directory during the build and is included in the package file. When the package is installed, the README file is copied into /usr/local/foonly and marked in the RPM database as being documentation.
The %config directive is used to flag the specified file as being a configuration file. RPM performs additional processing for config files when packages are erased, and during installations and upgrades. This is due to the nature of config files: They are often changed by the system administrator, and those changes should not be lost.
There is a restriction to the %config directive, and that restriction is that no more than one filename may follow the %config. This means that the following example is the only allowable way to specify config files:
%config /etc/foonly
Note that the full path to the file, as it is installed at build time, is required.
The %attr directive permits finer control over three key file attributes:
The %attr directive has the following format:
%attr(<mode>, <user>, <group>) file
The mode is specified in the traditional numeric format, while the user and group are specifed as a string, such as ``root''. Here's a sample %attr directive:
%attr(755, root, root) foo.bar
This would set foo.bar's permissions to 755. The file would be owned by user root, group root. If a particular attribute does not need to be specified (usually because the file is installed with that attribute set properly), then that attribute may be replaced with a dash:
%attr(755, -, root) foo.bar
The main reason to use the %attr directive is to permit users without root access to build packages. The techniques for doing this (and a more in-depth discussion of the %attr directive) can be found in chapter [*], on page [*].
RPM's ability to verify the integrity of the software it has installed is impressive. But sometimes it's a bit too impressive. After all, RPM can verify as many as nine different aspects of every file. The %verify directive can control which of these file attributes are to be checked when an RPM verification is done. Here are the attributes, along with the names used by the %verify directive:
How is %verify used? Say, for instance, that a package installs device files. Since the owner of a device will change, it doesn't make sense to have RPM verify the device file's owner/group and give out a false alarm. Instead, the following %verify directive could be used:
%verify(mode md5 size maj min symlink mtime) /dev/ttyS0
We've left out owner and group, since we'd rather RPM not verify those.7 However, if all you want to do is prevent RPM from verifying one or two attributes, you can use %verify's alternate syntax:
%verify(not owner group) /dev/ttyS0
This use of %verify produces identical results to the previous example.
While the two directives in this section perform different functions, each is related to directories in some way. Let's see what they do:
The %docdir directive is used to add a directory to the list of directories that will contain documentation. RPM includes the directories /usr/doc, /usr/info, and /usr/man in the %docdir list by default.
For example, if the following line is part of the %files list:
%docdir /usr/blather
any files in the %files list that RPM packages from /usr/blather will be included in the package as usual, but will also be automatically flagged as documentation. This directive is handy when a package creates its own documentation directory and contains a large number of files. Let's give it a try by adding the following line to our spec file:
%docdir /usr/blather
Our %files list contains no references to the several files the
package installs in the
/usr/blather directory. After building
the package, looking at the package's file list shows:
# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm
...
#
Wait a minute: There's nothing there, not even /usr/blather! What happened?
The problem is that %docdir only directs RPM to mark the specified directory as holding documentation. It doesn't direct RPM to package any files in the directory. To do that, we need to clue RPM in to the fact that there are files in the directory that must be packaged.
One way to do this is to simply add the files to the %files list:
%docdir /usr/blather /usr/blather/INSTALL
Looking at the package, we see that INSTALL was packaged:
# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm
...
/usr/blather/INSTALL
#
Directing RPM to only show the documentation files, we see that INSTALL has indeed been marked as documentation, even though the %doc directive had not been used:
# rpm -qdp ../RPMS/i386/blather-1.0-1.i386.rpm
...
/usr/blather/INSTALL
#
Of course, if you go to the trouble of adding each file to the %files list, it wouldn't be that much more work to add %doc to each one. So the way to get the most benefit from %docdir is to add another line to the %files list:
%docdir /usr/blather /usr/blather
Since the first line directs RPM to flag any file in /usr/blather as being documentation, and the second line tells RPM to automatically package any files found in /usr/blather, every single file in there will be packaged and marked as documentation:
# rpm -qdp ../RPMS/i386/blather-1.0-1.i386.rpm
/usr/blather
/usr/blather/COPYING
/usr/blather/INSTALL
/usr/blather/README
...
#
The %docdir directive can save quite a bit of effort in creating the %files list. The only caveat is that you must be sure the directory will only contain files you want marked as documentation. Keep in mind, also, that all subdirectories of the %docdir'ed directory will be marked as documentation directories, too.
As we mentioned in section [*], if a directory is specified in the %files list, the contents of that directory, and the contents of every directory under it, will automatically be included in the package. While this feature can be handy (assuming you are sure that every file under the directory should be packaged) there are times when this could be a problem.
The way to get around this, is to use the %dir directive. By adding this directive to the line containing the directory, RPM will package only the directory itself, regardless of what files are in the directory at the time the package is created. Here's an example of %dir in action.
The blather-1.0 package creates the directory /usr/blather as part of its build. It also puts several files in that directory. In the spec file, the /usr/blather directory is included in the %files list:
%files ... /usr/blather ...
There are no other entries in the %files list that have /usr/blather as part of their path. After building the package, we use RPM to look at the files in the package:
# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm
...
/usr/blather
/usr/blather/COPYING
/usr/blather/INSTALL
/usr/blather/README
...
#
The files present in /usr/blather at the time the package was built were included in the package automatically, without entering their names in the %files list.
However, after changing the /usr/blather line in the %files list to:
%dir /usr/blather
and rebuilding the package, a listing of the package's files now includes
only the
/usr/blather directory:
# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm
...
/usr/blather
...
#
The -f option is used to direct RPM to read the %files list from the named file. Like the %files list in a spec file, the file named using the -f option should contain one filename per line and also include any of the directives named in this section.
Why is it necessary to read filenames from a file rather than have the filenames in the spec file? Here's a possible reason:
The filenames' paths may contain a directory name that can only be determined at build-time, such as an architecture specification. The list of files, minus the variable part of the path, can be created, and sed can be used at build-time to update the path appropriately.
It's not necessary that every filename to be packaged reside in the file. If there are any filenames present in the spec file, they will be packaged as well:
%files latex -f tetex-latex-skel /usr/bin/latex /usr/bin/pslatex ...
Here, the filenames present in the file tetex-latex-skel would be packaged, followed by every filename following the %files line.
While every directive we've seen so far is used in the %files list, the %package directive is different. It is used to permit the creation of more than one package per spec file and can appear at any point in the spec file. These additional packages are known as subpackages. Subpackages are named according to the contents of the line containing the %package directive. The format of the package directive is:
%package: <string>
The <string> should be a name that describes the subpackage. This string is appended to the base package name to produce the subpackage's name. For example, if a spec file contains a name tag value of ``foonly'', and a ``%package doc'' line, then the subpackage name will be foonly-doc.
As we mentioned above, the name of a subpackage normally includes the main package name. When the -n option is added to the %package directive, it directs RPM to use the name specified on the %package line as the entire package name. In the example above, the following %package line would create a subpackage named foonly-doc:
%package doc
The following %package line would create a subpackage named doc:
%package -n doc
The %package directive plays another role in subpackage building. That role is to act as a place to collect tags that are specific to a given subpackage. Any tag placed after a %package directive will only apply to that subpackage.
Finally, the name string specified by the %package directive is also used to denote which parts of the spec file are a part of that subpackage. This is done by including the string (along with the -n option, if present on the %package line) on the starting line of the section that is to be subpackage-specific. Here's an example:
... %package -n bar ... %post -n bar ...
In this heavily edited spec file segment, a subpackage called bar has been defined. Later in the file is a post-install script. Because it has subpackage bar's name on the %post line, the post-install script will be part of the bar subpackage only.
For more information on building subpackages, please see chapter [*] on page [*].
While the ``exclude'' and ``exclusive'' tags (excludearch, exclusivearch, excludeos, and exclusiveos) provide some control over whether a package will be built on a given architecture and/or operating system, that control is still rather coarse.
For example, what should be done if a package will build under multiple architectures, but requires slightly different %build scripts? Or what if a package requires a certain set of files under one operating system, and an entirely different set under another operating system? The architecture and operating system-specific tags we've discussed earlier in the chapter do nothing to help in such situations. What can be done?
One approach would be to simply create different spec files for each architecture or operating system. While it would certainly work, this approach has some problems:
The other approach is to somehow permit the conditional inclusion of architecture- or operating system-specific sections of the spec file. Fortunately, the RPM designers chose this approach, and it makes multi-platform package building easier and less prone to mistakes.
We discuss multi-platform package building in depth in chapter [*]. For now, let's take a quick look at RPM's conditionals.
The %ifarch conditional is used to begin a section of the spec file that is architecture-specific. It is followed by one or more architecture specifiers, each separated by commas or whitespace. Here is an example:
%ifarch i386 sparc
The contents of the spec file following this line would be processed only by Intel x86 or Sun SPARC-based systems. However, if only this line were placed in a spec file, this is what would happen if a build was attempted:
# rpm -ba cdplayer-1.0.spec
Unclosed %if
Build failed.
#
The problem that surfaced here is that any conditional must be ``closed'' by using either %else or %endif. We'll be covering them a bit later in the chapter.
The %ifnarch conditional is used in a similar fashion to %ifarch, except that the logic is reversed. If a spec file contains a conditional block starting with %ifarch alpha, that block would be processed only if the build was being done on a Digital Alpha/AXP-based system. However, if the conditional block started with %ifnarch alpha, then that block would be processed only if the build were not being done on an Alpha.
Like %ifarch, %ifnarch can be followed by one or more architectures and must be closed by a %else or %endif.
The %ifos conditional is used to control RPM's spec file processing based on the build system's operating system. It is followed by one or more operating system names. A conditional block started with %ifos must be closed by a %else or %endif. Here's an example:
%ifos linux
The contents of the spec file following this line would be processed only if the build was done on a linux system.
The %ifnos conditional is the logical complement to %ifos: that is, if a conditional starting with the line %ifnos irix is present in a spec file, then the file contents after the %ifnos will not be processed if the build system is running Irix. As always, a conditional block starting with %ifnos must be closed by a %else or %endif.
The %else conditional is placed between a %if conditional of some persuasion, and a %endif. It is used to create two blocks of spec file statements, only one of which will be used in any given case. Here's an example:
%ifarch alpha make RPM_OPT_FLAGS="$RPM_OPT_FLAGS -I ." %else make RPM_OPT_FLAGS="$RPM_OPT_FLAGS" %endif
When a build is performed on a Digital Alpha/AXP, some additional flags are added to the make command. On all other systems, these flags are not added.
A %endif is used to end a conditional block of spec file statements. It can follow one of the %if conditionals, or the %else. The %endif is always needed after a conditional, otherwise the build will fail. Here's short conditional block, ending with a %endif:
%ifarch i386 make INTELFLAG=-DINTEL %endif
In this example, we see the conditional block started with a %ifarch and ended with a %endif.
Now that we have some more in-depth knowledge of the spec file, let's take a look at some of RPM's additional features. In the next chapter, we'll explore how to add dependency information to a package.