bendus im web

rpm micro howto

Vor ein paar Jahren kam ich in die Verlegenheit, die für meine verwendete Linux-Distribution passenden RPM-Pakete selbst bauen zu müssen.

Je nach Original Quellcode-Paket und bereits installierter Pakete gestaltete sich das mehr oder weniger schwierig. Um dem geneigten Leser das Unterfangen etwas zu erleichtern, habe ich die wesentlichen Schritte mit den Hindernissen, die dabei immer wieder auftauchten, auf dieser Seite zusammengefaßt.

Inhalt:

  1. Vorbereitung
    1. Verzeichnisse
    2. Konfiguration des RPM
  2. Die Quellen
    1. Erster Test
    2. Makefile (.in,.am)
  3. Das .spec-File
    1. Definitionen
    2. Angaben über das Paket
    3. Quellen und Patches
    4. %build Bereich
    5. %install Bereich
    6. Paketdateien (%files)
    7. Aufräumen (%clean)
  4. Paket fertig stellen
  5. Links

0 Vorbereitung

Um die .rpm's als normaler Benutzer erstellen zu können, was dringend empfohlen wird, sind ein paar kleinere Vorbereitungen im home-Verzeichnis des Benutzers (du selbst) nötig. Dazu zählt das Erstellen von einigen Verzeichnissen und das Abändern der Konfigurationsdateien für den RPM (Redhat Package Manager).

0.0 Verzeichnisse

Damit der RPM weiß, wo sich die Quelldateien befinden, und wo die fertigen Pakete hingepackt werden sollen, empfiehlt sich eine zu /usr/src/redhat (Redhat) bzw. /usr/src/packages (Suse) analoge Verzeichnisstruktur:

/home/user/packages
/home/user/packages/build
/home/user/packages/rpms
/home/user/packages/sources
/home/user/packages/specs
/home/user/packages/srpms 

0.1 Konfiguration des RPM

Der RPM wird über zwei Dateien konfiguriert, rpmmacros und rpmrc. Die systemweite Konfiguration sollte sich wie üblich in /etc befinden. Als normaler Benutzer kann man sich diese in das home-Verzeichnis kopieren und mit einem Punkt versehen:

bash> cp /etc/rpmmacros ~/.rpmmacros
bash> cp /etc/rpmrc ~/.rpmrc 

Allerdings befand sich das wirklich nutzbare rpmmacros in /usr/lib/rpm/macros sowie ein ausführliches rpmrc unter /usr/lib/rpm/rpmrc, worauf hier aber nicht näher eingegangen wird.

In ~/.rpmmacros müssen jetzt die richtigen Verzeichnisse für den Benutzer eingetragen werden. Dazu muß man die dafür interessanten Makros (oder Defintionen) aufsuchen und wie folgt abändern (oder ggf neu eintragen):

%_home          /home/user
%_topdir        %{_home}/packages
%_builddir      %{_topdir}/build
%_rpmdir        %{_topdir}/rpms
%_sourcedir     %{_topdir}/sources
%_specdir       %{_topdir}/specs
%_srcrpmdir     %{_topdir}/srpms 

Das ~/.rpmrc ist dazu da, um weiter Konfigurationen wie etwa Kompileroptimierungen für bestimmte Prozessoren festlegen zu können. Als Beispiel kann man sich mein rpmrc anschauen.

1 Die Quellen

Die Quelldateien (als .tar.gz) sollten sich im sources-Unterverzeichnis befinden, um vom RPM mit obiger Konfiguration gefunden zu werden.

1.0 Erster Test

Um sicherzugehen, daß das Programm auf dem eigenen Rechner ohne größere Umstände kompiliert werden kann, kann man folgendermaßen vorgehen:

  1. man entpackt die Quellen in ein temporäres Verzeichnis (z.B. /home/user/tmp) mittels
    bash> cd ~/tmp
    bash> tar xvfz ../packages/sources/<paket>.tar.gz 
    
  2. man wechselt in das neu entstandene Verzeichnis (und merke sich dessen Namen, da es später noch wichtig sein kann)
  3. man probiere den üblichen build-Prozeß:
    bash> ./configure
    bash> make 
    

