Friday, August 05, 2011

Perl ExtUtils::MakeMaker test overrides (for Makefile.PL).

Nerd post follows; non-nerds, move along.

This took me a while to figure out, and in the end I had to dig around in the source code to figure out just what was going on, so here’s a quick note for posterity since I’m sure somebody else will have exactly this problem at some point. Caveat lector: I’m probably making a bunch of mistakes here. And thanks to brian d foy for showing us how to use test_via_harness.

Basically what it boils down to is this: you are going to have either a bunch of test files in a t directory, or a single test.pl file in the main directory. Or possibly both.

The thing you need to override is different in each case. You might just want to do both in case you change your mind later about the test setup.

Let’s say, for instance, that my special little requirement is that I can get at some shared libraries in the /opt/special/lib directory. The examples below should be understood as code inside your Makefile.PL file. And yes, they are almost identical in this case.

For test.pl override test_via_script.

sub MY::test_via_script {

    my ( $self, $perl, $script ) = @_;

    # Here is some special sauce for the environment:
    my $env_setup = 'LD_LIBRARY_PATH=/opt/special/lib';

    # Here are some Makefile variables that are handled by `make`:
    my $harness_args =
      '"$(TEST_VERBOSE)", "$(INST_LIB)", "$(INST_ARCHLIB)"';

    # Here's the command that will create the target:
    my $command =
      sprintf "\t%s %s -MExtUtils::Command::MM -e 'test_harness(%s)' %s",
      $env_setup,
      $perl,
      $harness_args,
      $script;

    return $command;

}

For t/*.t override test_via_harness.

sub MY::test_via_harness {

    my ( $self, $perl, $tests ) = @_;

    # Here is some special sauce for the environment:
    my $env_setup = 'LD_LIBRARY_PATH=/opt/special/lib';

    # Here are some Makefile variables that are handled by `make`:
    my $harness_args =
      '"$(TEST_VERBOSE)", "$(INST_LIB)", "$(INST_ARCHLIB)"';

    # Here's the command that will create the target:
    my $command =
      sprintf "\t%s %s -MExtUtils::Command::MM -e 'test_harness(%s)' %s",
      $env_setup,
      $perl,
      $harness_args,
      $tests;

    return $command;

}

Etc. & So On

I think it’s a much better practice to keep your test files in t/ anyway, and to always run with the test harness. In fact I usually skip the explicit test harness altogether and run everything with the appropriate prove command (the one that belongs to the Perl I’m using).

However, if you’re dealing with legacy code or not interested in doing real Perl stuff or otherwise just need to “get on with it” then you might well need a test.pl – and remember, test_via_script for that guy!

Here are the obvious links you should follow if you want to know more about this:

And finally, for anyone nerdier than I who is reading this: I know about package MY and I agree it’s probably the better solution, but I think it’s (even more) confusing for novices; and I assume at least some people trying to do this aren’t even Perl programmers.