Ako na CPAN-like modul

CPAN moduly sú distribuované vo forme .tar.gz kde sú zkomprimované a uložené jednotlivé súbory. Takto pripravené modulu si môžu užiť širokú paletu open source nástrojov ako je napríklad dh-make-perl, ktorý z nich vytvorí Debian balíček. Moduly nemusia byť použité len pre upload na CPAN ale aj na "interné" - closed source použitie. Zjednodušia organizovanie a inštalovanie.

Na "naštartovanie" je najjednoduchšie použiť Module::Starter. Takže apt-get install libmodule-starter-perl alebo cpan -i Module::Starter pridá do systému module-starter skriptík. Tento vie vytvoriť celú súborovú štruktúru pre modul. Napríklad chceme vytvoriť modul ktorý reprezentuje vlak. ;-) Nepýtajte sa ma prečo. (pretože nič dômyselnejšie ma momentálne nenapadlo)

$ module-starter --builder=Module::Build --module=Acme::DopravnyProstriedok::Vlak \
                 --author="Jozef Kutej" --email=jozef@kutej.net
Created starter directories and files
$ tree Acme-DopravnyProstriedok-Vlak/
Acme-DopravnyProstriedok-Vlak/
|-- Build.PL
|-- Changes
|-- MANIFEST
|-- README
|-- lib
|   `-- Acme
|       `-- DopravnyProstriedok
|           `-- Vlak.pm
`-- t
    |-- 00-load.t
    |-- boilerplate.t
    |-- pod-coverage.t
    `-- pod.t

Čo znamenajú jednotlivé súbory:

Build.PL

use strict;
use warnings;
use Module::Build;

my $builder = Module::Build->new(
    module_name         => 'Acme::DopravnyProstriedok::Vlak',
    license             => 'perl',
    dist_author         => 'Jozef Kutej <jozef@kutej.net>',
    dist_version_from   => 'lib/Acme/DopravnyProstriedok/Vlak.pm',
    build_requires => {
        'Test::More' => 0,
    },
    add_to_cleanup      => [ 'Acme-DopravnyProstriedok-Vlak-*' ],
    create_makefile_pl => 'traditional',
);

$builder->create_build_script();

Build.PL pomocou `perl Build.PL` vytvorí Build ktorý sa dá spustiť. Build má veľa argumentov:

$ ./Build help

 Usage: ./Build <action> arg1=value arg2=value ...
 Example: ./Build test verbose=1
 
 Actions defined:
  build                          manifest    
  clean                          manpages    
  code                           pardist     
  config_data                    ppd         
  diff                           ppmdist     
  dist                           prereq_report
  distcheck                      pure_install
  distclean                      realclean   
  distdir                        retest      
  distmeta                       skipcheck   
  distsign                       test        
  disttest                       testall     
  docs                           testcover   
  fakeinstall                    testdb      
  help                           testpod     
  html                           testpodcoverage
  install                        versioninstall

Run `Build help <action>` for details on an individual action.
See `perldoc Module::Build` for complete documentation.

Najdôležitejšie sú - build, test, install, clean. "build" pripraví všetky potrebné súbory pre inštalovanie modulu a skopíruje ich do adresára blib. "test" spustí na týchto súboroch všetky testy nachádzajúce sa v adresári t/. "install" nainštaluje modul do systému. A "clean" poupratuje/zmaže súbory ktoré Build vytvoril.

Bežný návod ako nainštalovať module pozostáva z `perl Build.PL && ./Build && ./Build test && ./Build install`, v prípade že konfigurácia/inštalácia (ako nášho module) je postavená na Module::Build. Druhá možnosť je ExtUtils::MakeMaker. V tom prípade sa inštaluje pomocou `perl Makefile.PL && make && make test && make install`. Ak je v Build.PL zapnuté create_makefile_pl => 'traditional' tak Module::Build vytvorí aj Makefile.PL takže sa dá inštalovať aj druhým spôsobom. Okolo toho či používať Module::Build, alebo ExtUtils::MakeMaker sa vedú vojny, pričom existuje ešte Module::Install a iné...

Changes

Revision history for Acme-DopravnyProstriedok-Vlak

0.01    Date/time
        First version, released on an unsuspecting world.

súbor obsahuje zmeny ktoré boli urobené v konkrétnej verzií modulu.

MANIFEST

Build.PL
Changes
MANIFEST
README
lib/Acme/DopravnyProstriedok/Vlak.pm
t/00-load.t
t/pod-coverage.t
t/pod.t

MANIFEST obsahuje zoznam súborov ktoré patria k modulu. Ostatné súbory budú pri vytváraní distribúcie (Acme-DopravnyProstriedok-Vlak-0.01.tar.gz) zignorované. MANIFEST treba udržiavať "up-to-date". Ľahko sa stane že sa pridá nový súbor do distribúcie a zabudne sa na MANIFEST. Ako pomôcka slúži `./Build distcheck` ktorý upozorní že existujú nejaké súbory ktoré sa nenachádzajú v MANIFEST-e.

Dobré je si pridať súbor MANIFEST.SKIP v ktorom sa nachádza zoznam súborov ktoré sa majú pri tomto teste ignorovať.

MANIFEST.SKIP

# Avoid version control files.
\bRCS\b
\bCVS\b
,v$
\B\.svn\b

# Avoid Makemaker generated and utility files.
\bMakefile$
\bblib
\bMakeMaker-\d
\bpm_to_blib$
\bblibdirs$
^MANIFEST\.SKIP$

# Avoid Module::Build generated and utility files.
\bBuild$
\b_build
\bcover_db

# Avoid temp and backup files.
~$
\.tmp$
\.old$
\.bak$
\#$
\b\.#
\.cvsignore
\.svnignore

README

Keď sa do Build.PL pridá create_readme => 1, vyupdatuje sa tento súbor pri vytváraní distribúcie z PODu hlavného .pm súboru. V našom prípade lib/Acme/DopravnyProstriedok/Vlak.pm. Update teda nastane pri `./Build dist` alebo pri `./Build distmeta`. Okrem README sa vytvoria aj nové verzie META.yml a Makefile.PL.

META.yml

Tento súbor obsahuje základné údaje o module v YAML formáte. Je automaticky generovaný pri `./Build dist` alebo pri `./Build distmeta` z informácií v Build.PL .

.cvsignore

Obsahuje príklad toho čo ignorovať ak sa na revisioning control používa CVS. Ak používate Subversion dá sa použiť - `svn propset svn:ignore "`cat .cvsignore`" . && svn rm .cvsignore`. Alebo pre GIT `mv .cvsignore .gitignore`.

t/ adresár

v tomto adresáre sa nachádzajú testy. Každý dobrý module/distribúcia by mala obsahovať testy. Tieto zaručia jednak pri vývoji, ale hlavne pri prerábaní, veľkých fixoch a iných zmenách, že funkcionalita zostane zachovaná. Aj vďaka testom vďačia Perl moduly za svoju kvalitu. Keď autor modulu nahrá novú verziu na CPAN, automaticky si to "všimnú" smoke testing servery a zbehnú tieto testy. Výsledky o tom či prešli, alebo neprešli a pre ktoré verzie Perl-u a ktoré operačné systémy sa dajú nájsť opäť na CPAN-e.

t/00-load.t je jednoduchý test ktorý zistí či sa dá náš nový modul skompilovať. t/pod-coverage.t prejde všetky súbory a zistí či sú všetky funkcie okomentované. t/pod.t otestuje či je pod dokumentácie bez syntaktických chýb. Tieto posledné dva sú pomôcka pre autora modulu a je dobre si ich nechať. Ďalej je dobre si pridať nasledujúce testy:

00-compile.t skontroluje či všetky moduly v distribúcií sa skompilujú v poriadku -

use strict;
use Test::More;

eval "use Test::Compile 0.08";
plan skip_all => "Test::Compile 0.08 required for testing compilation"
    if $@;

my @pmdirs = qw(lib blib sbin bin);
all_pm_files_ok(all_pm_files(@pmdirs));

pod-spell.t skontroluje spelling v pod dokumentácií -

use strict;
use Test::More;

if (!$ENV{TEST_SPELLING}) {
    plan skip_all =>
      "Set the environment variable TEST_SPELLING to enable this test.";
}

eval 'use Test::Spelling;';

plan skip_all => "Test::Spelling required for testing POD spelling"
    if $@;

add_stopwords(qw(
    Jozef
));
all_pod_files_spelling_ok();

distribution.t otestuje či všetky súbory distribúcie sú v poriadku -

use Test::More;

eval 'use Test::Distribution not => "sig"';
plan( skip_all => 'Test::Distribution not installed') if $@;

signature.t otestuje digitálny podpis od autora modulu. Spustí sa len keď je nastavené $ENV{TEST_SIGNATURE} a keď existuje SIGNATURE súbor. Tento sa dá automaticky generovať ak sa do Build.PL pridá sign => 1, -

#!/usr/bin/perl

use strict;
use Test::More;

if (!$ENV{TEST_SIGNATURE}) {
    plan skip_all =>
      "Set the environment variable TEST_SIGNATURE to enable this test.";
}
elsif (!eval { require Module::Signature; 1 }) {
    plan skip_all =>
      "Next time around, consider installing Module::Signature, ".
      "so you can verify the integrity of this distribution.";
}
elsif ( !-e 'SIGNATURE' ) {
    plan skip_all => "SIGNATURE not found";
}
elsif ( -s 'SIGNATURE' == 0 ) {
    plan skip_all => "SIGNATURE file empty";
}
elsif (!eval { require Socket; Socket::inet_aton('pgp.mit.edu') }) {
    plan skip_all => "Cannot connect to the keyserver to check module ".
                     "signature";
}
else {
    plan tests => 1;
}

my $ret = Module::Signature::verify();
SKIP: {
    skip "Module::Signature cannot verify", 1
      if $ret eq Module::Signature::CANNOT_VERIFY();

    cmp_ok $ret, '==', Module::Signature::SIGNATURE_OK(), "Valid signature";
}

lib/Acme/DopravnyProstriedok/Vlak.pm

Konečne sme sa dostali k dôvodu prečo sme robili všetku túto parádu :-) , a to vytvoriť modul k všeobecnému blahobytu. Tu má každý voľnu ruku a môže sa do sýtosti zrealizovať. O tom čo je to modul a ako vyzerá z vnútra napíšem v inom návode.

Výcuc ;-)

Rýchly štart:

module-starter --builder=Module::Build --module=Acme::DopravnyProstriedok::Vlak::Rychlik \
                 --author="Jozef Kutej" --email=jozef@kutej.net
cp MANIFEST.SKIP Acme-DopravnyProstriedok-Vlak-Rychlik/
cp 00-compile distribution.t pod-spell.t  signature.t \
	Acme-DopravnyProstriedok-Vlak-Rychlik/t/
rm Acme-DopravnyProstriedok-Vlak-Rychlik/t/boilerplate.t
cd Acme-DopravnyProstriedok-Vlak-Rychlik/
vim Build.PL 
# pridat
#    create_readme       => 1,
#    sign                => 1,
vim lib/Acme/DopravnyProstriedok/Vlak/Rychlik.pm
# nieco zmysluplne nakodit
# zmenit verziu v hlavnom .pm, vyplnit Changes
perl Build.PL; ./Build clean; perl Build.PL && ./Build distcheck && ./Build disttest && ./Build dist
# upload => pause.cpan.org

Slovo na záver. Netreba sa báť pridať niečo na CPAN. Pokiaľ to nekoliduje s iným modulom bude to indexované a pretrvá to tam na veky vekov. Treba rešpektovať niektoré namespace ako DateTime ktoré sa ho snažia mať upratané, ale v takých prípadoch sa niekto ozve, že mu tam robíte bordel. ;-)

Linky: Čo skrýva CPAN

24. Jun 2008
Jozef