<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="parallel-installability" xml:lang="de">

  <info>
    <link type="guide" xref="index#maintainer-guidelines"/>

    <credit type="author copyright">
      <name>Havoc Pennington</name>
      <email its:translate="no">hp@pobox.com</email>
      <years>2002</years>
      <!-- Heavily based off Havoc’s original article about parallel
           installability: http://ometer.com/parallel.html.
           License CC-BY-SA 3.0 confirmed by e-mail with him. -->
    </credit>
    <credit type="author copyright">
      <name>Philip Withnall</name>
      <email its:translate="no">philip.withnall@collabora.co.uk</email>
      <years>2015</years>
    </credit>

    <include xmlns="http://www.w3.org/2001/XInclude" href="cc-by-sa-3-0.xml"/>

    <desc>
      Writing libraries to be future proof through parallel installation
    </desc>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Mario Blättermann</mal:name>
      <mal:email>mario.blaettermann@gmail.com</mal:email>
      <mal:years>2016, 2018</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Christian Kirbach</mal:name>
      <mal:email>christian.kirbach@gmail.com</mal:email>
      <mal:years>2016, 2020</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Stefan Melmuk</mal:name>
      <mal:email>stefan.melmuk@gmail.com</mal:email>
      <mal:years>2018</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Tim Sabsch</mal:name>
      <mal:email>tim@sabsch.com</mal:email>
      <mal:years>2021</mal:years>
    </mal:credit>
  </info>

  <title>Parallele Installierbarkeit</title>

  <synopsis>
    <title>Zusammenfassung</title>

    <p>Wenn zwei Pakete parallel installiert werden, haben Sie keine gemeinsamen Dateinamen, und Entwickler, die auf dem Paket aufbauen, kompilieren stets gegen die erwartete Version. Dies gilt auch für Daemons, Dienstprogramme und Konfigurationsdateien sowie Header-Dateien und Bibliothek-Binärdateien.</p>

    <list>
      <item><p>Stellen Sie sicher, dass alle Versionen einer Bibliothek parallel installierbar sind. (<link xref="#justification"/>)</p></item>
      <item><p>Versionieren Sie alle Dateien, die von einer Bibliothek installiert werden. (<link xref="#solution"/>)</p></item>
      <item><p>Definieren Sie die Paketversionsnummer separat vom Sonamen oder der libtool-Versionsnummer. Seien Sie sich bewusst, welcher Teil der Paketversionsnummer sich mit der API ändert. (<link xref="#version-numbers"/>)</p></item>
      <item><p>Installieren Sie C-Header-Dateien in <file><var>$(includedir)</var>/lib<var>Bibliothek</var>-<var>Version</var>/<var>Bibliothek</var>/</file>. (<link xref="#header-files"/>)</p></item>
      <item><p>Installieren Sie Bibliothek-Binärdateien in <file><var>$(libdir)</var>/lib<var>library</var>-<var>version</var>.so.<var>soname</var></file>. (<link xref="#libraries"/>)</p></item>
      <item><p>Installieren Sie pkg-config-Dateien in <file><var>$(libdir)</var>/pkgconfig/<var>Bibliothek</var>-<var>Version</var>.pc</file>. (<link xref="#pkg-config"/>)</p></item>
      <item><p>Sorgen Sie dafür, dass Konfigurationsdateien aufwärts- und abwärtskompatibel sind oder installieren Sie sie in <file><var>$(sysconfdir)</var>/<var>Bibliothek</var>-<var>Version</var>/</file>. (<link xref="#configuration-files"/>)</p></item>
      <item><p>Setzen Sie <code>GETTEXT_PACKAGE</code> auf <code><var>Bibliothek</var>-<var>Version</var></code>. (<link xref="#gettext"/>)</p></item>
      <item><p>Schließen Sie eine Versionsnummer in alle D-Bus-Schnittstellennamen, Dienstnamen und Objektpfade ein. Zum Beispiel: <code>org.domain.<var>Library</var><var>Version</var>.<var>Interface</var></code>, <code>org.domain.<var>Library</var><var>Version</var></code> und <code>/org/domain/<var>Library</var><var>Version</var>/</code>. (<link xref="#dbus"/>)</p></item>
      <item><p>Installieren Sie Daemon-Binaries in <file><var>$(libexecdir)</var>/<var>Bibliothek</var>-daemon-<var>Version</var></file>. (<link xref="#programs"/>)</p></item>
      <item><p>Installieren Sie Hilfs-Binärdateien in <file><var>$(bindir)</var>/<var>library</var>-utility-<var>version</var></file> und platzieren Sie symbolische Links in <file><var>$(bindir)</var>/<var>library</var>-utility</file>. (<link xref="#programs"/>)</p></item>
    </list>
  </synopsis>

  <section id="justification">
    <title>Begründung</title>

    <p>Alle öffentlichen Bibliotheken sollten so entworfen sein, dass sie parallel installiert werden können, damit eventuelle spätere API-Inkompatibilitäten leichter abgefangen werden können. Wenn eine Bibliothek von mehreren Projekten verwendet wird und eine inkompatible Änderung an der API einführt, müssen alle Projekte zeitgleich auf die neue API migrieren, da sie sonst aufgrund der unterschiedlichen Versionsabhängigkeiten nicht länger gemeinsam installierbar sind.</p>

    <p>Das ist aus Betriebssicht nicht vertretbar. Außerdem ist die Anforderung, dass alle Projekte unmittelbar auf die neue API portieren müssen, schwer zu organisieren und dürfte zu Unmut führen, da die meisten API-Änderungen keinen großen neuen Funktionen einführen, die eine Portierung motivieren würden.</p>

    <p>Die Lösung ist daher, dass alle Bibliotheken parallel installierbar sein müssen: alte und neue Versionen der API müssen ohne Konflikte gleichzeitig installiert und gegeneinander kompiliert sein. Es ist deutlich einfacher, dieses System von paralleler Installierbarkeit von Beginn an zu unterstützen, als es später nachträglich einzuführen.</p>

    <p>Es eliminiert das »Henne und Ei«-Problem, eine Reihe an Anwendungen von einer Bibliotheksversion auf die nächste zu portieren und erlaubt es den Maintainern von Bibliotheken, API-Änderungen einfacher einzuführen, wodurch schlussendlich schnellere Iterationen und Entwicklungsprozesse ermöglicht werden.</p>

    <p>Eine ebenfalls valide Alternative hierzu ist, dass die Bibliothek niemals API-Inkompatibilitäten einführt. Dieser Ansatz wird von <code>libc</code> verfolgt.</p>
  </section>

  <section id="solution">
    <title>Lösung</title>

    <p>Die Lösung für dieses Problem besteht im Prinzip darin, die Bibliothek umzubenennen. In den meisten Fällen ist es hier am einfachsten, die Versionsnummer in den Pfad jeder installierten Datei einzubinden. Dadurch können mehrere Versionen der Bibliothek gleichzeitig installiert sein.</p>

    <p>Nehmen wir an, die Bibliothek <code>Foo</code> installiert traditionell diese Dateien:</p>
    <list>
      <item><p><file>/usr/include/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-utils.h</file></p></item>
      <item><p><file>/usr/lib/libfoo.so</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo.pc</file></p></item>
      <item><p><file>/usr/share/doc/foo/foo-manual.txt</file></p></item>
      <item><p><file>/usr/bin/foo-utility</file></p></item>
    </list>

    <p>Nun können Sie Version 4 von <code>Foo</code> so anpassen, dass sie stattdessen diese Dateien installiert:</p>
    <list>
      <item><p><file>/usr/include/foo-4/foo/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-4/foo/utils.h</file></p></item>
      <item><p><file>/usr/lib/libfoo-4.so</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo-4.pc</file></p></item>
      <item><p><file>/usr/share/doc/foo-4/foo-manual.txt</file></p></item>
      <item><p><file>/usr/bin/foo-utility-4</file></p></item>
    </list>

    <p>Es kann dann parallel mit Version 5 installiert werden:</p>
    <list>
      <item><p><file>/usr/include/foo-5/foo/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-5/foo/utils.h</file></p></item>
      <item><p><file>/usr/lib/libfoo-5.so</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo-5.pc</file></p></item>
      <item><p><file>/usr/share/doc/foo-5/foo-manual.txt</file></p></item>
      <item><p><file>/usr/bin/foo-utility-5</file></p></item>
    </list>

    <p>Sie können dieses Verhalten mit <link href="http://www.freedesktop.org/wiki/Software/pkg-config/"> <cmd>pkg-config</cmd></link> umsetzen: <file>foo-4.pc</file> würde <file>/usr/include/foo-4</file> in den include-Pfad und <file>libfoo-4.so</file> zu der Bibliothekenliste hinzufügen; <file>foo-5.pc</file> würde <file>/usr/include/foo-5</file> und <file>libfoo-5.so</file> hinzufügen.</p>
  </section>

  <section id="version-numbers">
    <title>Versionsnummern</title>

    <p>Die Versionsnummer in den Dateipfaden ist eine <em>ABI/API</em>-Version. Es sollte sich nicht um die vollständige Versionsnummer Ihres Pakets handeln, sondern nur um den Teil, der die API-Kompatibilität angibt. Wenn Sie das Standard-Schema <code><var>major</var>.<var>minor</var>.<var>micro</var></code> für Ihre Projektversionierung verwenden, wird die API-Version typischerweise durch die Major-Versionsnummer angegeben.</p>

    <p>Minor-Veröffentlichungen (bei denen für gewöhnlich APIs hinzugefügt, aber <em>nicht</em> verändert oder entfernt werden) sowie Micro-Veröffentlichungen (für gewöhnlich Fehlerbehebungen) betreffen nicht die <link xref="api-stability">API-Abwärtskompatibilität</link> und erfordern daher nicht, dass alle Dateien verschoben werden.</p>

    <p>Die Beispiele in den nachfolgenden Abschnitten gehen davon aus, dass die API-Version und der Soname mit dem folgenden Code aus der <file>configure.ac</file> entnommen werden:</p>
    <listing>
      <title>API-Versionierung in Autoconf</title>
      <desc>Code zum Exportieren der API-Version und soname aus <file>configure.ac</file></desc>
      <code># Before making a release, the <var>LIBRARY</var>_LT_VERSION string should be modified.