1.1 Makefile (.in,.am)

Wenn bei der übersetzung keine Probleme aufgetreten sind, kann man sich dem Makefile widmen. Dabei gilt es festzustellen, ob der sogenannte DESTDIR-Mechanismus unterstützt wird. Dieser dient dazu, das Programm und die zugehörigen Dateien in ein beliebiges Verzeichnis installieren zu können. Das ist notwendig, um die Dateien als normaler Benutzer installieren zu können und hinterher vom RPM in ein fertiges Paket packen zu lassen.

Man nehme einen Texteditor seiner Wahl und überprüfe das (vom obigen ./configure (s.o.) erzeugte) Makefile auf

  • einen Eintrag der Form DESTDIR =
  • Einträge im install-Bereich auf die Form install ... $(DESTDIR)/$(prefix)/ ...

Sollten die obigen Einträge nicht vorhanden sein, so gibt es zwei Möglichkeiten, damit das Erstellen des .rpm erfolgreich wird:

  • man fügt die Einträge manuell in das Makefile.in (s.u.) und erstellt einen Patch
  • man modifiziert das .spec-File

Obwohl die zweite Variante die leichtere ist, werde ich hier die erstere ausführlicher beschreiben, da die Technik im Wesentlichen auch für andere Modifikationen geeignet ist, die dem Programm hinzugefügt werden sollen, ohne die original Quelldateien neu packen zu müssen.

In manchen Quellverzeichnissen findet sich eine Datei namens Makefile.am. Diese wird von automake(1) benutzt, um alle nötigen Dateien zu erstellen, wie etwa das configure-Skript oder das Makefile.in. Falls man nicht vorhat, das Programm von cvs-Quellen zu installieren, bei denen der automake-Prozeß üblich ist, kann man sich dem Makefile.in widmen.

Das Makefile.in wird von configure benutzt, um das Makefile zu erzeugen. Deshalb empfiehlt es sich, von dieser Datei und nicht vom Makefile selbst auszugehen, da im .spec-File configure und make als Anweisungen für das kompilieren zu finden sind. Es gilt also, das Makefile.in so umzugestalten, daß der DESTDIR Mechanismus problemlos funktioniert. Dazu gehe man folgendermaßen vor:

  1. man lösche das Verzeichnis der obigen ausgepackten Quellen und packe sie erneut aus (ohne ./configure und make erneut auszuführen)
  2. man sichere das Makefile.in in der Originalform mit
    bash> cd /home/user/tmp/<programm>-<version>
    bash> ci -l Makefile.in 
    
    (am Prompt einfach "." + <enter>) oder alternativ, falls ci nicht installiert wurde (mit dem rcs oder cvs Paket):
    bash> cp Makefile.in Makefile.in.orig 
    
  3. man editiere jetzt das Makefile.in wie oben beschrieben
  4. man erzeuge jetzt die .dif-Datei zu den Originalquellen und kopiere diese in das sources Verzeichnis:
    bash> cd ..
    bash> pkgmake diff <programm>-<version>
    bash> mv <programm>-<version>.dif /home/user/packages/sources 
    
    bzw.
    bash> cd ..
    bash> gendiff <programm>-<version> orig > /home/user/packages/sources/<programm>-<version>.dif 
    

Für <programm>-<version> ist das obige Verzeichnis einzutragen, das man sich bei 1.0 unter Punkt 2. gemerkt hat und normalerweise die Form programmname-versionsnummer hat.

2 Das .spec-File

Das .spec-File sagt dem RPM, was zu tun ist, um das Programm zu kompilieren, installieren und anschließend die richtigen Dateien in das Paket zu packen. Es sollte sich in dem specs-Verzeichnis befinden und ist vom Benutzer weitgehend selbst zu schreiben.

Manche Entwickler liefern in ihrem Quellenpaket ein .spec mit, was in der Regel benutzt werden kann. Allerdings kann es durchaus vorkommen, daß an diesem noch kleinere Veränderungen durchzuführen sind. Deshalb werde ich hier davon ausgehen, daß das .spec selbst geschrieben werden muß.

