Dist::Zilla, Pod::Weaver and bin
I use Dist::Zilla for managing my distributions. It's awesome and useful, although it lacks some bits of documentation every now and then; this lack is compensated in other ways, e.g. IRC.
I also use the PodWeaver plugin to automatically generate boilerplate POD stuff in the modules. Some time ago I needed to add some programs to a distribution of mine (which I also managed to forget at the moment, but this is another story), and this is where I got hit by all the voodoo.
The first program was actually a Perl program, consisting of a minimal script to call the appropriate run()
method in one of the modules of the distro:
$ cat bin/perl-program
#!/usr/bin/env perl
use prova;
prova->run();
This led Dist::Zilla to complain like this:
$ dzil build
[DZ] beginning to build prova
[DZ] guessing dist's main_module is lib/prova.pm
couldn't determine document name for bin/perl-program at ...
which isn't the best advice in the world (it actually complains about Pod::Weaver), but let's ignore it for the moment. After a bit of googling - or whatever, I actually don't remember - I found that there were basically two options:
put an explicit
package
declaration in the driver program, like this:$ cat bin/perl-program #!/usr/bin/env perl package prova; use prova; prova->run();
put a comment with a PODNAME:
$ cat bin/perl-program #!/usr/bin/env perl # PODNAME: prova use prova; prova->run();
The latter seems slightly less dumb so I opted for it. I say slightly because IMHO the bottom line should be that the name is equal to the filename, but this is (again) another story. Yes, I know, it's open source and I can propose patches - did I say I'm not complaining?
Anyway, I then needed to add a shell script to the lot:
$ cat bin/script.sh
#!/bin/bash
echo 'Hello, World!'
and again the error popped up:
$ dzil build
[DZ] beginning to build prova
[DZ] guessing dist's main_module is lib/prova.pm
[PodWeaver] [@Default/Name] couldn't find abstract in bin/perl-program
couldn't determine document name for bin/script.sh at ...
Now, I could use the PODNAME
trick above:
$ cat bin/script.sh
#!/bin/bash
# PODNAME: script.sh
echo 'Hello, World!'
but it turns out - with little surprise - that the file is considered a Perl one, with the consequence that in the distribution it gets POD added to it:
#!/bin/bash
# PODNAME: script.sh
echo 'Hello, World!'
__END__
=pod
=head1 NAME
script.sh
=head1 VERSION
version 0.1.0
=head1 AUTHOR
Flavio Poletti <polettix@cpan.org>
=head1 COPYRIGHT AND LICENSE
blah blah blah...
=cut
The only thing is that - ehr... - bash does not like POD very much.
Google was not my friend in this case, but I found one in Dist::Zilla's IRC channel (which is #distzilla
on irc.perl.org
, by the way), which I think (hope) is Christopher J. Madsen, the original contributor of Dist::Zilla::Plugin::FileFinder::ByName.
He instructed me to use that module - which entered the core as of version 4.300003 - to obtain what I was after: keep the PodWeaver plugin working on Perl stuff, while ignoring shell stuff. But, more importantly, he adviced me about why.
Many plugins - including the PodWeaver one - rely upon the Finder
role to do their work. This role is something that finds files to be used by the plugin; in the case of PodWeaver, the default is to take whatever module will be installed and whatever stuff is in the bin directory. OK, it can be a bit more complicated than this, but most of the times it's OK. This default is the same as if we configured the following in the dist.ini
file:
[PodWeaver]
finder = :InstallModules
finder = :ExecFiles
It turns out that if we explicitly configure a finder
all the defaults are wiped away, so - for example - if our bin
directory contained shell scripts only we could be happy with this:
[PodWeaver]
finder = :InstallModules
In our case, anyway, this would disable PodWeaver for Perl programs as well, which is not acceptable. This is where FileFinder::ByName kicks in:
[FileFinder::ByName / BinNotShell]
dir = bin
skip = .*\.sh$
This plugin lets us create new finders. In the example above, we are creating a finder that finds stuff in the bin
directory, skipping all files that end with .sh
(note that skip
sets a regular expression). At this point, we're ready for the configuration of the PodWeaver plugin:
[PodWeaver]
finder = :InstallModules
finder = BinNotShell
It's correct, the new finder does not want the initial colon. Now, when it's time for the Pod::Weaver plugin to find files, it will get all the modules that will be installed AND the files in the bin
directory whose name does not end with .sh
. Yay!
> put an explicit package declaration in the driver program
I do this by default since it helps me mark the context i'm working in. All .pl files i write for distribution or later use get a package statement.
Having read on:
Why the hell are you putting shell scripts in your CPAN distributions? The only possible excuse i could think of for doing something so incredibly hostile to cross-platform operation is if the module in question is so completely bound to one single platform that porting it would be meaningless.
And even then i'd default to Perl, just to make it easier for contributors to help out without having to make context switches.
Nice writeup, thx!
Please consider using perl for all scripts. It would make for much more portable distributions and (perl) developers would have less trouble understanding them.
Thank you for your comments! Which, by the way, let me remember why I had the problem in the first place - I developed an application that has to run in a Linux environment, and I wanted to ship a convenience driver script for easy calling from crontab (the script takes care to set up the right environment variables, e.g. where perl is, where PERL5LIB points to exactly, etc.).
In general, I agree with your comments to try and use Perl for all the stuff. Anyway, I'm also a big fan of getting things done with no more than the reasonably required effort, and the shell was the perfect tool in this case.
Flavio, thanks for the explanation. :)
Out of curiosity, could you show the shell script? From what you said it sounds like doing some $ENV{...} followed by an exec would do the task nicely too.
I wonder if having separate script.sh.pod file with POD would work...
@Mithaldu: here's the script: https://gist.github.com/1382375
It also involves doing some additional things - e.g. hop into the right directory and perform a final cleanup of older reports.
Nothing that cannot be replicated in a Perl program... but why bother when the shell is there?
@Jakub: interesting idea - e.g. for adding docs to non-Perl stuff - I'll try it, thanks!
Thanks for posting the script. I can see how a person versed in shell would opt for that first.
> why bother when the shell is there?
Because not every Perl developer also knows shell very well. Taking myself as an example, i deploy most of my software to linux. However reading that script i'd need to spend at least half an hour reading man pages to get a good grip of what it does. If it was in Perl that'd be half the time. :)
Just ran across this while Googling for my strange error message ... thanks so much for explaining this. It saved me lots of hair-pulling. ;->
I also found this via Google in trying to figure this out. Thanks!