Chapter 5. New features in RHEL 8

This section documents the most notable changes in RPM packaging between Red Hat Enterprise Linux 7 and 8.

5.1. Support for Weak dependencies

5.1.1. Introduction to Weak dependencies policy

Weak dependencies are variants of the Requires directive. These variants are matched against virtual Provides: and package names using Epoch-Version-Release range comparisons.

Weak dependencies have two strengths (weak and hint) and two directions (forward and backward), as summarized in the following table.

Note

The forward direction is analogous to Requires:. The backward has no analog in the previous dependency system.

Table 5.1. Possible combinations of Weak dependencies' strengths and directions

Strength/DirectionForwardBackward

Weak

Recommends:

Supplements:

Hint

Suggests:

Enhances:

The main advantages of the Weak dependencies policy are:

  • It allows smaller minimal installations while keeping the default installation feature rich.
  • Packages can specify preferences for specific providers while maintaining the flexibility of virtual provides.

5.1.1.1. Weak dependencies

By default, Weak dependencies are treated similarly to regular Requires:. Matching packages are included in the YUM transaction. If adding the package leads to an error, YUM by default ignores the dependency. Hence, users can exclude packages that would be added by Weak dependencies or remove them later.

Conditions of use

You can use Weak dependencies only if the package still functions without the dependency.

Note

It is acceptable to create packages with very limited functionality without adding any of its weak requirements.

Use cases

Use Weak dependencies especially where it is possible to minimize the installation for reasonable use cases, such as building virtual machines or containers that have a single purpose and do not require the full feature set of the package.

Typical use cases for Weak dependencies are:

  • Documentation

    • Documentation viewers if missing them is handled gracefully
  • Examples
  • Plug-ins or add-ons

    • Support for file formats
    • Support for protocols

5.1.1.2. Hints

Hints are by default ignored by YUM. They can be used by GUI tools to offer add-on packages that are not installed by default but can be useful in combination with the installed packages.

Do not use Hints for the requirements of the main use cases of a package. Include such requirements in the strong or Weak dependencies instead.

Package Preference

YUM uses Weak dependencies and Hints to decide which package to use if there is a choice between multiple equally valid packages. Packages that are pointed at by dependencies from installed or to be installed packages are preferred.

Note, the normal rules of dependency resolution are not influenced by this feature. For example, Weak dependencies cannot enforce an older version of a package to be chosen.

If there are multiple providers for a dependency, the requiring package can add a Suggests: to provide a hint to the dependency resolver about which option is preferred.

Enhances: is only used when the main package and other providers agree that adding the hint to the required package is for some reason the cleaner solution.

Example 5.1. Using Hints to prefer one package over another

Package A: Requires: mysql

Package mariadb: Provides: mysql

Package community-mysql: Provides: mysql

If you want to prefer the mariadb package over the community-mysql package → use:

Suggests: mariadb to Package A.

5.1.1.3. Forward and Backward dependencies

Forward dependencies are, similarly to Requires, evaluated for packages that are being installed. The best of the matching packages are also installed.

In general, prefer Forward dependencies. Add the dependency to the package when getting the other package added to the system.

For Backward dependencies, the packages containing the dependency are installed if a matching package is installed as well.

Backward dependencies are mainly designed for third party vendors who can attach their plug-ins, add-ons, or extensions to distribution or other third party packages.

5.2. Support for Boolean dependencies

Starting with version 4.13, RPM is able to process boolean expressions in the following dependencies:

  • Requires
  • Recommends
  • Suggests
  • Supplements
  • Enhances
  • Conflicts

This section describes boolean dependencies syntax, provides a list of boolean operators, and explains boolean dependencies nesting as well as boolean dependencies semantics.

5.2.1. Boolean dependencies syntax

Boolean expressions are always enclosed with parenthesis.

They are build out of normal dependencies:

  • Name only or name
  • Comparison
  • Version description

5.2.2. Boolean operators

RPM 4.13 introduced the following boolean operators:

Table 5.2. Boolean operators introduced with RPM 4.13

Boolean operatorDescriptionExample use

and

Requires all operands to be fulfilled for the term to be true.

Conflicts: (pkgA and pkgB)

or

Requires one of the operands to be fulfilled for the term to be true.

Requires: (pkgA >= 3.2 or pkgB)

if

Requires the first operand to be fulfilled if the second is. (reverse implication)

Recommends: (myPkg-langCZ if langsupportCZ)

if else

Same as the if operator, plus requires the third operand to be fulfilled if the second is not.

Requires: myPkg-backend-mariaDB if mariaDB else sqlite

RPM 4.14 introduced the following additional boolean operators:

Table 5.3. Boolean operators introduced with RPM 4.14