Besondere Bereiche im .spec fangen mit einem "%" an und die wichtigsten heißen: define, description, prep, setup, patch, build, install, files, clean. Zu weitern vordefinierten Wörtern kann man das ~/.rpmmacros konsultieren. Kommentare werden, wie auch in Makefiles und shell-Skripts üblich, mit einem Doppelkreuz "#" gekennzeichnet.

Ich habe eine Art Grundgrüst für ein .spec-File bereitgestellt, anhand dessen ich jetzt die wichtigen Einträge erläutern werde.

2.0 Definitionen

Definitionen dienen der Übersicht und einfachen Handhabung des .spec. Im günstigsten Fall reicht eine Änderung der Definitionen und man kann es für ein anderes Programm benutzen. Definitionen haben die Form:

%define <Bezeichner> <Wert> 

<Wert> ist dabei immer als Zeichenkette zu verstehen.

In meinem .spec müssen also am Anfang die Bezeichner wie folgt definiert werden:

%define name <programmname>    # der Programmname
%define ver  <versionsnummer>  # die Versionsnummer des Programms
%define rel  <paketnummer>     # die Releasenummer, wird meist fortlaufend gewählt
%define prefix <prefix>        # das Verzeichnis in das das Programm am Ende installiert werden soll 

Konkret könnte das etwa so aussehen:

%define name hallo_welt
%define ver  0.0.0
%define rel  0
%define prefix /usr 

2.1 Angaben über das Paket

Hier wird dem RPM gesagt, welchen Namen das Paket bekomme, welche Version usw. Dabei machen sich die obigen Definitionen sehr gut:

Name:      %{name}
Version:   %{ver}
Release:   %{rel}
Copyright: GPL
Packager:  bendus
URL:       http://www.url.com
Source:    %{name}-%{ver}.tar.gz
BuildRoot: /tmp/temp-%{name} 

Wie man sieht können die obigen Definitionen mit %{<Bezeichner>} referenziert werden. Ebenso alle anderen Makrodefinitionen (z.B. aus dem ~/.rpmmacros).

Die letzte Definition sagt dem RPM, in welches Verzeichnis das fertige Programm erstmal installiert werden sollen, um dann gemeinsam zum fertigen .rpm gepackt zu werden. Dieses Verzeichnis wird später mit ${RPM_BUILD_ROOT} referenziert.

Wichtig sind die folgenden Definitionen:

Summary:     <kurze Beschreibung>
%description
<1. Zeile der Beschreibung>
<2. Zeile...>
<...>
<Ende> 

ohne die der RPM eine Fehlermeldung ausgibt. Summary ist eine einzeilige Kurzbeschreibung des Pakets, bei %description folgt dann die ausführliche Beschreibung, die dann erst wieder mit dem nächsten "Befehl" (idR %prep) beendet wird.

2.2 Quellen und Patches

Die Angabe des Quellpakets erfolgt mit Source (s.o.) wobei es auch Source0, Source1 usw. heißen kann.

Die Angabe von Patches (z.B. dem in 1.1 erstellten für das Makefile.in) erfolgt mittels:

Patch: <patchdatei>.dif 

wobei dem Patch wieder Zahlen wie oben folgen können.

Die Quellen und Patches werden im %prep Abschnitt entpackt und für das kompilieren vorbereitet. Bei den Anweisungen dazu handelt es sich einfach um Befehle, die mit der Standardshell (/bin/sh) interpretiert werden. Der Einfachheit halber gibt es auch von RPM vordefinierte Makros, %setup und %patch, die das Auspacken und patchen übernehmen.

%setup erkennt folgende Argumente:

  • -n <name> : für den Namen des Verzeichnisses, in den die Quellen entpackt werden sollen.
  • -c : erstellt und wechselt in das mit -n angegebene Verzeichnis, bevor entpackt wird.
  • -b # : entpackt Source# bevor in das Verzeichnis gewechselt wird.
  • -a # : entpackt Source# nachdem in das Verzeichnis gewechselt wurde.
  • -T : ändert das Verhalten zum Auspacken und wird gebraucht, falls mehr als eine Quelldatei entpackt werden soll.
  • -D : verhindert das Löschen des Verzeichnisses vor dem Auspacken der Quellen, wird also gebraucht, falls mehr als eine %setup Anweisung gebraucht wird und dann auch nur in denen, die nach dem ersten %setup erfolgen.

