Edit

Examining dependency issues during the installation of modules

A common problem while installing a module is that one or more of its components have requirements which cannot be fulfilled.

This document describes scenarios in which this can happen and possible solutions. It assumes that the module in question is successfully built in official infrastructure and available on the examined system for installation. You can verify the latter with dnf module list or dnf module list <name of the module>.

Scenarios

Dependency problems while installing a module can happen for a variety of reasons:

RPM-level packaging errors

One of the packages involved is packaged wrongly, i.e. that the package is available but carries a requirement which can’t be fulfilled, for instance because of typos, versioning issues, requiring a private library that isn’t listed as being satisfied by the package carrying it. Solving this is beyond the scope of this document.

The dependency can be satisfied by another module

Another available module contains a package which would satisfy the requirement but that module isn’t pulled in as a runtime dependency. In most cases adding that missing dependency should fix the issue.

Another module could satisfy the dependency in theory but doesn’t

A package that would satisfy the requirement is built by this or another available module but is filtered out after building, by being listed in the data/filter/rpms section of the modulemd file. A likely reason that it is listed there is that it in turn isn’t installable because one of its requirements can’t be fulfilled. Depending on the circumstances, there are different possible approaches:

  • Not filter the package out in the other module (and solve any issues it may have in the process).
  • Remove the hard dependency from the package that depends on it. For instance, there are a lot of packages with hard dependencies which should rather be weak ones, but weak dependencies didn’t exist in Fedora by the time these dependencies were created. In this case converting that hard (“Requires:”) to a weak dependency (“Recommends:”) can do the trick.
  • Filter out the package pulling in the dependency in the first place.

The package isn’t part of any available module

If a package that would fulfill a dependency isn’t part of any available module, one can add it to an existing module as a component or to create a new module for it (if filtering out the package that causes the dependency isn’t an option). If the component on its own is only useful in conjunction with the software contained in the module that can’t be installed the best place is to put it there. If it has wider uses, a decision needs to be made if another existing module or a newly created one is the best place for it.

Package conflicts

If any of the packages which are installed on the system or would be installed with the module conflict with each other because of the contained files, version requirements or the like, these need to be resolved. How that is done is outside the scope of this document.

More detailed recipes

General considerations

Fixing component-level dependency issues can generally be done in two ways: Either by providing whatever is missing or by removing the need for it in the first place.

Filtering out packages

If a package whose dependency can’t be fulfilled isn’t part of the API of the module or required directly or indirectly by an API package it can be filtered out by adding it to the data/filter/rpms list in the modulemd file. This is an approach that usually requires little work beyond establishing that these prerequisites are met.

Making a component available

Adding a component to a module (or creating a new one) isn’t a one-step process because the component may have unfulfilled dependencies of its own (which can have other dependencies and so forth). Using a lather-rinse-repeat approach is time-consuming (you’d have to wait for a compose to happen before testing the changed module is possible) but the information to resolve these issues is available from the outset, assuming that the dependencies are resolvable within the set of Fedora packages. Instead of trying to resolve this manually (which can be time-consuming on its own), one can use tools like fedmod to generate the whole dependency chain programmatically.

The first step is to use fedmod rpm2module <component> which creates a modulemd file for a module containing the component and its dependencies. If one wanted to do just that (and nothing failed) besides getting the module reviewed and made available. Regardless of if the component should go into an existing module or shouldn’t be filtered out any longer, the information in the generated modulemd file can be used to augment the module that should carry it (arguably more work but still less than resolving dependencies manually, one at a time).

Identifying components

Consider this error while attempting to install a module 389-ds:

$ dnf module install 389-ds
Fedora - Bikeshed - Developmental packages for the next Fedora release                8.5 MB/s | 3.5 MB     00:00
Last metadata expiration check: 0:00:00 ago on Fri 13 Oct 2017 02:44:48 PM UTC.
Error:
 Problem 1: conflicting requests
  - nothing provides libtcmalloc.so.4()(64bit) needed by 389-ds-base-libs-1.3.7.4-1.module_506864e9.x86_64
 Problem 2: conflicting requests
  - nothing provides libtcmalloc.so.4()(64bit) needed by 389-ds-base-1.3.7.4-1.module_506864e9.x86_64

In this case one needs to find out which package provides libtcmalloc.so.4()(64bit), e.g. by running dnf repoquery --whatprovides <dependency> on an (as of now at least) not yet modular Fedora system and if necessary dnf info <package> to find out the source package which can then be added to a module.

Removing dependencies

Often, removing dependencies is done by disabling a component feature that is not needed in a module but would pull in a whole tree of other dependencies. It would be the right approach more often for components that aren’t part of a module’s API. This approach is trickier because you then need to introduce a macro to enable or disable the functionality (and the dependencies in question) in the spec file of the component, and you need to take care that its use in data/buildopts/rpms/macros doesn’t introduce side effects in the other components which are built as part of the module.