How to have Dist::zilla specifying min Perl version in YAML? - perl

I’d like to specify minimum required Perl version in the yaml file. While I did put perl among requirements in my dist.ini, I think I need some module to have it written to the yaml file at build time.
My dist.ini is https://metacpan.org/source/PROCH/Proch-N50-0.032/dist.ini
The kwalitee issue is described here https://cpants.cpanauthors.org/kwalitee/meta_yml_declares_perl_version
Thanks for your help.

The convention for specifying a minimum perl version for a distribution is a runtime-requires prerequisite on 'perl'. (Note: I say convention, because there is no 'perl' module, and you can't actually cause a CPAN client to install a newer Perl if it's too old.) The appropriate plugins will read this and set other things based on it, like "use 5.XXX" in the generated Makefile.PL. Instead of passing prereqs as options to [#Basic], you need to use a plugin which can set prereqs, like [Prereqs]. Here is the documentation. You would want to use it like:
[Prereqs / RuntimeRequires]
perl = 5.018
JSON = 0
You can use the Prereqs plugin additional times for different Phase+Relationship combinations, but they each must have a unique name (the part after the /).

You can do this explicitly as stated above or you can handle it automatically via Dist::Zilla::Plugin::MinimumPerlFast
# In your dist.ini:
[MinimumPerlFast]

Related

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

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;

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.

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.

Why is prefix=/path/to/perllib not enough to set PERL5LIB?

If I create a Perl module and install it as
perl Makefile.PL prefix=/path/to/perllib
Then I would expect to be able to set PERL5LIB to /path/to/perllib and the module be found. This doesn't seem to be the case. The module gets installed to /path/to/perllib/site_perl/5.8.5/, so that's what PERL5LIB has to be set to (or passed to "use lib").
Am I doing something wrong in how I install the modules or how I set PERL5LIB?
You're conflating PREFIX= and LIB=.
A distribution consists of more than just modules. Documentation, for one, isn't installed in the same directory as the modules themselves. PREFIX= tells Perl the base path for all components.
LIB= tells Perl where to install modules.
Use (for example)
perl Makefile.PL PREFIX=~ LIB=~/lib/perl5
Then you'd use
export PERL5LIB=~/lib/perl5
No, that's how it works. The prefix indicates the root of the installation, but the library directories are usually somewhere deeper within that directory structure.
If you install multiple modules with the same prefix, they should all be able to use the same PERL5LIB value, though.
Long story short, use INSTALL_BASE instead of PREFIX. It works more like the --prefix of other installers and creates a predictable install pattern. Then you can set PERL5LIB to <INSTALL_BASE>/lib/perl5 and go.
ExtUtils::MakeMaker::FAQ explains:
The behavior of PREFIX is complicated and depends closely on how your Perl is
configured. The resulting installation locations will vary from machine to machine
and even different installations of Perl on the same machine. Because of this, its
difficult to document where prefix will place your modules.
In contrast, INSTALL_BASE has predictable, easy to explain installation locations.
Now that Module::Build and MakeMaker both have INSTALL_BASE there is little reason
to use PREFIX other than to preserve your existing installation locations. If you
are starting a fresh Perl installation we encourage you to use INSTALL_BASE. If you
have an existing installation installed via PREFIX, consider moving it to an
installation structure matching INSTALL_BASE and using that instead.
What version of perl? You should only have to specify the path up to 'site_perl'. For libraries not under site_perl, you should also specify the path up to 'perllib'. More recent versions of perl (5.8 or better?) are better at letting you specify only these two directories, without having to specify version and architecture directories.

How do I tell CPAN.pm to use an application-specific mini-cpan?

Is there a way to use app-specific mini-CPANs (to ensure specific module versions for different apps and make an install work without net access). The 'standard' mini-cpan way of things relies on setting 'o conf urllist' with a file url in your CPAN/Config.pm
That's fine with one single mini-cpan. However, I need to automate having a CPAN install from a different directory for each app (local to the app), as each app has different version requirements.
The following almost works, but then has only a partially working shell, and still fetches from the web instead of using the mini-cpan from $file_url location:
use CPAN;
use FindBin qw($Bin);
my $file_url="file:///$Bin/../cpan";
CPAN::Shell->o(qw( conf urllist ),$file_url);
CPAN::shell();
You load a different configuration file for each application. The latest cpan command has a -j switch to do just that:
$ cpan -j some/app/Config.pm -i Foo::Bar
This latest feature isn't included in the CPAN.pm distribution yet since it's experimental. I've been using it for several months, however, as part of my DPAN work.
If you don't like any of that, you just have to provide your application-specific CPAN::Config module somewhere Perl will find it before it finds your personal or site-wide version. Copy your current CPAN/Config.pm into your application modules directory and ensure that the directory is first in #INC (perhaps using the PERL5LIB environment variable). CPAN.pm should find that one first and use it. It only uses the first one it finds. This is handy when the configuration is fixed, although it's a bit flexible since you can run code in the module. It's configuration as Perl code.
If that is not good enough for you, you can override CPAN::HandleConfig() in your application so CPAN.pm doesn't load any files. You then set the values yourself. This is especially handy when you have a lot of application logic to resolve to figure out some of the configurations values. The CPAN.pm configuration is just the %CPAN::Config hash, so you just have to set the right values for the appropriate keys.
In any case, you probably want to set the "connect_to_internet_ok" configuration to a false value and clean out your "urllist" to have only the local minicpans.