Die Argumente für %patch lauten:

  • # : führt Patch# aus.
  • -p # : wie im patch(1) Kommando die Anzahl der wegzulassenden Verzeichnisse im Patch (in den obigen Beispielen sollte das -p 1 heißen).
  • -P : ermöglicht das Ausführen anderer Patches als den "Hauptpacth", der in diesem Falle mit "0" bezeichnet wird.
  • man kann das ganze %patch# nennen als Abkürzung für: %patch # -P.

Im Beispiel sieht also alles wie folgt aus:

Source:    %{name}-%{ver}.tar.gz
Patch:     %{name}-%{ver}.dif
Summary:   Hallo Welt
%description
Das wohlbekannte "Hallo Welt"-Programm
fertig gepackt
als rpm.

%prep
%setup
%patch -p1 

2.3 %build Bereich

Im %build Abschnitt stehen die Kommandos für das kompilieren der Quellen. Diese werden, ebenso wie im %prep Abschnitt mit /bin/sh interpretiert. In einem der einfacheren Fälle steht hier:

%build
CFLAGS=${RPM_OPT_FLAGS} ./configure --prefix=%{prefix}
make 

Hier werden dem mitgelieferten configure Skript noch Kompilerflags (Optimierungen) mitgeteilt, die sich u.a. im ~/.rpmrc festlegen lassen. Außerdem wird der Installationspfad festgelegt, was auch mit %{_prefix} erfolgen könnte, wenn es im ~/.rpmmacros geeignet definiert wurde.

2.4 %install Bereich

Hier landen die nötigen Befehle, um die fertig übersetzten Dateien zu installieren und sieht meist so aus:

%install
make DESTDIR=${RPM_BUILD_ROOT} install 

Hierzu ist es notwendig, daß in das Makefile der DESTDIR-Mechanismus eingebaut wurde. Falls das nicht gemacht wurde und beim Installationsabschnitt Fehler auftreten, kann man sich wie folgt behelfen:

%install
make prefix=${RPM_BUILD_ROOT}%{prefix} install 

Wobei unter Umständen noch andere Variablen, z.B. mandir, datadir, local usw. für den Install-Prozeß umdefiniert werden müssen, da man als normaler Benutzer keine Schreibrechte auf z.B. /usr/bin oder /usr/man/man1 hat. Außerdem kann es zu Komplikationen kommen, wenn auf diese Weise Bibliotheken installiert werden, da diese u.U. dann auf ${RPM_BUILD_ROOT}%{prefix} anstatt nur auf %{prefix} verweisen und dann nicht korrekt gefunden werden können.

Weiterhin kann man im %install Abschnitt auch gleich die Manpage(s) packen, da das meist nicht vom Makefile erledigt wird:

gzip -9 ${RPM_BUILD_ROOT}/usr/share/man/*/* 

oder unter Benutzung von %{_mandir}:

gzip -9 ${RPM_BUILD_ROOT}%{_mandir}/*/* 

Ebenso kann man sich auch gleich eine Dateiliste erstellen, die die Dateien und Verzeichnisse enthält, die in das Paket aufgenommen werden sollen. Nützliche Tools hierfür sind find(1) und sed(1) und kann etwa so erfolgen:

cd ${RPM_BUILD_ROOT}
find . -type d | sed '1,2d;s:^\.:\%attr(-,root,root) \%dir :' \
        > ${RPM_BUILD_DIR}/file.list.%{name}
find . -type f | sed 's:^\.:\%attr(-,root,root) :' \
        >> ${RPM_BUILD_DIR}/file.list.%{name}
find . -type l | sed 's:^\.:\%attr(-,root,root) :' \
        >> ${RPM_BUILD_DIR}/file.list.%{name} 

