$VERSION Confusion

| 2 Comments

What's the right way to declare version numbers in your modules?

One obvious approach is:

use vars '$VERSION';
$VERSION = 1.0.1;

If you're using Perl 5.6.x or newer, you might instead write:

our $VERSION = 1.0.1;

That's obvious, but wrong; version specifiers aren't numbers. To work correctly, you must quote them:

use vars '$VERSION';
$VERSION = '1.0.1';
...
# alternately
our $VERSION = '1.0.1';
# or
$PACKAGE::VERSION = '1.0.1';

What the Documentation Said

If you look in the ExtUtils::MakeMaker documentation under the VERSION_FROM section, you'll see a special case heuristic the Perl 5 toolchain uses to determine the version number of a given module. To the best of my current understanding, this is only necessary if you haven't already specified the version number in a declarative form -- in a META.yml file generated by ExtUtils::MakeMaker or Module::Build, for example.

MakeMaker's particular magic uses a regular expression to match a single line in the file. Thus you must abut the vars version:

use vars '$VERSION'; $VERSION = '1.0.1';

If you've worked with Perl for a while, you may have encountered v-strings, a feature intended to encapsulate the differences between numbers, strings, and version numbers:

our $VERSION = v1.0.1;

However, the version documentation recommends against their use: their meaning has changed between major versions of Perl 5, they were only reliable with three-part version numbers.

Careful hackers may notice that the MakeMaker documentation never suggested using three-part version numbers. (The MakeMaker maintainer called me out on this in a discussion.) Careful documentation readers may recall the advice in Guidelines for Module Creation in perlmodlib:

To be fully compatible with the Exporter and MakeMaker modules you should store your module's version number in a non-my package variable called $VERSION. This should be a floating point number with at least two digits after the decimal (i.e., hundredths, e.g, $VERSION = "0.01" ). Don't use a "1.3.2" style version. See Exporter for details.

The Exporter documentation says:

The Exporter module will convert an attempt to import a number from a module into a call to $module_name->require_version($value). This can be used to validate that the version of the module being used is greater than or equal to the required version.

The Exporter module supplies a default require_version method which checks the value of $VERSION in the exporting module.

Since the default require_version method treats the $VERSION number as a simple numeric value it will regard version 1.10 as lower than 1.9. For this reason it is strongly recommended that you use numbers with at least two decimal places, e.g., 1.09.

Easy as pie, right?

Don't use version strings. Quote version numbers. Don't think of them as numbers; they don't follow the same rules as numbers (1.1 is different from 1.10). Write them all on one line. Above all, be consistent with everyone else's mixing and matching of all of the different ways to declare and consume version numbers.

What the Code Did

Don't fret; it's easy to get things wrong. Given a package Foo:

package Foo;

our $VERSION = '1.0.1';

1;

use Foo '1.0.2'; silently succeeds with Perl 5.10 and bleadperl.use Foo 1.0.2; is an error. So is use Foo v1.0.2;

If you change the version in Foo to 1.23.0, use Foo 1.23, both Perl 5.10 and bleadperl will fail with the error Foo version 1.23 required--this is only version 1.23.0.

Here's another fun one. What will this produce?

{
    package Bar;
    our $VERSION = v72.69.76.80;
}

package main;

say $Bar::VERSION;
say Bar->VERSION;

You get partial credit for guessing that the method call will produce v72.69.76.80. You get a week's supply of analgesic for guessing that the variable stringifies to HELP.

The version module exists to ameliorate some of this pain. The recommended approach to solving this madness is the one-liner:

use version; our $VERSION = version->declare("v1.2.3");

... though you can use the slightly shorter version:

use version; our $VERSION = qv("v1.2.3");

Note that this only helps on the declaration side.

Of course, if you've (wisely) taken the advice to eschew everything with the stench of v-strings, you can use the slightly-less recommended approach:

use version; our $VERSION = version->parse("1.02");

Yes, the quotes are necessary.

Of course, the version documentation suggests that you don't need to use the module if you use a simple dotted-integer version number with a single decimal place:

our $VERSION = 1.02;

Note that Perl 5 version numbers do not follow this suggestion. (Note that I explicitly avoided talking about alpha versions and the various heuristics and denotations thereof.)

What Could Be

In a thread on version 0.77, David Golden suggested an alternate, declarative package version syntax:

package Foo::Bar 1.00;

In an unrelated thread, Nicholas Clark gave a rule of thumb for borrowing syntax from Perl 6 for corresponding features in Perl 5:

The argument in favour of adding a := operator to give the := syntax is that Perl 6 is using that syntax, so it will become familiar, and that it's better to converge than diverge.

The Versioning section in the Perl 6 modules synopsis discusses many issues of versioning before offering a declarative syntax:

class Pooch:name<Dog>:auth<cpan:JRANDOM>:ver<1.2.1>

Thus I wonder if David Golden's suggestion might provide a nice way out of this mess:

package Foo::Bar :ver(1.00);

This has the advantage of being declarative, so it's much easier to parse (if EUMM and other utilities still need to parse it). It's much less code (and fewer expressions to write, understand, and maintain) than the package global variable version. Its effects can occur at compilation time. It can't possibly break existing code because it was impossible to overload the package keyword (at least without Devel::Declare magic or a source filter).

There are two downsides. It's only available to new code running a patched version of Perl 5. This is likely only corehackers Perl 5 for the forseeable future. (Update: This is for two important reasons. First, it's experimental code that may not ever work out. Second, Dave Mitchell asked that p5p concentrate on releasing Perl 5.10.1 for the near future, and I don't want to distract from that with what could be a bikeshed discussion.). It also needs to exist in parallel with other version declaration methods until and unless they switch over to the nicer version.

Given the confusion surrounding package version declarations, having one clearly obvious way to do things seems like a big improvement.

(Schwern suggested also patching the documentation for consistency, but even after writing all of this, I'm still unsure of where to start and what to say.)

2 Comments

I don't think this helps, but I thought it *might* just be another example to throw into the mix:

from http://movabletype.googlecode.com/svn/trunk/lib/MT/Meta.pm

perl -MModule::Build::ModuleInfo -e "Module::Build::ModuleInfo->new_from_file('/usr/share/perl5/MT/Meta.pm');"

bails out with a fatal exception. I'm not smart enough to figure out if that's a bug with MT, a failing with Module::Build or neither or both or something else.

All I know is that Module::Build::ModuleInfo gets it right for virtually every other CPAN module I have installed, but throws a fatal exception trying to grok the MT code. Grrr.

ps. (and even less related) ... the reason I care is a silly little side project ... http://psnic.sysmonblog.co.uk

@mintywalker, given there's no $VERSION defined explicitly for that module, my diagnosis is that the problem is with MT::Meta.

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide

Categories

Pages

About this Entry

This page contains a single entry by chromatic published on July 16, 2009 2:13 PM.

Expressing Visions for Perl 5 was the previous entry in this blog.

Deprecated Pointy Bits is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.


Powered by the Perl programming language

what is programming?