|Perl: the Markov chain saw|
Everything you wanted to know about Module Version Numbers and Checkingby John M. Dlugosz (Monsignor)
|on Aug 06, 2001 at 04:22 UTC||Need Help??|
HistoryThose of us who learned pm stuff when Perl 5 came out remember that supplying a numeric argument to the import list will assert that the module is that required version. The docs used to state that this had hidden potential, of being able to sense and react to the caller's requested version, in case updates are not exactly compatible.
Times ChangeHowever, this whole mechanism has changed. As of Perl 5.6.1, there is support for "v-strings" of the form v5.6.1. This raises immediate confusion because comparing v5.6.1 against '5.6.0' will not work, as the values are in totally different and incompatible formats. Since the single-argument for of require and use to demand a minimum Perl version will take either way, it might come as a surprise that your own code and module versions do not.
So, when assigning a value to $VERSION, do you use the old way or the new way? If there is a choice, the user must know the proper way to formulate numbers when refering to your module's version.
The RealityThe way module version checking works has changed. With Perl 5.6.0, use and require can take a version number as the "indirect object". In 5.6.0 this had to be a numeric literal; in 5.6.1 it can be a v-string.
indirect-object VERSION syntaxInstead of including the version number as an argument to import, now you are to write:
The thing between the module name and the list must be a v-string or numeric literal. Anything else is rejected by the parser, unlike the normal indirect-object syntax that can be overridden with braces even if it doesn't smell like an indirect object.
For example, you cannot write:
indirect-object VERSION check semanticsIf you use this syntax, Perl calls the module's VERSION method with that value as the argument. If you write such a sub, it can do anything you want, presumably do more elaborate version range checks.
If you don't write a sub VERSION, then your module inherits one from UNIVERSAL::, and that does a simple ge test.
So, if you expect to use the indirect-object form of version checking, your $VERSION variable must be a v-string or a floating point value using the three digit convension, explained below. If you use a literal such as $VERSION= '2.30.4', the old way, then the UNIVERSAL::VERSION function croaks with an error that '2.30.4' is not numeric, pointing to your caller's use statement. Not very useful, since the value it mentions isn't found anywhere where the error is reported!
Perl 5.6.0 used numbers for versions with this feature, with floating-point numbers encoding the version with three decimal digits per part. The idea was that version X.Y.Z would be represented as X+Y/1000 + Z/1000000. So 2.3.4 is the number 2.003004. That's why you had Perl version 5.005.
This is important because UNIVERSAL::VERSION will allow mixing of v-strings with floats by converting the v-string to a float using this convention.
So, if the module defines $VERSION=1.2 and you use Module v1.2 it will compare 1.2 against 1.002, which is not what you meant. If you reverse that and declare $VERSION=1.2.1 (v is implied if there is more than one dot in a literal) and then use Module 1.1, you will learn that 1.2.1 is less than 1.1, since the former is converted to 1.002001. The module will refuse to load since it is not a high enough version!
The lesson here is that if using numbers, not v-strings, always remember the 3-digit convension. It is improper to make a module version 1.2 (a floating point literal), unless you really did mean v1.200. Some modules do this wrong, and will cause this kind of surprise.
version import numberSo what of the "old way", of listing a number in the import list? E.g.
If you are using the normal Exporter::import function, this will call your module's require_version method. If you don't define one, you inherit one from Exporter. That implementation is surprising:
It will do a numeric comparison between your request and $VERSION. What's '1.2.3' going to do with a numeric comparison? It will parse the floating-point value 1.2 and ignore the rest. Basically, it only looks at the first two parts, and silently ignores everything else! This is different from the indirect-object version check, which gives a "not numeric" error instead.
Worse yet, it will see a v-string as a numeric zero. So, if you have a v-string in $VERSION and you use the import-number form, your version check will always fail but no testing had been done!
Hmm, an old program uses this technique, and the old module had $VERSION='1.2'. The new module has $VERSION=v2.0.1, and you run your old program. It will report that you asked for 1.2 but the file contained version 0.0, and it won't load.
Where are we now?Using the supplied functions as inherited from Exporter and UNIVERSAL, the old and new forms of module version checking are fundimentally incompatible.
If you change from old to new when updating a module, and intend it to be backward compatible, it won't be if a user checks the version! New code, though, is expected to use the new form, isn't it?
With two incompatible forms, the user would have to know which to use, on a per-module basis. It's easy to figure out because doing it the wrong way results in a run-time error during the version checking code.
An easy solution would be to supply a require_version method that also uses the v-string in $VERSION but expects a old-style string as an argument. Do this with something similar to $requested= eval "v$requested"; VERSION($requested);
SurveySocket.pm defines $VERSION="1.72". Being a string, the indirect-object syntax will be rejected. For the import-list syntax, it will be taken as a floating point number, and the user needs to understand that it's a single float, not a dotted pair, when comparing them. The quotes seem like a mistake knowing that the importer compares numbers not dotted strings, but look at what it does: it prevents the new syntax from being used. That can be a way to keep backward compatibility with old modules.
Symbol.pm says $VERSION = 1.02, which does not follow the 3-digit convension. This will give improper comparisons when using the indirect-object syntax.