Project:Quality Assurance/Subslots

From Gentoo Wiki
Jump to:navigation Jump to:search

Subslots can be used to provide a more fine-grained slot-like exposure of package versions without the burden of slotting. Slot operators can be used to bind packages to specific slots and subslots of dependencies, and trigger rebuilds on their upgrades/downgrades.

What do subslots represent?

Subslots can be used to represent different package versions. The exact use is up to the package in question, and may differ from package to package. The package maintainers are recommended to use subslots in the most obvious way to avoid confusion. Whenever the most obvious answer is incorrect, the subslots should be described specifically.

The use of subslots (and slots) can be described in the metadata.xml file using the <subslots/> element (and <slot/> elements).

Common examples of subslots:

  • For C/C++ libraries, slots usually represent ABI version of the library (and often match SOVERSION).

Notable exceptions:

  • Qt libraries are considered ABI-stable for each major release, and therefore the slot is used to represent public ABI version. Subslots are used to expose ABI changes in private ABI and normally should not be used by revdeps.
  • app-text/poppler uses subslots to expose the ABI of the low-level libpoppler library. Most of the revdeps use high-level libraries which have reasonably stable ABI and therefore should not use slot ops.

How to depend on packages exposing slots/subslots?

Possible cases

When determining what kind of slot dependency to use, you should check how subslots are used in the dependency and how that maps to the package in question. For example, if the dependency uses subslots to expose ABI versions of a C library, you should determine whether your package links to that library.

There are three possible cases:

  1. The package requires a specific subslot or accepts a closed range of subslots - e.g. a binary package that links to specific SOVERSION of a library which uses subslots for ABI,
  2. The package binds to one of subslots - e.g. a source package that links to a library which uses subslots for ABI,
  3. The package does not care about subslots - e.g. when you call an executable of a package that also provides a library.

Requiring specific subslot

To require a specific subslot, you need to specify the subslot in the slot dependency. If multiple subslots are allowed (and the package does not bind to either of them), then you can use the any-of dependency to expose that. Remember that in the latter case the package will not be rebuilt if one of the allowed subslots is replaced with the other.

CODE Example binary package subslot dependencies
RDEPEND="
  dev-foo/libfoo:0/4
  || (
    dev-foo/libbar:0/foo2+bar2
    dev-foo/libbar:0/foo2+bar3
  )"

Slot/subslot binding

To accept multiple subslots (slots) but bind to one of them, use the = slot operator. You can either use it as := to allow any slot (but bind to newest installed slot+subslot), or :x= to require slot x (and bind to the subslot of the installed version).

Subslot binding will cause the package to be rebuilt when the dependency is upgraded (downgraded) to a different subslot. If the rebuild fails or is not performed for some reason, it will cause the dependency graph to be inconsistent (and e.g. prevent depclean until the revdep is rebuilt).

Slot binding may cause the package to be rebuilt when the dependency is upgraded to a different slot. Alternatively, it will prevent the old slot from being uninstalled until the revdep is rebuilt.

It should be noted that when using the := slot operator, a matching dependency should be copied to DEPEND to ensure that the package in question is actually installed when building the package. Using := inside || blocks is forbidden by policy.

CODE Example source package subslot dependencies
RDEPEND="
  dev-foo/libfoo:0=
  dev-foo/libbar:="
DEPEND=${RDEPEND}

Non-binding slot dependencies

If the package does not need to bind to a specific subslot, it can either use the :* operator to allow any slot (i.e. package does not break when another slot is installed), or specify the required slot.

When the dependency is not slotted, the :* operator can be omitted. However, for packages that are slotted such a dependency may cause inconsistent behavior in different package managers and will trigger repoman warnings.

CODE Example dependencies on executable tools
RDEPEND="
  dev-foo/libfoo:*
  dev-foo/frobnicator:2
  dev-foo/nonslotted"

Handling packages that install multiple libraries

Exposing multiple ABIs

