rpm-guide rpm-guide-advanced-packaging-en.xml,NONE,1.1
Stuart Ellis (elliss)
fedora-docs-commits at redhat.com
Tue Oct 4 01:44:22 UTC 2005
Author: elliss
Update of /cvs/docs/rpm-guide
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv341
Added Files:
rpm-guide-advanced-packaging-en.xml
Log Message:
--- NEW FILE rpm-guide-advanced-packaging-en.xml ---
<!-- $Id: -->
<chapter id="ch-advanced-packaging">
<title>Advanced RPM Packaging</title>
<para>
Copyright (c) 2005 by Eric Foster-Johnson. This material may be
distributed only subject to the terms and conditions set forth in
the Open Publication License, v1.0 or later (the latest version is
presently available at http://www.opencontent.org/openpub/).
</para>
<para/>
<para>
In This Chapter
</para>
<para>
*Defining package dependency information
</para>
<para>
*Setting triggers
</para>
<para>
*Writing verification scripts
</para>
<para>
*Creating subpackages
</para>
<para>
*Creating relocatable packages
</para>
<para>
*Defining conditional builds
</para>
<para>
The previous chapter introduced the RPM spec file, which controls
how RPM packages are built and installed. This chapter delves into
advanced spec file topics such as using conditional commands and
making relocatable packages, starting with how to specify package
dependencies.
</para>
<sect1>
<title>Defining Package Dependencies</title>
<para>
Dependencies are one of the most important parts of the RPM
system. The RPM database tracks dependencies between packages to
better allow you to manage your system. A dependency occurs when
one package depends on another. The RPM system ensures that
dependencies are honored when upgrading, installing, or removing
packages. From that simple concept, RPM supports four types of
dependencies:
</para>
<para>
*Requirements, where one package requires a capability provided by
another
</para>
<para>
*Provides, a listing of the capabilities your package provides
</para>
<para>
*Conflicts, where one package conflicts with a capability provided
by another
</para>
<para>
*Obsoletes, where one package obsoletes capabilities provided by
another, usually used when a package changes name and the new
package obsoletes the old name
</para>
<para>
Cross Reference
</para>
<para>
Chapter 6 covers more on dependencies. The Obsoletes dependencies
are usually only used when a package is renamed, such as the
apache package becoming the httpd package, starting in Red Hat
Linux 8.0. The httpd package obsoletes the apache package.
</para>
<para>
You can list all of these dependencies in your spec file. The most
commonly used dependency information, though, is what a package
requires.
</para>
<sect2>
<title>Naming dependencies</title>
<para>
In your spec files, you can name the dependencies for your
package. The basic syntax is:
</para>
<para>
Requires: capability
</para>
<para>
In most cases, the capability should be the name of another
package. This example sets up a requires dependency. This means
that the package requires the given capability. Use a similar
syntax for the other kinds of dependencies:
</para>
<para>
Provides: capability
</para>
<para>
Obsoletes: capability
</para>
<para>
Conflicts: capability
</para>
<para>
You can put more than one capability on the dependency line. For
example:
</para>
<para>
Requires: bash perl
</para>
<para>
You can use spaces or commas to separate the capabilities. For
example:
</para>
<para>
Requires: bash, perl
</para>
<sect3>
<title>Specifying the Version of the Dependencies</title>
<para>
You can also add version information, for example:
</para>
<para>
Requires: bash >= 2.0
</para>
<para>
This states that the package requires the capability bash (a
package) at version 2.0 or higher. The same logic applies to
the other types of dependencies. For example:
</para>
<para>
Conflicts: bash >= 2.0
</para>
<para>
This example states that the package conflicts with all
versions of bash 2.0 or higher.
</para>
<para>
Table 11-1 lists the version comparisons you can use.
</para>
<para>
Table 11-1 Dependency version comparisons
</para>
<informaltable frame="all">
<tgroup cols="2">
<tbody>
<row>
<entry>
<para>
Comparison
</para>
</entry>
<entry>
<para>
Meaning
</para>
</entry>
</row>
<row>
<entry>
<para>
package < version
</para>
</entry>
<entry>
<para>
A package with a version number less than version
</para>
</entry>
</row>
<row>
<entry>
<para>
package > version
</para>
</entry>
<entry>
<para>
A package with a version number greater than version
</para>
</entry>
</row>
<row>
<entry>
<para>
package >= version
</para>
</entry>
<entry>
<para>
A package with a version number greater than or
equal to version
</para>
</entry>
</row>
<row>
<entry>
<para>
package <= version
</para>
<para/>
</entry>
<entry>
<para>
A package with a version number less than or equal
to version
</para>
</entry>
</row>
<row>
<entry>
<para>
package = version
</para>
</entry>
<entry>
<para>
A package with a version number equal to version
</para>
</entry>
</row>
<row>
<entry>
<para>
package
</para>
</entry>
<entry>
<para>
A package at any version number
</para>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>
RPM supports an extended version number syntax for
comparisons. The full format follows:
</para>
<para>
Epoch:Version-Release
</para>
<para>
For example:
</para>
<para>
1:5.6.0-17
</para>
<para>
In this case, the epoch is 1, the version 5.6.0, and the
release is 17. In most cases, you will need just the version
number. The epoch allows for handling hard-to-compare version
numbers. The release number is almost never used. This makes
sense, in that it ties a dependency to a particular build of
the RPM package, rather than a version of the software itself.
This type of dependency would only be useful if you
drastically changed the way you build a package.
</para>
</sect3>
<sect3>
<title>Creating Virtual CAPABILITIES</title>
<para>
Dependencies are based on capabilities, most of which are
packages. You can create virtual capabilities, which are just
names you define. For example, the sendmail package provides a
virtual capability named smtpdaemon. For example:
</para>
<para>
Provides: smtpdaemon
</para>
<para>
This capability refers to the general SMTP Internet service
for sending e-mail messages. There is no file of this name.
Instead, it is just a capability, arbitrary text. Other
packages require this capability, such as the fetchmail
mail-retrieval and forwarding application, and mutt, an e-mail
client program.
</para>
<para>
By using a virtual capability, other packages can provide the
capability, and most importantly, client applications can
require the capability without worrying which package provides
the ability to send e-mail messages. For example, the exim and
postfix packages, mail transport agents like sendmail, can
provide the same capability.
</para>
<para>
Note
</para>
<para>
Of course, you want to ensure that these packages specify that
they conflict with each other.
</para>
</sect3>
<sect3>
<title>Naming Dependencies on Script Engines and Modules</title>
<para>
Scripting languages such as Perl and Tcl allow for add-on
modules. Your package may require some of these add-on
modules. RPM uses a special syntax with parenthesis to
indicate script module dependencies. For example:
</para>
<para>
Requires: perl(Carp) >= 3.2
</para>
<para>
This indicates a requirement for the Carp add-on module for
Perl, greater than or equal to version 3.2.
</para>
</sect3>
</sect2>
<sect2>
<title>Setting prerequisites</title>
<para>
A prerequisite is similar to a require dependency, except that a
prerequisite must be installed prior to a given package. Specify
a prerequisite as follows:
</para>
<para>
PreReq: capability
</para>
<para>
You can include version-number dependencies, such as:
</para>
<para>
PreReq: capability >= version
</para>
<para>
In most usage, a PreReq: acts just like Requires:, in fact, the
PreReq: directive exists just to allow for a manual order to
dependencies. RPM guarantees that the PreReq: package will be
installed prior to the package that names the PreReq:
dependency.
</para>
<para>
Cross Reference
</para>
<para>
Chapter 14 covers a common problem of handling circular
dependencies using prerequisites.
</para>
</sect2>
<sect2>
<title>Naming build dependencies</title>
<para>
Your package, once built, has a set of dependencies. These
dependencies are important for anyone installing the package.
But there are also dependency issues when trying to build
packages. Build dependencies allow you to specify what is
necessary to build the package. While you may think this would
be the same as what is needed to install a package, this is
normally not true. Linux distributions tend to divide up
software into runtime and development packages. For example, the
python package contains the runtime for executing scripts
written in Python. The python-devel package provides the ability
to write extensions to the Python language.
</para>
<para>
RPM allows you to define build-time dependencies in your spec
files using the following directives:
</para>
<para>
BuildRequires:
</para>
<para>
BuildConflicts:
</para>
<para>
BuildPreReq:
</para>
<para>
These directives act like Requires:, Conflicts:, and PreReq:,
respectively, except that the dependencies are needed to build
the package, not install it. For example, your package may
require a C compiler to build, or may need a special build tool
or developer library.
</para>
</sect2>
<sect2>
<title>Generating dependencies automatically</title>
<para>
Because so many dependencies are related to shared libraries,
the RPM system will automatically generate provide dependencies
for any file in your packages that is a shared object, or .so,
file. RPM will also automatically generate require dependencies
for all files in the %files list that require shared libraries.
To do this, RPM uses the ldd command, which determines the
shared libraries used by an application.
</para>
<para>
In addition, the find-requires and find-provides scripts in
/usr/lib/rpm can determine Perl, Python and Tcl script
dependencies and other dependencies, such as Java package
dependencies, automatically. The find-requires script determines
requires dependencies automatically, and the find-provides
script determines provides dependencies.
</para>
<para>
Cross Reference
</para>
<para>
Chapter 14 covers how to turn off the automatic generation of
dependencies.
</para>
</sect2>
</sect1>
<sect1>
<title>Setting Triggers</title>
<para>
Triggers provide a way for one package to take action when the
installed status of another package changes. A trigger is a script
you define in your packageâs spec file that gets run by the RPM
system when the status of another named package changes. If your
package depends in some way on another package, a trigger can
allow your package to deal with changes to the other package.
</para>
<para>
Triggers are not a replacement for package dependencies. Instead,
triggers are useful when you need to change a packageâs
installation based on other packages installed on the system. For
example, if your package is a mail client program, your package
will need to have a mail transfer agent, or MTA. Linux supports a
number of different mail transfer agents, such as sendmail, vmail,
exim, qmail, and postfix.
</para>
<para>
Typically a system will have one mail transfer agent installed. In
most cases, a mail client wonât care which MTA is installed, as
long as one is installed. (In fact, most of these packages should
be marked that they conflict with one another, ensuring that a
given system can only have one.)
</para>
<para>
The %triggerin script is run when a given target package is
installed or upgraded. The %triggerin script is also run when your
package is installed or upgraded, should the target package be
already installed. Similarly, the %triggerun script is run if the
target package is removed. It is also run if your package is
removed and the target package is installed. The %triggerpostun
script is run after the target package has been removed. It is not
run if your package is removed.
</para>
<para>
To define one of these scripts, you need to list the name of the
target package; for example:
</para>
<para>
%triggerin -- tcsh
</para>
<para>
script commands...
</para>
<para>
This example sets up a trigger for the tcsh package. If the tcsh
package is installed or upgraded, RPM will run the script. If your
package is installed or upgraded and the tcsh package is presently
installed, RPM will also run the script.
</para>
<para>
Define the %triggerun script similarly:
</para>
<para>
%triggerun -- tcsh
</para>
<para>
script commands...
</para>
<para>
You can also use version numbers in the trigger script definition
to only run the script in the case of a particular version. For
example:
</para>
<para>
%triggerpostun -- vixie-cron < 3.0.1-56
</para>
<para>
/sbin/chkconfig --del crond
</para>
<para>
/sbin/chkconfig --add crond
</para>
<para>
This example, from the vixie-cron scheduling package, runs a
post-uninstall trigger for the same package, but for older
versions. To define trigger scripts for particular versions, use
the same syntax as for requires dependencies for naming the
version number and comparisons.
</para>
<para>
Triggers are run through /bin/sh, the most commonly used shell
script engine. With the -p option, though, you can specify a
different script interpreter. For example, to write a Perl script,
define your trigger like the following:
</para>
<para>
%triggerpostun -p /usr/bin/perl -- vixie-cron < 3.0.1-56
</para>
<para>
system("/sbin/chkconfig --del crond");
</para>
<para>
system("/sbin/chkconfig --add crond");
</para>
<para>
With subpackages, defined following, you can use a -n option to
tie the trigger script to a subpackage. For example:
</para>
<para>
%triggerpostun -n subpackage_name -- vixie-cron < 3.0.1-56
</para>
<para>
/sbin/chkconfig --del crond
</para>
<para>
/sbin/chkconfig --add crond
</para>
<para>
Inside your trigger scripts, $1, the first command-line argument,
holds the number of instances of your package that will remain
after the operation has completed. The second argument, $2, holds
the number of instances of the target package that will remain
after the operation. Thus, if $2 is 0, the target package will be
removed.
</para>
<para>
The anonftp package, mentioned in Chapter 6, has a lot of
triggers. Many of these set up a number of commands to be locally
available to the anonftp package. This networking package is also
closely tied to the version of the C library, glibc, as shown in
Listing 11-1
</para>
<para>
Listing 11-1: Anonftp package trigger scripts.
</para>
<para>
%triggerin -- glibc
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
# Kill off old versions
</para>
<para>
rm -f /var/ftp/lib/ld-* /var/ftp/lib/libc* /var/ftp/lib/libnsl*
/var/ftp/lib/lib
</para>
<para>
nss_files* &>/dev/null || :
</para>
<para>
# Copy parts of glibc, needed by various programs in bin.
</para>
<para>
LIBCVER=`basename $(ls --sort=time /lib/libc-*.so |head -n 1) .so
|cut -f2- -d-`
</para>
<para>
copy /lib/ld-${LIBCVER}.so /var/ftp/lib
</para>
<para>
copy /lib/libc-${LIBCVER}.so /var/ftp/lib
</para>
<para>
copy /lib/libnsl-${LIBCVER}.so /var/ftp/lib
</para>
<para>
copy /lib/libnss_files-${LIBCVER}.so /var/ftp/lib
</para>
<para>
md5sum /var/ftp/lib/lib*-*.so /var/ftp/lib/libtermcap.so.*.*.*
2>/dev/null >/var
</para>
<para>
/ftp/lib/libs.md5
</para>
<para>
chmod 0400 /var/ftp/lib/libs.md5
</para>
<para>
# Use ldconfig to build symlinks and whatnot.
</para>
<para>
[ ! -e /var/ftp/etc/ld.so.conf ] && touch
/var/ftp/etc/ld.so.conf
</para>
<para>
/sbin/ldconfig -r /var/ftp
</para>
<para/>
<para>
%triggerin -- fileutils
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
copy /bin/ls /var/ftp/bin
</para>
<para>
md5sum `ls /var/ftp/bin/* |grep -v bin.md5`
>/var/ftp/bin/bin.md5
</para>
<para>
chmod 0400 /var/ftp/bin/bin.md5
</para>
<para/>
<para>
%triggerin -- cpio
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
copy /bin/cpio /var/ftp/bin
</para>
<para>
md5sum `ls /var/ftp/bin/* |grep -v bin.md5`
>/var/ftp/bin/bin.md5
</para>
<para>
chmod 0400 /var/ftp/bin/bin.md5
</para>
<para/>
<para>
%triggerin -- tar
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
copy /bin/tar /var/ftp/bin
</para>
<para>
md5sum `ls /var/ftp/bin/* |grep -v bin.md5`
>/var/ftp/bin/bin.md5
</para>
<para>
chmod 0400 /var/ftp/bin/bin.md5
</para>
<para/>
<para>
%triggerin -- gzip
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
copy /bin/gzip /var/ftp/bin
</para>
<para>
ln -sf gzip /var/ftp/bin/zcat
</para>
<para>
md5sum `ls /var/ftp/bin/* |grep -v bin.md5`
>/var/ftp/bin/bin.md5
</para>
<para>
chmod 0400 /var/ftp/bin/bin.md5
</para>
<para/>
<para>
%triggerin -- libtermcap
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
rm -f /var/ftp/lib/libtermcap.so.*.*.* &>/dev/null || :
</para>
<para>
copy '/lib/libtermcap.so.*.*.*' /var/ftp/lib
</para>
<para>
md5sum /var/ftp/lib/lib*-*.so /var/ftp/lib/libtermcap.so.*.*.*
2>/dev/null >/var
</para>
<para>
/ftp/lib/libs.md5
</para>
<para>
chmod 0400 /var/ftp/lib/libs.md5
</para>
<para>
# Use ldconfig to build symlinks and whatnot.
</para>
<para>
[ ! -e /var/ftp/etc/ld.so.conf ] && touch
/var/ftp/etc/ld.so.conf
</para>
<para>
/sbin/ldconfig -r /var/ftp
</para>
<para/>
<para>
%triggerin -- ncompress
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
copy /usr/bin/compress /var/ftp/bin
</para>
<para>
md5sum `ls /var/ftp/bin/* |grep -v bin.md5`
>/var/ftp/bin/bin.md5
</para>
<para>
chmod 0400 /var/ftp/bin/bin.md5
</para>
<para/>
<para>
%triggerpostun -- anonftp 4.0
</para>
<para>
if [ "$2" != 1 ] ; then
</para>
<para>
# The user has multiple glibc packages installed. We can't read
the
</para>
<para>
# user's mind, so don't do anything.
</para>
<para>
exit 0
</para>
<para>
fi
</para>
<para>
copy() { file="`ls --sort=time $1 |head -n 1`"; ln -f "$file" "$2"
2>/dev/null |
</para>
<para>
| cp -df "$file" "$2"; }
</para>
<para>
# Kill off old versions
</para>
<para>
rm -f /var/ftp/lib/ld-* /var/ftp/lib/libc* /var/ftp/lib/libnsl*
/var/ftp/lib/lib
</para>
<para>
nss_files* &>/dev/null || :
</para>
<para>
# Copy parts of glibc, needed by various programs in bin.
</para>
<para>
LIBCVER=`basename /lib/libc-*.so .so | cut -f2- -d-`
</para>
<para>
copy /lib/ld-${LIBCVER}.so /var/ftp/lib
</para>
<para>
copy /lib/libc-${LIBCVER}.so /var/ftp/lib
</para>
<para>
copy /lib/libnsl-${LIBCVER}.so /var/ftp/lib
</para>
<para>
copy /lib/libnss_files-${LIBCVER}.so /var/ftp/lib
</para>
<para>
copy /bin/ls /var/ftp/bin
</para>
<para>
copy /bin/cpio /var/ftp/bin
</para>
<para>
copy /bin/tar /var/ftp/bin
</para>
<para>
copy /bin/gzip /var/ftp/bin
</para>
<para>
ln -sf gzip /var/ftp/bin/zcat
</para>
<para>
copy /usr/bin/compress /var/ftp/bin
</para>
<para>
rm -f /var/ftp/lib/libtermcap.so.*.*.* &>/dev/null || :
</para>
<para>
copy '/lib/libtermcap.so.*.*.*' /var/ftp/lib
</para>
<para>
# Use ldconfig to build symlinks and whatnot.
</para>
<para>
[ ! -e /var/ftp/etc/ld.so.conf ] && touch
/var/ftp/etc/ld.so.conf
</para>
<para>
/sbin/ldconfig -r /var/ftp
</para>
<para>
# Generate md5sums for verifyscript
</para>
<para>
md5sum /var/ftp/lib/lib*-*.so /var/ftp/lib/libtermcap.so.*.*.*
2>/dev/null >/var
</para>
<para>
/ftp/lib/libs.md5
</para>
<para>
chmod 0400 /var/ftp/lib/libs.md5
</para>
<para>
md5sum `ls /var/ftp/bin/* |grep -v bin.md5`
>/var/ftp/bin/bin.md5
</para>
<para>
chmod 0400 /var/ftp/bin/bin.md5
</para>
</sect1>
<sect1>
<title>Writing Verification Scripts</title>
<para>
RPM automatically handles package verification, checking to see
that the proper files are installed, and testing the files
themselves for the proper size and other attributes. You may need
to do more in your package, though, to ensure everything is
properly set up. With RPM, you can:
</para>
<para>
*Control the tests used to verify each file, as described in
Chapter 10
</para>
<para>
*Create a verify script that performs other tests
</para>
<para>
If you need to perform some other test to verify your package,
such as check that a configuration file has a particular setting
(and that the setting is valid), you can fill in the %verifyscript
in the spec file. The %verifyscript acts much like the %pre or
%post scripts, except that the %verifyscript gets executed during
package verification. Fill in a %verifyscript as follows:
</para>
<para>
%verifyscript
</para>
<para>
your script commands ....
</para>
<para>
Common %verifyscript actions are to check for an entry in a system
configuration file, such as an init-time startup script or
/etc/shells (which lists the available shells). These are files
owned by other packages that may need to be properly modified for
a package to be properly installed. If your package has a similar
circumstance, write a %verifyscript. In your script, send all
error output to stderr.
</para>
<para>
Cross Reference
</para>
<para>
See Chapter 5 for more on package verification.
</para>
</sect1>
<sect1>
<title>Creating Subpackages</title>
<para>
A spec file may define more than one package. This type of
additional package is called a subpackage. Subpackages exist to
handle cases where you donât want to associate one spec file
with one package. Instead, you can define multiple packages within
the spec file, as needed. For example, you may want to build the
runtime and developer packages together, or the client and server
portions of an application using subpackages. Splitting large
documentation sets into separate subpackages is also common.
</para>
<para>
With subpackages, you get:
</para>
<para>
*One spec file
</para>
<para>
*One source RPM
</para>
<para>
*One set of build commands
</para>
<para>
*Multiple binary RPMs, one per package or subpackage
</para>
<para>
In most cases, subpackages are created just as a means to
partition the files produced by a package into separate packages.
For example, you will often see development libraries and header
files split into a separate package from the main application
package. Sometimes documentation is split out into a separate
package, or client and server applications are divided into
separate packages. In the end, though, this usually results in
shifting files into subpackages and nothing more.
</para>
<para>
To define a subpackage within a spec file, you start with the
%package directive. For example:
</para>
<para>
%package sub_package_name
</para>
<para>
By default, the name of the subpackage will be the name of the
package, a dash, and the subpackage name provided with the
%package directive. For example:
</para>
<para>
%package server
</para>
<para>
This example names a subpackage server, which is a real subpackage
inside the telnet package. In this case, the name for the server
subpackage will be telnet-server, that is, the naming format is
package-subpackage.
</para>
<para>
If you donât want this naming format, you can use the ân
option to the %package directive to define an entirely new name,
using the following syntax:
</para>
<para>
%package -n new_sub_package_name
</para>
<para>
For example:
</para>
<para>
%package ân my-telnet-server
</para>
<para>
With the ân option, you specify the full name for the
subpackage. The RPM system will not prefix the name with the
enclosing package name.
</para>
<sect2>
<title>Providing information for subpackages</title>
<para>
When you define a subpackage, you need to provide as many of the
package information directives as you need, including at the
least Summary:, Group:, and %description directives. Anything
not specified will use the parent packageâs value, such as the
version. Place these directives after the %package directive.
For example:
</para>
<para>
%package server
</para>
<para>
Requires: xinetd
</para>
<para>
Group: System Environment/Daemons
</para>
<para>
Summary: The server program for the telnet remote login
protocol.
</para>
<para>
The %description directive for subpackages requires the name of
the subpackage using the following syntax:
</para>
<para>
%description subpackage
</para>
<para>
For example:
</para>
<para>
%description server
</para>
<para>
Telnet is a popular protocol for logging into remote systems
</para>
<para>
over the Internet. The telnet-server package includes a telnet
daemon that supports remote logins into the host machine. The
</para>
<para>
telnet daemon is enabled by default. You may disable the telnet
</para>
<para>
daemon by editing /etc/xinetd.d/telnet.
</para>
<para>
If you used the ân option with the %package directive, you
need to repeat the ân option with the %description directive.
For example:
</para>
<para>
%description ân my-telnet-server
</para>
<para>
Telnet is a popular protocol for logging into remote systems
</para>
<para>
over the Internet. The telnet-server package includes a telnet
daemon that supports remote logins into the host machine. The
</para>
<para>
telnet daemon is enabled by default. You may disable the telnet
</para>
<para>
daemon by editing /etc/xinetd.d/telnet.
</para>
<para>
The same concept works for the %files section. You need a
separate %files section for each subpackage. For example:
</para>
<para>
%files server
</para>
<para>
%defattr(-,root,root)
</para>
<para>
%{_sbindir}/in.telnetd
</para>
<para>
%{_mandir}/man5/issue.net.5*
</para>
<para>
%{_mandir}/man8/in.telnetd.8*
</para>
<para>
%{_mandir}/man8/telnetd.8*
</para>
<para>
Again, if you used the ân option with the %package directive,
you need to repeat the ân option with the %files section. For
example:
</para>
<para>
%files ân my-telnet-server
</para>
<para>
%defattr(-,root,root)
</para>
<para>
%{_sbindir}/in.telnetd
</para>
<para>
%{_mandir}/man5/issue.net.5*
</para>
<para>
%{_mandir}/man8/in.telnetd.8*
</para>
<para>
%{_mandir}/man8/telnetd.8*
</para>
</sect2>
<sect2>
<title>Defining scripts for subpackages</title>
<para>
Much as you define separate %files and %description sections for
subpackages, you can also define install and uninstall scripts
for subpackages. The syntax is similar to that for the %files
and %description sections:
</para>
<para>
%pre subpackage
</para>
<para>
For example, Listing 11-2 shows the scripts from the VNC
package.
</para>
<para>
Listing 11-2: VNC package install and uninstall scripts.
</para>
<para>
%post server
</para>
<para>
if [ "$1" = 1 ]; then
</para>
<para>
/sbin/chkconfig --add vncserver
</para>
<para>
fi
</para>
<para/>
<para>
%preun server
</para>
<para>
if [ "$1" = 0 ]; then
</para>
<para>
/sbin/service vncserver stop >/dev/null 2>&1
</para>
<para>
/sbin/chkconfig --del vncserver
</para>
<para>
fi
</para>
<para/>
<para>
%postun server
</para>
<para>
if [ "$1" -ge "1" ]; then
</para>
<para>
/sbin/service vncserver condrestart >/dev/null 2>&1
</para>
<para>
fi
</para>
</sect2>
<sect2>
<title>Building subpackages</title>
<para>
The build sections in the spec file serve double duty. These
sections are used for building the main package as well as
subpackages. This is one reason why there are so many options on
the %setup macro.
</para>
<para>
The %setup macro allows for selectively unpacking the sources,
rather than the default option of unpacking all the sources. For
example, the following %setup macro definition gives rpmbuild
specific instructions for unpacking one source file:
</para>
<para>
%setup âD- T âa 1
</para>
<para>
In this example, the âD option disables the automatic deletion
of the directory where the sources will be unpacked. This means
any previous contents of this directory, perhaps for other
subpackages, will be left alone. The âT option disables the
automatic unpacking of the source files, and the âa 1 option
specifies to only unpack the first source file. You may need to
use options like these when working with subpackages. Though, in
most cases, subpackages are just means to partition the package
files into separate packages. In cases like this, you will
likely not need any of these special %setup options.
</para>
<para>
Cross Reference
</para>
<para>
Chapter 10 covers the %setup macro and lists the available
options.
</para>
</sect2>
</sect1>
<sect1>
<title>Creating Relocatable Packages</title>
<para>
A relocatable package allows a user to specify where to install
the package. For example, if you build a package for Red Hat
Linux, the normal directory for binary executable programs is
/usr/bin. Other versions of Linux, though, may place executable
programs into /opt/bin, for example. If your package forces the
use of /usr/bin, then your package wonât work on these other
systems.
</para>
<para>
Cross Reference
</para>
<para>
Chapter 19 covers using RPM on other versions of Linux.
</para>
<para>
With a relocatable package, though, you allow the user to redefine
the top-level directories for your package, such as changing from
/usr/bin to /opt/bin in the previous example. Making relocatable
packages is generally considered a good thing, as you make the
userâs life easier.
</para>
<para>
To set up a relocatable package, you need to:
</para>
<para>
*Set up the prefix directives for the top-level directories
</para>
<para>
*Define the files under the prefix directories
</para>
<sect2>
<title>Setting up the prefixes</title>
<para>
The Prefix: directive names a top-level directory as a prefix
you can relocate to another directory. For example:
</para>
<para>
Prefix: /usr
</para>
<para>
This states that all files under /usr can be relocated to other
directories by simply mapping /usr to some other directory, such
as /opt, on the rpm command line when installing or upgrading
the package.
</para>
<para>
Note
</para>
<para>
You can define more than one Prefix: directive to list more than
one top-level directory.
</para>
</sect2>
<sect2>
<title>Define the files section</title>
<para>
When you use a Prefix: directive in your spec file, all files in
the %files section must be under the directory named with the
Prefix: directive. For example, from the jikes compiler package:
</para>
<para>
Prefix: /usr
</para>
<para/>
<para>
...
</para>
<para/>
<para>
%files
</para>
<para>
%defattr(-,root,root)
</para>
<para>
/usr/bin/jikes
</para>
<para>
%doc /usr/doc/jikes-%{version}/license.htm
</para>
<para>
%doc /usr/man/man1/jikes.1*
</para>
<para>
In this example, all the files are under the /usr directory. All
files in the %files section must be located under one of the
Prefix: directories. If you have more than one top-level
directory, such as /usr and /etc, define more than one Prefix:
directive. For example:
</para>
<para>
Prefix: /usr
</para>
<para>
Prefix: /etc
</para>
<para>
Cross Reference
</para>
<para>
Chapter 4 covers how to install or upgrade packages into
different directories using the --relocate and --prefix options.
</para>
</sect2>
<sect2>
<title>Problems creating relocatable packages</title>
<para>
Not all packages work well as relocatable packages. Some
packages have files that simply must go into a certain location
and are therefore not relocatable. Some packages have programs
that are hard-coded to look for files in a particular location
and therefore cannot be relocated elsewhere. Other packages have
symbolic links that also may not be relocatable. Furthermore,
your package may provide software that is referenced by other
packages, in the known directories. Relocating such a package
will disable other software packages, packages you may not even
know about.
</para>
<para>
If your packages face any of these problems, chances are that
making the package relocatable is not a good idea.
</para>
<para>
In addition, if you use the %doc directive with local file
names, remember that RPM will make a package-specific
documentation directory, normally under /usr/doc. For example:
</para>
<para>
%doc README NEWS
</para>
<para>
This may defeat your attempts to create a relocatable package,
unless you have a Prefix: directive with /usr, because the
normal location is under /usr/doc, and all files in the %files
section must start with one of the directories named with
Prefix: directives.
</para>
</sect2>
</sect1>
<sect1>
<title>Defining Conditional Builds</title>
<para>
With the ability to define macros inside spec files, and also to
use macros defined elsewhere, you gain a lot of control over how
your package gets built. You can go further, though, and use
special directives to perform only certain commands based on
certain conditions. This adds a powerful capability to your spec
files, and also makes it much easier to do things like build for
multiple versions of Linux or other operating systems, as well as
handle various backwards-compatibility issues.
</para>
<para>
To define conditional build commands, you need to create
conditional constructs in your packageâs spec file. In addition,
you need to define macros that the conditional constructs use to
determine whether or not to execute a set of spec file directives.
</para>
<para>
Cross Reference
</para>
<para>
See Chapter 21 for more on macro file locations, and Chapters 19
and 20 for more on using RPM on other versions of Linux and other
operating systems, respectively.
</para>
<para>
RPM supports a number of ways to make parts of your spec file
enabled or disabled based on certain conditions. These include
conditional macros, conditional blocks, and special directives
based on the system architecture.
</para>
<sect2>
<title>Defining conditional macros</title>
<para>
You can use a special syntax to test for the existence of
macros. For example:
</para>
<para>
%{?macro_to_test: expression}
</para>
<para>
This syntax tells RPM to expand the expression if the macro
macro_to_test exists. If the macro macro_to_test does not exist,
nothing will be output. You can also reverse this test. A
leading exclamation point, !, tests for the non-existence of a
macro:
</para>
<para>
%{!?macro_to_test: expression}
</para>
<para>
In this example, if the macro_to_test macro does not exist, RPM
will expand the expression.
</para>
<para>
If you want, you can omit the expression and just test for the
existence of the macro. If it exists, RPM will use the value of
the macro. If the macro does not exist, RPM will use nothing.
For example:
</para>
<para>
%build
</para>
<para>
./configure %{?_with_ldap}
</para>
<para>
make
</para>
<para>
In this case, if the _with_ldap macro exists, the value of that
macro will get passed on the command line to the configure
script. If the _with_ldap macro does not exist, nothing extra
will be passed on the command line to the configure script. This
is very important when creating commands to build or install
packages.
</para>
<para>
Cross Reference
</para>
<para>
Many of the macros you will test this way are set up with the
--with command-line parameter. See Chapter 19 for details.
</para>
</sect2>
<sect2>
<title>Using conditional blocks</title>
<para>
The %if macro enables all the directives up to the %endif
directive, if the condition is true. This is much like scripting
languages. For example:
</para>
<para>
%if %{old_5x}
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%endif
</para>
<para>
In this case, if the %old_5x macro has a value, the test will be
true and all the directives inside the block will get executed.
</para>
<para>
A %else allows you to specify what to do if the test is not
successful. For example:
</para>
<para>
%if %{old_5x}
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%else
</para>
<para>
%define b6x 1
</para>
<para>
%undefine b5x
</para>
<para>
%endif
</para>
<para>
In this case, if the %old_5x macro has a value, then all the
directives up to the %else will get executed. Otherwise, if the
%old_5x macro has no value, the directives from the %else to the
%endif will get executed.
</para>
<para>
Again, use an exclamation point to negate the test. For example:
</para>
<para>
%if ! %{old_5x}
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%endif
</para>
<para>
You can use a && for an AND test. For example:
</para>
<para>
%if %{old_5x} && %{old_6x}
</para>
<para>
%{error: You cannot build for .5x and .6x at the same time}
</para>
<para>
%quit
</para>
<para>
%endif
</para>
</sect2>
<sect2>
<title>Using architecture-based conditionals</title>
<para>
In addition to the general-purpose %if conditional directive,
you can use special directives that test for processor
architecture and operating system.
</para>
<para>
The %ifarch directive enables all the directives up to the
%endif directive, if the processor architecture matches the
values you pass to the %ifarch directive. For example:
</para>
<para>
%ifarch sparc
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%endif
</para>
<para>
This block will only get executed if the processor architecture
is SPARC.
</para>
<para>
Cross Reference
</para>
<para>
Chapter 21 covers RPM architecture and operating system names.
</para>
<para>
You can pass more than one architecture name, separated by
commas or spaces. For example:
</para>
<para>
%ifarch sparc alpha
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%endif
</para>
<para>
This example tests if the processor architecture is SPARC or
Alpha.
</para>
<para>
As with the %if directive, you can also use an %else, to cover
all cases where the test is not true. For example:
</para>
<para>
%ifarch sparc alpha
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%else
</para>
<para>
%define b6x 1
</para>
<para>
%undefine b5x
</para>
<para>
%endif
</para>
<para>
This example tests if the processor architecture is SPARC or
Alpha. If so, the directives from the %ifarch to the %else are
executed. If not, the directives from the %else to the %endif
are executed.
</para>
<para>
The %ifnarch directive reverses the %ifarch test. That is,
%ifnarch tests if the architecture is not one of the values
listed. The following example tests if the processor
architecture is not an i386 or an Alpha.
</para>
<para>
%ifnarch i386 alpha
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%endif
</para>
<para/>
<para>
The %ifos directive tests for the operating system. For example:
</para>
<para>
%ifos linux
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%endif
</para>
<para>
This example tests if the operating system is Linux. You can
reverse the test with the %ifnos directive. For example:
</para>
<para>
%ifnos irix
</para>
<para>
%define b5x 1
</para>
<para>
%undefine b6x
</para>
<para>
%endif
</para>
<para>
This example tests if the operating system is not Irix.
</para>
</sect2>
</sect1>
<sect1>
<title>Summary</title>
<para>
This chapter covers advanced topics in creating packages.
Dependencies are very important. You need to specify which
packages or capabilities your package requires, so the RPM system
can ensure that all requirements are met before allowing users to
install the package. If you do not specify the dependencies
properly, then you are defeating the integrity of the RPM system.
</para>
<para>
In addition to specifying what your package requires, it is also
important to specify other dependency information. For example, if
your package conflicts with another package, you need to very
clearly state this. E-mail and Web server packages often conflict
with other servers of the same type.
</para>
<para>
You can specify both package dependencies as well as build
dependencies. For example, you may need certain developer
libraries to build your package, but not to install it. These are
build dependencies.
</para>
<para>
To help manage dependencies between packages and system
configuration issues, you can set up trigger scripts. A trigger is
a script in your package that gets executed when another package
is installed or removed. If your package, for example, is an
e-mail client program, it may need to execute a script should the
e-mail server package change. This is a great usage for triggers.
</para>
<para>
If your package has a complicated installation, the normal RPM
verification wonât be sufficient. To help the RPM system ensure
the integrity of all the packages, you can write a verify script
in your spec file to perform any extra commands necessary to
verify your package has been properly installed.
</para>
<para>
Relocatable packages allow users to install your packages in
different locations than originally planned. This is very useful
when working with more than one version of Linux, or with other
operating systems. For example, most Linux commands are stored in
/usr/bin, at least for Red Hat Linux. Other Linux distributions,
or other operating systems may specify that programs added to the
original set should be stored in /opt/bin and not /usr/bin, for
example. Making your package relocatable helps users in these
situations.
</para>
<para>
Conditional directives in your spec file allow you to control the
build on different processor architectures and operating systems.
The %if directive tests if a value is set. If so, then all the
directives up to the %endif directive are executed. If you need to
execute a different set of directives, use %else. In this case, if
the %if test is true, RPM executes the directives up to the %else.
If the test is not true, RPM executes the directives up to the
%endif.
</para>
<para>
Once you have your spec file defined, the next step is to start
building packages. The next chapter covers options for the
rpmbuild command and how you can use rpmbuild to make your
packages.
</para>
</sect1>
</chapter>
<!--
Local variables:
mode: xml
sgml-parent-document:("rpm-guide-en.xml" "book" "chapter")
fill-column: 72
End:
-->
More information about the Fedora-docs-commits
mailing list