# The string is of the form c:r:a. Follow these instructions sequentially:
#
#  1. If the library source code has changed at all since the last update,
#     then increment revision (‘c:r:a’ becomes ‘c:r+1:a’).
#  2. If any interfaces have been added, removed, or changed since the last update,
#     increment current, and set revision to 0.
#  3. If any interfaces have been added since the last public release,
#     then increment age.
#  4. If any interfaces have been removed or changed since the last public release,
#     then set age to 0.
AC_SUBST([<var>LIBRARY</var>_LT_VERSION],[1:0:0])

AC_SUBST([<var>LIBRARY</var>_API_VERSION],[4])</code>
    </listing>
  </section>

  <section id="header-files">
    <title>C-Headerdateien</title>

    <p>Header-Dateien sollten immer in einen versionierten Unterordner installiert werden, der eine <cmd>-I</cmd>-Flag an den C-Compiler erfordert. Wenn zum Beispiel Ihr Header <file>foo.h</file> heißt und Ihre Anwendungen Folgendes tun:</p>
    <code mime="text/x-csrc">#include &lt;foo/foo.h&gt;</code>
    <p>dann sollten Sie diese Dateien installieren:</p>
    <list>
      <item><p><file>/usr/include/foo-4/foo/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-5/foo/foo.h</file></p></item>
    </list>

    <p>Anwendungen sollten die Flags <cmd>-I/usr/include/foo-4</cmd> oder <cmd>-I/usr/include/foo-5</cmd> an den Compiler übergeben. Zur Erinnerung: dies wird mittels <cmd>pkg-config</cmd> realisiert.</p>

    <p>Beachten Sie den zusätzlichen Unterordner <file>foo/</file>. Dieser legt den Namensraum <code mime="text/x-csrc">#include</code> an, um Dateinamen-Konflikte mit anderen Bibliotheken zu vermeiden. Wenn zum Beispiel zwei verschiedene Bibliotheken Header namens <file>utils.h</file> installieren: welcher wird verwendet, wenn Sie <code mime="text/x-csrc">#include &lt;utils.h&gt;</code> schreiben?</p>

    <p>Es klingt verlockend, eine der Header-Dateien außerhalb eines Unterordners zu platzieren:</p>
    <list>
      <item><p><file>/usr/include/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-5/foo.h</file></p></item>
    </list>

    <p>Das Problem hierbei ist aber, dass Nutzer immer aus Versehen den falschen Header abbekommen, da <cmd>-I/usr/include</cmd> immer mal wieder seinen Weg in die Kompilierungsbefehle findet. Wenn Sie Ihre Header-Dateien dennoch unbedingt so platzieren müssen, sollten Sie in Ihrer Bibliothek wenigstens einen Check hinzufügen, der überprüft, ob Anwendungen bei der Initialisierung der Bibliothek den falschen Header verwenden.</p>

    <p>Versionierte Header-Dateien können in automake über den folgenden Code installiert werden:</p>
    <listing>
      <title>Header-Dateien in Automake</title>
      <desc>Code, um versionierte Header-Dateien über <file>Makefile.am</file> zu installieren</desc>
      <code><var>library</var>includedir = $(includedir)/lib<var>library</var>-@<var>LIBRARY</var>_API_VERSION@/<var>library</var>
