Site Navigation:
 
 

18.2. Handling Problems Building RPMs

Given all these differences, how can you create RPMs while avoiding problems? With some work setting things up, you can create an RPM build environment that solves most vendor issues. This depends on taking a methodical approach to building your packages and using techniques to avoid vendor issues wherever possible.

When building RPMs, you will face many of the same problems@@mdand solutions@@mdas when installing RPMs. For example, due to the different ways Linux vendors divide software into packages, your RPMs will likely have issues defining the proper dependencies. There are also a number of issues that apply only when building RPMs.

The following sections cover the main issues when building RPMs.

18.2.1. Writing distribution-specific packages

One of the ways around all the differences between Linux distributions in RPM usage is to define distribution-specific packages. To do this, you create a separate package on each Linux distribution you support.

That’s a lot of work. If possible, fit the differences into macros and use a single spec file to reduce some of this work. This technique works up to a point. Sometimes, your spec file becomes too complicated and you may decide that it is easier to create multiple spec files, one per Linux distribution.

One way to help make vendor-specific packages, or to see which RPM macros are defined on a given Linux distribution, is to look for an RPM that contains the distribution-specific RPM configuration. For example, on Red Hat Linux systems, the Red Hat RPM configuration is defined by the redhat-rpm-config package.

You can list the files in this package to see where Red Hat defines macros specific to their Linux distribution.

$ rpm -ql redhat-rpm-config

/usr/lib/rpm/redhat

/usr/lib/rpm/redhat/brp-compress

/usr/lib/rpm/redhat/brp-redhat

/usr/lib/rpm/redhat/brp-sparc64-linux

/usr/lib/rpm/redhat/brp-strip

/usr/lib/rpm/redhat/brp-strip-comment-note

/usr/lib/rpm/redhat/brp-strip-shared

/usr/lib/rpm/redhat/find-lang.sh

/usr/lib/rpm/redhat/find-provides

/usr/lib/rpm/redhat/find-requires

/usr/lib/rpm/redhat/macros

/usr/lib/rpm/redhat/perl.prov

/usr/lib/rpm/redhat/perl.req

/usr/lib/rpm/redhat/rpmrc

These files, such as /usr/lib/rpm/redhat/macros, show you what is specific to a given Linux distribution. You can then look at the macros defined in these files to identify settings for a particular distribution, in this case, Red Hat. Armed with this knowledge, you can better create portable RPM spec files.

18.2.2. Dealing with automatic dependency generation

One of the features in RPM 4.x is the automatic generation of dependencies. For a variety of reasons including different package layouts, different directory structures, or different versions of RPM, you may need to disable some or all of automatic generation of dependencies.

You can disable the automatic generation of dependencies by placing the following directive in your spec file:

Autoreq: 0

If you do so, you need to use the Requires: tag to manually define all requirements. This is not a good solution to the issue of automatic dependencies however. Most likely, you will need to override the %{__find_requires} and %{__find_provides} macros in order to filter out any unwanted dependencies.

These two macros resolve to shell scripts that perform the automated dependency checks, as you can see with the rpm --eval command:

$ rpm --eval "%__find_provides"

/usr/lib/rpm/find-provides

rpm --eval "%__find_requires"

/usr/lib/rpm/find-requires

You can override these scripts to filter out any dependencies that cause problems for your packages.

18.2.3. Dealing with different macros

Different Linux vendors define different macros in their RPM setup. This may mean not only different values for the macros, but different macro names as well. Because of this, it is best to define your own local set of macros when building RPMs.

As much as possible, depend on your own RPM macros. You can define your macros in terms of vendor-specific macros using conditional statements in your spec files, a topic covered in Chapter 11. You can also read examples in the “Build Environment and Macros” section of this chapter.

This really boils down to creating a disciplined RPM build environment.

18.2.4. Making relocatable packages

You should aim to make your packages relocatable so that users can install your packages into any directory. This makes it easier to deal with the locations chosen by different Linux distributions, such as /usr, /usr/local, or /opt, for installing add-on software.

Cross Reference

Chapter 10 covers the spec file format. Chapter 11 covers making relocatable packages.

You can use the %{_bindir} macro in your spec files, which will help create per-distribution packages using the right settings.

In addition, you can define macros in your spec files that define the location for dependencies. You can then use the --define option to the rpmbuild command to define values for your macros that specify the locations for the dependencies.

Note

This technique of setting up Linux distribution-specific macros can help solve a lot of problems with cross-platform RPMs.

18.2.5. Creating an RPM build environment

If you start with the idea that you want to build RPMs for multiple versions of Linux, you can set up an RPM build environment that cleanly separates most vendor-specific issues.

