Alternatives

Alternatives provide means for parallel installation of packages which provide the same functionality by maintaining sets of symlinks (one per package) pointing to alternativized files like this: /path/original-file -> /etc/alternatives/packagename-original-file -> /path/original-file.suffix For more information, see update-alternatives(8) manpage.

Usage within Fedora

Alternatives MAY be used to allow parallel installation of software when:

  • the software can be used as a drop-in replacement and functions with sufficient similarity that users and other programs would, within reason, not need to know which variant is currently installed

AND

  • the selection of the software is only performed system-wide by the system administrator and end users do not have a need to switch between the variants.

Inversely, alternatives MUST NOT be used when:

  • The software is not a drop-in replacement. For instance, if common command line arguments are different between the two variants, alternatives MUST NOT be used.

OR

  • End users will care which variant they are using. If a non-root user would gain value by switching between the variants then alternatives MUST NOT be used.

A good example of using alternatives are the various MTAs which all provide /usr/bin/sendmail with similar command line arguments.

Bad examples of using alternatives include:

  • the various MPI environments where users care both about which MPI environment they compile against and which one they run against

  • choice of editor when the user invokes "vi" where the user will care about feature availability, compatibility with plugins, etc

Cases where parallel installation is desirable but alternatives is unsuitable may be scenarios where Environment Modules are appropriate. MPI and python-sphinx (until Fedora 31) are example packages using environment-modules for this purpose.

How to use alternatives

If a package is using alternatives, the files which would otherwise conflict MUST be installed with an appropriate suffix (for example: %{_bindir}/sendmail.postfix instead of %{_bindir}/sendmail), the original locations MUST be touched (for example: touch %{_bindir}/sendmail), the links set up by alternatives MUST be listed as %ghost in the file list and proper Requires: MUST be added, like in the examples below.

Putting the alternativized files in the file list ensures that they are owned by respective packages, which means that commands like:

  • rpm -qf /usr/bin/foo

  • dnf install /usr/bin/foo

  • repoquery --whatprovides /usr/bin/foo

all work properly. Using %ghost for this purpose allows using globs and generated file lists.

Examples

Example from antlr.spec:

Requires(post): %{_bindir}/update-alternatives
Requires(postun): %{_bindir}/update-alternatives
...
%install
...
touch %{buildroot}%{_bindir}/antlr

%post
update-alternatives --install %{_bindir}/antlr \
  %{name} %{_bindir}/antlr-java 10

%postun
if [ $1 -eq 0 ] ; then
  update-alternatives --remove %{name} %{_bindir}/antlr-java
fi
...
%files
...
%ghost %{_bindir}/antlr
%{_bindir}/antlr-java

And a more complex example of alternatives invocation from sendmail.spec, slightly edited:

Requires(post): %{_bindir}/update-alternatives
Requires(postun): %{_bindir}/update-alternatives
Requires(preun): %{_bindir}/update-alternatives
...
%install
...
# rename files for alternative usage
mv %{buildroot}%{_bindir}/sendmail %{buildroot}%{_bindir}/sendmail.sendmail
touch %{buildroot}%{_bindir}/sendmail
for i in mailq newaliases rmail; do
    mv %{buildroot}%{_bindir}/$i %{buildroot}%{_bindir}/$i.sendmail
    touch %{buildroot}%{_bindir}/$i
done
mv %{buildroot}%{_mandir}/man1/mailq.1 %{buildroot}%{_mandir}/man1/mailq.sendmail.1
touch %{buildroot}%{_mandir}/man1/mailq.1
mv %{buildroot}%{_mandir}/man1/newaliases.1 %{buildroot}%{_mandir}/man1/newaliases.sendmail.1
touch %{buildroot}%{_mandir}/man1/newaliases.1
mv %{buildroot}%{_mandir}/man5/aliases.5 %{buildroot}%{_mandir}/man5/aliases.sendmail.5
touch %{buildroot}%{_mandir}/man5/aliases.5
mv %{buildroot}%{_mandir}/man8/sendmail.8 %{buildroot}%{_mandir}/man8/sendmail.sendmail.8
touch %{buildroot}%{_mandir}/man8/sendmail.8

%postun
if [ "$1" -ge "1" ]; then
    if [ "`+readlink %{_sysconfdir}/alternatives/mta+`" == "%{_bindir}/sendmail.sendmail" ]; then
        %{_bindir}/alternatives --set mta %{_bindir}/sendmail.sendmail
    fi
fi

%post
# Set up the alternatives files for MTAs.
update-alternatives --install %{_bindir}/sendmail mta %{_bindir}/sendmail.sendmail 90 \
    --slave %{_bindir}/mailq mta-mailq %{_bindir}/mailq.sendmail \
    --slave %{_bindir}/newaliases mta-newaliases %{_bindir}/newaliases.sendmail \
    --slave %{_bindir}/rmail mta-rmail %{_bindir}/rmail.sendmail \
    --slave /usr/lib/sendmail mta-sendmail /usr/lib/sendmail.sendmail \
    --slave %{_sysconfdir}/pam.d/smtp mta-pam %{_sysconfdir}/pam.d/smtp.sendmail \
    --slave %{_mandir}/man8/sendmail.8.gz mta-sendmailman %{_mandir}/man8/sendmail.sendmail.8.gz \
    --slave %{_mandir}/man1/mailq.1.gz mta-mailqman %{_mandir}/man1/mailq.sendmail.1.gz \
    --slave %{_mandir}/man1/newaliases.1.gz mta-newaliasesman %{_mandir}/man1/newaliases.sendmail.1.gz \
    --slave %{_mandir}/man5/aliases.5.gz mta-aliasesman %{_mandir}/man5/aliases.sendmail.5.gz \
    --initscript sendmail
...

%preun
if [ $1 = 0 ]; then
    update-alternatives --remove mta %{_bindir}/sendmail.sendmail
fi
...

%files
...
%ghost %{_bindir}/sendmail
%ghost %{_bindir}/mailq
%ghost %{_bindir}/newaliases
%ghost %{_bindir}/rmail
%ghost /usr/lib/sendmail
%ghost %{_sysconfdir}/pam.d/smtp
%ghost %{_mandir}/man8/sendmail.8.gz
%ghost %{_mandir}/man1/mailq.1.gz
%ghost %{_mandir}/man1/newaliases.1.gz
%ghost %{_mandir}/man5/aliases.5.gz

%{_bindir}/sendmail.sendmail
%{_bindir}/mailq.sendmail
%{_bindir}/newaliases.sendmail
%{_bindir}/rmail.sendmail
/usr/lib/sendmail.sendmail
%config(noreplace) %{_sysconfdir}/pam.d/smtp.sendmail
%{_mandir}/man8/sendmail.sendmail.8.gz
%{_mandir}/man1/mailq.sendmail.1.gz
%{_mandir}/man1/newaliases.sendmail.1.gz
%{_mandir}/man5/aliases.sendmail.5.gz

%attr(0755,root,root) %{_initrddir}/sendmail