A couple of weeks ago I released a bunch of Test:: modules. Somewhere after The Perl Conference I started to convert all of my tests to Test::More and along the way I found I needed a lot of other stuff too.
With such rapid releases I made plenty of mistakes, and the most egregious one, missing dependencies for PREREQ_PM, caused almost all of the new modules to FAIL CPAN Testers. I think one base module missed a dependency, and it went south from there.
Every time something like this happens, I write a some sort of test to detect it. In this case, I need to determine if all of the modules I use in the distribution show up in Makefile.PL.
I started fooling around with %INC, the special hash that keeps track of loaded modules. That hash, however, keeps track of all loaded modules. Not only does it have keys for the modules that I explicitly loaded, but all of the modules that they loaded too. I do not need to know about those as long as I trust that their distributions list all of their prerequisites.
While I was looking for something else on CPAN, I came across Module::Info which reports all of the modules that I explicitly load. It actually walks the script's op-tree which is much better than regular expressions.
Module::Info gets me almost all of the way, but I need to filter that list to remove the modules supplied by the distribution. Since Business::ISBN depends on Business::ISBN::Data but both come from the same distribution, my prerequisite reporter should not warn me that Business::ISBN depends on Business::ISBN::Data. Now the fun starts.
I probably could not do this with other languages, but Perl lets me muck with namespaces that do not belong to me. In this case, I want to execute Makefile.PL but make ExtUtils::MakeMaker do something different. I do not need another Makefile, but the keys in the PREREQ_PM anonymous hash. I simply redefine the WriteMakeFile function.
sub ExtUtils::MakeMaker::WriteMakefile
{
my %hash = @_;
my $hash = $hash{PREREQ_PM};
@prereqs = sort keys %$hash;
}
To see which modules are in the distribution, I use File::Find::Rule to look inside blib, the build library.
my @files =
map {
my $x = $_;
$x =~ s|^$_[0]/?||;
$x =~ s/\.pm$//;
$x =~ s|/|::|g;
$x;
}
File::Find::Rule->file()->name( '*.pm' )->in( 'blib/lib' );
Any other module that Module::Info finds and does not show up in PREREQ_PM or blib I need to put in PREREQ_PM before I release the distribution.
I combined all of this in Test::Prereq and I have been using it for about a month. I have not had that same annoying problem again, and when I add to my modules, sometimes using a new module, my test suite immediately finds and tells me to fix the problem. I do not have to remember to fix PREREQ_PM or which modules I need to add. Test::Prereq does both of those for me.