Le besoin se résume ainsi:

Pouvoir construire un paquet RPM.

Pour plus d'information sur les paquets RPM, voir ce billet.

Introduction

Un paquet RPM est construit à partir d'un fichier SPEC. Ce fichier génère, en plus du RPM, un paquet SRPM (Source RPM). Ce paquet SRPM est très important car il permet en l'installant de récupérer le fichier SPEC et les sources du programme ayant servis pour la construction du RPM associé.

Le fichier SPEC décrit toutes les informations relatives au paquet RPM, comme son nom, sa version, sa description, sa licence, etc. Il indique aussi et surtout le déroulement des étapes pour arriver à constituer le paquet RPM. S'il s'agit par exemple de constituer un paquet fournissant un programme compilé à partir de sources, il faudra indiquer l'archive contenant les sources, les éventuels patchs à y appliquer, et les fameuses étapes liées à la compilation des sources: configuration, compilation, installation. Enfin le fichier SPEC se termine par la liste des fichiers qu'il doit empaqueter et par un journal contenant l'historique des évolutions apportées au paquet (le changelog).

Le fichier SPEC pour plus de clarté est divisé en sections:

  1. Une entête contenant les informations sur le paquet (nom, version, description, licence, groupe etc)
  2. Une section préparant les sources
  3. Une section concernant la compilation
  4. Une section pour l'installation des fichiers
  5. Une section de nettoyage
  6. Une ou plusieurs sections contenant des scripts à exécuter
  7. Une section listant les fichiers que le paquet doit contenir
  8. Une section contenant le changelog

Dans ce billet nous allons prendre l'exemple de la construction d'un paquet RPM contenant un plugin pour le logiciel Pidgin. Il s'agit du plugin msn-pecan.

Nous allons maintenant voir les outils nécessaires pour la construction de paquets RPM et ensuite les différentes sections du fichier SPEC dans le détail.

Les outils nécessaires

Sur une distribution RedHat/Fedora, il existe deux paquets importants pour la construction de RPM:

  • rpmdevtools
  • rpm-build

Le paquet rpmdevtools contient des outils en rapport avec l'environnement de construction, alors que rpm-build contient la commande "rpmbuild" qui sert à construire les paquets RPM. De plus rpmdevtools contient des squelettes de fichiers SPEC dans le répertoire /etc/rpmdevtools.

A noter que si vous utilisez vim, celui-ci est capable de proposer directement un squelette minimal quand vous éditez pour la première fois un fichier .spec.

Pour installer ces paquets:

[root@kighafarz] # yum install rpmdevtools rpm-build

Il faut maintenant constituer un environnement de construction, avec notre utilisateur il suffit pour cela de taper la commande:

[edouard@kighafarz] $ rpmdev-setuptree

Ce qui crée une arborescence dans le répertoire ~/rpmbuild. Les répertoires ainsi créés sont les suivants:

  • BUILD qui sert pour la construction
  • SOURCES qui contient les archives des sources et les éventuels patchs
  • SPECS qui contient les fichiers SPEC
  • RPMS qui contient les paquets RPMs construits
  • SRPMS qui contient les paquets SRPMS

L'entête du fichier SPEC

Dans cette entête vont être définit certaines variables afin de renseigner toutes les informations relatives au paquet RPM à construire.

Pour notre exemple, nous voulons donc "packager" un plugin pour Pidgin. Ce plugin repose sur la libpurple qui est une bibliothèque utilisée par Pidgin, et pour respecter les règles de nommage des plugins déjà existant, le nom de notre paquet devra s'appeler "purple-msn-pecan". Ce qui impose dans la bonne pratique que notre fichier SPEC soit purple-msn-pecan.spec.

Pour constituer ce fichier SPEC, le fichier /etc/rpmdevtools/spectemplate-minimal.spec sert de base.

[edouard@kighafarz] $ cp /etc/rpmdevtools/spectemplate-minimal.spec ~/rpmbuild/SPEC/purple-msn-pecan.spec

Après avoir récupérer les dernières sources sur le site officiel, en version 0.0.17, voici à quoi l'entête de notre fichier SPEC ressemble:

Name:           purple-msn-pecan
Version:        0.0.17
Release:        1%{?dist}
Summary:        Alternative MSN protocol plugin for libpurple
Group:          Applications/Internet
License:        GPLv2+
URL:            http://code.google.com/p/msn-pecan/
Source0:        http://msn-pecan.googlecode.com/files/msn-pecan-0.0.17.tar.bz2
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

Comme nous pouvons déjà le constater, le champ Name que nous avons renseigné peut être ensuite accessible via la variable %{name}. Il s'agit en fait de macro que nous pouvons aussi définir avec la commande %define. Nous allons donc modifier cette entête pour en tirer partie, en créant une nouvelle variable "realname":