Boolean operatorDescriptionExample use

with

Requires all operands to be fulfilled by the same package for the term to be true.

Requires: (pkgA-foo with pkgA-bar)

without

Requires a single package that satisfies the first operand but not the second. (set subtraction)

Requires: (pkgA-foo without pkgA-bar)

unless

Requires the first operand to be fulfilled if the second is not. (reverse negative implication)

Conflicts: (myPkg-driverA unless driverB)

unless else

Same as the unless operator, plus requires the third operand to be fulfilled if the second is.

Conflicts: (myPkg-backend-SDL1 unless myPkg-backend-SDL2 else SDL2)

Important

The if operator cannot be used in the same context with the or operator, and the unless operator cannot be used in the same context with and.

5.2.3. Nesting

Operands themselves can be used as boolean expressions, as shown in the below examples.

Note that in such case, operands also need to be surrounded by parenthesis. You can chain the and and or operators together repeating the same operator with only one set of surrounding parenthesis.

Example 5.2. Example use of operands applied as boolean expressions

Requires: (pkgA or pkgB or pkgC)
Requires: (pkgA or (pkgB and pkgC))
Supplements: (foo and (lang-support-cz or lang-support-all))
Requires: (pkgA with capB) or (pkgB without capA)
Supplements: ((driverA and driverA-tools) unless driverB)
Recommends: myPkg-langCZ and (font1-langCZ or font2-langCZ) if langsupportCZ

5.2.4. Semantics

Using Boolean dependencies does not change the semantic of regular dependencies.

If Boolean dependencies are used, checking for one match all names are checked and the boolean value of there being a match is then aggregated over the Boolean operators.

Important

For all dependencies with the exception of Conflicts:, the result has to be True to not prevent an install. For Conflicts:, the result has to be False to not prevent an install.

Warning

Provides are not dependencies and cannot contain boolean expressions.

5.2.4.1. Understanding the output of the if operator

The if operator is also returning a boolean value, which is usually close to what the intuitive understanding is. However, the below examples show that in some cases intuitive understanding of if can be misleading.

Example 5.3. Misleading outputs of the if operator

This statement is true if pkgB is not installed. However, if this statement is used where the default result is false, things become complicated:

Requires: (pkgA if pkgB)

This statement is a conflict unless pkgB is installed and pkgA is not:

Conflicts: (pkgA if pkgB)

So you might rather want to use:

Conflicts: (pkgA and pkgB)

The same is true if the if operator is nested in or terms:

Requires: ((pkgA if pkgB) or pkgC or pkg)

This also makes the whole term true, because the if term is true if pkgB is not installed. If pkgA only helps if pkgB is installed, use and instead:

Requires: ((pkgA and pkgB) or pkgC or pkg)

5.3. Support for File triggers

File triggers are a kind of RPM scriptlets, which are defined in a SPEC file of a package.

Similar to Triggers, they are declared in one package but executed when another package that contains the matching files is installed or removed.

A common use of File triggers is to update registries or caches. In such use case, the package containing or managing the registry or cache should contain also one or more File triggers. Including File triggers saves time compared to the situation when the package controls updating itself.

5.3.1. File triggers syntax

File triggers have the following syntax:

%file_trigger_tag [FILE_TRIGGER_OPTIONS] — PATHPREFIX…​
body_of_script

Where:

file_trigger_tag defines a type of file trigger. Allowed types are:

  • filetriggerin
  • filetriggerun
  • filetriggerpostun
  • transfiletriggerin
  • transfiletriggerun
  • transfiletriggerpostun

FILE_TRIGGER_OPTIONS have the same purpose as RPM scriptlets options, except for the -P option.

The priority of a trigger is defined by a number. The bigger number, the sooner the file trigger script is executed. Triggers with priority greater than 100000 are executed before standard scriptlets, and the other triggers are executed after standard scriptlets. The default priority is set to 1000000.

Every file trigger of each type must contain one or more path prefixes and scripts.

5.3.2. Examples of File triggers syntax

This section shows concrete examples of File triggers syntax:

%filetriggerin — /lib, /lib64, /usr/lib, /usr/lib64
/usr/sbin/ldconfig

This file trigger executes /usr/bin/ldconfig directly after the installation of a package that contains a file having a path starting with /usr/lib or /lib. The file trigger is executed just once even if the package includes multiple files with the path starting with /usr/lib or /lib. However, all file names starting with /usr/lib or /lib are passed to standard input of trigger script so that you can filter inside of your script as shown below:

%filetriggerin — /lib, /lib64, /usr/lib, /usr/lib64
grep "foo" && /usr/sbin/ldconfig

This file trigger executes /usr/bin/ldconfig for each package containing files starting with /usr/lib and containing foo at the same time. Note that the prefix-matched files include all types of files including regular files, directories, symlinks and others.

