How to make CPAN-like module
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
sk