Ako na CPAN-like modul

slovenská verzia | english version

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

How to make CPAN-like module

slovenská verzia | english version

Perl modules are distributed in form of .tar.gz. In this form they can be uploaded to CPAN or used with wide range of ready made open source utilities like for example dh-make-perl that will make debian package out of them. This form makes the modules organization and instalation really easy.

For a "quick start" the most easiest way is to use Module::Starter. So either do apt-get install libmodule-starter-perl or cpan -i Module::Starter. This will add a module-starter script to the system, which can create the whole file structure for us. For example we want to make a module representing train. ;-) Don't ask me why. (nothing more clever camed to my mind)

$ 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

What is a meaning of the files:

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 using `perl Build.PL` command will create a file called "Build" that is it self a Perl script and is executable. Build has many arguments:

$ ./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.

The most importat are - build, test, install, clean. "build" will prepare all files necessary for a module instalation by copying them to the blib folder. "test" will run all the test scripts that can be found in t/ folder. "install" will install all the files to the system. And "clean" will cleanup files after previous Build executions.

Common guide to install a module is `perl Build.PL && ./Build && ./Build test && ./Build install`, that is in case that the instalation (like this example one) is based on Module::Build. Second options is ExtUtils::MakeMaker. In this case the instalation procedure is `perl Makefile.PL && make && make test && make install`. If there is create_makefile_pl => 'traditional' in the Build.PL then Module::Build will create Makefile.PL in the time of creating distribution. A wars are fought if to use Module::Build or ExtUtils::MakeMaker and of course those are not all options. There is also Module::Install and ...

Changes

Revision history for Acme-DopravnyProstriedok-Vlak

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

Changes holds changes that occure between version releases.

MANIFEST

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

MANIFEST holds a list of files which belong to the module. The rest of the files will be ignored at the time a distribution (Acme-DopravnyProstriedok-Vlak-0.01.tar.gz) is created. MANIFEST should be kept up to date. It's easy to add a file to a folder and forget to update MANIFEST. As a helper there is `./Build distcheck` that will "complain" if there is some extra file.

It's good to add MANIFEST.SKIP where is the list of files that should be really ignored. (mostly the extra files autogenerated by `Build`)

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

When there is create_readme => 1, in the Build.PL, then README file is autogenerated from the POD of the main .pm file. In our case lib/Acme/DopravnyProstriedok/Vlak.pm. This update will happend by `./Build dist` or by `./Build distmeta`.

META.yml

This file holds basic information about the module in YAML format. It is automaticaly generated by `./Build dist` or by `./Build distmeta` from the information found in Build.PL .

.cvsignore

Holds examples what can be ignored when using revisioning control system for module development. In this for can be directly used by CVS. Subversion users can do `svn propset svn:ignore "`cat .cvsignore`" . && svn rm .cvsignore` or GIT users `mv .cvsignore .gitignore`.

t/ folder

A test scripts are in t/ folder. Every good module/distribution has a couple of them. They will guarranty as during the development time, but mainly when fixing or refactoring code that the functionality stay unchanged. Also thanks to these test most modules are high quality. When author upload new module version to the CPAN, this is automaticaly tracked by smoke testing servers that will run these tests. The output are the reports about passed/failed tests for different versions of Perl and different operating systems. This reports can then be see along other module information.

t/00-load.t is a basic test that will check if the module compiles. t/pod-coverage.t will go through the code and check if all functions has a pod documentation. t/pod.t will check if the pod documentation is without errors. It's good to add also following tests:

00-compile.t checks if all modules in the folder structure will compile. -

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 checks pod spelling -

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 checks if all distribution files are ok -

use Test::More;

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

signature.t checks digital signature of the module author. This test will run only if $ENV{TEST_SIGNATURE} will be set and if SIGNATURE file is present. SIGNATURE can be automaticaly generated by Build.PL by adding 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

Finaly we are at the purpouse of doing all this parade :-) , and that is to create a module for a general purpouse. Here everyone has a free hand and can let his fantazy fly. What it is a module and how does it look inside will be a scope of a differen tutorial.

Resume

Quick start:

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
# code something that makes sence
# change version in .pm, fill out the Changes
perl Build.PL; ./Build clean; perl Build.PL && ./Build distcheck && ./Build disttest && ./Build dist
# upload => pause.cpan.org

Word at the end. Don't worry what you upload to CPAN too much. If it's not coliding with a different module it will be indexed and will stay for ever. Even it is good to respekt some namespaces like DateTime that try to have the house tidy, but in those case you'll be contacted that you should stop doing the mess... ;-)

Links: CPAN - Comprehensive Perl Archive Network

24. Jun 2008
Jozef