Do all my perl module .pm files need a `$VERSION` definition? - perl

I just added a new file to the PDL::IO::Touchstone distribution and noticed that CPAN's indexer says version is undef since $VERSION is missing:
module : PDL::IO::MDIF
version: undef
in file: PDL-IO-Touchstone-1.009/lib/PDL/IO/MDIF.pm
status : indexed
So ::MDIF does not have $VERSION but really it is the same as the distribution version as noted in Makefile.PL:
my %WriteMakefileArgs = (
VERSION_FROM => 'lib/PDL/IO/Touchstone.pm',
...
);
Questions:
So does this module within the distribution need a version?
If so, should the new module's $VERSION be maintained separately from $VERSION provided by VERSION_FROM in Makefile.PL?
I could do $VERSION = $PDL::IO::Touchstone::VERSION but not sure if CPAN will figure that out. Will it?
I looked around and found lots of discussion of versioning practices, but nothing about versions of modules within the same Perl distribution package. Please share what the best practice here should be, I'm new to Perl modules and this is the first 2-file distribution that I've pushed out.
I'm sure I'll update the primary file when releasing a new dist, but not sure if I'll remember to bump the version of other modules in the dist when they change. It would be nice if there is a low-maintenance option here.
Update
I tried the suggestion in some answers below. Neither of these work:
$VERSION = do { use PDL::IO::Touchstone; $PDL::IO::Touchstone::VERSION };
use PDL::IO::Touchstone; our $VERSION = $PDL::IO::Touchstone::VERSION;
This is the MDIF.pm file at github: https://github.com/KJ7LNW/perl-PDL-IO-Touchstone/blob/master/lib/PDL/IO/MDIF.pm#L22
CPAN still shows version: undef:
Status: Version parsing problem
===============================
module : PDL::IO::MDIF
version: undef
...
Ok, so who gets the checkmark... any other ideas?

I like to give all of my modules versions. It's not a requirement, but consider the user experience while trying to upgrade when they don't know anything, or very little, about how CPAN works. ikegami already gave the tl;dr for this.
When you want to update a module from CPAN, the tool looks at that module's $VERSION (well VERSION sub). If that module doesn't have a version, that's undef. When CPAN.pm (or other tools maybe) looks to see if there is a more recent version, it does not find one it thinks is more recent because the latest version is also undef.
A concrete example is Mojo::UserAgent. Mojolicious only versions the main module. The other modules do not have versions. I typically do a lot of web client stuff, so my work product doesn't care about the web server side of Mojo. People using some of my stuff might not even know there is a different part of Mojo aside from the client stuff (they might even know Perl).
If I run cpan Mojo::UserAgent, it compares my currently installed version (undef) to the latest on CPAN (undef). This means that CPAN.pm guesses that I do not have something earlier than the latest available.
$ cpan -D Mojo::UserAgent
Mojo::UserAgent
-------------------------------------------------------------------------
(no description)
S/SR/SRI/Mojolicious-9.28.tar.gz
/usr/local/perls/perl-5.36.0/lib/site_perl/5.36.0/Mojo/UserAgent.pm
Installed: undef
CPAN: undef up to date
Sebastian Riedel (SRI)
kraihx#googlemail.com
Now, I have this special knowledge that this distro does this. But someone merely using something I wrote with Mojo::UserAgent probably doesn't. If they need Mojolicious 8 to get particular CSS selectors then, how do they know that?
Normally, you can specify a minimum version of a module:
use File::Glob 8.0;
I can't require a minimum version of Mojo::UserAgent:
use Mojo::Usergent 8.0;
I get a weird error (there are other import issues and design decisions going along with this):
Can't locate 8.pm in #INC (#INC contains: ...)
This isn't a big deal because Mojolicious is mostly self-contained, so I can specify its minimum version:
use Mojolicious 8.0;
But, I have to know how all of this works, how that distro works, and how Perl works to do that. For the casual user of my stuff who just wants to get work down, including modifying what I've done, they have to discover some lore to know this. They have to know how CPAN.pm or PAUSE decides what the latest version is, and so on.
Furthermore, this is a problem for modules in a distribution that don't change and have no logical reason for a version bump. Well, they do because they have an implicit dependency on the main module that does have a version, but we're not in the habit of declaring that version. Consider, for example, some module that works with an API that has changed, but the meat of the change is in another module it uses. The module you use directly has not changed its literal text, but it can't work as is because the world is different. But, since the literal text in that file hasn't changed so we typically don't bump the version.
Maybe we should declare that main module dependency, but that's a lot of work. It adds some seemingly gratuitous churn to files, which is annoying in repo history. Ah hell, now that I've thought of that, I need to fix this in some distros. Some-not all. I don't know. Something to think about.
Of course, I can document all of this in the distro, script, or whatever. But we know that I can say it and they can read it and still not appreciate the importance. And, by this time we're a far way away from "just working".

CPAN only requires a version for the distribution, and you have provided that using VERSION_FROM.
There are a variety of practices.
Only the "main module" has a version.
Each file has its own version.
Each file shares the distro's version.
CPAN evals the line that sets $VERSION, so the following should work if it's all on one line:
use PDL::IO::Touchstone; $VERSION = $PDL::IO::Touchstone::VERSION;

