openSUSE:Package dependencies

Jump to: navigation, search

Package Dependency Types

Requires

RPM has very good capabilities of automatically finding dependencies for libraries, Perl modules, etc. There is usually no need to explicitly list "Requires:" (e.g. Requires: libqt4) when the dependency has already been picked up by RPM in the form of depending on shared libraries in the libqt4 package.

If your package splits into sub-packages, they may need to require the base package using a versioned dependency.

Requires: %{name} = %{version}

-devel sub-packages need to explicitly depend on packages with the shared libraries that the devel package has symlinks to. For example, libfoobar-devel needs:

Requires: libfoo2 = %{version}, libbar5 = %{version}

See Libzypp/Dependencies for a list of dependency and resolvable types.

PreReq

PreReq is a hack; it is a high-priority "Requires" and is normally only required for breaking circular dependencies when unforgiving scriptlets are involved. Almost always, the regular Requires, Requires(pre), Requires(post), etc. work and should be used instead.

BuildArch

Typical usage:

BuildArch: noarch

Use this with your package, if you have no architecture dependent files in your file list.

Note that all subpackages must be also noarch, if your main package is noarch.

BuildRequires

You must explicitly list the packages that your package requires to build successfully.

Unlike with Requires, for BuildRequires there is no automatic procedure to find dependencies. Having proper build requirements saves the time of all developers and testers because they will not need to search for missing build requirements manually. It also ensures that the build is reproducible and uses always the same features. configure scripts, for example, may exclude PNG support depending on the availability of libpng in the build system. With BuildRequires: libpng-devel (or since openSUSE 11.4: BuildRequires: pkgconfig(libpng14), you ensure that the build will fail if libpng is not present.

OBS Caveat

Conditional BuildRequires are limited to simple variables. RPM usually supports complex constructs like this in an %if expression:

%if 0%(test "%something" = "enabled" && echo 1)

But when evaluating BuildRequires, the RPM parser of the BuildService runs in an environment where subshell expansions are disabled. You'll see warnings like this:

Warning: spec file parser line 109: can't expand %(...)

%lua scripts are not interpreted by the dispatcher either; no warning is emitted.

The following example uses only a simple variable, which works with BuildRequires:

%if ! %{with own_mpfr}
BuildRequires: mpfr-devel
%endif

Exceptions

There is no need to include the following packages or their dependencies as BuildRequires, because they would occur too often. These packages are considered the minimum build environment.

Also try this command for a list:

osc meta prjconf openSUSE:Factory | egrep '^(Preinstall:|Support:|Required:)'
Preinstall: aaa_base acl attr bash coreutils diffutils
Preinstall: filesystem fillup glibc grep insserv libacl libattr
Preinstall: libbz2-1 libgcc%{gcc_version} libxcrypt m4 libncurses5 pam
Preinstall: permissions libreadline6 rpm sed tar zlib libselinux1
Preinstall: liblzma5 libcap2 libpcre0
Preinstall: libpopt0 libelf1 liblua5_1
Required: gcc gcc%{gcc_version} glibc perl rpm tar patch
Support: autoconf automake binutils bzip2 gcc gcc%{gcc_version}
Support: gettext-runtime glibc libtool perl rpm zlib
Support: libncurses5
Support: libaudit1 cpio cpp cpp%{gcc_version} cracklib cvs
Support: file findutils gawk gdbm gettext-tools
Support: glibc-devel glibc-locale groff gzip info less
Support: libbz2-devel libdb-4_8
Support: libstdc++%{gcc_version}
Support: udev
Support: libxcrypt libzio
Support: linux-glibc-devel make man netcfg
Support: net-tools pam-modules patch perl-base sysvinit-tools
Support: texinfo timezone util-linux libmount1 login
Support: libgomp%{gcc_version} libuuid1 psmisc
Support: terminfo-base update-alternatives pwdutils build-mkbaselibs
Support: brp-check-suse post-build-checks rpmlint-Factory
Support: build-compare
Support: libunwind libunwind-devel

Conflicts

Whenever possible, openSUSE packages should avoid conflicting with each other. Unfortunately, this is not always possible. For full details on openSUSE Conflicts policy, see openSUSE:Packaging conflicts.

Recommends

SUSE's RPM supports the Recommends tag, but it was not available upstream until very recently. As a result, packages with Recommends will fail to build for OBS targets Fedora_18, RHEL_6 and CentOS_6, for example. A workaround is to exclude the Recommends tag from non-SUSE targets:

%if 0%{?suse_version}
Recommends: foo
%endif

Alternatively the Recommends tag could become a Requires tag so that a recommended package (foo) gets installed in any case on systems that do not support RPM Recommends:

%if 0%{?suse_version}
Recommends: foo
%else
Requires: foo
%endif

See also openSUSE:Build Service cross distribution howto

File Dependencies

RPM gives you the ability to depend on files instead of packages. Whenever possible, you should avoid file dependencies outside of /etc, /bin, /sbin, /usr/bin, or /usr/sbin. Using file dependencies outside of those directories requires yum (and other depsolvers using the repomd format) to download and parse a large XML file looking for the dependency. Helping the depsolvers avoid this processing by depending on the package instead of the file saves our end users a lot of time. There are times when other technical considerations outweigh these considerations. One specific example is packages installing into %{_libdir}/mozilla/plugins. In this case, mandating a specific browser in your package just to own this directory could drag in a large amount of needless packages. Requiring the directory to resolve the dependency is the better choice.

kmod Dependencies

It's now possible for a userland package to pull a specific kernel flavor upon installation. This require the package's specfile to contain the new kmod Requires.

This is especially useful for the SLES and openSUSE Minimal-VM images, as it is focused on virtual environment (VMs & Cloud) and is careful about the size of the images. Therefore for these images and other variations of the distribution we use a reduced kernel (kernel-default-base) with selected modules, whereas kernel-default contains all supported modules.

Prior to SLES 15 SP2, if a user installs a userland package that require a specific kernel module which is not installed in the system then the service/package will not work properly. This was a common scenario with our images, where our users had to manually install the complete kernel-default to workaround this.

Now packages can set dependencies with a kernel module or a kernel flavor, so they will be installed together automatically.

Let's take a concrete example

If a user installs multipath-tools a Minimal-VM then start the multipath service, it will fail since the required dm_multipath kernel module is excluded from kernel-default-base and thus not loaded by default. A "Failed to find module 'dm_multipath'" systemd error message will be shown until the module is manually loaded (bsc#1119414 - VMware: Failed to find modules scsi_dh_alua, scsi_dh_emc, scsi_dh_rdac, and dm_multipath).

These dependencies are auto generated for packages that configure the module loading using the /usr/lib/modules-load.d/ mechanism, but it needs to be put in place manually for other cases.

Another example nfsd.ko module is needed by the NFS server package to properly start the NFS services.

So we introduced a "Requires" for the specified module:

Requires: (kmod(nfsd.ko) if kernel)

Note that autogenerated kmod Requires include the conditional if kernel to make sure that the kernel module will be required only if there is a kernel installed. This is to avoid issues like with installation-images, which build an initrd with packages, but that doesn't include the kernel itself which is stored separately.


Strong and Weak dependencies

Requires tags always need to be fulfilled by the solver, Recommends are ignored if not present and can also be ignored on purpose by the package manager, in zypper for example you can use "zypper in --no-recommends", or a configuration key ("solver.onlyRequires = true") that disables installation of suggested and recommended packages by default, what for example is the default configuration for our Minimal (former JeOS) images.

What to take into account when working with dependencies

If your software calls external tools, or need some another package or capabilities that does not get auto generated by RPM during build, you need to explicitly add a dependency to your spec file.

When deciding which of the dependency level tag you are gonna use (Requires, Recommends, Suggests) there are important considerations to be made. Packages can be broken into several components and have different levels of functional dependency

  • Requires MUST be used if the dependency is required for the software to provide a significant amount of functionality.
  • Requires MUST be used if any of the components will deliberately FAIL if the dependent package is missing.
  • Recommends can be used to express ADDITIONAL functionality.
  • Suggests can be used to provides hints for the user, or solver and should never be considered a hard dependency.

Using Suggests tells the packaging system and the user that the listed packages are related to this one and can perhaps enhance its usefulness, but that installing this one without them is perfectly reasonable.

Some of the worries of excessive Requires in a package can usually be solved by splitting the package in smaller components, that then can have the appropriate Requires. For example, if your package has a an EXTRA functionality that is not used by default like SSL support, you might consider splitting it into yourpackage-ssl and do all the appropriate Requires in the subpackage so you don't have to Recommend the packages in the main one.

Implications for image building and system installation.

Currently on SUSE use to mechanisms to define the list of packages to be installed,

1 - The one used by the installer where patterns packages are used to list all packages to be installed

2 - Kiwi description files that are used to build images with a pre-defined set of packages

All that should take into account the relationship of packages (Requires, Recommends, etc) and we should carefully consider when to add more Requires to existing packages, specially in the base system.

Although patterns in openSUSE can be used as a meta package for the installation of software, they can not be uninstalled in group, and so are mainly using during installation and upgrades.

One should avoid "fixing" problems with the installer by adding packages as Requires/Recommends in the patterns instead of deliberately modifying the package itself to have appropriate dependencies. These can actually break other types of install, Minimal (former JeOS) for example uses --no-recommends and would have a different set of packages than an installation done by YAST.

Suggests are specially tricky here as sometimes people use this in patterns to "hint" what packages the solver (zypper or dnf) should look for when multiple packages offer the same capabilities, this can also sometimes be done with the "Prefer:" tag in the build system (OBS)

This should be carefully considered as well, as other types of build can assume different defaults due to the nature of the build.


How to do distribution upgrades

There are two update modes which need different behaviour

  1. Bugfixes (Patches)
  2. Distribution upgrade

In the first mode, installed packages are patched in order to fix bugs or to introduce missing functionality. However, the package version usually stays since the pristine tarball is the same; only the package release number is changed.

Also, the build environment (compiler, libraries, etc.) only sees minimal changes.

Distribution upgrade lifts the complete system to a new level and the operating system version gets increased. (If the system is e.g. openSUSE 10.2, it will increase to openSUSE 10.3) The package changes allowed during distribution upgrade can be much more severe and include

  • dropped packages
  • new packages
  • renamed packages
  • merged packages
  • splitted packages

Such changes are hard (or even impossible) to express by RPM dependencies. Thus, a more expressive form of package dependencies is needed so the system upgrade can be performed.

This Wiki page documents how to express dependencies for distribution upgrade.

If you are adding dependencies for seamless system upgrade, do not forget to specify the openSUSE version for which these are required. It will allow to recognize these lines as obsolete in future versions (most probably it would be SLE version + 2).


Means to express dependencies

Each package can express dependencies on other packages by defining symbolic names for Provides, Requires, and Conflicts.
These symbolic names are usually names of packages but can also be “abstract” names not related to a package.

These symbolic names can be used to express capabilities which are unrelated to a specific package. For example, the capability MTA (mail transport agent) is provided by multiple packages, sendmail and postfix being the most prominent.

It is important to remember that all symbolic names (being package or abstract names) reside in the same namespace. When choosing an abstract name (e.g. for a capability), it must not conflict with another package name.

To express update dependencies in order to replace one (old) package with another (newer) package, the Obsoletes tag is used.

Facts for RPM dependencies

  • Each package implicitly provides its name.
    There is no need to provide the package name, but other packages can require (or conflict with) the package name.
  • A package can not conflict with a symbolic tag it provides.
    Since the package name is provided implicitly, a package can not conflict with itself.
  • Obsolete works on real package names only.
    One can not obsolete a symbolic tag.
  • Symbolic names, as used in Requires, Provides and Conflicts are case-sensitive.

Basic dependencies

Installing a new package

New.png

pac.spec
Name: pac
Version: 1.0
Requires:
Provides:
Obsoletes:
Conflicts:

Updating a package

Update.png

pac.spec
Name: pac
Version: 1.1
Requires:
Provides:
Obsoletes:
Conflicts:

The matching name and the higher version make this an update for pac-1.0.

Renaming a package

package.spec
Name: package
Version: 1.0
Requires:
Provides: pac = %version-%release
Obsoletes: pac < %version-%release
Conflicts:

The Provides: field serves three purposes: it enables users to find the package by its old name, it satisfies package dependencies if other packages rely on the old name, and it gives the solver in libzypp a hint to prefer this package if several packages obsolete the old name. As a policy it is a required that packages have Provides: tag.

The Obsoletes: field serves two purposes: most importantly it tells rpm to just delete a package by the specified name and version number, and it gives the solver in libzypp a hint that the renamed package is newer.

Both tags have to be versioned. The version has to include both version and release to increase the likelihood that new builds cover installed variants of the old package in the wild.

The usage of a versioned Obsoletes: field is supposed to handle the case when a package with the old name comes back into the distribution. Since it is not possible to predict the future, it has to be assumed that the version number of it will be higher when this happens. No assumptions can be made what purpose the package will serve at that point. It can either replace the just renamed package (in which case this package renaming rule applies), or it may be able to coexist with the renamed package. If for some reason the returned package gets a lower version number, the just renamed package must be adjusted again to not claim ownership of the old, then new package anymore. Then both will be able to coexist.

When a package is renamed, the version of the package may remain the same. This means a plain Obsoletes: pac < %version will not delete the old package when the renamed package is installed. The most likely result is a fileconflict because the new package would be installed in parallel with the old package, hence the usage of %version-%release.

The usage of %version-%release will reduce, but can not entirely avoid, the chance that the new, renamed package is still seen as older than the old, existing package. This can happen if an older codestream received many changes for a package, which means it gets a high %release number. Another newer codestream may start with a much lower version number. As a result, the suggested usage of Obsoletes: can not work as desired, the old package will never be deleted and a fileconflict occurs. If such case is encountered, an unversioned Obsoletes: is acceptable, at the expense that extra work has to be done if a package with the old name is supposed to be added to the distribution. Ask the releasemanagers for an exception.


If you obsolete a subpackage with a different version number than the main package, you can not use %version in Provides/Obsoletes. Of course, you can use the same version string as in Version of the subpackage.

Please also add a simple comment to the specfile, e. g. “# pac was last used in openSUSE 12.3”. Each package has to take care of its heritage, which means the Provides and Obsoletes tags stay essentially forever.

Replace a package by another with the same functionality

Rename.png

package.spec
Name: package
Version: 1.1
Requires:
Provides:
Obsoletes: pac < %version
Conflicts:

Instead of renaming a package, pac will be replaced by package, although package provides no files of pac. So package does not provide pac.

This only works if one package obsoletes pac only. If there are more packages, the solver will not do this update.

Splitting and Merging

Splitting off a sub-package

Split-a.png

pac.spec pac-devel.spec
Name: pac Name: pac-devel
Version: 1.1 Version: 1.1
Requires: Requires:
Provides: Provides: pac:/file/from/pac
Obsoletes: Obsoletes:
Conflicts: Conflicts:

Splitting a package into two

Split-b.png

firstpac.spec secondpac.spec
Name: firstpac Name: secondpac
Version: 1.1 Version: 1.1
Requires: Requires:
Provides: pac = 8.15 Provides: pac:/file/from/pac
Obsoletes: pac <= 8.15 Obsoletes:
Conflicts: Conflicts:

The example shown lists separate spec files for pac and pac-devel. (Remember that RPM allows to define sub-packages from a “main” package with a single .spec file.

The dependency resolution (installing both new packages) is done by YaST with the split-alias mentioned in Provides: of pac-devel.

It is up to the package maintainer to decide about dependencies between pac and pac-devel. For example, if one wants to split a library package into a main, a -devel, and a -doc package, the main and the -doc package are independent, but the -devel package requires the main package.

During an update, the aformentioned split-alias tag is important. YaST only updates already installed packages, in this case only the main package. Without the split-alias, this would remove files needed for development which were part of the old main package but are not in the new main package.

The given Provides: for pac now triggers this update dependency as it tells YaST also to install pac-devel even though it was not installed before.

Icon-warning.png
Warning: Split-alias tags are used by YaST only, RPM never sees them. zypper/libzypp only honors them if recommends are turned on.

If a package is split in more than two packages, the split-alias tags must be mutually exclusive. It is the only way for YaST to correctly handle such update dependencies.

Important enhancement for the future

Splitting a library package into a main and a -devel package is always a good idea. But it does not make sense in every case â€” because sometimes, the -devel package would be too small or does not contain anything. In this case, just add a Provides: pac-devel = %version in the main package to do the trick that other packages can require this virtual -devel package.

Please also add a simple comment to the specfile, e. g. “# -devel splitted in openSUSE 12.3”. Without it, it will be hard to decide when it is possible to wipe these lines out. Otherwise it may stay there forever.

Merging a package

Merge-a.png

package.spec
Name: package
Version: 1.1
Requires:
Provides: pac = 8.15
Obsoletes: pac <= 8.15
Conflicts:

This is comparable to a package rename. The new package must also incorporate the Provides and Obsoletes tags from the old package. This is needed to properly update from distribution before X.

Merging two packages into a new one

Merge-b.png

package.spec
Name: package
Version: 1.1
Requires:
Provides: pac1 = 8.15, pac2 = 2.3
Obsoletes: pac1 <= 8.15, pac2 <= 2.3
Conflicts:

Handling alternatives

Non conflicting

Non-conflicting.png

firstpac.spec secondpac.spec thirdpac.spec
Name: firstpac Name: secondpac Name: thirdpac
Version: 1.0 Version: 1.0 Version: 1.0
Requires: Requires: Requires:
Provides: Pac = 1.0 Provides: Pac = 1.0 Provides: Pac = 1.0
Obsoletes: Obsoletes: Obsoletes:
Conflicts: Conflicts: Conflicts:

All three package provide the capability Pac and can be installed in parallel.

(The upper case letter in Pac is choosen on purpose to distinguish this tag from a package. Such capabilities must be agreed upon within the openSUSE community. Ask on the "opensuse-packaging" mailing list before inventing your own tag!).

Example: xdm and kdm provide capability "Display-Manager".

Partially conflicting

Partly-conflicting.png

firstpac.spec secondpac.spec thirdpac.spec
Name: firstpac Name: secondpac Name: thirdpac
Version: 1.0 Version: 1.0 Version: 1.0
Requires: Requires: Requires:
Provides: Pac = 1.0 Provides: Pac = 1.0 Provides: Pac = 1.0
Obsoletes: Obsoletes: Obsoletes:
Conflicts: secondpac Conflicts: firstpac Conflicts:

The Conflicts express that only either firstpac or secondpac might be installed, but not both.

Mutually exclusive

Mutually-exclusive.png

firstpac.spec secondpac.spec thirdpac.spec
Name: firstpac Name: secondpac Name: thirdpac
Version: 1.0 Version: 1.0 Version: 1.0
Requires: Requires: Requires:
Provides: Pac = 1.0 Provides: Pac = 1.0 Provides: Pac = 1.0
Obsoletes: Obsoletes: Obsoletes:
Conflicts: secondpac thirdpac Conflicts: firstpac thirdpac Conflicts: firstpac secondpac

Only one of firstpac, secondpac, or thirdpac might be installed.