<var>library</var>_headers = \
	lib<var>library</var>/example1.h \
	lib<var>library</var>/example2.h \
	$(NULL)

# The following headers are private, and shouldn't be installed:
private_headers = \
	lib<var>library</var>/example-private.h \
	$(NULL)
# The main header simply #includes all other public headers:
main_header = lib<var>library</var>/<var>library</var>.h
public_headers = \
	$(main_header) \
	$(<var>library</var>_headers) \
	$(NULL)

<var>library</var>include_HEADERS = $(public_headers)</code>
    </listing>

    <p>Zusätzlich zur korrekten Versionierung sollten alle APIs in installierten Header-Dateien die <link xref="namespacing">richtigen Namensräume</link> besitzen.</p>
  </section>

  <section id="libraries">
    <title>Bibliotheken</title>

    <p>Bibliothek-Objektdateien sollten einen versionierten Namen besitzen. Zum Beispiel:</p>
    <list>
      <item><p><file>/usr/lib/libfoo-4.so</file></p></item>
      <item><p><file>/usr/lib/libfoo-5.so</file></p></item>
    </list>

    <p>Dies erlaubt Anwendungen, die richtige Version zur Kompilierungszeit zu erhalten und stellt sicher, dass die Versionen 4 und 5 keine gemeinsamen Namen besitzen.</p>

    <p>Versionierte Bibliotheken können in automake über den folgenden Code gebaut und installiert werden:</p>
    <listing>
      <title>Bibliotheken in Automake</title>
      <desc>Code, um versionierte Bibliotheken über <file>Makefile.am</file> zu bauen und installieren</desc>
      <code>lib_LTLIBRARIES = lib<var>library</var>/lib<var>library</var>-@<var>LIBRARY</var>_API_VERSION@.la

lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_SOURCES = \
	$(private_headers) \
	$(<var>library</var>_sources) \
	$(NULL)
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_CPPFLAGS = …
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_CFLAGS = …
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_LIBADD = …
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_LDFLAGS = \
	-version-info $(<var>LIBRARY</var>_LT_VERSION) \
	$(AM_LDFLAGS) \
	$(NULL)</code>
    </listing>

    <section id="library-sonames">
      <title>Bibliothek-Sonamen</title>

      <p>Bibliothek-Sonamen (auch als libtool-Versionsnummern bekannt) adressieren nur das Problem, dass bereits früher kompilierte Versionen zu Anwendungen verlinkt werden. Sie adressieren nicht das Problem, wenn kompilierende Anwendungen vorherige Versionen benötigen, und betreffen ausschließlich Bibliotheken.</p>

      <p>Aus diesem Grund sollten Sonamen zwar verwendet werden, allerdings <em>zusätzlich</em> zu versionierten Bibliotheksnamen. Beide Lösungen gehen unterschiedliche Probleme an.</p>
    </section>
  </section>

  <section id="pkg-config">
    <title>pkg-config-Dateien</title>

    <p>pkg-config-Dateien sollten einen versionierten Namen besitzen. Zum Beispiel:</p>
    <list>
      <item><p><file>/usr/lib/pkgconfig/foo-4.pc</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo-5.pc</file></p></item>
    </list>

    <p>Da jede pkg-config-Datei versionierte Informationen über den Bibliotheksnamen und die include-Pfade enthält, sollte jedes Projekt, das von der Bibliothek abhängt, von einer Version auf die nächste wechseln können, indem sie einfach ihren pkg-config-Check von <file>foo-4</file> auf <file>foo-5</file> ändert (und eventuelle API-Änderungen portiert).</p>

    <p>Versionierte pkg-config-Dateien können über Autoconf und Automake mit dem folgenden Code installiert werden:</p>
    <listing>
      <title>pkg-config-Dateien in Autoconf und Automake</title>
      <desc>Code, um versionierte pkg-config-Dateien über <file>configure.ac</file> und <file>Makefile.am</file> zu installieren</desc>

      <code>AC_CONFIG_FILES([
lib<var>library</var>/<var>library</var>-$<var>LIBRARY</var>_API_VERSION.pc:lib<var>library</var>/<var>library</var>.pc.in
],[],
[<var>LIBRARY</var>_API_VERSION='$<var>LIBRARY</var>_API_VERSION'])</code>

      <code># Note that the template file is called <var>library</var>.pc.in, but generates a
# versioned .pc file using some magic in AC_CONFIG_FILES.
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = lib<var>library</var>/<var>library</var>-$(<var>LIBRARY</var>_API_VERSION).pc

