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
.
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.
Thanks for your comments. I've heard a lot of praise about Dist::Zilla, but haven't used it yet -- now that I know that it does such a good job of handling READMEs, I'm even more inclined to try it.
Anyway, I hope some people who are still using ExtUtils::MakeMaker or Module::Build will find this post useful.
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 theREADME.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 givenINSTALLDIR
unlessINSTALL.SKIP
is used.Also, why do you run the output of
perldoc
throughtee
?@sshaw: I know that gist of yours, as I found it when I was first looking for a method to generate a README.pod file with MakeMaker -- so thanks for the inspiration :)
As for
tee
, it's there to make the generated README.pod file go both into$(DISTVNAME)
and the current directory.