openSUSE:Migrating to libalternatives with alts
Migrating to libalternatives by leveraging the alts
package
There are multiple ways to migrate to libalternatives:
- Using the
alts
binary as a dependency, then symlinking the packageâs binary to%{_bindir}/alts
. - Creating a custom binary written in C and linking said binary to
libalternatives.so
.
Weâll discuss approach #1 here.
Goal
Rather than describing the perfect sequence of steps that will guide you to migrate your package, weâll describe the end goal.
That way, even if some of the steps described here donât perfectly apply to your package, youâll still have a clear mental model of what the migration is trying to achieve. Adapt as needed.
- There should be a symlink linking the common/shared executable to
/usr/bin/alts
. For example, if we were packagingjava-17-openjdk
andjava-18-openjdk
, then weâd have a third packagejava-openjdk-common
that would provide/contain/usr/bin/java
. That file would be a symlink to/usr/bin/alts
.java-17-openjdk
andjava-18-openjdk
would then requirejava-openjdk-common
, making it a strict dependency. - Each package providing its respective âalternativeâ should NOT provide this executable. Instead, they will provide their unique executable. Thatâs why they would depend on
java-openjdk-common
. For example, the actual binary for packagejava-17-openjdk
would be/usr/lib64/jvm/jre-17-openjdk/bin/java
. - Each package providing its respective alternative will also provide its unique configuration file for
libalternatives
. This is the file that is read by/usr/bin/alts
, when it decides how to callexec()
. It also denotes the alternativeâs priority. - The man page should also be unique, for each alternative (no conflicting files). Which man page is displayed when
man
is invoked is decided by theman
executable itself. Our openSUSE/SLE version has been patched to follow the same decision that/usr/bin/alts
makes when deciding which executable to invoke. That way, the alternative decision applies to both the executable, and its man page.
Letâs get started.
Common package: create it and add alts
as a dependency
The runtime dependency on /usr/bin/alts
is required because we donât want to break that symlinkâs target. It should be present at all times.
This dependency should be part of the common package. For instance, the aforementioned java-openjdk-common
(which doesnât exist, itâs just an example).
As every alternative package should have a dependency on the common package, they would pull /usr/bin/alts
through the indirect relationship.
In the case of Transmission, we introduced the transmission-common
package. Here is an excerpt:
Package: transmission-common Requires: alts
Common package: set up the symlink to /usr/bin/alts
and create the libalternatives configuration directory
The %install
phase for the common package should create the symlink to /usr/bin/alts
, and create the configuration directory for libalternatives, where all the priority files will be placed.
If youâre using a single spec file to create multiple packages, of course, there will still only be one %install
section.
But in any case, both the symlink and the directory will still be owned by the common package.
%install # alternatives - create binary symlink ln -s -f %{_bindir}/alts %{buildroot}%{_bindir}/%{name} # alternatives - create libalternatives configuration directory mkdir -p %{buildroot}%{_datadir}/libalternatives/%{name}
Seeing as %{name}
in this case would expand to transmission
, this would be the result. The directory is named after the whole alternative group.
# PARTIALLY MACRO-EXPANDED (except for %{buildroot}) %install # alternatives - create binary symlink ln -s -f /usr/bin/alts %{buildroot}/usr/bin/transmission # alternatives - create libalternatives configuration directory mkdir -p %{buildroot}/usr/share/libalternatives/transmission
Alternative packages: declare the dependency on the common package
For each alternative package, we must declare a dependency on the common package.
In the case of transmission, these are the packages transmission-cli
, transmission-gtk
, transmission-qt
.
The following is an excerpt for how the dependency would look like:
Package: transmission-cli Requires: transmission-common Package: transmission-gtk Requires: transmission-common Package: transmission-qt Requires: transmission-common
Alternative packages: create the libalternatives configuration files
Each alternative package will have its own libalternatives configuration file.
This configuration file serves a few purposes:
- It indicates which actual executable should be invoked, once
alts
is called through the common/shared symlink (/usr/bin/transmission
, for instance). - It indicates which set of man pages relates to this particular alternative (
/usr/share/man/man1/transmission-gtk.1.gz
relates to/usr/bin/transmission-gtk
). - It indicates that alternativeâs priority.
Therefore, for each alternative package, we need to create a custom configuration file.
Below is an example.
5 is the priority here, because the file is named 5.conf
. This priority relates to the priority 5 that transmission-cli
had when it was still set up using update-alternatives
.
Bear in mind that the directory name is shared across all alternatives. So here, the directory is /usr/share/libalternatives/transmission
.
This point was mentioned before, but itâs good to bear it in mind.
%install # alternatives - create 'cli' configuration file cat > %{buildroot}%{_datadir}/libalternatives/%{name}/5.conf <<EOF binary=%{_bindir}/%{name}-cli man=%{name}-cli.1 EOF
And this would be the example for transmission-gtk
, priority 15:
%install # alternatives - create 'gtk' configuration file cat > %{buildroot}%{_datadir}/libalternatives/%{name}/15.conf <<EOF binary=%{_bindir}/%{name}-gtk man=%{name}-gtk.1 EOF
Make sure the correct packages own the correct files
As we mentioned earlier, the main executable symlink is owned by the common package. And so is the libalternatives configuration file directory, for the whole group of alternatives this package represents.
%files common %{_bindir}/%{name} # symlink: /usr/bin/transmission -> /usr/bin/alts %dir %{_datadir}/libalternatives/%{name} # libalternatives config dir: /usr/share/libalternatives/transmission
As for each alternative package, they should own their respective executables, libalternatives configuration file, and man page.
%files gtk %{_bindir}/%{name}-gtk # executable: /usr/bin/transmission-gtk %{_mandir}/man1/%{name}-gtk.1%{?ext_man} # man page: /usr/share/man/man1/transmission-gtk.1.gz %{_datadir}/libalternatives/%{name}/15.conf # libalternatives config file: /usr/share/libalternatives/transmission/15.conf
This should be it
If you go back and check our original goals when migrating the package, they shouldâve been accomplished by now.