views:

208

answers:

2

I'm using Module::Build to perform build, test, testpod, html, & install actions on my Perl module that I'm developing. The HTML files that are generated are okay, but I'd be much happier if I could somehow configure Module::Build to use the perltidy -html formatting utility instead of its own HTML formatter.

Anyone know of a way I can replace the HTML formatter that comes with Module::Build with the prettier perltidy HTML formatter?

Addendum: When I said "replace" above, that was probably misleading. I don't really want to write code to replace the html formatter that comes with Module::Build. I really want to know if Module::Build has any other HTML formatter options. The HTML it generates is so plain and generic looking. It's so boring. I like perltidy's output a lot.

Here is how I got it working right now in a build script that I wrote, but it's totally a hack ... falling out to the command line perltidy script:

use strict;
use warnings;

# get list of files in directory
my $libLocation = "lib/EDF";
opendir( DIR, $libLocation );
my @filenameArray = readdir(DIR);

# iterate over all files to find *.pm set
for my $file (@filenameArray) {
    if ( $file =~ m/      # matching regex
                      \.  # literal period character
                      pm  # the pm file extenstion
                  /x      # end of regex
       )
    {

        my $return = `perl D:/Perl/site/bin/perltidy -q --indent-columns=4 --maximum-line-length=80 -html -opath blib/libhtml2 -toc $libLocation/$file`;

        if ($return eq "") {
            print "HTMLized " . $file . "\n";
        }
        else {
            print "Error: " . $return . "\n";
        }

    }

}

But I was really hoping there was a way to use Module::Build and just tell it with a flag or an argument or whatever to tell it to use a different HTML formatter. I guess that's a pipe dream, though:

use strict;
use warnings;
use Module::Build;

my $build = Module::Build->resume (
  properties => {
    config_dir => '_build',
  },
);

$build->dispatch('build');
$build->dispatch('html', engine => 'perltidy');

or maybe:

$build->dispatch('htmltidy');
+1  A: 

Well, the action is implemented in

htmlify_pods

in Module::Build::Base.

It should be possible to override that method.

Much Later ...

Here is my attempt (tested only once):

package My::Builder;

use strict;
use warnings;

use base 'Module::Build';

sub htmlify_pods {
  my $self = shift;
  my $type = shift;
  my $htmldir = shift || File::Spec->catdir($self->blib, "${type}html");

  require Module::Build::Base;
  require Module::Build::PodParser;
  require Perl::Tidy;

  $self->add_to_cleanup('pod2htm*');

  my $pods = $self->_find_pods( 
      $self->{properties}{"${type}doc_dirs"},
      exclude => [ Module::Build::Base::file_qr('\.(?:bat|com|html)$') ] );
  return unless %$pods;  # nothing to do

  unless ( -d $htmldir ) {
    File::Path::mkpath($htmldir, 0, oct(755))
      or die "Couldn't mkdir $htmldir: $!";
  }

  my @rootdirs = ($type eq 'bin') ? qw(bin) :
      $self->installdirs eq 'core' ? qw(lib) : qw(site lib);

  my $podpath = join ':',
                map  $_->[1],
                grep -e $_->[0],
                map  [File::Spec->catdir($self->blib, $_), $_],
                qw( script lib );

  foreach my $pod ( keys %$pods ) {

    my ($name, $path) = File::Basename::fileparse($pods->{$pod},
        Module::Build::Base::file_qr('\.(?:pm|plx?|pod)$'));
    my @dirs = File::Spec->splitdir( File::Spec->canonpath( $path ) );
    pop( @dirs ) if $dirs[-1] eq File::Spec->curdir;

    my $fulldir = File::Spec->catfile($htmldir, @rootdirs, @dirs);
    my $outfile = File::Spec->catfile($fulldir, "${name}.html");
    my $infile  = File::Spec->abs2rel($pod);

    next if $self->up_to_date($infile, $outfile);

    unless ( -d $fulldir ){
      File::Path::mkpath($fulldir, 0, oct(755))
        or die "Couldn't mkdir $fulldir: $!";
    }

    my $path2root = join( '/', ('..') x (@rootdirs+@dirs) );
    my $htmlroot = join( '/',
             ($path2root,
              $self->installdirs eq 'core' ? () : qw(site) ) );

    my $fh = IO::File->new($infile) or die "Can't read $infile: $!";
    my $abstract = Module::Build::PodParser->new(fh => $fh)->get_abstract();

    my $title = join( '::', (@dirs, $name) );
    $title .= " - $abstract" if $abstract;

    my %opts = (
        argv => join(" ", 
            qw( -html --podflush ),
            "--title=$title",
            '--podroot='.$self->blib,
            "--htmlroot=$htmlroot",
            "--podpath=$podpath",
        ),
        source => $infile,
        destination => $outfile,
    );

    if ( eval{Pod::Html->VERSION(1.03)} ) {
      $opts{argv} .= ' --podheader';
      $opts{argv} .= ' --backlink=Back to Top';
      if ( $self->html_css ) {
          $opts{argv} .= " --css=$path2root/" . $self->html_css;
      }
    }

    $self->log_info("HTMLifying $infile -> $outfile\n");
    $self->log_verbose("perltidy %opts\n");
    Perl::Tidy::perltidy(%opts);    # or warn "pod2html @opts failed: $!";
  }
}
1;

** To use it .. **

#!/usr/bin/perl

use strict;
use warnings;

use My::Builder;

my $builder = My::Builder->new(
    module_name => 'My::Test',
    license     => 'perl',
);

$builder->create_build_script;
Sinan Ünür
That sounds like work I'm not comfortable taking on. I was hoping there was just a flag I could set or something. :-)
Kurt W. Leucht
It probably isn't that hard. I'll take a look later.
Sinan Ünür
This is interesting. I'm assuming that you just changed the last line from whatever it used to be to this perltidy call. My worry here is that this is for a work project, and I'd be copying an existing CPAN subroutine and freezing it forever and never getting the benefit of any future Module::Build upgrades to this particular routine. Not a really good software engineering proactice. Might not be a big deal for this one small case, though. I'll consider it. Thanks for showing me how to do it.
Kurt W. Leucht
I also changed how the options were built. I also filed http://rt.cpan.org/Ticket/Display.html?id=45830Your comment about upgrades is confusing to me. I am not recommending that you go and actually edit the installed Module-Build files. This is a subclass of Module::Build that overrides one method. If you upgrade the installed Module-Build, you get to inherit from the upgraded library. The only thing that is overridden is this particular method. In the mean time, if a future version Module-Build provides the required functionality, you get rid of My::Builder.
Sinan Ünür
+1  A: 

It's very easy to define new Module::Build actions that you can call with dispatch, and there are plenty of examples in the Module::Build documentation. Define an action to handle your new step:

sub ACTION_htmltidy
    {
    my( $self ) = @_;

    $self->depends_on( ...other targets... );

    require Perl::Tidy;

    ...do your damage...
    }

If you want another action to use yours, you can extend it so you can make the dependency:

sub ACTION_install
    {
    my( $self ) = @_;

    $self->depends_on( 'htmltidy' );

    $self->SUPER::install;
    }
brian d foy
Thanks for showing this. Definitely elegant.
Sinan Ünür
So in the "do your damage" section, I would put in my code that reads my library directory and parses out the list of PM files and then processes each PM file using more code that I write? Aren't I doing a bunch of work there in the "do your damage" section that Module::Build already knows how to do? Doesn't seem like the right thing to do to me.
Kurt W. Leucht
@Kurt: you'd be using the Module::Build API, so you'd be reusing the stuff that Module::Build already knows how to do.
brian d foy