openSUSE:Migrating to libalternatives with alts

Jump to: navigation, search

Migrating to libalternatives by leveraging the alts package

There are multiple ways to migrate to libalternatives:

  1. Using the alts binary as a dependency, then symlinking the package’s binary to %{_bindir}/alts.
  2. 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.

  1. There should be a symlink linking the common/shared executable to /usr/bin/alts. For example, if we were packaging java-17-openjdk and java-18-openjdk, then we’d have a third package java-openjdk-common that would provide/contain /usr/bin/java. That file would be a symlink to /usr/bin/alts. java-17-openjdk and java-18-openjdk would then require java-openjdk-common, making it a strict dependency.
  2. 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 package java-17-openjdk would be /usr/lib64/jvm/jre-17-openjdk/bin/java.
  3. 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 call exec(). It also denotes the alternative’s priority.
  4. 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 the man 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:

  1. It indicates which actual executable should be invoked, once alts is called through the common/shared symlink (/usr/bin/transmission, for instance).
  2. 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).
  3. 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.