In a nutshell:
how do you deliver a Perl-based utility
that just works "out of the box" for most users,
even those that don't have
the ability to upgrade their Perl version
or install new modules?
I work on
Cons,
a Perl-script software construction tool
(i.e., Make substitute)
that was recently added to the stable of GNU programs.
We try to make it as easy as possible for people to try Cons,
so we put a lot of effort into making sure it works
as nearly "out of the box" as possible.
In other words, it should look like a stand-alone utility,
(almost) regardless of what Perl version they're running
or whether they have permission to install modules.
Having to install a lot of new modules,
or a new version of Perl,
would turn away people
that we'd like to encourage to
to test-drive the utility.
(Yes, non-administrators could install modules
in their own directories and fiddle with -I options or @INC,
but even with the CPAN module,
that starts taking things too far into
the realm of Perl Gurudom for many people.
Sad, but true.)
Accomodating different Perl versions is relatively easy.
As far as the coding itself is concerned,
this just means using
lowest-common denominator
idioms that work in all versions we support:
no "foreach my $var" (not introduced until 5.004),
no "\z" in regexes (not until 5.005), etc.
We use an extensive regression test suite
to make sure Cons works under Perl versions back to at least 5.003
(the earliest version I can test).
Modules are a stickier issue.
In some cases,
the lowest-common-denominator approach works:
just use module interfaces that
date back to 5.003 Core Modules.
But in other cases,
a feature we add requires using a newer version of a module,
or a module that wasn't in the Core
in an earlier Perl version.
For example,
portable multi-volume support in file names
requires
File::Spec
0.8,
which wasn't available until Perl 5.6.0--and
even then, File::Spec 0.8 used "\z" in regexes,
so the module itself doesn't work with pre-5.005 Perl.
One solution would be
simply to not rely on these (or any?) modules
and code everything on our own.
But this loses big-time because:
it reinvents the wheel;
it introduces potential errors;
and it risks perpetuating "cargo cult"
code if someone else cuts-and-pastes the
reinvented code
(as someone is almost sure to do).
So what to do?
Our current middle-ground solution is
to use the real module's
interface,
but ship an
internal version of the module inside the script--for
example, our own "package File::Spec" inside Cons.
These "internal modules" contain
mostly cut-and-pasted methods
from the real modules,
with as few fixes/changes/home-brew code
as we can get away with.
And, almost most importantly,
they have big comments
that warn people to
not
cut-and-paste this internal code,
but to
use the real modules.
The main advantage here is that
we still use the correct, up-to-date module interfaces.
Consequently,
anyone looking at the rest of the
code still sees the "right"
way to use File::Spec and the other modules.
But most importantly,
in the future
we can get rid of the "internal" modules
whenever we drop support for
5.003, 5.004, etc.,
and can rely on those interfaces we use
being part of the the Core Modules
in whatever Perl versions we'll still support.
I thought this was worth a meditation
not because this is necessarily
the most brilliant solution,
but because of how this set of requirements--delivering
a Perl-based "product" that works out-of-the-box
the way a precompiled binary would--changed the way
we had to think about incorporating Perl modules.
This solution still has drawbacks,
but it's the best we've come up with
to try to deliver a utility that
satisfies the requirement and
just happens to be written in Perl.
(Of course, if a more enlighted monk has a better way,
I'd be eager to hear about it...)