Building Good CPAN Modules
by Rob KinyonApril 14, 2005
When you are planning to release a module to CPAN, one of your first tasks is figure out what OS, Perl version(s), and other environments you will and will not support. Often, the answers will come from what you can and cannot support, based on the features you want to provide and the modules and libraries you have used.
Many CPAN modules, however, unintentionally limit the places where they can work. There are several steps you can take to remove those limitations. Often, these steps are very simple changes that can actually enhance your module's functionality and maintainability.
It Runs On My Machine
You have the latest PowerBook, update from CPAN every day, and run the latest Perl version. The people using your module are not. Remember, just because an application or OS is older than your grandmother doesn't mean that it isn't useful anymore. Code doesn't spontaneously develop bugs over time, nor does it collect cruft that makes it run slower. Some vitally important applications have run untouched for 30+ years in languages that were deprecated when you were in diapers. These applications keep the lights on and keep track of all the money in the world, for example, and they typically run on very old computers.
Companies want to keep using their older systems because these systems work and they want to use Perl because Perl works everywhere. If you can leverage CPAN, you already have 90 percent of every Perl application written.
When in Rome
Perl runs on at least 93
different operating systems. In addition, there are 18 different productionized Perl 5
versions floating around out there (not counting the development
branches and build options). 93 x 18 = 1674. That means your
module could run on one of well over 1500 different OS/Perl version
environments. Add in threading, Unicode, and other options, and there is simply
no way you can test your poor module in all of the places it will end up!
Luckily, Perl also provides (many of) the answers.
Defining Your Needs
If you know that your module simply will not run in a certain environment, you should set up prerequisites. These allow you to provide a level of safety for your users. Prerequisites include:
OSes that your module will not run under
Check
$^Oand%Configfor this.$^Owill tell you the name of the operating system. Sometimes, this isn't specific enough, so you can check%Config.use Config; if ( $Config{ osname } ne 'solaris' || $Config{ osver } < 2.9 ) { die "This module needs Solaris 2.9 or higher to run.\n"; }It's usually better to limit yourself to a specific set of OSes that you know to be good. As your module's popularity grows, users will let you know if it works elsewhere.
Perl versions/features
Check
$]and%INCfor this.$]holds the Perl version and%INCcontains a list of loaded Perl modules so far. (See the Threading section for an example.) If your module simply cannot be run in Perl before a certain version, make sure you have ause 5.00#(where#is the version you need) within your module. Additionally, Module::Build allows you to specify a minimum Perl version in therequiresoption for the constructor.Modules/libraries
In ExtUtils::MakeMaker, you can specify a
PREREQ_PMin your call toWriteMakefile()to indicate that your module needs other modules to run. That can include version numbers, both the minimum and maximum acceptable.Module::Buildhas a similar feature with therequiresoption to the constructor.If you depend on external, non-Perl libraries, you should see if they exist before continuing onwards. Like everything else, CPAN has a solution: App::Info.
use App::Info::HTTPD::Apache; my $app = App::Info::HTTPD::Apache->new; unless ( $app->installed ) { die "Apache isn't installed!\n"; }
|
Related Reading
Learning Perl Objects, References, and Modules |
Operating System
What OS your module happens to land on is both less and more of an issue than most people realize. Most of us have had to work in both Unix-land and Windows-land, so we know of pitfalls with directory separators and hard-coding outside executables. However, there are other problems that only arise when your module lands in a place like VMS.
The VMS filesystem, for example, has the idea of a volume in a fully qualified filename. VMS also handles file permissions and file versioning very differently than the standard Unix/Win32/Mac model. An excellent example of how to handle these differences is the core module File::Spec.
Because this is an issue most authors have had to face at some point, there
is a standard perlpod called, fittingly, perlport. If you
follow what's in there, you will be just fine.
Perl Version
It's been over ten years since the release of Perl 5.0.0, and Perl has changed a lot in that time. Most installations, however, are not the latest and greatest version. The main reason is "If it ain't broke, don't fix it." There is no such thing as a safe upgrade.
Most applications have no need for the latest features and will never trip most of the bugs or security holes. They just aren't that complex. If you restrict your module to features only found in 5.8, or even 5.6, you will ignore a large number of potential users.
Security Improvements
Most security fixes are transparent to the programmer. If the algorithms
behind Perl hashes improve, you won't see it. If a new release fixes a hole in
suidperl, your module won't care.
Sometimes, however, a security fix is a new feature whose usage will (and
should) become the accepted norm: for example, the three-arg form of
open() of 5.6. In these cases, I use string-eval to try to use the
new feature and default to the old feature if it doesn't work. (Checking
$] here isn't helpful because if your Perl version is pre-5.6, it
will still try to compile the three-arg form and complain.)
eval q{
open( INFILE, ">", $filename )
or die "Cannot open '$filename' for writing: $!\n";
}; if ( $@ ) {
# Check to see if it's a compile error
if ( $@ =~ /Too many arguments for open/ ) {
open( INFILE, "> $filename" )
or die "Cannot open '$filename' for writing: $!\n";
}
else {
# Otherwise, rethrow the error
die $@;
}
}
Bug Fixes
Like security fixes, most bug fixes are transparent to the programmer. Most of us didn't notice that the hashing algorithm was less than optimal in 5.8.0 and had several improvements in 5.8.1. I know I didn't. In general, these will not affect you at all.
Unlike security fixes, if your module breaks on a bug in a prior version of Perl, there's probably not much you can do other than require the version where the bug fix occurred.
New Features
Everyone knows about use warnings; and our
appearing in 5.6.0. You may, however, not know about the smaller changes. A
good example is sorting.
5.8.0 changed sorting to be stable. This means that if the two items compare equally, the resulting list will preserve their original order. Prior versions of Perl made no such guarantee. This means that code like this may not do what you expect:
my @input = qw( abcd abce efgh );
my @output = sort {
substr( $a, 0, 3 ) cmp substr( $b, 0, 3 )
} @input;
If you depend on the fact that @output will contain qw(
abcd abce efgh ), your module may be run into problems on versions prior
to 5.8.0. @output could contain qw( abce abcd efgh)
because the sorting function considers abcd and abce
identical.
Gotchas With OS and Perl Versions
Your module may be pristine when it comes to OS or Perl versions. Is the rest of your distribution? Your tests may betray a dependency that you weren't aware of.
For example, 5.6.0 added lexically scoped warnings. Instead of using the -w
flag to the Perl executable, you can now say use warnings. Because
enabling warnings is generally a good thing, this is a very common header for
test files written by conscientious programmers using Perl 5.6.0+:
use strict;
use warnings;
use Test::More tests => 42;
Now, even if your module runs with Perls older than 5.6.0, your tests won't! This means your distribution will not install through CPAN or CPANPLUS. For administrators who install modules this way and who have better things to do that debug a module's tests, they won't install it.
Pages: 1, 2 |





