HowTo RPM Paket Erstellung

1 Vorbereitung

Um ein RPM Paket als normaler Benutzer erstellen zu können, sind ein paar kleinere Vorbereitungen im home-Verzeichnis des Benutzers nötig. Hierzu zählt das Erstellen das erstellen des Build-Root Verzeichnisses und das Abändern der Konfigurationsdateien für den RedHat Package Manager.

1.1 Verzeichnisse

Damit der RedHat Package Manager weiß, wo sich alle Quelldateien und späteren RPMs befinden sollen, empfiehlt es sich eine analoge Build-Root Verzeichnisstruktur wie von CentOS/RHEL an zu legen.

Beispiel:
cd ~/
mkdir -p ~/rpmbuild/BUILD
mkdir -p ~/rpmbuild/RPMS
mkdir -p ~/rpmbuild/SOURCES
mkdir -p ~/rpmbuild/SPECS
mkdir -p ~/rpmbuild/SRPMS


1.2 Konfiguration des RedHat Package Manager

Der RedHat Package Manager wird über zwei Dateien gesteuert bzw. konfiguriert /usr/lib/rpm/macros und /usr/lib/rpm/redhat/rpmrc. Als normaler Benutzer kann man sich diese in das eigene Build-Root Verzeichnis kopieren.

Beispiel:
cd ~/
cp /usr/lib/rpm/macros ~/.rpmmacros
cp /usr/lib/rpm/redhat/rpmrc ~/.rpmrc
In ~/.rpmmacros


Jetzt müssen die richtigen Verzeichnisse für den Benutzer eingetragen werden. Hierfür müssen die relevanten Makro-Defintionen wie folgt abgeändert bzw. neu eingetragen werden.

Beispiel:
%_home  /home/„user“
%_topdir  %{_home}/rpmbuild
%_builddir  %{_topdir}/BUILD
 %_rpmdir %{_topdir}/RPMS
%_sourcedir %{_topdir}/SOURCES
%_specdir %{_topdir}/SPECS
%_srcrpmdir %{_topdir}/SRPMS


Alternativ:
su - „user“
cp -r /usr/src/redhat/ ~/rpmbuild
echo '%_topdir %(echo $HOME)/rpmbuild' >> ~/.rpmmacros


2 Die Quellen

Die Quelldateien „source-paket“.tar.gz müssen sich im Verzeichnis ~/rpmbuild/SOURCES befinden, um vom Redhat Package Manager mit obiger Konfiguration gefunden zu werden.

2.1 Erster Test

Um sicherzugehen, dass das Paket kompiliert werden kann, empfiehlt es sich zuvor einmalig dies durch den 2Satz (configure, make) zu überprüfen.

Beispiel:
mkdir -p ~/tmp
tar -C ~/tmp -xvfz ~/rpmbuild/sources/„paket“.tar.gz
cd ~/tmp/„paket“
./configure
make

2.2 Makefile (.in,.am)

Wenn während des Übersetzungstestes keine Probleme aufgetreten sind, kann sich jetzt dem Makefile gewidmet werden. Hierbei gilt es festzustellen, ob der DESTDIR-Mechanismus unterstützt wird. Dieser dient zur Verifikation, ob das Paket und alle zugehörigen Dateien in ein beliebiges Verzeichnis installierbar sind. Dies ist unbedingt notwendig, um die Dateien als normaler Benutzer installieren zu können und hinterher vom Redhat Package Manager in ein RPM-Paket packen zu lassen.
Überprüfe mit dem Texteditor das Makefile, welches durch den Befehl ./configure erzeugt wurde auf die folgenden Einträge:
  • 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 und erstellt einen Patch
  • man modifiziert das „name“.spec
Obwohl die zweite Variante wahrscheinlich in den meisten Fällen die leichtere sein wird, beschreibe ich an dieser Stelle die erste Variante ausführlicher. In manchen Quell-Verzeichnissen findet sich eine Datei namens Makefile.am. Diese Datei wird von automake verwendet, um alle nötigen Dateien zu erstellen, wie z.B. das configure Skript oder das Makefile.in. Falls das Programm nicht aus einer CVS-Quelle installiert wird, denn hier ist der automake Prozess üblich, kann sich der Datei Makefile.in gewidmet werden.
Die Datei Makefile.in wird von configure verwendet, um das eigentliche Makefile zu erzeugen. Deshalb empfiehlt es sich, das Makefile.in so umzugestalten, dass der DESTDIR-Mechanismus problemlos funktioniert. Dazu gehe folgendermaßen vor:
lösche das Verzeichnis der zuvor entpackten Quellen und packe sie erneut aus (ohne ./configure und make erneut auszuführen)
  • rm -rf ~/tmp/„paket“
  • tar -C ~/tmp -xvfz ~/rpmbuild/sources/„paket“.tar.gz
  • cd ~/tmp/„paket“
  • sichere die Datei Makefile.in in der Originalform: cp Makefile.in Makefile.in.orig
  • editiere die Datei Makefile.in wie zuvor beschrieben
  • erzeuge eine .patch Datei im Bezug auf die Originalquellen und kopiere diese in das ~/rpmbuild/SOURCES Verzeichnis
  • gendiff ~/tmp/„paket“.orig  ~/rpmbuild/SOURCES/„paket“.patch
  • alternativ
  • diff -uNr Makefile.in.orig Makefile.in >> ~/rpmbuild/SOURCES/Makefile.patch

