Finding Prerequisites

brian_d_foy on 2002-09-29T01:56:55

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. :)