%define         realname msn-pecan
Name:           purple-%{realname}
Version:        0.0.17
Release:        1%{?dist}
Summary:        Alternative MSN protocol plugin for libpurple
Group:          Applications/Internet
License:        GPLv2+
URL:            http://code.google.com/p/msn-pecan/
Source0:        http://msn-pecan.googlecode.com/files/%{realname}-%{version}.tar.bz2
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

Ceci simplifiera les futures modifications, particulierement lors des changements de version du programme.

Il faut aussi renseigner les variables "BuildRequires" et "Requires" afin de préciser sur quoi repose notre programme pour se construire, mais aussi pour plus tard fonctionner. Il n'est, par exemple, pas nécessaire d'avoir un compilateur pour faire fonctionner un programme déjà compilé. Cette distinction permet d'avoir une liste de dépendances qui ne sont nécessaires qu'à la constitution de programme (compilateur, entêtes de bibliothèques, outils divers) et une liste de dépendances qui elles ne sont uniquement nécessaires au fonctionnement du programme final (bibliothèques partagées, programmes tiers, etc).

Notre plugin, comme signalé plus haut, utilise la bibliothèque purple. Il faut donc le paquet libpurple-devel pour la compilation et libpurple pour l'exécution.

BuildRequires:  libpurple-devel
Requires:       libpurple

L'entête contient ensuite une section de description afin d'expliquer plus longuement ce que propose notre paquet. Voici son contenu pour notre exemple:

%description
The project aims to have a much faster development process, low barrier for
contributions, and close contact with the user-base.
Features include:
* Support for personal messages
* Server-side storage for display names (private alias)
* Partial direct connection support
* Improved network IO
* Improved error handling
* Network issues tested with netem
* GObject usage

Section préparant les sources

Cette étape consiste principalement à décompresser l'archive contenant les sources du programme. Cette décompression d'archive est gérée par la macro %setup qui ira récupérer automatiquement le nom de l'archive à l'aide de la variable %{source0} définie dans l'entête. L'archive doit être présente dans le répertoire SOURCES et sera décompressée dans le repertoire BUILD/<nom du paquet>.

Pour notre paquet, voici à quoi ressemble cette section:

%prep
%setup -q -n %{realname}-%{version}

L'option -q permet d'effectuer cette tâche en "silence", c'est à dire sans rien afficher à l'écran. L'option -n précise le nom du répertoire contenu dans l'archive car après la décompression %setup se place dans ce repertoire. Pour notre paquet, %setup aurait utilisé la variable %{name}-%{version}, ici purple-msn-pecan-0.0.17, alors que l'archive des sources contient un répertoire msn-pecan-0.0.17, d'où l'utilisation de l'option -n.

Il est aussi possible dans cette section %prep d'appliquer des patchs aux sources du programme en vue d'éventuelles corrections. La macro est %patch0 pour la variable Patch0, %patch1 pour Patch1, etc.

A ce moment précis de la construction, le programme de construction se trouve donc dans le répertoire "~/rpmbuild/BUILD/purple-msn-pecan-0.0.17/msn-pecan-0.0.17".

Nous pouvons donc imaginer que ces deux lignes dans cette section sont remplacées par les macros de rpmbuild par les commandes suivantes:

cd %{_topdir}/BUILD
mkdir %{name}-%{version}
cd %{name}-%{version}
tar jxf %{_topdir}/SOURCES/%{source0}
cd %{realname}-%{version}

soit en réalité:

cd ~/rpmbuild/BUILD
mkdir purple-msn-pecan-0.0.17
cd purple-msn-pecan-0.0.17
tar jxf ~/rpmbuild/SOURCES/msn-pecan-0.0.17.tar.bz2
cd msn-pecan-0.0.17

Section concernant la compilation

Cette section contient les étapes de configuration des sources et de compilation. Elle est donc vide ou inutilisée quand il s'agit de programmes non compilés comme des scripts par exemple.

Pour notre paquet, voici son contenu:

%build
make

Bien souvent, le script ./configure est appelé avant la compilation, mais pour notre paquet ce n'est pas la peine. Le script ./configure est de plus remplacé par une macro %configure afin de passer certaines options spécifique à l'environnement de construction des RPMs.

Le programme de construction se trouve toujours à ce moment précis dans le répertoire "~/rpmbuild/BUILD/purple-msn-pecan-0.0.17/msn-pecan-0.0.17".

Section pour l'installation des fichiers

Il s'agit ici de déplacer les fichiers issues des étapes précédentes dans leurs répertoires de destination. Ce sont ces fichiers qui constituent notre programme à distribuer sous forme de paquet RPM. Pour plus de sécurité, il n'est pas envisageable d'installer ces fichiers réellement sur le système lors de la construction du RPM. D'une part parce que ceci pourrait rendre le système instable, et d'autre part parce que notre utilisateur n'en a pas forcement les droits. Pour palier à ce problème un répertoire temporaire est créé lors de la construction d'un paquet RPM. Ce répertoire sert de racine simulant le système de fichier futur dans lequel seront placés les fichiers du paquet. Ce répertoire est accessible via la variable $RPM_BUILD_ROOT.

