Archive for November, 2011

GitHub-friendly README files with ExtUtils::MakeMaker and Module::Build

Wednesday, November 30th, 2011

GitHub is a great place to host open-source projects and expose them to a wide community of developers, so it’s not surprising that more and more Perl modules are making it their home.

One of the features of GitHub is that it checks if a repository has a README file in its root directory, and displays it on the home page of the repository. This makes the README file a good place to introduce your project to the public.

GitHub also understands a number of markup languages, such as Markdown and Textile, and if the README file is in one of these formats, it will be transformed into nicely formatted HTML. One of the supported formats is POD, which means that the standard documentation of a Perl module can be used as its README file and serve as the repository’s home page (much like on CPAN).

Module::Starter, which is Perl’s recommended tool for building modules, does not create a GitHub-friendly README file — instead, the README that it produces contains installation instructions (the "perl Makefile.PL; make..." mantra) and a couple links to module resources. This means that if you want to have a GitHub-friendly README file in your module, you need to either create it yourself, or tweak your build script a bit to have it generate it for you automatically.

This article wouldn’t be particularly interesting if I told you to now go and make the README file yourself, would it? So let me show you how to do this automatically with ExtUtils::MakeMaker and Module::Build based modules (generated with Module::Starter). I will demonstrate how to create two README files: one being the POD version (named README.pod), the other one plain text (named just README).

ExtUtils::MakeMaker

Since ExtUtils::MakeMaker creates a Makefile with shell commands, you can tell it to generate the README files using two core Perl command-line utilities: perldoc (to generate POD from module’s source) and pod2text (to convert POD into plain text). Extend Makefile.PL by adding the shell commands as the PREOP attribute of the dist target configuration:

my $preop =
    'perldoc -uT $(VERSION_FROM) | tee $(DISTVNAME)/README.pod > README.pod;' .
    'pod2text README.pod | tee $(DISTVNAME)/README > README';

WriteMakefile(
    NAME                => 'Foo::Bar',
    AUTHOR              => q{Michal Wojciechowski <odyniec@cpan.org>},
    VERSION_FROM        => 'lib/Foo/Bar.pm',
    ABSTRACT_FROM       => 'lib/Foo/Bar.pm',
    ($ExtUtils::MakeMaker::VERSION >= 6.3002
      ? ('LICENSE'=> 'perl')
      : ()),
    PL_FILES            => {},
    PREREQ_PM => {
        'Test::More' => 0,
    },
    dist                => {
        COMPRESS => 'gzip -9f',
        SUFFIX => 'gz',
        PREOP => $preop,
    },
    clean               => { FILES => 'Foo-Bar-*' },
);

Now, when you run perl Makefile.PL and make dist, the two README files will be created for you.

Don’t worry if running make dist produces warnings that README and README.pod are missing — it’s no big deal, as the warnings will only be seen by you when making a distribution package, and not by the user building the module.

Module::Build

Module::Build defines a docs action, and it’s the appropriate place for the code that builds the README files. Two modules that you can use for this purpose are Pod::Select and Pod::Readme. In your Build.PL file, create a subclass of Module::Build, and define a subroutine named ACTION_docs, similar to the one shown below:

my $class = Module::Build->subclass(
    class => 'My::Builder',
    code => q{
        sub ACTION_docs {
            use Pod::Readme;
            use Pod::Select;

            my $self = shift;

            podselect({ -output => 'README.pod' },
                'lib/Foo/Bar.pm');

            my $parser = Pod::Readme->new();
            $parser->parse_from_file('README.pod', 'README');

            return $self->SUPER::ACTION_docs;
        }
    }
);

my $builder = $class->new(
    module_name         => 'Foo::Bar',
    license             => 'perl',
    dist_author         => q{Michal Wojciechowski <odyniec@cpan.org>},
    dist_version_from   => 'lib/Foo/Bar.pm',
    requires => {
        ...
    },
    configure_requires => {
        'Pod::Readme' => 0,
        'Pod::Select' => 0,
    },
    build_requires => {
        'Pod::Readme' => 0,
        'Pod::Select' => 0,
        'Test::More' => 0,
    },
    add_to_cleanup      => [ 'Foo-Bar-*' ],
    create_makefile_pl => 'traditional',
);

$builder->create_build_script();

You can now run perl Build.PL, and then ./Build docs, to build the README files.

Remember to add the Pod::Select and Pod::Readme modules to configure_requires and build_requires, as shown in the above example.

Installation instructions

Since these methods overwrite the original README file provided by Module::Starter, the installation instructions in it are also lost. It’s a good practice to always include installation instructions, so go ahead and add an INSTALL file to your module’s distribution files. It can be really simple and straight to the point:

Foo-Bar

INSTALLATION

To install this module, run the following commands:

    perl Build.PL
    ./Build
    ./Build test
    ./Build install

Finally, remember to add README.pod and INSTALL to your module’s MANIFEST.