Related

What is the real purpose of the $VERSION variable in Perl?

I've read that it's considered good practice to always include a $VERSION number in all of your Perl scripts, but I never fully understood the purpose.
What do you really gain by including a version number in your script?
You can specify a minimum version of a module in the use statement:
use Foo 1.23;
When Perl loads that module, it looks at the $VERSION variable to check that it's equal to or greater than that number. That way you get the right set of features and fixes.
The CPAN clients use $VERSION to determine if you are up-to-date or if you should install the latest version of a module.
For programs or scripts, you typically don't need a $VERSION.
Only modules, and only those pushed to CPAN really benefit from specifying $VERSION. There's not much use for it in a script except to use a familiar name rather than inventing a new one if you want the script's version to be accessible.
Primary uses:
CPAN uses it as the version of the distribution, allowing it to index different versions of the same distribution.
cpan and cpanm uses it to check if an installed module is of sufficiently high level to meet the required minimum version of a dependency.
As brian d foy mentioned, it can also be checked by use Foo 1.23;, but most people avoid this in favour of specifying the required versions of dependencies in their distribution's META file. This allows cpan and cpanm to update the dependency if needed (where use using use Foo 1.23; would cause the installation to fail during testing). Due to the lack of use of the feature, it's hardly a primary use.

Perl module installation structure and version control

I am just starting to organize some stuff on the cluster and would like some advice on it. I posted a recent question How to organize Perl modules and got some good answers about what I was doing incorrectly. I was trying to install each perl module independently by setting the PREFIX for Makefile.PL each time to /path/to/lib/module-name/module-version/installation happens here.
For e.g. for a module JSON, I installed it like this:
perl Makefile.PL --PREFIX=/path/to/lib/perl5/5.22.1/JSON/2.53
make
make test
make install
For module Data-UUID, I did it like this:
perl Makefile.PL --PREFIX=/path/to/lib/perl5/5.22.1/Data-UUID/1.221
make
make test
make install
So it made a directory JSON/2.53 in /path/to/lib/perl5/5.22.1 and that's where it installed the package. But because I change the PREFIX for each individual module, I have to set the PATH in the bash_profile accordingly, which is kind of messy.
My main goal to do this was for version control. In a hypothetical scenario where different versions work for two of your teammates, say JSON/2.52 works for X and JSON/2.53 works for Y, how do you control for versions without having to ask them to install their versions locally? In another scenario, what if a version worked for you 3 months back and the updated version doesn't work for you anymore? How do you keep track of versions if you install everything in one directory?
I also have more questions on the module local::lib but I will post it as another question.
Thanks!
Maintaining concurrent versions of CPAN modules is asking for grief. I would suggest instead - don't, use docker for anything that's got any sort of deployment. That way you can have local installation of stuff + deps in an isolated container.
It's a bit like early days yet on docker, but they're a significant amount of enthusiasm and support for it from some very big names.
Personally I'm just using it to bundle up "mojolicious" perl webapps behind a reverse proxy, and maintain their dependencies as a self contained installation (which I can run/test/deploy autonomously)

Fixing Perl CPAN modules - where to add prerequsites and what version to use?

When I find a problem with a CPAN module where there are missing prerequisites I want to help out by providing a patch via rt.cpan or a pull request via GitHub.
Say I find a problem with ACME::MadeUpName, which is missing a prerequisite Foo::Bard. Would I just update the Makefile.PL adding ACME::MadeUpName to the PREREQ_PM section? Are there any other places I should update this?
Also, how do I figure out which version of Foo::Bard to require? Do I just go with the latest CPAN release?
If the release uses ExtUtils::MakeMaker or Module::Install, then Makefile.PL needs updating. If it uses Module::Build, then Build.PL instead. But in many cases, the Build.PL or Makefile.PL file won't be something the author manually maintains, but is built by some kind of release management tool, such as Dist::Zilla, in which case it's better to change the tool's config file rather than Build.PL or Makefile.PL (which would simply be overwritten anyway).
If you're unsure, it may be easier to simply submit a bug report without a patch for this kind of issue. It's probably a matter of 30 seconds work for the module author to fix - they're the ones who are going to be most familiar with their own build tools and release process.
In terms of versions, unless there's a particular feature of Foo::Bard used by ACME::MadeUpName that you know was added in a later version, declare a dependency on Foo::Bard 0. Maybe later on it will turn out that 0 was overly optimistic, and a higher version number is required, but that bridge can be crossed when you come to it.

Check module compatibility with different Perl versions

