amackey has asked for the wisdom of the Perl Monks concerning the following question:

I'd like all the classes in my large package to all have the same $VERSION (as defined in $My::Package::Version::VERSION). This is easy with Exporter and @EXPORT = qw($VERSION); all of my classes must then simply "use My::Package::Version".

But I wanted to make it a little easier by only having to add the "use" statement to the small handful of base classes (rather than the nearly hundred other classes that inherit from them). I tried to work up this idea:

package My::Package::Version; use strict; use vars qw(@ISA $VERSION @EXPORT); @ISA = qw(Exporter); $VERSION = 3.14; @EXPORT = qw($VERSION); sub import { # try to handle multiple levels of inheritance: my $i = 0; my $pkg = caller($i); while ($pkg && $pkg =~ m/^My::Package::/o && !defined ${"$pkg::VERSION"}) { __PACKAGE__->export_to_level($i+1); $pkg = caller(++$i); } } 1;
The problem, of course, is that this doesn't work; even though My::Package::A uses My::Package::B (and ISA ::B), which itself uses My::Package::Version, only $B::VERSION gets set, not $A::VERSION (since caller(2) becomes "main", not My::Package::A when A compiles B).

Is there some tricky solution to this?

Thanks,

-Aaron

Replies are listed 'Best First'.
Re: variable "inheritance"; viral $VERSION
by Abigail-II (Bishop) on Mar 09, 2003 at 03:17 UTC
    Unfortunally, CPAN doesn't know how to deal with that. It looks for lines that assign, or appear to assign to a variable $VERSION. If you are inheriting $VERSION, CPAN will think the module doesn't have a version.

    Whether that's going to be a problem for you or not, I do not know.

    Abigail

      Right, except that all of these classes get packaged together in the same CPAN module (hence the My::Package nomenclature). My understanding is that Makefile.PL's VERSION_FROM does actually compile a module, and read its $VERSION variable. Does it really only parse the file, not compile it?
        CPAN does track the VERSION of each .pm file, and it will not install a .pm file if it thinks the one already installed is newer, regardless what the version number is of the package. (In fact, as far as I can tell, there's no relationship between the version of the package (the number in the tar file), and the versions of the .pm files).

        As far as CPAN is concerned, all the .pm files without a VERSION assignment in the file itself do not have a VERSION. It'll list them as undef. Whether it will then actually replace an already installed file with a newer one, I do not know. For me, CPAN usually doesn't do what I mean, so I wouldn't dare take the risk.

        Abigail

        The things in PREREQ are required and then $VERSION checked for. The version specified using VERSION_FROM is retrieved by parsing the file. From the ExtUtils::MakeMaker docs:

        Instead of specifying the VERSION in the Makefile.PL you can let MakeMaker parse a file to determine the version number. The parsing routine requires that the file named by VERSION_FROM contains one single line to compute the version number. The first line in the file that contains the regular expression

        /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/
Re: variable "inheritance"; viral $VERSION
by Anonymous Monk on Mar 09, 2003 at 03:08 UTC
    Why would you like this?

    The point of $VERSION is to document what version of the code you have. When the $VERSION of one package reflects the version of a file appearing elsewhere you make it even easier for $VERSION to not represent what it is supposed to.

    Why do you want a $VERSION with these properties?

    If you want to do something so counterproductive, you certainly can. One appropriately silly way is to create a module named "am.pm" with a method named "I" which loads a module, inherits from it, and sets $VERSION. Then in every module you only need:

    package Bar; use am; I am "Foo"; ...
    The implementation of am::I is left as an exercise for the reader.
      You didn't read my original post carefully. I don't need to implement "I am 'Foo'", I can simply "use My::Package::Version" wherever I want it.

      The classes in my package all have "My::Package::*" names, and I only want $VERSION to propagate through all of these packages, not any other. I want a simple way to keep $VERSION "synchronized" throughout all classes of my package, without having to type something in all of them.

        I did read your original post.

        My suggestion adds typing. Yes. It also removes typing. You no longer need the "use Foo" line. You no longer need to declare your @ISA. The typing saved exceeds the typing added. I consider that reasonable if your intention is to save typing.

        Now can you explain why on Earth you would want this?

Re: variable "inheritance"; viral $VERSION
by bbfu (Curate) on Mar 09, 2003 at 07:56 UTC

    The answer to your specific question as to why your code doesn't export the version to My::Package::A is that, as you already noted, caller(2) is main for some reason. However, if you do a full call stack trace from the import, you will see that My::Package::A is included but there are two instances of main in between it. I don't really have any idea why that is (perhaps a more enlightened monk can explain) but this causes the condition of your while loop to fail and thus not go futher into the call stack.

    I believe what you want, and probably intended to do, is to simply skip packages not begining with My::Package but continue the loop. Like so:

    ./My/Package/Version.pm:

    package My::Package::Version; use strict; use vars qw(@ISA @EXPORT $VERSION); use Exporter; @ISA = qw(Exporter); @EXPORT = qw($VERSION); $VERSION = 3.14; sub import { my $i = 0; my $pkg = caller(0); print "Import Caller Trace:\n"; while($pkg) { print " Called from package $pkg\n"; # Note that these tests have been moved from the # loop condition to inside the loop so the loop # will not terminate if it fails. if($pkg =~ /^My::Package::/ and not defined ${"$pkg\::VERSION"}) { print " (Exporting VERSION to $pkg)\n"; __PACKAGE__->export_to_level($i+1); } $pkg = caller(++$i); } }

    ./My/Package/B.pm:

    package My::Package::B; use My::Package::Version; print "My::Package::B loaded.\n"; 1;

    ./My/Package/A.pm:

    package My::Package::A; use My::Package::B; @ISA = ('My::Package::B'); print "My::Package::A loaded.\n"; 1;

    ./tst.pl:

    #!/usr/bin/perl use lib '.'; use My::Package::A; print "\$My::Package::A::VERSION = $My::Package::A::VERSION\n";

    Output of ./tst.pl:

    Import Caller Trace: Called from package My::Package::B (Exporting VERSION to My::Package::B) Called from package main Called from package main Called from package My::Package::A (Exporting VERSION to My::Package::A) Called from package main Called from package main Called from package main Called from package main Called from package main My::Package::B loaded. My::Package::A loaded. $My::Package::A::VERSION = 3.14

    bbfu
    Black flowers blossum
    Fearless on my breath

      Excellent, thank you. I figured once I had hit "main", that the party was over. I never thought to keep looking.
Re: variable "inheritance"; viral $VERSION
by chromatic (Archbishop) on Mar 09, 2003 at 02:57 UTC

    How about inheriting a VERSION() method? That's nearly free of typing.

      That doesn't help people that want to "use My::Package::A 3.14" to get versioning.
Re: variable "inheritance"; viral $VERSION
by Zaxo (Archbishop) on Mar 09, 2003 at 06:59 UTC
CVS
by zby (Vicar) on Mar 09, 2003 at 03:59 UTC
    How about using a versioning system like CVS (or Subversion)? Perhaps it would eliminate your problems.
      Yes, if I used a cvs tag of "3.14" and used a BEGIN block to compile my CVS keywords into $VERSION.

      Otherwise, CVS revision numbers between different classes will not correspond to a true "release version 3.14" value for $VERSION.

        OK - that was just a suggestion. What I meant was actually that perhaps when you use the CVS tags you would not need a version variable.