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

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.

6 Comments

Luckily Dist::Zilla saves me from knowing about all this detail :) Whether I'm using E::MM or M::B, README files are generated automatically for me.

Btw, I also noticed that recently Github stopped showing warning message about my projects not having a README at the top-level directory. It used to for all my Perl projects since they use dzil and does not have a README file checked in.

I was using "make dist" for a while but over time naturally switched to "pod2text Module.pm > README". It's just a bit annoying to generate tarball with every little change in documentation as with "make dist".

Most of the core CPAN community has moved on from Module::Starter toward Dist::Zilla. See Dist::Zilla::Plugin::ReadmeMarkdownFromPod (which uses Pod::Markdown under the hood) for this task.

Initially I found browsing Perl projects on GitHub disappointing because they didn't display the module's doc like search.cpan.org.

I hope more Perl authors that use GitHub take advantage of this feature.

I do something similar with MakeMaker, except I generate the README.pod from my main module's doc:

https://gist.github.com/889041/ccabc18c68c78ddbb7aac226e32ae4b0f778f630

You might want to mention that README.pod will be installed in the given INSTALLDIR unless INSTALL.SKIP is used.

Also, why do you run the output of perldoc through tee?

Leave a comment

About Michał Wojciechowski

user-pic I blog about Perl.