This post exists also in english

Ce billet fait partie d'une série sur comment déployer une application XULRunner. Voir le préambule pour le contexte.

2 - Linux

2.4 Empaqueter un rpm

2.4.1 Préalable

Dans cette partie nous allons créer un paquet rpm, pour les distributions basée sur rpm, comme Red Hat, Fedora, Suse,... (j'ai testé uniquement sur Fedora, merci de me faire savoir si vous rencontrez des problèmes sur les autres).

Nous pouvons créer ce rpm aussi bien depuis d'autres distributions Linux, basées sur Debian par exemple (c'est ce que je fais), nous avons juste besoin de l'executable rpmbuild, et pour celà il suffit d'installer rpm :

apt-get install rpm

Quelques liens de documentation :

Je suppose que nous n'avons pas besoin de compilation pour notre appli, mais si ce n'est pas le cas, compiler simplement avant l'empaquètement (malgré le fait que ce soit déconseillé dans ce guide).

2.4.2 Fichiers RPM

pour créer un rpm, nous sommes supposé paramétrer une configuration utilisateur pour la création de tout rpm. Mais il est possible de faire celà dans un dossier local, c'est ce que nous allons faire.

Nous devons créer les dossiers suivants, et un fichier myapp.spec :

    |- samples_chapter_2/
|- myapp/
|- rpmbuild/
|- BUILD/
|- RPMS/
|- SOURCES/
|- SPECS/
|- SRPMS/
|- myapp.spec

En fait c'est la même structure que pour un environnement général de création rpm, mais local.

Le fichier .spec est le 'script' utilisé par rpmbuild pour créer le rpm. Il est supposé utiliser une archive source au format tar.gz, que nous allons créer spécialement pour lui plus tard.

Voici le contenu de notre spec :

Name:           myapp
Version: 1.0
Release: 1%{?dist}
Summary: simple Hello World powered by XULRunner.

Group: Development/Tools
License: MPLv1.1 or GPLv2+ or LGPLv2+
URL: http://example.com/myapp/
Source: myapp-1.0.tar.gz
BuildArch: noarch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id} -un)

#BuildRequires:
Requires: firefox >= 3.6

%description
simple Hello World
Powered by XULRunner.

%prep
%setup

%build

%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_libdir}
cp -R myapp %{buildroot}%{_libdir}/
mkdir -p %{buildroot}%{_bindir}

ln -s %{_libdir}/myapp/myapp.sh %{buildroot}%{_bindir}/myapp

mkdir -p %{buildroot}%{_datadir}/applications
cp data/myapp.desktop %{buildroot}%{_datadir}/applications/

mkdir -p %{buildroot}%{_datadir}/icons/hicolor/16x16/apps
cp data/icons/icon16.png \
%{buildroot}%{_datadir}/icons/hicolor/16x16/apps/myapp.png

mkdir -p %{buildroot}%{_datadir}/icons/hicolor/22x22/apps
cp data/icons/icon22.png \
%{buildroot}%{_datadir}/icons/hicolor/22x22/apps/myapp.png

mkdir -p %{buildroot}%{_datadir}/icons/hicolor/32x32/apps
cp data/icons/icon32.png \
%{buildroot}%{_datadir}/icons/hicolor/32x32/apps/myapp.png

mkdir -p %{buildroot}%{_datadir}/icons/hicolor/48x48/apps
cp data/icons/icon48.png \
%{buildroot}%{_datadir}/icons/hicolor/48x48/apps/myapp.png

mkdir -p %{buildroot}%{_datadir}/icons/hicolor/scalable/apps
cp data/icons/icon48.svg \
%{buildroot}%{_datadir}/icons/hicolor/scalable/apps/myapp.svg

%post
touch --no-create %{_datadir}/icons/hicolor &>/dev/null || :
if [ -x %{_bindir}/gtk-update-icon-cache ]; then
%{_bindir}/gtk-update-icon-cache -q %{_datadir}/icons/hicolor;
fi

%postun
touch --no-create %{_datadir}/icons/hicolor &>/dev/null || :
if [ -x %{_bindir}/gtk-update-icon-cache ]; then
%{_bindir}/gtk-update-icon-cache -q %{_datadir}/icons/hicolor;
fi


%clean
rm -rf $RPM_BUILD_ROOT


%files
%defattr(-,root,root,-)
%{_bindir}/myapp
%{_libdir}/myapp
%{_datadir}/applications/myapp.desktop
%{_datadir}/icons/hicolor/16x16/apps/myapp.png
%{_datadir}/icons/hicolor/22x22/apps/myapp.png
%{_datadir}/icons/hicolor/32x32/apps/myapp.png
%{_datadir}/icons/hicolor/48x48/apps/myapp.png
%{_datadir}/icons/hicolor/scalable/apps/myapp.svg
#%doc

%changelog
* Fri Oct 8 2010 John Doe <johndoe@example.com> 1.0-1
- first publication

Voyons en détail certaines entrées de ce fichier :

Name ne devrait contenir aucun caractère blanc.

Version doit contenir uniquement des chiffres (et des points), pas de chaîne comme "beta".

L'entrée Group doit être choisie dans la liste du fichier /usr/share/doc/rpm/GROUPS . Vous devez l'avoir sur votre ordinateur avec l'installation de rpm.

Release est la version du rpm pour cette version de l'appli, et %{?dist} sera remplacé par rpmbuild par la distribution courante. Celà ne sera pas complété sur une distribution non rpm (comme Debian), vous pouvez le supprimer si vous le désirez.

Pour l'entrée License, vous devez choisir un mot clé court (short keyword) dans cette liste.

