RPM Boolean Dependencies

Jump to: navigation, search

One of the great features of rpm 4.13 are boolean dependencies. Those dependencies work with all soft and hard requires and also build dependencies. For the basics see the official documentation above.

Examples

Avoid Recommends to install unwanted dependencies

# nginx.spec main package:
Recommends:  nginx-vim

On a system where recommends are enabled and vim is not installed, installing nginx would trigger installing vim. In the past a solution via packageand() could have been used. But a more portable solution is:

# nginx.spec main package:
Recommends:  (nginx-vim if vim_client)

Same can be done with the reverse dependency.

# nginx.spec nginx-vim subpackage:
Supplements:  (nginx and vim_client)

Both solutions are valid and it depends on where you want to express the dependency.

Ranged dependencies

In the old days we had a little %py_requires macro, which boiled down to something like

Requires: python >= 2.7.0
Requires: python < 3.0

If you now have a package installed that provides python=2.6.0 and one that provides python=3.4.0 you technically fulfilled the require statements but did not actually achieve what you wanted. And more and more languages adopt similar range dependencies. In Ruby you have the ~> operator e.g. So to solve the problem in a correct way, you combine the range in a boolean requirement acting on the same package using with:

Requires (python(abi) >= 3.7 with python(abi) < 3.10)

(Note that the python flavors for major version 3 do not provide a plain python = 3.x anymore. See openSUSE:Packaging_Python for a more detailed description of how the python-rpm-macros rewrite the runtime requirements and how you can leverage boolean dependecies for BuildRequires and %{python_module foo})

For python modules (PEP440, PEP508):

# setup(install_requires=['foo ~= 1.1',  'bar>=2.0,<2.5', 'dataclasses;python_version<"3.7"'])
BuildRequires: %{python_module foo >=1.1 with %python-foo < 2.0}
BuildRequires: %{python_module bar >=2.0 with %python-bar < 2.5}
BuildRequires: %{python_module dataclasses if %python-base < 3.7}
Requires: (python-foo >= 1.1 with python-foo < 2.0)
Requires: (python-bar >= 2.0 with python-bar < 2.5)
Requires: (python-dataclasses if python-base < 3.7)

For rubygems

# rails ~> 6.1.0
Requires: (rubygem(%{rb_default_ruby_abi}:rails) >= 6.1.0 with rubygem(%{rb_default_ruby_abi}:rails) < 6.2)
# rails ~> 6.1
Requires: (rubygem(%{rb_default_ruby_abi}:rails) >= 6.1  with rubygem(%{rb_default_ruby_abi}:rails) < 7)

Optional (build)requires

BuildRequires: (pkgconfig(libfoo) or libfoo-devel)

While the first example was straight forward, we prefer the pkgconfig provides and fallback to the pkgname. The next example is a double edged sword but it might make spec files more readable. Use wisely:

BuildRequires: (pkgconfig(libfoo) if pkgconfig(libfoo) >= 1.2.3)

Only add a BuildRequires if the library is new enough.

WARNING: While this is easier to read than the explicit %if %suse_version .... constructs currently used, this very quickly leads to problems with unreproducible builds and should therefore be avoided.