Article publié dans Linux Magazine 128, juin 2010.
Copyright © 2010 - Jérôme Quelin
Dist::Zilla
Dist::Zilla
par l'exemple
Si vous utilisez Perl, vous connaissez déjà le CPAN, proposant plus de 10000 modules réutilisables pour gagner en temps et en qualité. Dans cet article, nous allons nous mettre dans la peau d'un auteur ayant publié des modules sur CPAN et voir comment simplifier le processus de maintenance associée.
Lorsqu'un auteur publie un module sur CPAN [0], il publie une distribution. Cette distribution est en fait :
CPAN
ou
CPANPLUS
, chaque distribution fournit certains fichiers
supplémentaires.
Regardons plus en détails le contenu d'une distribution ...
Le but premier d'une distribution est de fournir un ou des
modules réutilisables. Une distribution va donc comprendre un répertoire
lib/ qui contiendra les modules à installer, proprement hiérarchisés.
Ainsi, le module Foo::Bar
sera implémenté dans le fichier
lib/Foo/Bar.pm.
Une distribution peut aussi fournir un programme qui est l'intérêt
principal du module - par exemple, le module SVK
fournit l'utilitaire
svk
. Ces programmes sont localisés dans le répertoire bin/ de la
distribution (certains auteurs préfèrent utiliser le répertoire
scripts/).
Mais ce n'est pas tout : en effet, un auteur CPAN va devoir fournir d'autres choses pour que cette distribution s'installe facilement.
Une distribution fournit aussi un script d'installation. Ce script est
traditionnellement appelé selon son fonctionnement Makefile.PL
(utilisation de ExtUtils::MakeMaker
) ou Build.PL (utilisation de
Module::Build
).
Ce script est utilisé de la manière suivante :
$ perl Makefile.PL $ make $ make test $ make install
Si un script Build.PL
est fourni, alors les incantations deviennent :
$ perl Build.PL $ ./Build $ ./Build test $ ./Build install
Bien sûr, cette séquence est lancée pour vous par les utilitaires d'installation automatisés, mais c'est la présence de ces scripts qui garantit une installation facile.
Avant toute chose, le script d'installation va s'assurer que la distribution est complète. Pour cela, il s'appuie sur un fichier MANIFEST contenant la liste des fichiers devant être présents dans l'archive.
Pour permettre d'indexer la distribution et l'analyser facilement, mais aussi pour que les scripts d'installation automatique puissent calculer les dépendances, une distribution se doit d'avoir un fichier META.yml qui contiendra des méta-informations :
Ce fichier répond à des spécifications précises [1], comprises par les
utilitaires d'installation tels que cpan
.
Perl est connu pour l'importance qu'il accorde aux tests. C'est pourquoi une
distribution incorpore un répertoire t/ contenant les tests de la
distribution. Suivant l'auteur et la distribution, on trouvera des tests
s'assurant de la bonne compilation des modules, des tests unitaires sur
les fonctions et méthodes des modules, des tests fonctionnels ... mais
aussi des tests pour s'assurer que la documentation (écrite en Pod) est
bien formée et couvre bien l'ensemble de l'API, des tests lançant
perlcritic
[2] sur les modules pour s'assurer que le code respecte
certains standards de programmation, et bien d'autres destinés à
s'assurer de la qualité d'une distribution.
Une distribution fournit aussi des fichiers permettant d'en savoir plus à son sujet : les traditionnels README et LICENSE, mais aussi un fichier Changes listant les versions et les changements apportés.
Ces fichiers ne sont pas nécessaires pour l'installation automatisée, mais ils sont toutefois importants pour les utilisateurs et les personnes packageant ces modules.
Enfin, une distribution peut fournir d'autres fichiers, selon son but final. Ainsi, les applications peuvent fournir des fichiers .po et .mo pour leur internationalisation / localisation, des icônes, des images ou des données... Tout dépend de la distribution !
À cela s'ajoutent les conventions auxquelles le code doit se plier.
Par exemple, les modules doivent fournir une variable $VERSION
. Cette
version doit être celle de la distribution pour le module
principal. Il est recommandé que les autres modules fournissent la
même version (cela permet de savoir si un module n'est plus fourni par
la distribution, car sa version ne sera plus incrémentée).
De plus, chaque module comporte également des paragraphes AUTHOR et LICENSE (au moins pour le module principal), une documentation idoine...
Enfin, si l'auteur veut suivre les recommandations de la FSF [3], il lui faut ajouter un en-tête à tous ses fichiers avec le copyleft.
Voyons maintenant certaines opérations qu'un auteur est amené à faire.
distribution ?
Pour ajouter un module, il vous faut d'abord créer un fichier, copier
les en-têtes utilisés dans votre projet, puis ajouter le Pod contenant
licence, auteur et autres méta-informations. Bien sûr, n'oubliez pas
d'ajouter une variable $VERSION
égale à la version de la
distribution. Et n'oublions pas de rajouter le fichier dans le
MANIFEST de votre distribution !
Admettons maintenant que vous êtes prêt à publier une nouvelle version de votre distribution... Voyons ce que vous devez faire.
Il vous faut tout d'abord modifier la version de tous vos modules et s'assurer que les pré-requis listés sont toujours les bons (certains peuvent avoir été rajoutés ou supprimés).
Puis vous lancerez les tests, mettrez à jour les fichiers Changes et META.yml, pour enfin créer une distribution que vous pourrez télécharger sur le site des auteurs CPAN.
Ouf ! C'est bien compliqué ! Voyons s'il est possible de se simplifier la vie.
Au vu des tâches requises au-delà de la programmation pure, différentes solutions sont apparues pour aider les auteurs.
ExtUtils::MakeMaker
et Module::Build
Puisque l'auteur fournit un script d'installation qui contient un certain nombre de méta-informations, ces scripts permettent de générer les fichiers MANIFEST et META.yml.
Cependant, il faut bien penser à lancer la commande mettant à jour ces fichiers, et mis à part la création de l'archive tar, ils n'aident pas pour le reste.
Module::Starter
L'un des premiers modules pour aider les auteurs fut Module::Starter
.
Il permet de créer une distribution avec les différents fichiers
nécessaires et fournit un module squelette à remplir par l'auteur.
Cependant, Module::Starter
ne fournit qu'un point de départ (d'où son
nom). Il ne permet pas de rajouter des fichiers à votre distribution, ni
de modifier les fichiers existants si vous décidez de changer la licence
ou autre méta-information... ou même la version de tous vos modules !
Bref, la solution n'est pas idéale.
Module::Release
A l'autre bout du spectre, Module::Release
aide à pousser votre
distribution sur différents sites. Cela peut être PAUSE (pour le CPAN),
ou SourceForge, ou autres sites FTP. Il permet aussi une intégration
minimale avec votre gestionnaire de source. Bien que pratique pour cette
phase de publication, il ne résout pas les problèmes rencontrés lors de
l'écriture de votre distribution.
Il existe d'autres modules qui aident sur un point particulier (e.g., le
module Perl::Version
fournit un exemple permettant de modifier les
numéros de version de tous les modules d'une distribution), mais aucune
solution ne sort du lot pour aider les auteurs.
Dist::Zilla
Partant du constat que programmer c'est fun, mais qu'écrire des licences
et déclamations légales ne l'est pas, Ricardo Signes (dit rjbs) a
donc créé Dist::Zilla
, avec le slogan Roooar !
Ce module fournit en fait une application nommée dzil
pour aider les
auteurs. Voici comment...
Dist::Zilla
va être utilisé par l'auteur du module, et par l'auteur
uniquement, pour créer sa distribution. La distribution créée
par Dist::Zilla
sera exactement la même, suivra les mêmes conventions
(cf. plus haut) et s'installera de la même manière qu'une distribution
classique.
Par contre, Dist::Zilla
va créer des fichiers et en réécrire d'autres
à la volée sans que l'auteur n'ait à s'en préoccuper.
Dist::Zilla
a énormément de pré-requis à installer, mais ce n'est pas
un problème car Dist::Zilla
n'est lancé que par l'auteur. Il n'y a
aucun pré-requis additionnel pour la distribution finale et donc pour
l'utilisateur de votre distribution.
Pour utiliser Dist::Zilla
pour votre distribution, vous allez créer
un nouveau fichier dist.ini contenant les informations statiques de
votre projet. Ce fichier suit le format .ini [4] popularisé par Windows,
avec des sections définies entre crochets et des paires clef / valeur.
Vous allez me dire (à raison) que cela fait un fichier supplémentaire à gérer... C'est vrai, mais attendez de voir ce que ce simple fichier va vous permettre de faire !
Ce fichier dist.ini permet aussi de personnaliser ce que vous voulez
que la commande dzil
fasse. En effet, Dist::Zilla
utilise des
plugins, et c'est à vous de choisir quels plugins vous souhaitez
utiliser et dans quel ordre. Chaque plugin est indiqué par une nouvelle
section dans le fichier, de cette manière :
[MyPlugin] ; commentaire si on veut parametre = valeur
dzil
Une fois Dist::Zilla
installé et votre fichier dist.ini créé, la
commande dzil
sera votre nouvelle interface.
Là où vous utilisiez :
$ perl Makefile.PL $ make disttest
Vous utiliserez maintenant :
$ dzil test
Et pour remplacer :
$ perl Makefile.PL $ make dist
Vous utiliserez :
$ dzil build
Vous remarquerez déjà que nous gagnons une commande lors de l'invocation...
Enfin, la commande :
$ dzil new Foo::Bar
va créer un répertoire Foo-Bar/ contenant un fichier dist.ini de base pour démarrer une nouvelle distribution. Il ne vous restera qu'à créer les modules et fichiers nécessaires, en prenant en compte bien sûr les particularités des plugins que vous allez choisir d'utiliser.
Dist::Zilla
par l'exempleDist::Zilla
propose de nombreux plugins. Pour montrer leur
fonctionnement, nous allons prendre dans la suite de cet article
l'exemple d'un module (fictif) App::Frobnizer
, que nous allons migrer
pour utiliser Dist::Zilla
. Voici les fichiers contenus la
distribution de ce module, tels qu'ils sont dans notre système de
gestion de source :
$ cd ~/code/app-frobnizer $ find . ./Changes ./LICENSE ./MANIFEST ./MANIFEST.SKIP ./Makefile.PL ./META.yml ./README ./bin/frobnizer ./lib/App/Frobnizer.pm ./lib/App/Frobnizer/Reticulator.pm ./lib/App/Frobnizer/Util/mtfnpy.pm ./t/0-compile.t ./t/1-unit-test.t ./t/2-integration.t ./t/9-critic.t ./t/9-pod.t ./t/9-pod-coverage.t
Nous allons donc tout d'abord créer notre fichier dist.ini avec les informations statiques suivantes :
name = App-Frobnizer author = Jerome Quelin version = 1.23 license = Perl_5 copyright_holder = Jerome Quelin copyright_year = 2009
La ligne auteur peut être répétée plusieurs fois et la personne possédant le copyright peut être différente.
La licence doit être l'une des licences répertoriées dans
Software::License
[5].
Tel quel, notre fichier de configuration ne nous sert à
rien... Il faut ajouter des plugins pour que dzil
comprenne ce qu'il
faut faire.
Indiquons tout d'abord à dzil
quels fichiers feront partie de la
distribution. Le plus simple pour cela est donc d'ajouter le plugin
GatherDir qui va, comme son nom l'indique, parcourir l'arborescence
et ajouter tous les fichiers qu'il trouvera dans la distribution.
Comme nous ne souhaitons pas inclure les anciennes distributions ni les fichiers générés durant les builds précédents, nous allons les exclure grâce au plugin PruneCruft. Cela supprimera aussi tous les fichiers cachés (qui commencent par un point), afin que votre distribution ne contienne pas les fichiers de votre gestionnaire de source.
Enfin, pour ne pas inclure certains fichiers ou répertoires, nous allons utiliser le plugin ManifestSkip qui va utiliser le fichier MANIFEST.SKIP, contenant une liste d'expressions régulières, pour ignorer les fichiers correspondants. Ainsi, pour ne pas inclure le répertoire private/ et tout son contenu dans notre distribution, il suffira de rajouter dans MANIFEST.SKIP la ligne suivante :
private/
Il est aussi possible d'utiliser le plugin PruneFiles, qui prend en paramètre les fichiers à supprimer de la distribution. Cependant, il n'accepte que des noms de fichier exacts, ce qui le rend moins pratique pour supprimer un répertoire entier ou un ensemble de fichiers correspondant à un motif.
Notre fichier dist.ini contient donc les plugins suivants :
[GatherDir] [PruneCruft] [ManifestSkip]
Aucun de ces modules ne prend d'arguments, ils apparaissent donc tous comme une section vide.
Maintenant que nous avons rajouté ces trois plugins, la commande :
$ dzil test
va créer un répertoire temporaire dans lequel seront copiés tous les fichiers de la distribution, et lancer le fameux couple de commandes :
$ perl Makefile.PL $ make test
Quant à la commande :
$ dzil build
elle va maintenant créer un répertoire App-Frobnizer-1.23/ ainsi qu'une archive App-Frobnizer-1.23.tar.gz prête à être publiée sur CPAN.
Puisqu'on parle de publication, dzil
va nous aider avec deux plugins
sympathiques. Le premier, CheckChangeLog, va s'assurer que nous avons bien
rajouté les notes de version dans le fichier Changes. Si ce fichier
ne contient pas un paragraphe ressemblant à ceci :
1.23 2009-09-17 - added blort option - fixed crash when called with slurp
alors les appels à dzil
échoueront avec le message suivant :
[CheckChangeLog] No Change Log in Changes [CheckChangeLog] Please edit
Ceci est intéressant pour s'assurer que la documentation de notre
distribution est bien à jour. Mais le plugin UploadToCPAN est encore
mieux. Il accepte les paramètres user
et password
:
[UploadToCPAN] user = myusername password = S3kr3t
Une fois cette section renseignée, nous allons pouvoir utiliser la commande :
$ dzil release
qui va créer la distribution (ce que fait dzil build
) puis la publier
automatiquement pour vous sur CPAN. Voilà du temps gagné !
Si vous souhaitez publier une version de test, qui sera donc disponible sur CPAN mais qui ne sera pas automatiquement installée par les clients, nous allons utiliser l'option:
$ dzil release --trial
Bien sûr, laisser ses identifiant et mot de passe dans le fichier dist.ini de votre distribution n'est pas la meilleure chose à faire. En effet, en tant qu'auteur, vous publiez sans doute plusieurs distributions et dupliquer ces informations dans le fichier dist.ini de chacune d'entre elle n'est pas efficace. Mais surtout, ce fichier sera ajouté à votre SCM (git, svn ou celui que vous utilisez pour votre distribution), donc tout le monde y aura accès.
Heureusement, rjbs y a pensé et la commande dzil
accepte un fichier
de configuration global pour l'utilisateur. Il vous suffit donc de créer
un fichier ~/.dzil/config.ini dans lequel vous ajouterez :
[!release] user = myusername password = S3kr3t
(le point d'exclamation indique à Dist::Zilla
qu'on parle de la
commande release
)
Dans votre fichier dist.ini, vous ne référencerez maintenant que le plugin UploadToCPAN sans paramètres : ceux-ci seront récupérés dans votre fichier de configuration utilisateur.
Enfin, si vous utilisez git comme SCM, certains plugins peuvent vous aider. En ajoutant :
[Git::Check]
dans dist.ini, la phase de publication échouera si votre copie de travail n'est pas propre. Il faut que votre index soit vide, ne pas avoir de fichier non gérés par git, et ne pas avoir de modification dans votre copie de travail (hormis le changelog et dist.ini, qui contient la version de votre distribution).
Après la publication, le plugin Git::Commit
va committer
automatiquement votre changelog et dist.ini dans git, en prenant
comme message de commit l'entrée correspondante de votre fichier
changelog. Le plugin Git::Tag
va créer un tag dans git correspondant
à cette version juste publiée, et enfin Git::Push
va pousser les
commits et tags de la branche courante vers sa branche distante.
Tous ces plugins peuvent être instanciés en une seule ligne avec un bundle (regroupement de plugins) dans votre dist.ini :
[@Git]
(le arobase dénote un bundle de plugins)
Jusque là, dzil
nous aide un peu dans notre tâche d'auteur. Mais, mis
à part la publication automatisée, cela reste marginal et à la hauteur
de ce que Module::Release
apporte déjà.
Nous allons donc aborder les plugins permettant d'injecter des fichiers dans notre distribution.
Commençons par les tests. Beaucoup d'entre eux sont présents pour tester la forme de votre distribution, sa documentation, etc. Ces tests sont donc les mêmes d'une distribution sur l'autre - et qui dit répétition dit automatisation possible !
dzil
propose donc les plugins suivants pour vous aider :
Ce plugin va créer pour vous un fichier t/00-compile.t qui va
chercher tous les modules de votre distribution et tenter de les
compiler un par un. Il va aussi faire de même avec les scripts présents
si vous disposez du module Test::Script
. Plus besoin donc de lister
vos modules (et d'en oublier !) dans un fichier qui va juste tester
s'ils peuvent être correctement compilés...
Ce plugin va lui établir un fichier de test qui lancera perlcritic
sur
l'ensemble de votre code pour s'assurer qu'il suit un ensemble de bonnes
pratiques.
Un autre fichier test sera généré grâce à ce plugin. Ce test vérifiera si le fichier META.yml est valide et conforme aux spécifications.
Ce plugin va créer un fichier testant que votre documentation au format Pod est syntaxiquement correcte.
Ce plugin var créer un fichier testant que toutes vos fonctions et méthodes publiques sont documentées. Cependant, comme il peut être normal pour une raison ou une autre de ne pas documenter certaines d'entre elles, ce test accepte un mécanisme pour les exclure de cette vérification. Il suffit de rajouter des paragraphes spécifiques dans votre documentation, qui seront invisibles sauf pour ce test :
=for Pod::Coverage::TrustPod foo_it sub_covered
Les fonctions foo_it
et sub_covered
seront considérées comme
documentées lors de ces tests. Se référer à la documentation de
Pod::Coverage::TrustPod
pour plus d'informations sur ce mécanisme.
Ce plugin crée un fichier qui vérifie la kwalitee de votre distribution - un ensemble de règles que celle-ci doit suivre pour s'assurer que l'installation se déroulera bien.
Ce plugin crée un fichier de test qui vérifie si votre code est a priori portable, et ne présume pas trop de la plateforme sous-jacente.
Ce plugin va créer un fichier test qui va simplement afficher les version des modules utilisés pendant les tests. Cela permettra de reconstituer l'environnement de l'utilisateur remontant un bug.
Ainsi, grâce à 8 lignes ajoutées dans notre fichier dist.ini, vous
avez fait l'économie d'autant fichiers, qu'il vous faudrait normalement
écrire et maintenir ! Et ce, sans aucune perte de fonctionnalité
- on commence à voir là toute la puissance de Dist::Zilla
.
Et ce n'est que le début : il y a 8 autres plugins fournissant des tests génériques (correction orthographique du Pod, vérification de la version minimum de Perl demandée, etc.). Gageons que d'autres plugins générant divers tests vont faire leur apparition.
Mais les tests ne sont pas les seuls candidats à la génération. En particulier, comme vous avez déjà spécifié un certain nombre d'informations sur votre travail en début de dist.ini, beaucoup de méta-fichiers qui permettent à votre distribution d'être de bons citoyens CPAN peuvent être générés. Voyons donc cela...
Commençons par le plus simple : le fichier LICENSE va être créé automatiquement grâce au plugin License. Il contiendra le texte complet de la licence choisie et, dans le cas d'une double licence (comme Perl 5, qui est disponible soit sous licence Artistic soit sous licence GPL), il contiendra un chapeau avec les termes de l'alternative, suivi par le texte intégral des deux licences.
Le fichier README sera lui généré grâce au plugin Readme. Il contiendra un bref rappel du nom de la distribution, la version considérée et son but, ainsi que la mention du copyright et de la licence utilisée. Si vous préférez que le fichier README soit plus conséquent, le plugin ReadmeFromPod le créera en convertissant la documentation de votre module principal en texte. Bien sûr, ces deux modules ne peuvent pas être utilisés en même temps.
Continuons notre génération de fichiers... Le fichier INSTALL sera
généré par le plugin InstallGuide et contiendra un rappel des
commandes à lancer pour installer la distribution. Le plugin MetaYAML
va générer pour vous le fichier META.yml, et le plugin MakeMaker
quant à lui s'occupera du fichier Makefile.PL. Si vous souhaitez
fournir aussi un fichier Build.PL (utilisant Module::Build
), alors
le plugin ModuleBuild s'en chargera pour vous. Si vous avez des
besoins très particuliers, les modules OverridableMakeMaker
et
ModuleBuild::Custom
permettront de les personnaliser.
Pour terminer sur la génération de ce genre de fichiers, citons le plugin Manifest qui va créer automatiquement le fichier MANIFEST. Ainsi, plus besoin de devoir lancer (quand on y pense !) à la main :
$ perl Makefile.PL $ make manifest
Votre fichier MANIFEST sera maintenant toujours à jour. Bien sûr, ce plugin doit être listé après tous les autres plugins générant des fichiers, sinon le manifeste de votre distribution ne contiendra pas les nouveaux fichiers créés.
Avec tous ces fichiers générés pour vous, le code que vous avez à gérer s'est maintenant réduit à ce qui est réellement spécifique à votre application :
$ cd ~/code/app-frobnizer $ find . ./Changes ./dist.ini ./bin/frobnizer ./lib/App/Frobnizer.pm ./lib/App/Frobnizer/Reticulator.pm ./lib/App/Frobnizer/Util/mtfnpy.pm ./t/1-unit-test.t ./t/2-integration.t
Vous pouvez maintenant vraiment vous concentrer sur votre code, et oublier les à-côtés, nécessaires mais ennuyeux !
En tant qu'auteur averti, vous avez certainement tiqué au paragraphe sur les méta-fichiers générés : les fichiers META.yml, Makefile.PL et Build.PL ont besoin de méta-informations supplémentaires. En effet, il leur faut savoir quels sont les pré-requis de votre distribution... Pour cela, il vous faut rajouter une section pour le plugin Prereq, listant ces pré-requis :
[Prereq] Foo::Bar = 2.43 Blah::Compat = 0
Vous pouvez spécifier la version minimum que vous souhaitez, ou laisser 0 pour indiquer que n'importe quelle version convient, du moment que le module est disponible.
Cependant, dzil
propose encore mieux : trouver automatiquement les
pré-requis pour vous ! Il vous suffit pour cela de lister le plugin
AutoPrereq, qui tentera de trouver les modules que vous utilisez.
L'analyse est statique mais devrait récupérer la majorité d'entre eux,
ainsi que les versions minimales des modules que vous souhaitez. Il ne
trouvera cependant pas les utilisations conditionnelles et/ou
obfusquées. Par exemple, si votre code teste la présence d'un module
en faisant :
eval "use My::Module";
alors AutoPrereq ne le trouvera pas. Mais si vous testez sa présence
ainsi, cela veut dire que vous vous attendez à son absence et que
ce module est optionnel. Il est donc logique de ne pas le lister dans
les pré-requis de votre distribution. AutoPrereq va aussi filtrer
automatiquement les modules se trouvant dans la hiérarchie de votre
distribution. Ainsi, pour notre distribution App::Frobnizer
, il ne va
pas lister App::Frobnizer::Reticulator
comme pré-requis, même s'il
utilisé dans votre code. Enfin, il est possible de supprimer des
pré-requis trouvés grâce au paramètre skip
qui accepte une expression
régulière en paramètre :
[AutoPrereq] skip = ^Private::
Tous les modules commençant par Private
seront filtrés et ne seront
donc pas listés comme pré-requis de votre distribution.
Enfin, notons qu'il est possible d'utiliser Prereq en complément du plugin AutoPrereq pour lister manuellement des pré-requis non trouvés automatiquement.
Il est possible d'ajouter d'autres informations sur votre distribution, qui seront agrégées dans le META.yml ou utilisées dans le script d'installation.
Citons d'abord donc le plugin MetaResources qui permet d'ajouter des URL se rapportant à votre projet :
[MetaResources] homepage = http://search.cpan.org/dist/App-Frobnizer repository = http://github.com/jquelin/app-frobnizer MailingList = http://groups.google.com/group/app-frobnizer
Si la distribution utilise les valeurs par défaut de CPAN, les plugins HomePage et BugTracker permettront de remplir ces valeurs pour vous. Et le plugin Repository quant à lui fournira tout seul l'url de votre gestionnaire de source, en allant la chercher dans votre copie locale.
Comme notre magnifique module est en fait une application, il faut indiquer au script d'installation de s'occuper du ou des scripts fournissant l'interface de notre projet. Cela se fait avec le plugin ExecDir qui permet d'installer des programmes supplémentaires à des endroits particuliers. Par défaut, le plugin ExecDir va automatiquement considérer le répertoire bin/ comme contenant les programmes à installer, mais si vos scripts sont dans scripts/, alors il vous faut l'indiquer ainsi :
[ExecDir] dir = scripts
Le plugin ShareDir permet d'installer des fichiers additionnels parmi
les modules et autres fichiers utilisés par Perl, qui pourront ensuite
être trouvés facilement pendant l'exécution du programme via le module
File::ShareDir
.
Enfin, pour que le fichier META.yml contienne tous les modules fournis par votre distribution, ajoutez le plugin MetaProvides::Package qui va les extraire automatiquement pour vous. Ils seront intégrés de manière transparente lors de la génération de ce fichier.
Nous avons réussi à réduire drastiquement le nombre de fichiers que vous
devez maintenir dans votre distribution. Mais le rapport code / non-code
dans les fichiers module reste peu élevé. Heureusement, Dist::Zilla
va nous aider là aussi.
Prenons notre fabuleux module App::Frobnizer
comme exemple :
# # This file is part of App-Frobnizer # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # package App::Frobnizer; =head1 NAME App::Frobnizer - my awesome app =head1 VERSION version 1.23 =cut our $VERSION = 1.23; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut sub this_method { ... } =head2 that_method Also stuff. =cut sub that_method { ... } =head1 AUTHOR Jerome Quelin <jquelin@cpan.org> =head1 COPYRIGHT AND LICENSE Copyright (c) 2010, Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut 1; __END__
Le premier plugin qui va nous simplifier la vie est PkgVersion. Il va automatiquement rajouter à vos modules une ligne :
our $VERSION = '1.23';
Bien sûr, la version réelle sera celle spécifiée dans le fichier de configuration dist.ini. Fini la modification de tous vos fichiers pour mettre à jour la version lorsque vous souhaitez publier !
Et si par flemme vous ne versionniez que le module principal, vous pouvez maintenant fournir un numéro de version pour tous vos modules, sans plus d'effort...
Pour s'assurer de notre copyright, la FSF recommande d'ajouter un en-tête à tous les fichiers du projet. Ces en-têtes sont toutefois toujours les mêmes - le plugin Prepender permet donc de les ajouter pour vous !
Ce plugin est en fait plus générique : il permet d'insérer
automatiquement des lignes au début de votre fichier (ou juste après la
ligne shebang dans le cas de scripts). Cependant, l'ajout de copyright
étant une opération commune, vous pouvez le spécifier grâce au paramètre
booléen copyright
.
Si vous adjoignez ceci à votre fichier de configuration :
[Prepender] copyright = 1 line = use strict; line = use warnings;
tous vos fichiers se retrouveront alors avec un commentaire
contenant les en-têtes légaux, mais auront aussi les pragmas strict
et warnings
activés.
Si vous êtes consciencieux, vos modules comportent beaucoup de documentation. Cependant, celle-ci comporte beaucoup de redites.
De plus, si vous voulez que la documentation de vos méthodes et fonctions soit près de leur objet, cela vous oblige à mettre les informations de votre module au début de votre code, ce qui n'est pas idéal. Damian Conway le déconseille d'ailleurs dans son livre Perl Best Practices.
C'est pourquoi le plugin PodWeaver a été créé : il pallie ces inconvénients. Il va récupérer le Pod de vos modules, le torturer dans tous les sens et le recréer pour le remettre d'aplomb.
La première section à sauter sera NAME
, qui sera générée pour vous.
Afin que la description de haut niveau de votre module soit présente, il
vous faut ajouter un commentaire juste après la définition de votre
package :
package App::Frobnizer; # ABSTRACT: my awesome app
Il va aussi rajouter automatiquement une section VERSION
étant donné
qu'il dispose de cette information.
De même, comme l'auteur, le copyright et la licence ne changent pas d'un module sur l'autre, vous pouvez les supprimer et PodWeaver va les créer automatiquement pour vous.
Enfin, les méthodes seront automatiquement groupées dans une section
METHODS
, en utilisant le nouveau marqueur Pod =method
. Les
attributs d'une classe peuvent de même être indiqués avec le marqueur
Pod =attr
, et seront regroupés dans une section ATTRIBUTES
.
Et comme le Pod est automatiquement regroupé, vous pouvez garder la documentation de vos méthodes près de celles-ci, tout en mettant la documentation générale de votre module à la fin...
Voici donc notre code tel que nous allons l'écrire et le stocker dans notre SCM :
package App::Frobnizer; # ABSTRACT: my awesome app =method this_method This method does stuff. =cut sub this_method { ... } =method that_method Also stuff. =cut sub that_method { ... } 1; __END__ =head1 DESCRIPTION This app is awesome. =cut
Nous avons réduit sa taille de moitié, en supprimant toutes les redites.
Et encore une fois, nous n'avons rien perdu, car dzil
va rajouter
pour vous ces redites lors des tests, ou création de la distribution
finale !
Il faut toutefois garder à l'esprit un inconvénient découlant des
capacités de réécriture de Dist::Zilla : si vous recevez un rapport
de bug, les lignes fautives ne seront sans doute pas celles pointées par
le numéro de ligne remonté dans l'erreur. Cependant, cet inconvénient
reste mineur comparé aux gains de temps et de clarté que nous apporte
l'utilisation de Dist::Zilla
. Il est toutefois de votre ressort de
comprendre cela et de décider en conscience si vous souhaitez utiliser
les fonctionnalités de modification à la volée de Dist::Zilla
.
Un dernier plugin permet de modifier à la volée des fichiers - des tests cette fois.
Avec la prolifération de tests dits d'auteur (testant la documentation, la qualité du code, etc.), des voix se sont élevées pour qu'ils ne soient pas activés par défaut lors de l'installation des modules, mais uniquement lorsque l'auteur teste sa distribution avant publication.
Ces tests se sont donc vus déplacer dans le répertoire xt/ contenant les sous-répertoires suivants :
Et pour éviter que les tests ne soient lancés lors de l'installation de
la distribution par un utilisateur, ces tests doivent s'assurer que les
variables d'environnement respectives AUTHOR_TESTING
, RELEASE_TESTING
ou
AUTOMATED_TESTING
soient positionnées. Si elles sont
absentes, les tests ne seront pas exécutés.
Cependant, écrire la logique de saut de ces tests en fonction des variables d'environnement est répétitive... Le plugin ExtraTests va donc réécrire les tests de xt/ pour automatiquement les sauter si les variables d'environnement idoines ne sont pas présentes.
Bien sûr, en tant qu'auteur, nous souhaitons lancer ces tests. Les
variables d'environnement sont donc positionnées pour nous lorsque nous
lançons la commande dzil test
.
Si vous ne souhaitez pas vous embarrasser par la gestion des versions,
alors le plugin AutoVersion
va générer la version de votre
distribution automatiquement pour vous. Ceci n'est possible
principalement que pour les versions basées sur la date de publication,
mais comme vous pouvez utiliser n'importe quel code Perl pour formater
ce numéro de version, rien ne vous empêche de faire plus compliqué,
comme une intégration avec votre SCM... (Sauf que dans ce cas, vous
pouvez même utiliser le plugin BumpVersionFromGit.)
Par exemple, si vous ajoutez ceci à votre dist.ini (en supprimant la ligne version au début du fichier, bien sûr) :
[AutoVersion] major = 1 format = {{ $major }}.{{ cldr('yyDDD') }}{{ sprintf '%01u', ($ENV{N} || 0) }}
alors la prochaine version de votre distribution sera 1.yydddn
avec :
yy
les deux derniers chiffres de l'année
ddd
sera le jour de l'année (entre 1 et 366)
n
sera un entier valant zéro par défaut, permettant de faire
plusieurs publications le même jour en appelant dzil release
avec la
variable d'environnement N
:
$ dzil release # publication de 1.092650 $ N=1 dzil release # publication de 1.092651
Ce schéma de version est d'ailleurs le schéma par défaut du plugin
AutoVersion
, il n'est donc pas nécessaire de spécifier le paramètre
format
s'il vous convient. De même, le paramètre major
vaut 1
par défaut.
Les plugins BumpVersion, VersionFromPrev et AutoVersion::Relative permettent aussi de générer la prochaine version automatiquement selon une autre logique, je vous renvoie à leur documentation pour en savoir plus.
Si vous utilisez l'un de ces plugins, alors écrire les notes de version dans le fichier Changes devient plus difficile, car la version n'est pas connue avant le moment de la publication. Le plugin NextRelease permet donc d'utiliser la notation :
{{$NEXT}} - added blort option - fixed crash when called with slurp
dans votre fichier Changes, et la ligne {{$NEXT}}
sera remplacée
par la version et la date à laquelle vous aurez publié votre distribution.
Pour récapituler, voici le fichier dist.ini complet correspondant à notre exemple, trié et commenté :
name = App-Frobnizer author = Jerome Quelin license = Perl_5 copyright_holder = Jerome Quelin copyright_year = 2009 ; -- static meta-information [AutoVersion] [HomePage] [BugTracker] [Repository] [MetaResources] MailingList = http://groups.google.com/group/app-frobnizer ; -- fetch & generate files [GatherDir] [CompileTests] [CriticTests] [MetaTests] [KwaliteeTests] [PodSyntaxTests] [PodCoverageTests] [PortabilityTests] [ReportVersions] ; -- remove files [PruneCruft] [ManifestSkip] ; -- get prereqs [AutoPrereq] ; -- munge files [ExtraTests] [NextRelease] [PkgVersion] [PodWeaver] [Prepender] copyright = 1 ; -- dynamic meta information [ExecDir] [ShareDir] [MetaProvides::Package] ; -- generate meta files [License] [MakeMaker] [ModuleBuild] [MetaYAML] [Readme] [InstallGuide] [Manifest] ; should come last ; -- release [CheckChangeLog] [@Git] [UploadToCPAN]
L'ordre des plugins est bien sûr important, pour s'assurer qu'ils ont tous l'effet escompté.
Si vous utilisez couramment un ensemble de plugins, il est possible de définir un ensemble (bundle) de plugins et de remplacer leurs lignes dans dist.ini par :
[@MyBundle]
Je vous renvoie à la documentation correspondante [6] pour la définition de ces bundles.
dzil
est un outil actuellement en plein essor, et gagne rapidement de
nouveaux adeptes. La fondation Perl a d'ailleurs fourni une bourse à
Ricardo pour améliorer l'outil et sa documentation (cet article reflète
bien les dernières nouveautés).
Cet outil puissant remplace make dist
et bien d'autres choses encore.
Votre distribution reste une distribution standard, utilisant les outils
et conventions du CPAN. Vos utilisateurs ne se rendent pas compte pas
que vous utilisez Dist::Zilla
, tout est transparent pour eux. Ils
s'aperçoivent juste que vous êtes plus productifs...
Bref, je vous conseille d'essayer Dist::Zilla
... Roooar !
Copyright © Les Mongueurs de Perl, 2001-2011
pour le site.
Les auteurs conservent le copyright de leurs articles.