5.3.3. File triggers types

File triggers have two main types:

File triggers are further divided based on the time of execution as follows:

  • Before or after installation or erasure of a package
  • Before or after a transaction

5.3.3.1. Executed once per package File triggers

File triggers executed once per package are:

  • %filetriggerin
  • %filetriggerun
  • %filetriggerpostun
%filetriggerin

This file trigger is executed after installation of a package if this package contains one or more files that match the prefix of this trigger. It is also executed after installation of a package that contains this file trigger and there is one or more files matching the prefix of this file trigger in the rpmdb database.

%filetriggerun

This file trigger is executed before uninstallation of a package if this package contains one or more files that match the prefix of this trigger. It is also executed before uninstallation of a package that contains this file trigger and there is one or more files matching the prefix of this file trigger in rpmdb.

%filetriggerpostun

This file trigger is executed after uninstallation of a package if this package contains one or more files that match the prefix of this trigger.

5.3.3.2. Executed once per transaction File triggers

File triggers executed once per transaction are:

  • %transfiletriggerin
  • %transfiletriggerun
  • %transfiletriggerpostun
%transfiletriggerin

This file trigger is executed once after a transaction for all installed packages that contain one or more files that match the prefix of this trigger. It is also executed after a transaction if there was a package containing this file trigger in that transaction and there is one or more files matching the prefix of this trigger in rpmdb.

%transfiletriggerun

This file trigger is executed once before a transaction for all packages that meet the following conditions:

  • The package will be uninstalled in this transaction
  • The package contains one or more files that match the prefix of this trigger

It is also executed before a transaction if there is a package containing this file trigger in that transaction and there is one or more files matching the prefix of this trigger in rpmdb.

%transfiletriggerpostun

This file trigger is executed once after a transaction for all uninstalled packages that contain one or more file that matches the prefix of this trigger.

Note

The list of triggering files is not available in this trigger type.

Therefore, if you install or uninstall multiple packages that contain libraries, the ldconfig cache is updated at the end of the whole transaction. This significantly improves the performance compared to RHEL 7 where the cache was updated for each package separately. Also the scriptlets which called ldconfig in %post and %postun in SPEC file of every package are no longer needed.

5.3.4. Example use of File triggers in glibc

This section shows a real-world example of use of File triggers within the glibc package.

In RHEL 8, File triggers are implemented in glibc to call the ldconfig command at the end of an installation or uninstallation transaction.

This is ensured by including the following scriptlets in the glibc’s SPEC file:

%transfiletriggerin common -P 2000000 – /lib /usr/lib /lib64 /usr/lib64
/sbin/ldconfig
%end
%transfiletriggerpostun common -P 2000000 – /lib /usr/lib /lib64 /usr/lib64
/sbin/ldconfig
%end

Therefore, if you install or uninstall multiple packages, the ldconfig cache is updated for all installed libraries after the whole transaction is finished. Consequently, it is no longer necessary to include the scriptlets calling ldconfig in RPM SPEC files of individual packages. This improves the performance compared to RHEL 7, where the cache was updated for each package separately.

5.4. Stricter SPEC parser

The SPEC parser has now some changes incorporated. Hence, it can identify new issues that were previously ignored.

5.5. Support for files above 4 GB

On Red Hat Enterprise Linux 8, RPM can use 64-bit variables and tags, which enables operating on files and packages bigger than 4 GB.

5.5.1. 64-bit RPM tags

Several RPM tags exist in both 64-bit versions and previous 32-bit versions. Note that the 64-bit versions have the LONG string in front of their name.

Table 5.4. RPM tags available in both 32-bit and 64-bit versions

32-bit variant tag name62-bit variant tag nameTag description

RPMTAG_SIGSIZE

RPMTAG_LONGSIGSIZE

Header and compressed payload size.

RPMTAG_ARCHIVESIZE

RPMTAG_LONGARCHIVESIZE

Uncompressed payload size.

RPMTAG_FILESIZES

RPMTAG_LONGFILESIZES

Array of file sizes.

RPMTAG_SIZE

RPMTAG_LONGSIZE

Sum of all file sizes.

5.5.1.1. Using 64-bit tags on command line

The LONG extensions are always enabled on the command line. If you previously used scripts containing the rpm -q --qf command, you can add long to the name of such tags:

rpm -qp --qf="[%{filenames} %{longfilesizes}\n]"

5.6. Other features

Other new features related to RPM packaging in Red Hat Enterprise Linux 8 are:

  • Simplified signature checking output in non-verbose mode
  • Support for the enforced payload verification
  • Support for the enforcing signature checking mode
  • Additions and deprecations in macros