3 Das „paket“.spec File

Das „paket“.spec File informiert den RedHat Package Manager wie das Programm zu kompilieren, zu installieren und im Anschluss die Dateien in das RPM-Paket zu packen sind. Manche Entwickler liefern in ihrem Quellenpaket ein „paket“.spec File mit, welches in der Regel nach ein paar kleineren Anpassungen an die Distribution verwendet werden kann. Da dies nicht immer der Fall ist, gehe ich davon aus, dass das „paket“.spec File selbst geschrieben werden muss.
Besondere Bereiche im „paket“.spec File fangen mit einem „%“ an. Die wichtigsten heißen:
  • define
  • description
  • prep
  • setup
  • patch
  • build
  • install
  • files
  • clean

3.1 Definitionen

Definitionen dienen der Übersicht und einfachen Handhabung des .spec Files. Im günstigsten Fall reicht eine Änderung der Definitionen und man kann es für ein weiteres Programm wieder verwenden.

3.2 Angaben zum Paket

An dieser Stelle wird vom RedHat Package Manager abgefragt, welchen Namen das Paket bekommt, welche Version etc. An dieser Stelle können die obigen Definitionen sehr gut als Referenzierung dienen.

Beispiel:
%define name ntp
%define pkg ntpd
%define ver 4.2.4p7
%define release 21.el5.maj
%define nsusr ntp
%define nsgrp ntp
%define nnmmsg logger -t %{name}/rpm
%define mibdir %{_datadir}/mibsSummary:          System time synchronization using the
Network Time Protocol (NTP)
Name:             %{name}
Version:          %{ver}
Release:          %{release}
License:         
distributableBuildroot:        %{_tmppath}/%{name}-root
Provides:         ntpserver ntpclient sntpclient
Requires(pre):    /usr/sbin/groupadd /usr/sbin/useradd
Requires(pre):    /bin/awk sed grep
Requires(post):   /sbin/chkconfig
Requires(preun):  /sbin/chkconfig
Requires(preun):  /sbin/service
Requires(postun): /sbin/service
Requires:         libcap
Requires:         ntpdate = %{version}-%{release}
%{?with_avahi:BuildRequires:    avahi-compat-libdns_sd-devel}
BuildRequires:    perl-HTML-Parser
BuildRequires:    libcap-devel
BuildRequires:    readline-devel
BuildRequires:    ncurses-devel
BuildRequires:    openssl-devel
BuildRequires:    lm_sensors-devel
BuildRequires:    kernel-headers
Obsoletes:        xntp3 ntpstat
%description
The Network Time Protocol (NTP) is used to synchronize a computer's time with another reference time source. This package contains utilities and daemons to synchronize the computer's time to Coordinated Universal Time (UTC) via the NTP protocol and other NTP servers. It includes ntpdate (a program for retrieving the date and time from remote machines via a network) and ntpd (a daemon which continuously adjusts system time).

Die letzte Definition beschreibt, in welchem Verzeichnis das Programm installiert werden sollen. Dieses Verzeichnis wird später mit ${RPM_BUILD_ROOT} referenziert. Ohne einer Summary Angabe gibt der RedHat Package Manager eine Fehlermeldung aus. Die Summary Definition ist eine einzeilige Kurzbeschreibung des eigentlichen Pakets, in der
%description Definition erfolgt die ausführliche Beschreibung. Diese wird mit dem
nächsten Abschnitt meistens %prep beendet.

3.3 Quellen und Patches

Die Angabe des Quellpackets erfolgt über die Definition Source. Es können mehrere Quelldateien referenziert werden.

Beispiel:
Source0: %{name}-%{ver}.tar.gz
Source1: %{name}-refclock
Source2: %{name}.init

Die Angabe von Patches erfolgt via der Definition Patch, auch hier gilt es können mehrere Dateien referenziert werden.