If a package installs multiple libraries whose ABIs are not updated in sync, a single subslot can not efficiently expose the ABI. When splitting it into multiple packages is not feasible, one can either choose to:

  1. expose ABI of one of the libraries (e.g. when others change ABI rarely, e.g. app-text/poppler), and support subslot rebuilds of revdeps partially,
  2. or expose all of the ABIs and accept more frequent subslot rebuilds than necessary.

For the latter case, it should be noted that slots and subslots are free-text, so you can use library names in them. For example, app-text/evince uses the following subslot name: evd3.4-evv3.3, to indicate libevdocument3.so.4 and libevview3.so.3.

Using virtuals to expose multiple ABIs

Alternatively, virtual packages can be introduced to expose multiple subslot variants for a single package. In this case, the virtual package should be named for the library it handles, its subslot should match the library ABI and the dependency should accept all subslots of the library provider that match the specific ABI.

The dependencies inside the virtual can either use version ranges or subslot ranges. For the former, the := slot operator can be used to trigger rebuild of the virtual (and therefore re-reading of the RDEPEND exposed by it) on provider upgrade. This can be useful e.g. when it is unknown which version of the provider will change the ABI.

It is often convenient for the virtual version to match library provider package versions. Multiple versions of the virtual can be committed to allow exposing versioned >= dependencies on the underlying library.

CODE Example code for virtual/libpoppler-glib
SLOT="0/8" # libpoppler-glib.so.8
# all versions since X are installing libpoppler-glib.so.8
# we will add <app-text/poppler-Y when SOVERSION changes
# := guarantees this file will be rechecked on each poppler upgrade
RDEPEND=">=app-text/poppler-X:="

Handling libraries that are provided by multiple packages (virtuals)

The problem

If a particular library has multiple providers (forks or independent implementations), the dependency should ensure that the package is not only rebuilt on the provider ABI change but also on switching between incompatible providers. The solution used depends on whether the different providers aim to be ABI compatible.

ABI-compatible library variants

If the different providers aim to provide a library versions with matching, compatible ABI, then the best solution is to expose the library ABI in the virtual. For example, this is done in virtual/libudev which handles libudev.so which is provided by sys-apps/systemd, sys-fs/udev and sys-fs/eudev.

Similar rules as for virtuals that expose sub-libraries of a single package apply. The dependency should accept all provider versions that install the specific library SOVERSION. However, please note that using := inside || is forbidden.

CODE Example code for virtual/libudev
SLOT="0/1" # libudev.so.1
RDEPEND="
  || (
    sys-apps/systemd:0/u1+s1
    sys-apps/systemd:0/u1+s0
    sys-fs/udev:0/1
    sys-fs/eudev:0/1
  )"

ABI-incompatible library variants

If the different providers do not care about ABI compatibility between their versions of the same library, Gentoo does not provide a convenient solution. The virtual solution would not work since each version could expose only a single subslot, and the PM would always want to upgrade to a newer version (= force a specific provider). The := operator in || is forbidden since it does not work reliably.

The patchwork solution employed currently is to use USE flags to expose provider switch in all reverse dependencies, and make the dependency on the providers USE-conditional. This provides rebuilds on provider upgrade via := operator, and rebuilds on provider change via USE flag change. Sadly, it is inconvenient and the complexity raises with the number of providers.

For example, packages supporting both dev-libs/openssl and dev-libs/libressl use USE=libressl to switch from OpenSSL to LibreSSL. Similarly, switching from media-video/ffmpeg to media-video/libav is done via USE=libav.

CODE Example code for openssl/libressl dependency
# USE=ssl means 'i want some SSL/TLS support'
# USE=libressl means 'prefer LibreSSL over OpenSSL'
IUSE="ssl libressl"
RDEPEND="
  ssl? (
    !libressl? ( dev-libs/openssl:= )
    libressl? ( dev-libs/libressl:= )
  )
"
DEPEND="${RDEPEND}"