ErikR
ErikR

Reputation: 52039

getting "defined" modules from a CPAN dist file?

Given a CPAN dist file (e.g. like Acme-Chef-1.01.tar.gz), what is the algorithm to determine what module-versions are "defined" (or present) in the dist file?

For instance, in the 02packages.details.txt file, there are four lines which correspond to this dist file:

Acme::Chef                         1.01  S/SM/SMUELLER/Acme-Chef-1.01.tar.gz
Acme::Chef::Container              1.00  S/SM/SMUELLER/Acme-Chef-1.01.tar.gz
Acme::Chef::Ingredient             1.00  S/SM/SMUELLER/Acme-Chef-1.01.tar.gz
Acme::Chef::Recipe                 1.00  S/SM/SMUELLER/Acme-Chef-1.01.tar.gz

I basically want to know how those lines are generated.

Is the procedure something like:

  1. find all of the .pm files in the dist file
  2. load each of the .pm files and print out ${ "${pkg}::VERSION"} where $pkg is the package name corresponding to the .pm file name (i.e. if the .pm file name is Foo/Bar.pm then $pkg is Foo::Bar.)

Is there code which does this indexing procedure?

Do you really have to load the module in order to determine what its version is?

Upvotes: 4

Views: 73

Answers (1)

hobbs
hobbs

Reputation: 239871

The actual code that does it for PAUSE is on GitHub here. The subroutines that parse package and version declarations are in lib/PAUSE/pmfile.pm (packages_per_pmfile and parse_version). This is authoritative as far as what happens to make CPAN work, but it's not code that you'd ever want to use for yourself — PAUSE is almost 20 years old and even after some recent cleanup it's still pretty gross.

Instead, look at Module::Metadata. You give it a file, and it provides a pretty simple interface to discover the names of the packages inside of that file and what their versions might be.

It's about as simple as:

my $mm = Module::Metadata->new_from_file("My/Module.pm");
for my $package ($mm->packages_inside) {
    print "$package: ", $mm->version($package), "\n";
}

And indeed this "one-liner" works:

find Acme-Chef-1.01 -name \*.pm \
| perl -MModule::Metadata -ln \
-e 'my $mm = Module::Metadata->new_from_file($_); ' \
-e 'print "$_: ", $mm->version($_) for $mm->packages_inside' \
| sort

and outputs:

Acme::Chef: 1.01
Acme::Chef::Container: 1.00
Acme::Chef::Ingredient: 1.00
Acme::Chef::Recipe: 1.00

Upvotes: 4

Related Questions