Pour notre plugin, la compilation exécutée dans la section précédente a générée un fichier libmsn-pecan.so. Ce fichier devra se situer, une fois le paquet RPM installé, dans le répertoire /usr/lib/purple-2/. Voici donc notre section d'installation des fichiers:

%install
rm -rf $RPM_BUILD_ROOT
install -Dp -m 755 libmsn-pecan.so $RPM_BUILD_ROOT/%{_libdir}/purple-2/libmsn-pecan.so

Section de nettoyage

Cette section permet de nettoyer le répertoire temporaire, et se résume souvent aux lignes suivantes:

%clean
rm -rf $RPM_BUILD_ROOT

Il est vivement conseillé de nettoyer le répertoire en cas de nouvel tentative de construction du paquet RPM.

Section des scripts à exécuter

Comme nous l'avons vu dans le billet concernant les paquets RPMs, il est possible et des fois obligatoire, d’exécuter des scripts lors de la manipulation du RPM. Par exemple pour créer un utilisateur dédié au programme, etc. Ces petits bouts de scripts (ou scriptlets) peuvent être écrits en différents langage mais bien souvent il s'agit de shell script en Bash.

Les sections pouvant contenir ces scripts sont:

  • %pre: avant l'installation, avant de décompresser les fichiers du RPM
  • %preun: avant la désinstallation, avant de supprimer les fichiers du RPM
  • %post: après l'installation, les fichiers du RPMs sont déjà déployés sur le systèmes
  • %postun: après la désinstallation, les fichiers du RPMs ne sont plus sur le système.

Section listant les fichiers

Cette section a pour objectif de lister les fichiers qui sont présents dans le RPM. Chaque fichier est précisés avec son répertoire complet, indiquant ainsi l'emplacement final une fois le RPM installé. C'est cette liste qui permet avec la commande "rpm -qpl <fichier.rpm>" d'afficher les fichiers contenu dans un paquet. Et inversement, grâce à cette liste, nous pouvons retrouver à quel RPM appartient un fichier avec la commande "rpm -qf /chemin/fichier".

Cette liste sert aussi à vérifier qu'il n'y aura pas de conflit de fichiers lors de l'installation du RPM. Elle servira aussi lors de la désinstallation du RPM pour savoir quels fichiers supprimer.

Voici à quoi ressemble notre section %files pour notre paquet purple-msn-pecan:

%files
%defattr(-, root, root, -)
%{_libdir}/purple-2/libmsn-pecan.so

La macro %defattr permet de préciser les attributs de nos fichiers, comme les permissions et propriétaires.

La macro %{_libdir} sera remplacée par le chemin habituellement utilisé pour stocker les bibliothèques (normalement /usr/lib).

Mais cette section %files peut être plus précise, en incluant par exemple la documentation fournit avec les sources du programme:

%files
%defattr(-, root, root, -)
%{_libdir}/purple-2/libmsn-pecan.so
%doc AUTHORS COPYING TODO README COPYRIGHT ChangeLog

Ces fichiers de documentation seront ainsi déployés dans /usr/share/doc/<programme>-<version>.

Il existe d'autres macros comme %config indiquant qu'il s'agit de fichier de configuration, pour préciser si on doit les garder en cas de suppression ou mise à jour du RPM, ou encore %dir pour indiquer un répertoire.

Section contenant le changelog

Cette section contient le journal des modifications effectuées sur le fichier SPEC, aussi appelé changelog. Il ne s'agit pas du changelog du programme à empaqueter (précisé dans notre section %files d'ailleurs), donc à ne pas confondre.

A chaque modification dans le fichier SPEC, il faut incrémenter la variable Release dans l'entête et ecrire une brève description de ce changement dans le changelog.

Voici à quoi ressemble le changelog pour notre paquet RPM:

%changelog
* Sat Jan 31 2009 Edouard - 0.0.17-1
- version initiale

La construction du RPM

Enfin, notre fichier SPEC est complet! Les sources du programme (archive msn-pecan-0.0.17.tar.bz2) sont placées dans le répertoire ~/rpmbuild/SOURCES et notre fichier spec dans ~/rpmbuild/SPECS. La construction, à l'aide de rpmbuild, s'exécute avec la commande suivante:

[edouard@kighafarz] $ rpmbuild -ba ~/rpmbuild/SPECS/purple-msn-pecan.spec

Logiquement, à la fin du processus de construction, si tout se passe bien, un paquet RPM purple-msn-pecan-0.0.17-1.fc10.x86_64.rpm sera présent dans ~/rpmbuild/RPMS/x86_64, ainsi que la version Source RPM dans ~/rpmbuild/SRPMS.

La construction fera l'objet d'autres billets, avec surtout l'utilisation de l'outil Mock.