Skip Menu |

This queue is for tickets about the Module-Metadata CPAN distribution.

Maintainer(s)' notes

Attention bug reporters: issues MUST include the version of Module::Metadata that you are running that exhibit the stated symptoms. thank you!

Report information
The Basics
Id: 101773
Status: rejected
Priority: 0/
Queue: Module-Metadata

People
Owner: ether [...] cpan.org
Requestors: jens [...] jebecs.de (daily)
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 1.000026
Fixed in: (no value)



Subject: Failure when a package VERSION references a package with XSLoader::load()
When trying to add Sereal 3.005 to a Pinto repository I get an error from Module::Metadata: in [..]/Sereal-Decoder-3.005/lib/Sereal/Decoder/Constants.pm: Attempt to reload Sereal/Decoder.pm aborted. It took me hours to dig into the code, but here's the problem: - Dist::Metadata::Dist::packages_from_directory (line 213) calls Module::Metadata->package_versions_from_directory(@pvfd); where @pvfd = ( '/tmp/Sereal-Decoder-3.005', [ '/tmp/Sereal-Decoder-3.005/lib/Sereal/Decoder.pm', '/tmp/Sereal-Decoder-3.005/lib/Sereal/Decoder/Constants.pm', '/tmp/Sereal-Decoder-3.005/lib/Sereal/Performance.pm' ] ); - Sereal::Decoder is parsed correctly: our $VERSION = '3.005' - Sereal::Decoder::Constants fails: use Sereal::Decoder; our $VERSION= $Sereal::Decoder::VERSION; This happens in the following lines in M::Md::evaluate_version_line(): my $vsub = __clean_eval($eval); # line 660 # "Can't locate loadable object for module Sereal::Decoder in @INC" # --> but this is catched by eval # next this is done: local @INC = ('lib',@INC); # line 664 $vsub = __clean_eval($eval); # line 665 # "Error evaling version line ... Attempt to reload Sereal/Decoder.pm aborted." # --> dies in 667 From what I understood the problem is XSLoader::load() in Sereal::Decoder that always fails. I guess it's not wanted/possible to compile XS code just to extract the version numbers. Without having detailed knowledge about Module::Metadata, I see two possible solutions (please take my apologies in advance if this was complete rubbish): 1. In _evaluate_version_line (line 663): insert a new if-block matching /Can't locate loadable object/ where we extract the name of the wanted package from the "$VERSION= $Sereal::Decoder::VERSION" line. Now check if we already know the version number for package. To achieve this, we could store intermediate results of already extracted version numbers in a gobal variable. It can't be class attribute because the loop iterating over all files is in package_versions_from_directory() - before object instantiation. So this might get ugly. 2. Insert new if-block like above, but only mark unsatisfied dependency to be resolved later. Something like: 'Sereal::Decoder::Constants' => { 'version_from' => 'Sereal::Decoder' } Later on, we could try to resolve the version references. Option 2 is my preferred one. It could even be a standard way to resolve VERSION dependencies without use'ing referenced packages multiple times within a distribution/directory.
On 2015-01-27 09:11:01, MAXHQ wrote: Show quoted text
> When trying to add Sereal 3.005 to a Pinto repository I get an error > from Module::Metadata: > > in [..]/Sereal-Decoder-3.005/lib/Sereal/Decoder/Constants.pm: Attempt > to reload Sereal/Decoder.pm aborted. > > It took me hours to dig into the code, but here's the problem: > > - Dist::Metadata::Dist::packages_from_directory (line 213) > calls Module::Metadata->package_versions_from_directory(@pvfd); > where @pvfd = ( > '/tmp/Sereal-Decoder-3.005', > [ > '/tmp/Sereal-Decoder-3.005/lib/Sereal/Decoder.pm', > '/tmp/Sereal-Decoder- > 3.005/lib/Sereal/Decoder/Constants.pm', > '/tmp/Sereal-Decoder-3.005/lib/Sereal/Performance.pm' > ] > ); > > - Sereal::Decoder is parsed correctly: > our $VERSION = '3.005' > > - Sereal::Decoder::Constants fails: > use Sereal::Decoder; our $VERSION= $Sereal::Decoder::VERSION; > > This happens in the following lines in M::Md::evaluate_version_line(): > my $vsub = __clean_eval($eval); # line 660 > # "Can't locate loadable object for module Sereal::Decoder in @INC" > # --> but this is catched by eval > > # next this is done: > local @INC = ('lib',@INC); # line 664 > $vsub = __clean_eval($eval); # line 665 > # "Error evaling version line ... Attempt to reload Sereal/Decoder.pm > aborted." > # --> dies in 667 > > From what I understood the problem is XSLoader::load() in > Sereal::Decoder that always fails. > I guess it's not wanted/possible to compile XS code just to extract > the version numbers. > > Without having detailed knowledge about Module::Metadata, I see two > possible solutions > (please take my apologies in advance if this was complete rubbish): > > 1. In _evaluate_version_line (line 663): insert a new if-block > matching > /Can't locate loadable object/ where we extract the name of the > wanted package from > the "$VERSION= $Sereal::Decoder::VERSION" line. > Now check if we already know the version number for package. > To achieve this, we could store intermediate results of already > extracted version > numbers in a gobal variable. It can't be class attribute because > the loop iterating > over all files is in package_versions_from_directory() - before > object instantiation. > So this might get ugly. > > 2. Insert new if-block like above, but only mark unsatisfied > dependency to be > resolved later. > Something like: > 'Sereal::Decoder::Constants' => { 'version_from' => > 'Sereal::Decoder' } > Later on, we could try to resolve the version references. > > Option 2 is my preferred one. It could even be a standard way to > resolve > VERSION dependencies without use'ing referenced packages multiple > times within > a distribution/directory.
Option 3: declare your version number directly, rather than depending on code that doesn't exist (since it is not compiled): - use Sereal::Decoder; our $VERSION= $Sereal::Decoder::VERSION; + our $VERSION= '1.23'; Module::Metadata does support declaring a version depending on another module's version: https://metacpan.org/source/ETHER/Module-Metadata-1.000026/lib/Module/Metadata.pm#L663 ...but that other module has to be loadable/runnable for that to happen. This is not an uncommon situation -- e.g. I've been converting my Dist::Zilla-based repositories to having the version right in the source, rather than added in later on at build/release time, precisely to make it easier to run the code right out of the repository. However, I do see an improvement I can make to this code -- it's only adding 'lib' to @INC on recepit of the "Can't locate" error, but for the purposes of evaluating locally-built code, it really should add the blib directories as well. i.e. I can add these lines: if ( -d 'blib' ) { require blib; blib->import; } ... which now makes Serial::Decoder::Constants parsable by Module::Metadata.
On Tue Jan 27 13:38:06 2015, ETHER wrote: Show quoted text
> On 2015-01-27 09:11:01, MAXHQ wrote: > However, I do see an improvement I can make to this code -- it's only > adding 'lib' to @INC > on recepit of the "Can't locate" error, but for the purposes of > evaluating locally-built > code, it really should add the blib directories as well. i.e. I can > add these lines: > > if ( -d 'blib' ) { require blib; blib->import; } > > ... which now makes Serial::Decoder::Constants parsable by > Module::Metadata.
This only works if the module has already been built, which it may not be when you are trying to extract the version numbers. It also won't work in a Safe compartment. PAUSE won't be able to extract a version from the module for both of these reasons, and in the future Module::Metadata is also planned to use a Safe compartment as well. The real solution here is to always just put the version directly in the file.
On 2015-01-27 13:23:55, haarg wrote: Show quoted text
> This only works if the module has already been built, which it may not > be when you are trying to extract the version numbers. It also won't > work in a Safe compartment.
Right, if I make this change now (which I think, given the current state of the code, is validish - if we are going to add lib to @INC, we should add blib/lib as well) we'll probably have an unresolvable regression when we move to using a Safe compartment.
Adding reference to same problem (I suppose) on Alien::SVN module, this module provides Subversion bindings through SVN::*, and Module::Metadata couldn't get right VERSION from SVN::Core module as you can see in the example: $ perl -MModule::Metadata -e 'print Module::Metadata->new_from_file("/usr/local/lib/perl/5.14.2/SVN/Core.pm")->version' $ 0 The right version installed on my system is 1.8.11: $ perl -MSVN::Core -e 'print $SVN::Core::VERSION' $ 1.8.11 I'm using Debian Gnu/Linux Wheezy and Perl v5.14.2.
On 2015-07-23 20:23:59, joenio wrote: Show quoted text
> Adding reference to same problem (I suppose) on Alien::SVN module, > this module provides Subversion bindings through SVN::*, and > Module::Metadata couldn't get right VERSION from SVN::Core module as > you can see in the example:
This isn't the same problem as what was originally described. SVN::Core is simply defining its version based on external code, which static analysis will not see because it doesn't run the file. I've opened https://github.com/evalEmpire/Alien-SVN/issues/12 for it.
On 2015-01-27 13:41:46, ETHER wrote: Show quoted text
> On 2015-01-27 13:23:55, haarg wrote: >
> > This only works if the module has already been built, which it may > > not > > be when you are trying to extract the version numbers. It also won't > > work in a Safe compartment.
> > Right, if I make this change now (which I think, given the current > state of the code, is validish - if we are going to add lib to @INC, > we should add blib/lib as well) we'll probably have an unresolvable > regression when we move to using a Safe compartment.
Rejecting this bug as "by design" -- $VERSIONs should not be declared containing references to other modules. The version must be extractable from a single line of code run in isolation (nothing else loaded).