Beispiel:
Patch0: Makefile.ini.patch
Patch1: %{name}.sysconfig

Die Quellen- und Patch-Dateien werden im Abschnitt %prep entpackt und für das Kompilieren vorbereitet. Bei diesen Anweisungen handelt es sich um Befehle, welche mit der Standardshell (/bin/sh) interpretiert werden. Zur Vereinfachung stellt der Redhat Package Manager vordefinierte Makros hierfür bereit %setup und %patch.
Eine kleine Übersicht der durch das Macro %setup verfügbaren Argumente, mehr Details via dem The %setup and %patch Macro.

3.4 Der %build Abschnitt

Im Abschnitt %build werden die Befehle für das Kompilieren der Quellen definiert. Ebenso wie im Abschnitt %prep werden diese Befehle durch die Shell /bin/sh interpretiert. In diesem Abschnitt können dem mitgelieferten configure Skript noch Kompilerflags (Optimierungen) mitgeteilt werden. Ebenfalls wird der Installationspfad hier festgelegt, was auch mit %{_prefix} erfolgen könnte, wenn es im ~/.rpmmacros geeignet definiert wurde.

3.5 Der %install Abschnitt

Dieser Abschnitt beinhaltet alle Befehle, um die zuvor durch den Abschnitt %build übersetzten Dateien zu erfolgreich zu installieren. Hierzu ist es notwendig, dass der DESTDIR-Mechanismus in das Makefile.in eingebaut wurde.

Beispiel:
%install
make DESTDIR=${RPM_BUILD_ROOT} install


Sollte der DESTDIR-Mechanismus im Makefile.in nicht eingebaut sein, kann dies alternativ an dieser Stelle durch eine feste Zuweisung erfolgen.

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


Zu beachten ist, dass unter Umständen weitere Variablen, wie z.B. mandir, datadir für den Installationsprozess umdefiniert werden müssen. Ebenfalls ist zu beachten, dass es zu Komplikationen bei der Bibliotheken Zuweisung kommen kann auf Grund der Verknüpfung von %{prefix} nach ${RPM_BUILD_ROOT}%{prefix}. Des Weiteren empfiehlt es sich innerhalb des Abschnittes %install die Manpages zu entpacken und innerhalb des späteren Dateisystemes zu installieren.

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

Unter zur Hilfenahme der Tools find und sed kann an dieser Stelle eine Fileliste für den Abschnitt %files erstellt werden.

Beispiel:
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}


3.6 Der %files Abschnitt

Dieser Abschnitt beinhaltet eine Liste von Dateien und entsprechende User- und Rechtezuweisungen, welche das Paket enthalten soll.

Soll nicht jeder einzelne Datei oder Gruppe aufgeführt werden, kann alternativ auch eine Dateiliste angegeben werden. Es sollte die Dateiliste zuvor angepasst werden. Da es sein kann das Dateien teilweise schon vorhanden sind, denn von ${RPM_BUILD_ROOT} ausgehend werden alle Unterverzeichnisse als zum Paket zugehörig eingetragen. Dies kann beim Entfernen des Paketes zu nicht gewünschten Warnmeldungen führen.

3.7 Der %clean Abschnitt

Der Abschnitt %clean hat die Aufgabe das ${RPM_BUILD_ROOT} Verzeichnis nach dem Erstellen, Übersetzen und Installieren des Paketes wieder zu bereinigen.
%clean rm -rf ${RPM_BUILD_DIR}/%{name}-%{ver} rm -rf ${RPM_BUILD_DIR}/file.list.%{name} rm -rf ${RPM_BUILD_ROOT}

4 Das RPM-Paket fertig stellen

Wenn die Quellen und das „name“.spec File erstellt wurden, kann mit einem ersten Versuch gestartet werden.
rpmbuild -ba -vv „specfile“ | tee ~/rpmbuild.out

Mit dem gerade aufgezeigten Befehl werden folgende Aktionen ausgelöst:
Die Quellen und das „name“.spec File durch cipio gepackt und im Verzeichnis ~/rpmbuild/SRPMS archiviert. Diese Datei kann für ein späteres rebuilden verwendet werden.
Die Quellen ausgepackt, übersetzt und das Paket gebildet und im Verzeichnis ~/rpmbuild/RPMS verschoben.
Zusätzlich ist es möglich durch die Option -vv detaillierte Informationen während der RPM Erstellung angezeigt zu bekommen und der Output wird im User-Homeverzeichnis in eine Datei umgeleitet.

5 weiterführende Links

Fedora RPM-Guide
Howto create an RPM package
Fedora Mock Project