(Re-)Bonjour à tous :-)
Les dernières discussions autour de Ncooker m'ont amené à réfléchir sur les
fichiers utilisés pour créer un paquet NBUILD.
Je viens de terminer le traitement du fichier changelog dans la commande
Check. Pour analyser les informations qui se trouvent dans ce fichier, j'ai
du écrire un mini-parser qui permet de récupérer les éléments qui constituent
les entrées de ce fichier (entête, informations ...). Dernièrement, Geekitus
a proposé d'ajouter une nouvelle information dans les nbuilds pour connaître
le motif de sortie d'une release (security-fix, bug-fix ...). Et l'ajout de
cette information oblige à modifier le parser pour qu'il puisse reconnaître
la nouvelle syntaxe.
En fait, la moindre modification de syntaxe dans ce fichier oblige à modifier
le parser. Par exemple, chaque entrée du fichier changelog à une entête de la
forme :
% <release> <author name and email> <creation date>
le simple fait de changer l'ordre des informations, par exemple :
% <author name and email> <creation date> <release>
oblige à revoir le parser.
Et ce problème n'est pas spécifique au fichier changelog, car pour le fichier
« desc », c'est pareil. Chaque description commence par un entête
%Summary[<lang>] ou %Description[<lang>]. La moindre modification ou ajout
d'informations oblige à revoir le parser du fichier desc.
Le seul fichier qui ne pose pas ce type de problème est le fichier « infos ».
Et la raison en est simple : il n'y a pas besoin de développer un parser,
parce que le parser c'est ... Bash lui-même ! :-) En effet, le fichier infos
est un simple script bash contenant des déclarations de variables. Il est
donc possible de profiter des différentes types de déclaration fournis par
bash dans ce fichier (simple variable, tableau ...). Toute modification ou
ajout d'informations dans ce fichier ne pose donc aucun problème, on utilise
ce que bash propose.
Avec ce fichier, il y a un problème d'un autre ordre : vu qu'il s'agit d'un
script bash, un petit malin peut très bien y glisser de « vicieuses »
commandes, comme un « rm -rf $HOME » par exemple. Pour limiter ce genre de
choses, les variables du fichier infos sont évaluées « à la demande ». Par
exemple, lorsque la commande check doit vérifier la valeur de la variable
NPKG_VERSION, on ne fait pas :
source infos
qui exécuterait tout le fichier (et donc les commandes qui s'y trouvent),
mais :
eval `cat infos | grep "^NPKG_VERSION"`
pour exécuter uniquement la déclaration de la variable NPKG_VERSION.
Pour autant, cela ne protège pas de tout. Il suffit de déclarer la variable
NPKG_VERSION comme ceci dans le fichier infos :
NPKG_VERSION=`rm -rf $HOME; echo 0`
pour que notre répertoire home soit irrémédiablement effacé.
Pour essayer de contourner ce problème, j'ai proposé d'écrire un petit jeu de
fonctions pour récupérer une variable et sa valeur dans le fichier infos sans
exécuter les éventuelles commandes qui pourraient y être glissées. C'est ce
sur quoi marc[i1] s'est proposé de travailler. Le fichier infos ne serait
alors plus un script bash, mais un simple fichier texte.
Après réflexion, je me suis dit que ça consistait finalement à écrire à
nouveau un parser qui analyserait la syntaxe du fichier infos pour retrouver
la variable demandée et sa valeur. Du coup, on retrouve le même risque que
celui évoqué pour les fichiers changelog et desc :-) : la moindre
modification dans la syntaxe va nous obliger à revoir le code de ce parser.
Idéalement, il faudrait que les fichiers utilisés pour créer un nbuild (ou
tout du moins les fichiers infos, desc et changelog) soient de simples
fichiers textes pour empêcher l'exécution de commandes. Mais il faudrait
aussi que la syntaxe utilisée pour y déclarer des informations soit
suffisamment souple pour pouvoir ajouter de nouvelles informations ou
modifier la manière dont sont déclarées les informations existantes sans
avoir à retoucher le code du parser (« extensibilité » de la syntaxe).
Pour cela, il y a deux solutions possibles : soit on crée une nouvelle syntaxe
permettant de répondre à ces besoins, ainsi que le parser associé ; soit on
regarde du coté des solutions existantes.
La solution qui m'est venue à l'esprit, ou plutôt revenue, est le format XML.
Je dis « revenue » parce que j'avais envisagé d'utiliser ce langage pour les
fichiers de configuration il y a déjà un petit moment, mais je n'y voyais par
de réel intérêt. Maintenant que la version de développement de Ncooker a pas
mal avancé, les problèmes évoqués plus haut sont apparus (bien que certains
existent déjà avec la version actuelle de Ncooker) et plusieurs
caractéristiques du langage XML sont devenues intéressantes pour les
résoudre : les fichiers XML sont de simples fichiers textes non exécutables,
ce qui rend impossible l'exécution de commandes malicieuses, et le langage
XPath permet d'accéder directement à une information donnée d'un fichier XML,
comme ce qu'on cherche à faire avec la commande grep ci-dessus.
J'ai imaginé la solution suivante en utilisant ce langage : l'idée est de
réunir le contenu des fichiers infos, desc et changelog en un unique fichier
XML nommé infos ; comme desc et changelog contiennent des informations qui
concerne le paquet et son contenu au même titre que infos, autant les réunir
dans un même fichier.
Voici un exemple :
<NPkg>
<project>
<version>21.2</version>
<license>GPL</license>
<author>RMS</author>
<copyrights>Richard Stallman</copyrights>
<homepage>http://www.gnu.org/software/emacs/emacs.html</homepage>
<files>
<archive file="emacs-$version.tar.gz"
checksum="442fed534043a8d66976bdfd7b80eedf">
<url>mirror://gnu/emacs/</url>
<url>ftp://ftp.gnu.org/emacs/</url>
</archive>
</files>
<domain>
<node>/kind/application</node>
<node>/interface/text/command-line</node>
<node>/topic/system/console/terminal</node>
<node>/audience/end-user</node>
</domain>
<purpose lang="en">
<summary>A full-featured text editor.</summary>
<description>here is the full description of the
software.</description>
</purpose>
<purpose lang="fr">
<summary>Un éditeur de texte avancé.</summary>
<description>Voici la description complète du
logiciel</description>
</purpose>
</project>
<package release="1">
<maintainer name="Gontran" email="[EMAIL PROTECTED]" />
<changelog>
<release version="21.2-nga1" author="Gontran <[EMAIL PROTECTED]>"
date="2005-07-08T18:03:00Z" purpose="security-fix bug-fix">
- First package release for version 21.2
</release>
</changelog>
</package>
</NPkg>
Ici, le fichier est composé de deux parties :
* il y a les informations sur le logiciel parmi lesquelles on retrouve sa
version, sa licence, son auteur, son copyright et l'url de son site web. Puis
viennent les fichiers sources à télécharger, suivis de la définition du
domaine d'application du logiciel et enfin sa description dans différentes
langues.
* il y a les informations sur le paquet lui-même (<package>) qui comprennent
son mainteneur et les modifications qui ont été apportées aux releases
successives (<changelog>).
Quels sont les outils permettant d'utiliser un fichier XML en bash ?
Il y a le toolkit StarLet (http://xmlstar.sourceforge.net/) qui offre un
ensemble complet d'outils pour manipuler des fichiers XML en ligne de
commande. Il permet de valider la structure d'un document XML par rapport à
une DTD (Document Type Definition, document décrivant la structure que doit
avoir un document XML), de récupérer des informations à l'aide de chemins
définis avec le langage XPath, et même de modifier ou d'ajouter des
informations au sein du fichier. Ces outils sont écrits en C et sont basés
sur les librairies libxml2 et libxslt.
Récupérer la version du logiciel dans une variable de Ncooker peut se faire
comme ceci :
NPKG_VERSION=`xml sel -t -v "/NPkg/project/version" infos`
Une fois cette syntaxe connue, il suffit de changer le chemin entre guillemets
pour accéder au contenu de n'importe quel élément. Mais le toolkit StarLet
permet de faire des opérations bien plus puissantes :-)
Pour moi, cette solution présente les avantages suivants :
- réduction de 50% du nombre de fichiers nécessaires pour créer un paquet
nbuild, puisqu'on passe de 4 fichiers (desc, changelog, infos et build) à 2
(infos et build). Combinée à la proposition que j'ai faite pour le fichier
build dans un autre post, un unique fichier infos peut suffir pour créer
certains paquets NBUILD :-)
- Sécurité : en définissant une DTD, on peut utiliser StarLet pour vérifier la
syntaxe et la structure du fichier infos. Toute anomalie sera alors détectée,
qu'il s'agisse d'un texte mal placée ou de l'utilisation d'une balise non
reconnue ;
- Evolutivité : si on a besoin d'ajouter des informations comme la raison
d'une nouvelle release d'un paquet proposé par Geekitus, il suffit de mettre
à jour la DTD. Pas de parser à mettre à jour : les développeurs peuvent se
concentrer sur le coeur même du programme.
- Souplesse : l'ordre dans lequel apparaissent les éléments importe peu dans
le fichier XML, seule l'imbrication des balises doit être respectée. On peut
indifféremment déclarer la section <package> avant <project> ou inversement
sans que cela pose problème.
- Facilité d'utilisation des Nbuilds par des applications tierce : comme de
nombreux langages de développement dispose de fonctions XML (Python, Ruby,
Perl, ...), des applications développées dans ces langages (comme Nsetup)
peuvent directement lire les informations contenues dans un nbuild sans avoir
à passer par Ncooker. De même, Le site web étant fait en PHP, on peut faire
très facilement une page affichant les informations des nbuilds situés sur le
site puisque PHP permet d'utiliser facilement des fichiers XML. Mieux : il
suffit simplement d'extraire le fichier infos d'un nbuild avec PHP et de le
renvoyer au navigateur accompagné d'une feuille de style pour obtenir une
belle page présentant toutes les informations :-)
Voilà, dites-moi ce que vous en pensez ;-)
++
Gontran