The key issues with the build environment are:

*Detecting the vendors

*Using macros to define a clean build process

*Handling different dependencies

18.2.5.1. Detecting Vendors

To make a clean build environment, you need to be able to detect the Linux vendor and make build settings based on this vendor. To help with this, many Linux vendors install a special file with the vendor name, or a special package with the vendor name. You can query for either of these.

For files, the convention follows:

/etc/vendor-release

For example:

$ more /etc/redhat-release

Red Hat Linux release 8.0 (Psyche)

For packages, the convention is vendor-release for a package name. For example:

$ rpm -q redhat-release

redhat-release-8.0-8

You can use either approach or simply define a macro for the vendor and use the --define option to set the macro. For example:

# rpmbuild –ba --define 'linuxVendor suse'

With this definition, you can use the macro %linuxVendor inside your spec files. It is generally easier, though, if your scripts can automatically detect the Linux vendor instead of having to define it manually. The manual approach works, though, if it becomes too much effort to detect the vendor automatically.

18.2.5.2. Build environment and macros

Once you can detect the Linux vendor, you can create macros based on the differences between Linux distributions that affect your applications.

Cross Reference

Chapter 21 covers RPM macros.

The macros that specifically help you with platform differences include the %if .. %endif conditional. You can use this in combination with special macros you define. In addition, command-line options such as --with, --without, and --target allow you to control features and the build target within an RPM.

The %if macro allows you to specify a condition within your spec file. For example:

%if %{old_5x} && %{old_6x}

%{error: You cannot build for .5x and .6x at the same time}

%quit

%endif

%if %{old_5x}

%define b5x 1

%undefine b6x

%endif

%if %{old_6x}

%define b6x 1

%undefine b5x

%endif

You can also use %if to control settings such as the Requires:, as shown in the following example:

%if %{build6x}

Requires: util-linux, pam >= 0.66-5

%else

Requires: util-linux, pam >= 0.75-37, /etc/pam.d/system-auth

%endif

The --with command-line option defines a special macro starting with _with_. For example, the following command-line option defines a feature to use:

$ rpmbuild –bc --with ssh filename.spec

This example defines the macro _with_ssh to --with-ssh. This format was specially designed to work with GNU configure. You can use this for conditional builds for platform-dependent issues.

The --without command-line option similarly defines a macro starting with _without_. The convention is that this option defines a feature the code should not use.

You can combine --with and --without to turn on and off features referenced in your spec files. For example:

./configure %{?_with_ssh}

This will pass the following command line if the _with_ssh macro is defined:

./configure --with-ssh

If this option is not defined, the command will be:

./configure

The --target option sets the spec file macros %_target, %_target_arch, and %_target_os . For example:

$ rpmbuild -bc --target ppc-ibm-aix /usr/src/redhat/SPECS/jikes.spec

18.2.5.3. Compatibility and Glue Packages

Not all Linux distributions are the same. Macros alone won’t provide work-arounds for all the differences. You can, though, get a lot of mileage from compatibility and glue packages.

A compatibility package provides a legacy API on newer systems that no longer support the legacy API. By convention, compatibility packages are named with a leading compat- to signify their purpose.

For example:

$ rpm -q --qf "%{description}" compat-libstdc++

The compat-libstdc++ package contains compatibility Standard C++

Using a compatibility package allows you to create programs that use a least-common-denominator approach, programming to the oldest but most common APIs. As some Linux distributions eliminate the old APIs, compatibility packages can provide the missing APIs.

Similarly, a glue package provides a dependency that exists on some Linux distributions but not others. It glues together your package with the Linux distribution that is missing an essential capability.

Note

A key point in both of these approaches is to separate the compatibility and glue packages from your main application packages. The application packages should be as clean of vendor issues as possible. Instruct your users to install the compatibility or glue packages as needed (based on their Linux distribution) along with the main application package or packages.

With all this discussion of RPM and Linux differences, you might think that Linux is one big mess. That’s not true. Linux maintains a high degree of compatibility among Linux distributions as well as among processor architectures. Most programs originally created for Linux on Intel-based architectures compile cleanly on Linux versions running on other processor architectures such as MIPS, SPARC, and ARM.

The main differences lie in how Linux vendors split up the huge number of files associated with Linux into RPM packages as well as which versions of tools like C compilers the vendors ship.

18.2.5.4. Dealing with Signatures

With SUSE Linux, or any Linux based on UnitedLinux 1.0, the RPM packages are signed with OpenPGP version 4, not 3, as used in RPM 4.1. This means that you must use some other, non-RPM means to extract the signatures from an RPM package, and then verify these signatures with gpg.