L'entrée Source doit correspondre exactement au tar.gz que nous créerons plus tard.

La ligne BuildArch: noarch spécifie que notre appli n'est pas spécifique à l'architecture. Si votre appli contient du code compilé, vous devez supprimer cette ligne, et un rpm spécifique à l'architecture courante sera construit.

La valeur de BuildRoot est une valeur habituelle.

BuildRequires est commenté (avec un #), comme nous n'avons pas de compilation.

Enfin, l'entrée Requires contient les autres paquets qui sont nécessaires au fonctionnement de notre appli. Vous pourriez en ajouter d'autres, séparés par des virgules (,). Notez que vous ne pouvez pas déclarer de dépendances alternatives, comme pour un deb. C'est pourquoi nous déclarons Firefox et non XULRunner comme dépendance, dans le cas éventuel où le Firefox de la distribution n'est pas basée sur XULRunner.

Par la suite nous copierons notre archive tar.gz dans le dossier SOURCES.
Puis la section %prep et l'instruction %setup la décompresseront dans le dossier BUILD.
La section %build est vide, parce que nous n'avons pas de compilation à faire.

Dans la section %install, nous installons notre appli.
Ce sont des commandes shell, le dossier courant de travail est la source tar.gz non compressée. %{buildroot} est la racine de l'installation local de notre appli, ce sera la racine de l'ordinateur / lors de l'installation réelle.
%{_libdir}, %{_bindir}, et %{_datadir} correspondent respectivement à /usr/lib/, /usr/bin/, et /usr/share/. Je suppose que c'est le cas pour les principales distributions basées sur rpm, si je me trompe, merci de me corriger, car il faudrait alors redéfinir certaines variables.
Donc dans cette section, nous recréons le dossier initial buildroot. Puis nous créons le lien vers notre lanceur dans /usr/bin/, copions notre appli dans /usr/lib/, et copions notre fichier desktop et nos icônes aux bons emplacements dans /usr/share/.

les sections %post et %postun déclarent quelques actions après que notre appli sera installée ou désinstallée. Nous mettons à jour la base des icônes pour le gestionnaire de fenêtre.
Si l'appli prend en charge certains types mime, i.e. peut ouvrir certains type de fichier, celà doit être spécifier dans le fichier .desktop . Et vous devez ajouter le code suivant dans ces sections %post et %postun :

if [ -x /usr/bin/update-desktop-database ]; then
update-desktop-database &> /dev/null || :
fi

La section %files est obligatoire, et contient la liste complète des fichiers/dossiers installés par notre appli.

Enfin, le format de la section %changelog, est décrite dans cette doc.

2.4.3 Créer le rpm

Maintenant que nous avons tout ce dont nous avons besoin, nous allons créer un script pour créer une "fausse" archive source pour rpmbuild, qui contiendra notre appli, notre fichier desktop, et nos icônes. Nous placerons cette archive dans le dossiers SOURCES, puis invoquerons rpmbuild localement avec notre fichier spec. Et en fait, nous ferons tout celà dans un dossier temporaire, pour laisser nos sources propres.

Note: rmpbuild semble bugger si il y a des espaces dans le chemin du dossier de travail, donc faites attention avec le nom des (sous)dossiers où vous placez les sources.
Le script suivant s'arrêtera si c'est le cas.

Voilà ce script :

#!/bin/bash

# exit the script on errors
set -e

TMP_DIR=./tmp
SRC_VERSION=1.0

# get the absolute path of our TMP_DIR folder
TMP_DIR=$(readlink -f -- "$TMP_DIR")

# re-create the TMP_DIR folder
rm -rfv "$TMP_DIR"
mkdir -v "$TMP_DIR"

# copy the rmpbuild tree folder
cp -rv rpmbuild/* "$TMP_DIR/"

# create a source tar.gz archive for rpmbuild into SOURCES
mkdir -v "$TMP_DIR/myapp-$SRC_VERSION"
cp -rv myapp "$TMP_DIR/myapp-$SRC_VERSION/"
cp -rv data "$TMP_DIR/myapp-$SRC_VERSION/"
cd "$TMP_DIR"
# clean eventual tempory files
find . -type f -name *.*~ -exec rm {} \;
tar -zcvf "$TMP_DIR/SOURCES/myapp-$SRC_VERSION.tar.gz" \
"myapp-$SRC_VERSION"
cd -
rm -rv "$TMP_DIR/myapp-$SRC_VERSION"

# rmpbuild seems to have a bug, it can't handle a _topdir with whitespace
if [ -n "$(echo "$TMP_DIR" | grep '[ ]')" ]; then
echo "the path '$TMP_DIR' contains whitespace, rpmbuild will failed. script aborted!"
exit 1
fi

# build the rpm from our spec, specify our local build tree
rpmbuild --define="_topdir $TMP_DIR" -bb "$TMP_DIR/myapp.spec"

# and finally copy our created rpm in to into the current folder
cp -v "$TMP_DIR/RPMS/noarch/myapp-$SRC_VERSION-1.noarch.rpm" ./

Pour créer notre rpm, dans un terminal :

  • cd samples_chapter_2
  • sh ./build_rpm.sh

Et en résultat nous avons finalement le fichier myapp-1.0-1.noarch.rpm dans le dossier myapp-src :) .

Nicolas Martin

Vous pouvez télécharger tous les exemples de ce chapitre 2 (Linux) dans l'archive samples_chapter_2.tar.gz .

L'application myapp, de developer.mozilla.org, est dans le Domaine Public.

L'icône utilisée est issue du Tango Desktop Project, et est dans le Domaine Public.

Toutes les autres données ajoutées, et les fichiers en exemple, sont dans le Domain Public également.