Is there a command line switch or any other easy way that I can check the compatibility of my module with different versions of perl? I understand that if I put use 5.6.1; at the top of a script, the script will have any features later than 5.6.1 disabled. But if I have a module which uses several other modules and so on, I need a quicker way to check what minimum version of Perl to require in my Makefile.PL or Build.PL.
Run your module against Perl::MinimumVersion.
use v5.6.1 and similar will primarily raise a compile-time error if the version of Perl in use is older than that specified. In addition, the corresponding feature bundle is enabled, and if the version is v5.11 or higher, the strict pragma is enabled as well.
use v5.6.1 will not make Perl v5.8 behave like v5.6.1, and there is no way of achieving this. Even in later versions it is only features that can be controlled using the feature pragma that are adjusted to a given version.
To test your module you will have to install each different version of Perl and test it separately
You can just install different versions of perl and test against those. I install them by hand in different locations but if you want to automate that, use perlbrew.
There might be scripts or strategies that try to find the minimum version of perl needed, but everything i tried several years ago to obtain and aggregate the minimum perl version i found clunky and unreliable. Which of course doesn't mean, that it doesn't exist or can't be done, but just that i couldn't find it.

How do I find the module dependencies of my Perl script?

I want another developer to run a Perl script I have written. The script uses many CPAN modules that have to be installed before the script can be run. Is it possible to make the script (or the perl binary) to dump a list of all the missing modules? Perl prints out the missing modules’ names when I attempt to run the script, but this is verbose and does not list all the missing modules at once. I’d like to do something like:
$ cpan -i `said-script --list-deps`
Or even:
$ list-deps said-script > required-modules # on my machine
$ cpan -i `cat required-modules` # on his machine
Is there a simple way to do it? This is not a show stopper, but I would like to make the other developer’s life easier. (The required modules are sprinkled across several files, so that it’s not easy for me to make the list by hand without missing anything. I know about PAR, but it seems a bit too complicated for what I want.)
Update: Thanks, Manni, that will do. I did not know about %INC, I only knew about #INC. I settled with something like this:
print join("\n", map { s|/|::|g; s|\.pm$||; $_ } keys %INC);
Which prints out:
Moose::Meta::TypeConstraint::Registry
Moose::Meta::Role::Application::ToClass
Class::C3
List::Util
Imager::Color
…
Looks like this will work.
Check out Module::ScanDeps and the "scandeps.pl" utility that comes with it. It can do a static (and recursive) analysis of your code for dependencies as well as the %INC dump either after compiling or running the program.
Please note that the static source scanning always errs on the side of including too many dependencies. (It is the dependency scanner used by PAR and aims at being easiest on the end-user.)
Finally, you could choose to distribute your script as a CPAN distribution. That sounds much more complicated than it really is. You can use something like Module::Starter to set up a basic skeleton of a tentative App::YourScript distribution. Put your script in the bin/ subdirectory and edit the Makefile.PL to reference all of your direct dependencies. Then, for distribution you do:
perl Makefile.PL
make
make dist
The last step generates a nice App-YourScript-VERSION.tar.gz
Now, when the client wants to install all dependencies, he does the following:
Set up the CPAN client correctly. Simply run it and answer the questions. But you're requiring that already anyway.
"tar -xz App-YourScript-VERSION.tar.gz && cd App-YourScript-VERSION"
Run "cpan ."
The CPAN client will now install all direct dependencies and the dependencies of those distributions automatically. Depending on how you set it up, it will either follow the prerequisites recursively automatically or prompt with a y/n each time.
As an example of this, you might check out a few of the App::* distributions on CPAN. I would think App::Ack is a good example. Maybe one of the App::* distributions from my CPAN directory (SMUELLER).
You could dump %INC at the end of your script. It will contain all used and required modules. But of course, this will only be helpful if you don't require modules conditionally (require Foo if $bar).
For quick-and-dirty, infrequent use, the %INC is the best way to go. If you have to do this with continuous integration testing or something more robust, there are some other tools to help.
Steffen already mentioned the Module::ScanDeps.
The code in Test::Prereq does this, but it has an additional layer that ensures that your Makefile.PL or Build.PL lists them as a dependency. If you make your scripts look like a normal Perl distribution, that makes it fairly easy to check for new dependencies; just run the test suite again.
Aside from that, you might use a tool such as Module::Extract::Use, which parses the static code looking for use and require statements (although it won't find them in string evals). That gets you just the modules you told your script to load.
Also, once you know which modules you loaded, you can combine that with David Cantrell's CPANdeps tool that has already created the dependency tree for most CPAN modules.
Note that you also have to think about optional features too. Your code in this case my not have them, but sometimes you don't load a module until you need it:
sub foo
{
require Bar; # don't load until we need to use it
....
}
If you don't exercise that feature in your trial run or test, you won't see that you need Bar for that feature. A similar problem comes up when a module loads a different set of dependency modules in a different environment (say, mod_perl or Windows, and so on).
There's not a good, automated way of testing optional features like that so you can get their dependencies. However, I think that should be on my To Do list since it sounds like an interesting problem.
Another tool in this area, which is used by Dist::Zilla and its AutoPrereqs plugin, is Perl::PrereqScanner. It installs a scan-perl-prereqs program that will use PPI and a few plugins to search for most kinds of prereq declaration, using the minimum versions you define. In general, I suggest this over scanning %INC, which can bring in bogus requirements and ignores versions.
Today I develop my Perl apps as CPAN-like distributions using Dist::Zilla that can take care of the dependencies through the AutoPrereq plugin. Another interesting piece of code in this area is carton.