Die erste Anweisung findet alle Verzeichnisse und schreibt ein %dir davor, damit der RPM weiß, daß es sich um ein Verzeichnis handelt. Die zweite und dritte tragen die normalen Dateien und die Links in die Dateiliste ein.

Zusammengenommen sieht der %install Abschnitt also etwa so aus:

%install
make DESTDIR=${RPM_BUILD_ROOT} install
gzip -9 ${RPM_BUILD_ROOT}%{_mandir}/*/*
cd ${RPM_BUILD_ROOT}
find . -type d | sed '1,2d;s:^\.:\%attr(-,root,root) \%dir :' \
        > ${RPM_BUILD_DIR}/file.list.%{name}
find . -type f | sed 's:^\.:\%attr(-,root,root) :' \
        >> ${RPM_BUILD_DIR}/file.list.%{name}
find . -type l | sed 's:^\.:\%attr(-,root,root) :' \
        >> ${RPM_BUILD_DIR}/file.list.%{name} 

2.5 Paketdateien (%files)

Hier steht normalerweise eine Liste von Dateien, die das Paket enthalten soll. Alternativ kann man auch eine Datei angeben, in der diese stehen. Wenn man also eine Dateiliste hat (s. 2.5), dann wird diese eingebunden mit:

%files -f ${RPM_BUILD_DIR}/file.list.%{name} 

Hier muß man eventuell aufpassen, da sich u.U. nicht alle Dateien mit der Distribution vertragen, d.h. schon teilweise vorhanden sind.

Da von ${RPM_BUILD_ROOT} aus alle Unterverzeichnisse als zum Paket zugehörig eingetragen werden, kann es daher Warnmeldungen beim Entfernen des Paketes geben, daß ein Verzeichnis nicht entfernt werden konnte, weil es nicht leer ist. Wenn man Zeit und Lust hat, kann man ja die nicht unbedingt zugehörigen Verzeichnisse mittels find(1) und grep(1) vorher herausfiltern.

2.6 Aufräumen (%clean)

Der letzte Abschnitt, der vorhanden sein sollte heißt %clean und in diesem stehen die Anweisungen, die nach dem Erstellen des Paketes die Überreste beseitigen sollen, die zum Übersetzen und Installieren wichtig waren, jetzt aber nicht mehr gebraucht werden.

Hier würde das so aussehen:

%clean
rm -rf ${RPM_BUILD_DIR}/%{name}-%{ver}
rm -rf ${RPM_BUILD_DIR}/file.list.%{name}
rm -rf ${RPM_BUILD_ROOT} 

3 Paket fertig stellen

Wenn die Quellen und das .spec-File fertig bearbeitet wurden, kann man einen ersten Versuch starten mit:

bash> rpm -ba <specfile> 

z.B. also

bash> rpm -ba hallo_welt.spec 

Mit diesem Befehl werden

  1. die Quellen und das .spec zusammen in ein .srpm gepackt (und in /home/user/packages/srpms/ abgelegt) sowie
  2. die Quellen ausgepackt, übersetzt, installiert und zu einem .rpm gepackt (und in /home/user/packages/rpms/<arch>/ [1] abgelegt).

Das .srpm erlaubt eine erneute übersetzung mittels

bash> rpm --rebuild <paket>.srpm 

Falls man dennoch der Meinung ist, daß man es nicht braucht, kann man das -ba durch ein -bb ersetzen. In diesem Falle wird nur das .rpm erstellt.

[1] <arch> steht für den Prozessortyp, für den das Paket übersetzt wurde, also z.B. i386, i486, i586, i686, sparc... Mittels buildarchtranslate im rpmrc kann man den Wert für <arch> überschreiben.

4 Links

Die folgenden Links sollten bei noch ausstehenden Fragen Hilfe leisten können und etwas ausführlicher in das Geheimnis des RPM einführen:

Sollte das bis hierher nicht ausreichen, dann kannst du dich auch persönlich per Elektropost an mich wenden. Das gleiche gilt, falls Verbesserungs- bzw. Erweiterungsvorschläge anliegen sollten.


Index Musik Reisen Computer Links Kontakt