DISTCLEANFILES += $(pkgconfig_DATA)
EXTRA_DIST += lib<var>library</var>/<var>library</var>.pc.in</code>
    </listing>
  </section>

  <section id="configuration-files">
    <title>Konfigurationsdateien</title>

    <p>Für den Nutzer ist der beste Ansatz für Konfigurationsdateien, ihr Format <link xref="api-stability">aufwärts- und abwärtskompatibel</link> zu halten (beide Bibliotheksversionen erlauben die genau gleiche Konfigurationsdatei-Syntax und -Semantik). Damit kann die gleiche Konfigurationsdatei für alle Versionen der Bibliothek verwendet werden, und die Konfigurationsdatei selbst muss nicht versioniert werden.</p>

    <p>Falls das nicht möglich ist, sollten die Konfigurationsdateien schlicht umbenannt werden, und Nutzer müssen jede Version separat konfigurieren.</p>
  </section>

  <section id="gettext">
    <title>Gettext-Übersetzungen</title>

    <p>Wenn Sie für Übersetzungen gettext im Kombination mit autoconf und automake verwenden, werden Übersetzungen für gewöhnlich in <file>/usr/share/locale/<var>lang</var>/LC_MESSAGES/<var>package</var></file> installiert. Sie müssen dabei <var>package</var> ändern. Die GNOME-Konvention ist, dafür <file>configure.ac</file> zu verwenden:</p>

    <code>GETTEXT_PACKAGE=foo-4
AC_SUBST([GETTEXT_PACKAGE])
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"])</code>

    <p>Verwenden Sie <code>GETTEXT_PACKAGE</code> als den Paketnamen, der an <code mime="text/x-csrc">bindtextdomain()</code>, <code mime="text/x-csrc">textdomain()</code> und <code mime="text/x-csrc">dgettext()</code> übergeben wird.</p>
  </section>

  <section id="dbus">
    <title>D-Bus-Schnittstellen</title>

    <p>Eine D-Bus-Schnittstelle ist eine andere Form von API, ähnlich zu einer C-API mit der Ausnahme, dass die Versionsauflösung zur Laufzeit statt beim Kompilieren stattfindet. Abgesehen davon werden D-Bus-Schnittstellen genauso wie C-APIs versioniert: Versionsnummern müssen in den Namen der Schnittstelle und Diensten sowie in den Objektpfaden enthalten sein.</p>

    <p>Für einen Dienst <code>org.example.Foo</code>, der die Schnittstellen <code>A</code> und <code>B</code> auf den Objekten <code>Controller</code> und <code>Client</code> bereitstellt, würden die Versionen 4 und 5 der D-Bus-API wie folgt aussehen:</p>
    <list>
      <title>Dienstnamen</title>
      <item><p><code>org.example.Foo4</code></p></item>
      <item><p><code>org.example.Foo5</code></p></item>
    </list>
    <list>
      <title>Schnittstellennamen</title>
      <item><p><code>org.example.Foo4.InterfaceA</code></p></item>
      <item><p><code>org.example.Foo4.InterfaceB</code></p></item>
      <item><p><code>org.example.Foo5.InterfaceA</code></p></item>
      <item><p><code>org.example.Foo5.InterfaceB</code></p></item>
    </list>
    <list>
      <title>Objektpfade</title>
      <item><p><code>/org/example/Foo4/Controller</code></p></item>
      <item><p><code>/org/example/Foo4/Client</code></p></item>
      <item><p><code>/org/example/Foo5/Controller</code></p></item>
      <item><p><code>/org/example/Foo5/Client</code></p></item>
    </list>
  </section>

  <section id="programs">
    <title>Programme, Daemons und Dienstprogramme</title>

    <p>Desktop-Anwendungen müssen für gewöhnlich nicht versioniert werden, da keine anderen Module von ihnen abhängen. Daemons und Dienstprogramme interagieren hingegen mit anderen Teilen des Systems und erfordern daher eine Versionierung.</p>

    <p>Es sei ein Daemon und ein Dienstprogramm vorhanden:</p>
    <list>
      <item><p><file>/usr/libexec/foo-daemon</file></p></item>
      <item><p><file>/usr/bin/foo-lookup-utility</file></p></item>
    </list>
    <p>diese sollten wie folgt versioniert werden:</p>
    <list>
      <item><p><file>/usr/libexec/foo-daemon-4</file></p></item>
      <item><p><file>/usr/bin/foo-lookup-utility-4</file></p></item>
    </list>

    <p>Vermutlich möchten Sie einen symbolischen Link von <file>/usr/bin/foo-lookup-utility</file> zur empfohlenen versionierten Kopie des Dienstes setzen, um Nutzern die Verwendung zu vereinfachen.</p>
  </section>
</page>
