Article publié dans Linux Magazine 69, février 2005.
Copyright © 2004 - Sébastien Aperghis-Tramoni.
Après bien des articles sur l'utilisation de modules divers et variés,
nous allons passer de l'autre côté du use
et vous montrer comment
créer votre propre module correctement empaqueté.
Comme vous avez bien appris vos leçons, vous avez commencé à écrire un module pour encapsuler de manière un tant soit peu propre un ensemble de fonctionnalités. Nous ne nous étendrons pas ici sur l'interface fournie (procédurale, objet) mais sur un autre point qui est la manière d'en faire une distribution apte à aller sur le CPAN.
Plus précisément, même si vous ne comptez (ou ne pouvez) pas publier votre module sur le CPAN, tout programmeur digne de ce nom vous conseillera de créer votre paquetage proprement pour des raisons de qualité et de maintenance. En effet, même (en fait surtout) si votre module, développé dans un cadre professionnel, est destiné à ne jamais sortir de la société où il a vu le jour, ceux qui devront le maintenir ou le déployer vous remercieront d'avoir fait les choses proprement.
Commençons déjà par définir quelques termes importants, afin de lever certaines ambiguïtés pour le reste de cet article.
Un module est un package
qui est normalement défini dans
un fichier portant le même nom terminé par .pm.
Nous précisons normalement car Perl n'impose pas de créer un
fichier par package. Un même fichier peut donc contenir du code
déclarant plusieurs packages que le programmeur a jugé préférable
de ne pas séparer (même si ce n'est pas conseillé). Inversement,
un package peut être déclaré dans plusieurs fichiers différents.
C'est la méthode utilisée par AutoSplit
pour éclater un module
en plusieurs fichiers afin de réduire le temps de chargement.
Le terme module désigne donc le ou les fichiers qui définissent
le package
correspondant.
Un module d'extension est un module qui apporte dans l'espace Perl les fonctionnalités d'un logiciel externe (typiquement une bibliothèque en C). Ces modules sont programmés en partie en Perl et en partie dans un langage permettant de lier le logiciel externe à l'interpréteur perl(1) comme XS ou SWIG. Ne seront traités dans cet article que les modules pur Perl, l'empaquetage de modules d'extension comportant quelques subtilités.
Une distribution est une archive qui contient un ou plusieurs modules ainsi que les fichiers et programmes permettant son installation. Par convention, les archives sur le CPAN sont au format tar(1) compressées avec GNUzip (gzip(1)).
Un bundle est une distribution virtuelle permettant de regrouper
plusieurs distributions. C'est utile pour installer d'un seul coup
un ensemble de modules avec des dépendances multiples. Toutefois,
depuis que le module CPAN.pm
gère mieux les dépendances, ils sont
encore moins utilisés qu'ils ne l'étaient déjà.
Malgré tout, il reste que la plupart des programmeurs vont parler de module ou de package en désignant indifféremment le module en lui-même, le fichier qui contient le module, ou la distribution (qui peut contenir plusieurs modules ou sous-modules)...
Comme précisé dans l'introduction, nous ne nous attarderons pas sur l'écriture d'un module Perl, cet aspect ayant déjà été couvert par l'article de Sylvain Lhullier (cf. [1]).
Rappelons simplement qu'un module peut comporter plusieurs fichiers
pour différentes raisons : il peut avoir été écrit ainsi par
l'auteur ou comporter des fichiers de données, ou être chargé par
AutoSplit
qui l'a préalablement découpé en plusieurs petits
fichiers, ou encore c'est un module dont une partie est compilée
et qui comporte donc des binaires, dépendants de l'architecture.
Dans tous les cas, ces fichiers doivent être rangés de manière précise
dans l'arborescence de Perl pour qu'il puisse recoller les morceaux.
Pour rappel, vous pouvez exécuter la commande perl -V
pour voir
le contenu de la variable @INC
qui contient les répertoires
de recherche de perl(1).
Comme cette arborescence est spécifique à chaque plate-forme, voire à chaque machine, vous ne pouvez pas deviner où doivent se placer les différents fichiers. Mais pas de panique : perl(1) « sait » comment il a été installé et dispose des informations nécessaires pour une installation correcte. Votre rôle dans la création d'une distribution consiste donc principalement à fournir des informations sur votre module et à laisser des programmes faire le reste.
Examinons un peu la hiérarchie d'une distribution Perl. Elle comporte plusieurs ensembles de fichiers aux vocations et aux buts différents.
README, INSTALL, Changes, NEWS...
LICENSE, ARTISTIC, COPYING...
MANIFEST
META.yml
scripts .PL, .SH
*.pm, lib/*
scripts/*
test.pl, t/*.t
exemples d'utilisation dans le répertoire eg/
sources, patches pour programmes externes
Il s'agit des fichiers que l'on retrouve classiquement dans toute distribution de logiciel libre (et non-libre) et qui donnent des informations sur le logiciel, son installation et son utilisation. Leurs noms sont variables, d'où les différentes possibilités présentées ici.
README, ReadMe.txt... - informations générales sur le logiciel;
INSTALL, Install.txt... - informations relatives à l'installation du logiciel;
Changes, ChangeLog... - détails des modifications apportées au logiciel;
TODO - prévision des fonctionnalités à implémenter;
NEWS, News.txt... - résumé des changements apportés depuis la version précédente;
LICENSE, License.txt... - informations relatives à la licence d'utilisation du logiciel, qui pour les modules Perl est généralement constituée de la Licence Artistique (Artistic License, fichier ARTISTIC ou LICENSE.Artistic) et de la Licence Publique Générale du GNU (GNU General Public License ou GPL, fichier COPYING ou LICENSE.GPL).
Ce sont les fichiers qui vont être utilisés pour l'installation du module. Ils contiennent des informations formatées et sont nommés en suivant des règles assez précises.
Le fichier MANIFEST contient la liste complète des fichiers présents dans l'archive. La première tâche réalisée par le programme d'installation est de vérifier que tous les fichiers indiqués dans MANIFEST sont bien présents. Si ce n'est pas le cas, l'installation est annulée. Notez que les chemins sont toujours indiqués avec la notation Unix afin d'éviter toute ambiguïté.
Le fichier MANIFEST.SKIP permet d'exclure certains fichiers présents dans la distribution lors de la création de l'archive. Comme pour MANIFEST, les chemins sont indiqués en notation Unix, mais ici chaque ligne est interprétée comme une expression régulière.
Le fichier META.yml est apparu assez récemment et contient
des méta-informations sur la distribution. Il est au format
YAML[2] et indique entre autres choses les dépendances du module
à installer. Les versions récentes de ExtUtils::MakeMaker
(à installer depuis le CPAN) génèrent ce fichier automatiquement.
Les scripts de préparation sont des scripts dont le nom se termine en .PL (pour des scripts Perl) ou .SH (pour des scripts shell) et qui génèrent le fichier du même nom, sans l'extension. Par exemple, lorsqu'il est exécuté, Makefile.PL génère le fichier Makefile.
C'est la « charge utile » de la distribution, les fichiers
qui vont être installés. Il s'agit des fichiers nommés en .pm
à la racine de l'archive ou des fichiers (.pm ou autres) situés
dans l'arborescence lib/. Ainsi le module Encode
de Dan Kogai
inclut les tables de codage des caractères Unicode.
Des programmes, situés dans scripts/, peuvent aussi être installés
dans les répertoires d'exécutables comme /usr/local/bin/.
De la documentation spécifique peut aussi être comprise dans le lot
sous la forme de fichiers au format Pod (extension .pod).
L'un des points forts des distributions de modules Perl est la possibilité de réaliser très simplement des batteries de tests unitaires afin de vérifier que le module va fonctionner comme attendu sur la machine cible. Nous reviendrons plus tard sur le fonctionnement de ces tests. Précisons simplement pour le moment que les tests doivent se trouver soit dans un fichier test.pl, soit dans des fichiers situés dans le répertoire t/ et dont le nom se termine par l'extension .t.
L'auteur d'un module peut inclure dans sa distribution d'autres fichiers qu'il a jugés utiles, comme des fichiers d'exemples d'utilisation, des patches ou des sources pour des programmes externes optionnels, etc.
L'installation d'un module packagé dans une distribution proprement
réalisée est tout ce qu'il y a de plus simple en Perl. Et bien que
ExtUtils::MakeMaker
reste à l'heure actuelle la méthode canonique
pour installer un module, d'autres systèmes existent, que nous allons
présenter ici.
ExtUtils::MakeMaker
Extutils::MakeMaker
est le module officiel utilisé pour installer
une distribution. Cette opération se résume généralement à exécuter ces
quelques commandes :
$ tar zxf Theory-Strings-3.54.tar.gz $ cd Theory-Strings-3.54/ $ perl Makefile.PL $ make $ make test $ sudo make install
Vous pouvez aussi installer des modules dans un répertoire privé
en indiquant un paramètre PREFIX
:
$ make install PREFIX=/home/myself/perl5lib
Les modules installés dans ce répertoire pourront être utilisés
en affectant la variable d'environnement PERL5LIB
ou en utilisant
l'option -I
:
$ perl -I/home/myself/perl5lib program.pl
L'avantage de Extutils::MakeMaker
est qu'il est standard et
intégré à la distribution Perl depuis très longtemps. Il dérive
en effet d'un script shell, Makefile.SH, écrit par Andy Dougherty
et qui permettait l'installation des modules à l'époque de Perl 4.
Avec le développement de Perl 5 et sa bien meilleure gestion des
packages
, ce script a été réécrit en Perl par Andy Dougherty,
Andreas Koenig et Tim Bunce. Cela a permis d'éliminer la dépendance
vis-à-vis du shell, qui était un facteur limitant pour la
personnalisation du fichier Makefile généré.
Aujourd'hui, ExtUtils::MakeMaker
semble souffrir des mêmes
défauts que Perl lui-même : il est capable de tout faire,
mais son architecture rend certaines choses difficiles à réaliser.
Par ailleurs, le rôle de Makefile.PL reste avant tout de générer
un fichier Makefile. Cela signifie que Perl conserve donc une
dépendance vis-à-vis de make(1), ce qui est une source de problèmes
car suivant les systèmes, la syntaxe reconnue par make(1) n'est
pas exactement la même (voir par exemple les différences entre
les versions GNU et BSD), et certains systèmes n'ont pas nativement
d'utilitaire make(1) (Windows) voire pas de shell (Mac OS Classic).
Module::Build
C'est afin de s'affranchir de cette dépendance d'utilitaires
externes que Ken Williams a créé Module::Build
, le futur
successeur d'ExtUtils::MakeMaker
. La philosophie de base
est de ne dépendre de rien d'autre que Perl, ce qui permet de
remplacer tout utilitaire externe dont on aurait pu dépendre.
Actuellement encore en développement, il est néanmoins déjà utilisable, et il est recommandé de s'en servir pour le connaître et le tester. Il a par conséquent les défauts de sa jeunesse, à savoir qu'il n'est pas encore intégré à la distribution standard de Perl et qu'il faut donc l'installer depuis le CPAN.
Son utilisation est aussi simple qu'avec ExtUtils::MakeMaker
:
$ tar zxf Theory-Strings-3.54.tar.gz $ cd Theory-Strings-3.54/ $ perl Build.PL $ ./Build $ ./Build test $ ./Build install
Comme avec ExtUtils::MakeMaker
, la première étape est d'exécuter
le script Build.PL qui génère le fichier Build, qui est le
programme d'installation de la distribution. Celui-ci s'utilise
avec les mêmes commandes qu'avant.
Petite note pour les infortunés utilisateurs d'un système
d'exploitation édité par Microsoft, leur interpréteur de commandes
n'autorisant pas l'exécution de ./Build
, ils devront à la place
taper perl Build
.
Après ce rapide tour d'horizon, passons aux choses sérieuses et aux moyens pour créer une distribution.
La manière officielle de créer un nouveau module est d'utiliser le programme h2xs(1). Il est présent depuis fort longtemps dans la distribution Perl, mais il est en réalité assez mal employé. En effet, comme son nom l'indique, h2xs sert avant tout à créer des modules d'extensions en XS à partir des fichiers d'en-têtes d'une bibliothèque C (fichiers .h). Son intérêt est qu'il est capable de générer la majeure partie du code XS de manière automatique, libérant ainsi le programmeur Perl de cette pénible tâche.
Toutefois comme les auteurs de modules pur Perl voulaient bénéficier du squelette de distribution qu'il construisait pour les modules XS, une option a été ajoutée afin de ne pas générer de code XS. Son utilisation dans ce cas-ci ressemble à ceci :
$ h2xs -XAn Acme::GreatModule Writing Acme/GreatModule/GreatModule.pm Writing Acme/GreatModule/Makefile.PL Writing Acme/GreatModule/test.pl Writing Acme/GreatModule/Changes Writing Acme/GreatModule/MANIFEST
Examinons un peu le début de distribution qu'il vient de nous créer. La première constatation est que l'arborescence Acme/GreatModule/ est un peu inutile, on s'attend plutôt à trouver Acme-GreatModule/. De plus, il place le fichier GreatModule.pm à la racine de la distribution, alors qu'il est plus propre de le placer dans une arborescence lib/Acme/. Pour un seul module, ce n'est pas très grave, mais quand d'autres modules viennent s'ajouter au premier, cela peut rapidement devenir fouillis !
Autre défaut, le code d'exemple dans le fichier GreatModule.pm a
tendance à inclure beaucoup de choses inutiles comme une contrainte
sur la version de Perl et l'utilisation d'Exporter
alors que nous ne lui avons rien demandé. Encore une fois, rien de
dramatique, mais comme la plupart des modules Perl développés aujourd'hui
offrent une interface objet, un auteur passe finalement déjà trop de
temps à effacer tous ces trucs inutiles (et à modifier Makefile.PL
pour prendre en compte ces modifications). Enfin il n'y a pas de
fichier README ni d'information de droit d'auteur et de licence.
Le h2xs des versions récentes de Perl (5.8.x) a
un peu amélioré les choses : un fichier README est généré
avec le squelette pour ajouter le copyright et la licence, et le
test de base (le chargement du module) est déplacé dans le répertoire
t/. Toutefois il génère toujours par défaut une contrainte sur la
version de Perl en cours (donc 5.8). Cela peut se changer avec l'option
-b
, mais il faut y penser.
Pour résumer, h2xs a l'avantage d'être présent dans toute distribution de Perl, et s'il peut suffire dans le cas de modules simples pur Perl, il peut rapidement fatiguer l'auteur de nombreux modules.
Après un tel réquisitoire contre h2xs, certains vont demander si la solution est d'écrire son petit script Perl ou shell qui génère les fichiers et qui... La réponse est : NON !
Non, parce que ce script sera forcément faux ou incomplet.
Non, parce qu'il y a déjà une meilleure solution.
Pour pallier cette épine qui se faisait de plus en plus sentir dans
la communauté Perl, Andy Lester a développé un programme qui fait ça
de manière propre et complète, module-starter. Ses avantages par
rapport à h2xs sont qu'il est prévu pour créer le squelette d'une
distribution, qu'il le fait de manière propre et moderne, et qu'en plus
il gère Module::Build
. Son inconvénient est qu'il n'est pas installé
par défaut, mais comme il n'est utile que pour l'auteur d'un module
et non pas son utilisateur, il ne faut pas hésiter à l'installer :
$ sudo perl -MCPAN -e'install Module::Starter'
Son utilisation est tout aussi simple que ne l'est h2xs, si ce n'est qu'on fournit le nom et l'adresse email de l'auteur dès la création de la distribution (ce qui évite d'éditer moults fichiers) et qu'on peut spécifier plusieurs modules :
$ module-starter --module=Acme::GreatModule,Acme::SecondGreat \ --author="Jean Suifier" --email="jean@suifier.net" Created starter directories and files
module-starter crée cette arborescence :
Acme-GreatModule/ Changes MANIFEST Makefile.PL README lib/ Acme/ GreatModule.pm SecondGreat.pm t/ 00.load.t pod-coverage.t pod.t
On constate que c'est déjà bien mieux rangé, les fichiers .pm sont
dans une arborescence dans lib/, et plusieurs tests sont présents
dans t/. On peut éventuellement regretter le fait que les tests
utilisent Test::More
(qui n'a été intégré à Perl qu'à partir de 5.8),
mais on peut facilement remédier à cela.
Les squelettes de modules générés utilisent le mode de documentation
mixé, c'est-à-dire que la documentation de chaque fonction est située
juste à côté de sa définition. Le code Perl et la documentation Pod
s'entremêlent donc tout au long du fichier. Certains apprécient le
fait que la documentation soit aussi près du code concerné, d'autres
préfèrent la conserver tout à la fin, après __END__
pour des raisons
de performances. En effet, l'interpréteur doit sauter chaque passage
entre les balises =pod
et =cut
, mais d'un autre côté, ce coût
est très minime et n'est payé qu'à la compilation, pas à l'exécution.
Au final, il s'agit donc surtout d'une question de goût personnel.
Autre aspect intéressant, module-starter tente dès le départ de
vous forcer à fournir un module propre et documenté en incluant les
tests de vérification du Pod (fichier t/pod.t) et de couverture
de documentation (fichier t/pod-coverage.t). Le premier vérifie
simplement que votre documentation Pod ne contient pas d'erreurs
de balisage, alors que le second vérifie que vous avez bien documenté
toutes les fonctions et méthodes définies dans chaque module,
sous réserve que vous installiez Test::Pod
et Test::Pod::Coverage
,
ce que vous devriez faire dès maintenant.
En résumé, module-starter est vraiment l'outil que vous devez utiliser. Mais pour être tout à fait honnête, la comparaison entre module-starter et h2xs est nécessairement défavorable à ce dernier : la création d'un squelette de distribution n'est pas son véritable rôle, qui est la création de fichiers XS (rôle qu'il remplit très bien d'ailleurs), alors que module-starter a été écrit spécifiquement pour ça.
L'introduction de Module::Build
a créé une certaine agitation
dans la communauté Perl, car CPAN.pm
(le module, pas le réseau)
ne connaissant que ExtUtils::MakeMaker
, les distributions ne
comportant qu'un fichier Build.PL ne pouvaient pas être
installées par ce biais si commode. Il s'agit en réalité d'un faux
problème qui montre que beaucoup d'auteurs ne se sont pas rendus
compte que l'utilisation de l'un n'est pas exclusive de l'autre.
Plus précisément, il est parfaitement possible pour l'auteur
d'utiliser le moderne Module::Build
pour programmer ses modules
et construire sa distribution tout en fournissant un Makefile.PL
pour que CPAN.pm
puisse l'installer sans se poser de questions.
Voyons immédiatement comment faire en reprenant l'exemple de tout
à l'heure. Comme nous ne lui avions pas spécifié un module de
construction particulier, un builder, il a automatiquement
utilisé ExtUtils::MakeMaker
. On peut lui indiquer le builder
à utiliser avec les options suivantes :
--builder=module Build with 'ExtUtils::MakeMaker' or 'Module::Build' --eumm Same as --build=ExtUtils::MakeMaker --mb Same as --build=Module::Build
On peut même lui indiquer d'utiliser les deux en même temps. La création d'une distribution utilisant les deux builders devient alors triviale :
$ module-starter --module=Acme::GreatModule,Acme::SecondGreat \ --author="Jean Suifier" --email="jean@suifier.net" --eumm --mb
module-starter crée alors les deux fichiers Build.PL et Makefile.PL avec les bons paramètres (et l'astuce permettant aux deux de cohabiter).
En réalité, module-starter ne permettait pas au départ
d'utiliser les deux builders, mais suite aux suggestions de
l'auteur, Andy Lester a gentiment accepté d'ajouter ces
fonctionnalités dans module-starter version 1.00, ce qui
vous permet, chers lecteurs, de rester paresseux et d'éviter
de devoir bidouiller des trucs inutiles et ennuyeux ;-)
.
Notez que vous pouvez déjà construire votre distribution (avec Build.PL ou Makefile.PL, au choix) sans devoir éditer aucun fichier ! Pas question de distribuer une archive ne contenant rien d'utile, mais cela vous montre que tout le travail ennuyeux de préparation de la distribution a déjà été effectué, vous pouvez donc vous concentrer sur l'écriture de votre module. La première exécution de l'un des builders affichera un avertissement sur le fait que META.yml est manquant. C'est normal, il sera automatiquement créé lorsque vous construirez la distribution. Voyons d'ailleurs comment faire.
Lorsque vous exécutez perl Makefile.PL
ou perl Build.PL
,
ces programmes créent un builder, respectivement Makefile
et Build. Les deux offrent les mêmes actions, exécutées
avec make action
ou ./Build action
.
help
- donne la liste des actions disponibles.
build
- action par défaut si aucune n'est spécifiée;
elle copie les fichiers .pm, compile les fichiers .xs
et génère la documentation.
test
- exécute les tests unitaires.
dist
- construit la distribution et crée l'archive .tar.gz,
prête à être déposée sur le CPAN.
disttest
- construit la distribution et exécute dans le
répertoire créé l'un de Build.PL ou Makefile.PL, puis
exécute les actions build
et test
.
distmeta
- (disponible seulement avec Module::Build
);
génère le fichier META.yml.
manifest
- met à jour le fichier MANIFEST par rapport aux
fichiers présents dans l'arborescence.
Quelques détails en plus sur l'action manifest
. Elle permet de
mettre à jour automatiquement le fichier MANIFEST en examinant
l'arborescence courante, et en vérifiant si le nom ne correspond
pas aux expressions régulières présentes dans MANIFEST.SKIP.
Voici un bon point de départ pour un fichier MANIFEST.SKIP :
# fichiers et répertoires de CVS, RCS, et autres /CVS/ ^.cvsignore$ # fichiers et répertoires générés par les builders ^blib ^_build ^Build$ ^cover_db ^Makefile(\.old)?$ ^MANIFEST\. ^pm_to_blib$ # fichiers temporaires, de backup, etc ~$ \.bak$ \.old$ \.sw[a-z]$
Lors de la construction d'une distribution, un auteur va
typiquement exécuter les commandes suivantes (l'exemple est ici
donné avec Module::Build
) :
$ perl Build.PL $ ./Build $ ./Build test $ ./Build disttest $ ./Build dist
Vous devez alors obtenir une archive correctement nommée, dans notre exemple Acme-GreatModule-0.01.tar.gz, prête à être déposée sur le CPAN ou à être installée sur une autre machine par les commandes habituelles.
Si vous voulez que votre module soit apprécié, vous devez le documenter correctement. C'est d'ailleurs une conséquence logique de la première vertu du programmeur, la Paresse, qui pousse à écrire une documentation complète afin d'éviter de devoir répondre inlassablement aux mêmes questions (ou de pouvoir se contenter de jeter un « lisez ce foutu manuel » aux utilisateurs les plus ennuyeux). Comme vous avez été de bons élèves, vous avez utilisé module-starter pour créer votre distribution, et celui-ci, respectant votre paresse, a déjà écrit un squelette de documentation. Vous n'avez plus qu'à compléter le fichier README et à écrire la documentation de votre module au fur et à mesure de son développement.
Gardez à l'esprit que quand votre module sera diffusé (en
particulier sur search.cpan.org
), des liens seront créés au
sein de la documentation, permettant de relier les différentes
pages entre elles. En particulier, n'hésitez pas à indiquer les
modules similaires ou associés (ou les dépendances) dans la
section SEE ALSO. Cela facilitera la vie de l'utilisateur de
votre module, et donc la vôtre.
Pensez à préciser de manière explicite votre droit d'auteur et la licence d'utilisation de votre module dans tous les fichiers qui seront installés. Par défaut, module-starter (ainsi que h2xs) ont déjà rempli pour vous la partie sur le droit d'auteur (section COPYRIGHT), mais vous pouvez être amenés à la modifier ou la compléter suivant les cas (développement au sein d'une société par exemple).
La licence est par défaut celle de Perl, qui est constituée de la disjonction de l'Artistic License (licence artistique) et de la GNU General Public License (licence publique générale du GNU). Vous pouvez spécifier une autre licence libre (LGPL, BSD révisée) ou non-libre. Dans tous les cas, vous devez ajouter un fichier LICENSE qui spécifie explicitement les termes de la licence d'utilisation. Un petit rappel : licence (avec un c) est le mot français, license (avec un s) est le mot anglais.
D'ailleurs, souvenez-vous que la communauté Perl est mondiale, et que la plupart des personnes s'attendront donc à pouvoir lire la documentation en anglais. Cela n'interdit pas d'inclure aussi une version française, mais la politesse conseille toutefois d'écrire un minimum de documentation (au moins le README) en anglais, afin que les non-francophones aient une chance de savoir à quoi sert votre module.
Comme tout logiciel qui se respecte, votre module doit avoir un
numéro de version. En Perl, il est toutefois demandé d'utiliser
des numéros de versions à deux composantes plutôt qu'à trois,
ceci pour des raisons techniques. En effet un numéro de version
à deux composantes peut être vu comme un nombre flottant, ce qui
simplifie la comparaison. Pour cette raison, les versions des
modules Perl suivent généralement un format V.RR
, où V
est
le numéro de version majeur et RR
(ou RRR
) est le numéro
de release, de distribution. Vous pouvez aussi ajouter le
suffixe _DD
pour signaler une version de développement.
Prenons quelques exemples pour mieux comprendre.
Notre ami Jean Suifier distribue une première version de son
module Acme::GreatModule
, qu'il numérote 0.01. Ayant un bon
retour de la part des utilisateurs, il continue et distribue une
version de développement 0.01_01, qui indique qu'elle est
basée sur la version 0.01. Il peut ainsi distribuer plusieurs
versions beta 0.01_02, 0.01_03, avant de distribuer une version
0.02 finale.
Pour une distribution comportant plusieurs modules, il est
généralement conseillé de donner un numéro de version séparé
à chaque module, la distribution ayant comme version celle du
module principal. Vous pouvez complètement décorréler la version
de la distribution de la version des modules inclus en supprimant
le paramètre dist_version_from
de Build.PL et en le
remplaçant par dist_version
, avec comme valeur la version.
Pour Makefile.PL on fait de même en remplaçant VERSION_FROM
par VERSION
.
À noter toutefois qu'il est déconseillé d'utiliser le numéro de
révision CVS (ou de votre système de gestion de versions) comme
valeur pour $VERSION
car cela peut engendrer des résultats
bizarres (en particulier lorsqu'un programme existe dans plusieurs
branches).
Nous verrons plus loin en quoi attribuer un numéro de version unique à chaque nouvelle release est important.
L'un des points forts du système d'installation des modules Perl
est le support, lors de la procédure d'installation, d'une phase
de tests automatiques permettant de vérifier le bon fonctionnement
du ou des modules sur la machine cible avant l'installation proprement
dite. Ces tests unitaires ont un protocole de déroulement très simple
et codifié dans la documentation du module Test::Harness
. Ce
module est utilisé par le builder pour exécuter les tests qui
doivent se trouver dans des fichiers .t dans le répertoire t/.
Test::Harness
attend de ces programmes qu'ils affichent
simplement l'état de chaque test, "ok"
ou "not ok"
, suivi du
numéro du test. Ces programmes peuvent être réalisés de manière
complètement indépendante de tout module Perl, mais il est conseillé
d'utiliser l'un des modules Test
ou Test::More
, qui offrent des
fonctions pour faciliter grandement le travail et évite de devoir
connaître les subtilités du protocole de dialogue.
Ces deux modules offrent comme fonctionnalités de base la fonction
ok()
dont le but est d'afficher "ok"
ou "not ok"
en fonction
de la valeur des arguments. Voyons immédiatement un exemple :
use Test; use Acme::GreatModule; plan tests => 2; # est-ce que $VERSION est défini ? ok( $Acme::GreatModule::VERSION ); # est-ce que $VERSION est un numéro de version valide ? ok( $Acme::GreatModule::VERSION =~ /^\d+\.\d+(?:_\d{2})?$/ );
Juste après l'utilisation du module Test
(ou Test::More
) on
trouve la déclaration du plan prévu, qui indique le nombre de tests
qui seront effectués. Ensuite on charge le module qui doit être testé
et on vérifie qu'il a bien défini un numéro de version, puis que
celui-ci correspond bien à un numéro de version Perl. Vous l'aurez
deviné, ok()
affiche "ok"
si l'expression qu'on lui donne est
vraie, et "not ok"
dans le cas contraire. On peut aussi utiliser
la forme à deux arguments de ok()
, pour lui laisser faire la
comparaison :
# est-ce que $PI a la bonne valeur ? ok( $PI, 4 * atan2(1,1) );
Test
fournit aussi une fonction skip()
pour passer des tests
qui ne peuvent pas être exécutés (parce qu'il manque un module, parce
que la plate-forme ne supporte pas certaines fonctionnalités, etc).
Test::More
fournit d'autres fonctions qui offrent des sémantiques
plus pointues que ok()
, telles que is()
et isnt()
pour tester
si une valeur est égale ou différente d'une autre, like()
pour
vérifier qu'une valeur correspond à une expression régulière, is_deeply()
pour vérifier si deux structures complexes sont identiques à tous les
niveaux.
À noter que le répertoire t/ permet d'inclure des modules et
des fichiers qui ne seront utilisés que pour les tests, et qui ne
seront pas indexés (ce point sera développé plus loin).
En particulier, cela signifie que vous pouvez sans risque inclure
une copie des modules Test
et Test::More
dans t/, afin
de ne pas créer de dépendances artificielles.
Dernier point intéressant à connaître pour ce rapide tour des tests
de votre module : la couverture de code, c'est-à-dire vérifier que
les tests mis en place exécutent réellement l'ensemble du code des
modules testés. Le module Perl Devel::Cover
permet justement de
vérifier cet aspect. Et son utilisation est rendue encore plus simple
qu'elle ne l'est naturellement grâce à Module::Build
qui offre
depuis la version 0.25_01 une action testcover
. L'exécution de
./Build testcover
exécutera donc la suite de tests en chargeant
Devel::Cover
et générera un rapport HTML très lisible dans
cover_db/coverage.html.
Le programmeur orgueilleux sera alors tenté (avec raison) d'écrire
des tests pour avoir une couverture maximale.
Les scripts de tests peuvent (et doivent) être utilisés pour tester tous les aspects possibles de votre distribution. Cela concerne aussi bien le code du module fourni (vérifier qu'il se comporte comme attendu), que des aspects plus extérieurs : présence de tous les fichiers, vérification du POD, des dépendances, etc. module-starter a déjà créé certains scripts de tests, t/00.load.t pour vérifier que le module se charge bien, t/pod.t pour vérifier que le POD est correct et t/pod-coverage.t pour vérifier que la documentation couvre bien toutes les fonctions définies.
On peut ensuite ajouter les vérifications fournies par
Test::Distribution
qui examine plusieurs points. Pour
l'utiliser, il suffit d'ajouter le script t/distchk.t :
use strict; use Test::More; eval "use Test::Distribution"; plan skip_all => "Test::Distribution required for checking distribution" if $@;
Évidemment, il est plus que fortement conseillé d'installer tous les
modules utilisés par ces scripts de tests pour qu'ils soient utiles,
à savoir Test::Pod
, Test::Pod::Coverage
, Test::Distribution
,
ainsi que toutes leurs dépendances. Avec le shell cpan, il suffit
de confirmer leur installation.
Un auteur avisé doit garder ce point à l'esprit : Perl est
portable, et porté sur beaucoup de systèmes aux caractéristiques
très différentes. Il doit donc en tenir compte lorsqu'il écrit
un module. Nous ne nous attarderons pas ici sur les problèmes
de portabilité liés aux fonctionnalités de Perl (par exemple la
disponibilité de fonctions comme fork()
), mais aux problèmes
sur les noms de fichiers, cet article traitant de la manière de
créer une distribution. Pour les autres aspects de portabilité,
le lecteur est invité à consulter perlport[3].
En effet, les systèmes d'exploitation ne sont pas tous égaux (et sont même très différents) sur les noms de fichiers valides. Voici donc quelques conseils pour éviter de vous attirer les récriminations d'utilisateurs mécontents.
Ne mettez jamais dans le même répertoire deux fichiers dont le nom ne diffère que par la casse (majuscules-minuscules) comme test.pl et Test.PL. Plusieurs systèmes d'exploitation (DOS, Windows, Mac OS Classic, Mac OS X, VMS) utilisent des systèmes de fichiers insensibles à la casse, ce qui ferait que l'un des fichiers écraserait l'autre lors du désarchivage de la distribution. C'est par une erreur de ce genre que bien des utilisateurs de Mac OS X ont vu leur commande Unix head(1) écrasée par la commande HEAD(1) installée par LWP.
Tentez de vous limiter sur la longueur des noms de fichiers. Plusieurs systèmes ont des limitations sur la longueur des noms de fichier : DOS n'autorise que des noms de 8 caractères plus 3 d'extension alors que Mac OS Classic autorise jusqu'à 31 caractères. En conséquence essayez de nommer les fichiers de sorte que les premiers caractères permettent d'obtenir tout de même un nom unique.
N'utilisez pas de caractères comme / : \ ? * < ! > @ | & ;
car certains sont utilisés comme séparateur des noms de répertoires
ou ont une signification spéciale.
N'utilisez pas non plus d'espace car la plupart des systèmes (y compris les Unix) s'en accommodent mal voire ne les autorisent pas du tout.
N'utilisez pas plus d'un point dans un nom car des systèmes comme DOS et VMS n'aiment pas ce genre de plaisanteries.
En fait, n'utilisez que les caractères suivants, recommandés par
perlport
qui le tient de la norme C ANSI :
a b c d e f g h i j k l m n o p q r t u v w x y z A B C D E F G H I J K L M N O P Q R T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 . _ -
en évitant de mettre autre chose qu'un alpha-numérique en premier caractère. (Et ne nommez pas les répertoires en .s).
Vous trouvez ces limitations effrayantes ? C'est bien, c'était le but : vous rappeler qu'Unix n'est pas le seul système (ou architecture de système) au monde. D'autres systèmes existent, avec leurs caractéristiques propres qu'il s'agit de respecter dans la mesure du possible.
Vous pourriez écrire un script pour vérifier que les fichiers
sont nommés en respectant ces conseils mais comme ça ne présente
que peu d'intérêt et que vous n'avez pas envie de tout vous retaper
à chaque fois, autant utiliser directement le module
Test::Portability::Files
qui est à votre disposition sur le CPAN.
Le script de test, t/portfs.t, se résume alors à :
use Test::More; eval "use Test::Portability::Files"; plan skip_all => "Test::Portability::Files required for testing filenames portability" if $@; run_tests();
Et vous pouvez même copier-coller le synopsis du module, voire
directement copier le fichier t/portfs.t inclus pour vous éviter
même cette saisie ;-)
.
Notez que vous pouvez appeler la fonction options()
pour
sélectionner les tests à exécuter. La documentation vous expliquera
le reste.
Le CPAN est le Comprehensive Perl Archive Network, le réseau complet d'archives Perl. Il est l'une des pierres angulaires de la communauté Perl, et l'une des principales raisons du succès de ce langage, car il offre aux utilisateurs et aux programmeurs un réseau distribué où sont centralisés quasiment tous les modules Perl existants. C'est la raison pour laquelle il est demandé à tout programmeur qui désire contribuer de vérifier d'abord s'il n'y a pas déjà un module offrant les fonctionnalités désirées (ce qui est généralement le cas). On peut noter que tant sur la structure que sur le nom, le CPAN s'est inspiré du CTAN (Comprehensive TeX Archive Network), mais a dépassé son modèle tant en fonctionnalités qu'en popularité.
CPAN est aussi le nom du module Perl du même nom qui permet d'installer des modules de manière simplifiée, en gérant automatiquement les dépendances.
PAUSE est le Perl Authors Upload SErver, le serveur d'upload pour les auteurs Perl[4]. C'est un service qui est mis à la disposition des auteurs de modules Perl qui leur permet de déposer sur un serveur les modules qu'ils ont écrits (ceux-ci devant bien sûr être proprement emballés). Ces archives sont ensuite copiées en miroir sur les serveurs formant le CPAN. PAUSE est donc le point d'entrée des modules sur le CPAN.
Pour vous connecter sur le serveur PAUSE, vous devez au préalable obtenir un identifiant CPAN. Par ce login, un répertoire à votre nom sera créé sur les serveurs du CPAN, où vous pourrez ensuite déposer les modules et programmes que vous désirez partager avec la communauté.
Pour obtenir votre identifiant CPAN, connectez-vous sur le site web
de PAUSE https://pause.perl.org/ et cliquez sur le lien
Request PAUSE account. Vous arrivez sur un formulaire où on vous
demande de remplir votre nom, votre adresse mail, votre site web,
l'identifiant que vous désirez obtenir, et une description de ce que
vous comptez contribuer. Une fois validé, ce formulaire envoie un
mail sur la liste modules@perl.org
. Cette liste est un peu spéciale
car ce n'est pas une vraie liste de diffusion mais juste un alias
vers les mainteneurs du CPAN. Vous pouvez néanmoins en consulter les
archives[5] pour suivre l'avancement de votre requête. Vous devriez
recevoir votre identifiant dans la semaine qui suit.
Une fois votre identifiant créé, vous pouvez vous authentifier sur le
site de PAUSE. Comme cela vous sera recommandé par mail, vous avez
alors la possibilité d'éditer les informations de votre compte,
et notamment de définir une adresse de courrier publique et une
adresse confidentielle. Vous pouvez aussi définir un alias
identifiant@cpan.org
pointant vers l'une de ces adresses.
Encore plus que pour les autres logiciels libres, le choix du nom
d'un module Perl est souvent un sujet sensible. La raison en est
que le nom d'un module définit un « espace de noms », mais que
contrairement à Java par exemple, les espaces de noms sont en quelque
sorte globaux, alors qu'en Java ils commencent souvent par le nom de
domaine inversé (comme org.w3c.dom
ou org.apache.xerces
), afin
de garantir une unicité dès les premiers niveaux. Si le module
n'introduit pas une fonctionnalité ou le support d'un logiciel tiers
qui n'existait pas encore, il est conseillé qu'il s'inscrive dans un
espace de noms déjà existant. Une règle commune est que le nom
commence par la catégorie dans laquelle s'inscrit le module, et se
termine par l'implémentation spécifique, s'il y a lieu.
Par exemple, un module implémentant un protocole réseau Proto
devrait s'appeler Net::Proto
, alors qu'un module interrogeant
un site web s'inscrirait plutôt dans WWW::
ou WebService::
.
À noter que l'espace de noms Acme::
vous permet d'exprimer votre
créativité en vous autorisant à y inclure des modules quelque peu
fantaisistes (mais qui doivent néanmoins être fonctionnels).
À propos des espaces de noms
Les débats sur les espaces de noms sont souvent à l'origine de
discussions au lance-flammes sur les listes de diffusions où le sujet
est abordé. En résumé, si la notation par domaine inversée semble
intéressante, elle pose plus de problème qu'elle n'en résout.
Imaginez que Gisle Aas ait nommé son module Org::Aas::LWP
au
lieu de LWP
! Outre le fait que c'est très laid, ça pose
de nouveaux problèmes : comment faire quand le module change
de mainteneur ? En particulier, comment faire si le module est
intégré à la distribution standard de Perl ? Les problèmes de
compatibilité que cela induit ne sont pas mineurs, et la plupart
des programmeurs ont autre chose à faire qu'à perdre du temps à
s'occuper de ce genre de futilités.
Le plus simple est de chercher sur http://search.cpan.org/ si
d'autres modules similaires à celui que vous voulez écrire existent
déjà. D'ailleurs, vous devriez chercher sur ce site avant même
de commencer un module ! Vous pouvez aussi, c'est même
très fortement recommandé, demander conseil sur la liste
modules-authors@perl.org
[6] qui est prévue pour ça. C'est bien
sûr une liste internationale donc anglophone, mais vous pouvez
aussi demander sur la liste francophone perl@mongueurs.net
[7].
En fait, l'attribution de l'espace de nom se fait dès le dépôt de la distribution sur le CPAN sur la base du « premier arrivé, premier servi ». Vous pouvez ensuite l'enregistrer de manière plus officielle dans la liste des modules : connectez-vous sur PAUSE, cliquez sur Register Namespace, puis remplissez les champs demandés et validez.
Ca y est ! Tout est prêt. Vous avez écrit votre module, vous avez
demandé conseil pour le nom, vous avez obtenu votre identifiant CPAN,
votre archive est prête (surtout, ne changez pas le nom de l'archive
créée avec l'action dist
, car il est formaté de la manière attendue
pour la suite des opérations). Vous êtes paré pour charger votre module
sur le CPAN, et comble de chance, l'opération est on ne peut plus simple.
Connectez-vous sur PAUSE, puis cliquez sur Upload a file to CPAN.
Cette page vous indique tous les moyens de charger une archive sur le
CPAN : envoi par formulaire, téléchargement depuis un site distant
ou dépôt sur un serveur FTP. Une fois que PAUSE a récupéré votre archive,
elle est automatiquement décompressée pour être examinée et indexée.
En particulier les fichiers README et META.yml sont extraits et
stockés à côté de l'archive .tar.gz mais avec les extensions
.readme et .meta. Les informations contenues dans META.yml et
dans Build.PL ou Makefile.PL sont ensuite extraites pour être
stockées dans une base de données qui servira à créer le fichier
http://www.cpan.org/modules/03modlist.data.gz, utilisé par le
module CPAN.pm
pour installer les modules.
Ensuite, quand votre archive arrive sur http://search.cpan.org/,
l'archive est décompressée pour en indexer les différents fichiers,
sauf ceux situés dans le répertoire t/, et générer les pages HTML
correspondantes.
Il faut par ailleurs connaître quelques particularités du fonctionnement de CPAN. La principale est que chaque version doit être impérativement unique. En fait, vous ne pouvez pas déposer deux archives avec le même numéro de version, par exemple pour corriger une erreur idiote que vous n'avez vue qu'au dernier moment. Dans ce genre de cas, il vous faudra charger la nouvelle archive avec un nouveau numéro de version. Cela peut paraître curieux, mais c'est une mesure de sécurité qui a été mûrement réfléchie.
À noter que vous pouvez supprimer les vieilles versions de vos modules afin de ne pas encombrer les disques des miroirs du CPAN. Pour cela, connectez-vous sur PAUSE, puis cliquez sur Delete Files. Vous pouvez alors sélectionner les fichiers à supprimer. N'hésitez pas, il y aura toujours une version archivée dans BackPAN[8], et cela permettra d'améliorer le Facteur Schwartz[9].
Voici un petit résumé pour les programmeurs impatients :
créez votre squelette de distribution avec module-starter en
spécifiant les deux constructeurs ExtUtils::MakeMaker
et
Module::Build
(options --eumm --mb
) ;
utilisez Module::Build
pour construire votre distribution ;
documentez correctement tous les modules et scripts inclus ;
donnez des numéros de version appropriés ;
écrivez des tests, n'ayez pas peur d'en inclure beaucoup ;
exécutez vos tests avec les actions test
, testcover
et disttest
;
pensez portabilité.
Cette présentation sur les bonnes pratiques pour créer votre
distribution de modules Perl est volontairement restée assez
générale, pour montrer l'ensemble des étapes de l'idée au CPAN.
Il y a encore bien à dire à ce sujet, mais ce sera abordé dans
de futurs articles consacrés aux modules s'interfaçant avec du
code C (les modules XS), à la configuration avancée des
constructeurs Module::Build
et ExtUtils::MakeMaker
, et
à la programmation de tests complexes avec Test::More
et
Test::Builder
.
[1] Introduction à Perl - Partie 7, Écrire un module, par Sylvain Lhullier, in Linux Magazine n°46, janvier 2003; in Les Dossiers de Linux Magazine n°2, printemps 2004 ; https://formation-perl.fr/guide-perl.html
[2] YAML (YAML Ain't Markup Language) est un format destiné à la sérialisation de données. Il est similaire dans ses fonctionnalités à XML mais contrairement à ce dernier, il reste toujours lisible pour un humain. Pour plus d'informations, voir http://www.yaml.org/
[3] perlport: Writing portable Perl - http://search.cpan.org/dist/perl/pod/perlport.pod (version anglaise), http://www.mongueurs.net/perlfr/perlport.html (version française)
[4] The Perl Authors Upload Server - http://www.cpan.org/modules/04pause.html
[5] Archives de modules@perl.org - http://www.xray.mpe.mpg.de/mailing-lists/modules
[6] The module-authors Mailing List - http://lists.perl.org/showlist.cgi?name=module-authors
[7] Liste de diffusion perl@mongueurs.net - http://paris.mongueurs.net/mail.html
[8] BackPAN - http://backpan.cpan.org/
[9] Schwartz Factor - http://use.perl.org/~brian_d_foy/journal/8314
Sébastien Aperghis-Tramoni <sebastien@aperghis.net>, "Maddingue" - de Marseille.pm
Bricoleur de Perl depuis 1994, Sébastien est actuellement administrateur système à Marseille, où il essaye de devenir un véritable BOFH.
Copyright © Les Mongueurs de Perl, 2001-2020
pour le site.
Les auteurs conservent le copyright de leurs articles.