man-pages: Generate man pages with Pod::Man

The generate-man script is currently a shell script which invokes the
pod2man command for each pod file to be converted into a man page. This
makes the pod conversion slow, since we load Perl and create a Pod::Man
parser for each pod file.  In addition to being slow, generate-man
leaves behind a partially created man page when an error is
encountered during the pod2man execution.

To fix these issues, rewrite generate-man as a Perl script which uses
the Pod::Man module directly.  The Pod::Man parser is created only once
and is reused to generate each man page.  The Pod::Man module supports
this type of batch mode operation by clearing its internal state after
each man page is created.

We have some special processing to determine the man page names for the
pages in section 3, so create a sub class to handle the pod filename to
man page title determination, and add a helper function to support
processing more than one section with a single parser instance.

Be sure to cleanup any partially created man pages if an error is
encountered during the pod to man conversion.  This will let us use this
script in the Makefiles in the future.

Change-Id: I8d3cce1edc62c490e93d05f72609dfde4b599a1b
Reviewed-on: https://gerrit.openafs.org/15774
Reviewed-by: Mark Vitale <mvitale@sinenomine.net>
Tested-by: Mark Vitale <mvitale@sinenomine.net>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Cheyenne Wills <cwills@sinenomine.net>
Reviewed-by: Michael Meffie <mmeffie@sinenomine.net>
This commit is contained in:
Michael Meffie 2024-06-19 14:48:40 -04:00
parent 168badb037
commit c66971ce42

View File

@ -1,78 +1,101 @@
#!/bin/sh
#!/usr/bin/perl -w
#
# Generate the OpenAFS man pages from POD source. Run merge-pod
# first to generate the POD source files.
#
# Generate the OpenAFS man pages from POD source. This script is normally
# invoked by regen.sh but may be run at any time to rebuild all of the man
# pages (with a newer version of pod2man than was used for the release, for
# instance).
# Exit on any error.
set -e
package OpenAFS::Man;
if [ ! -d pod1 ] ; then
echo 'generate-man must be run from the doc/man-pages directory' >&2
exit 1
fi
use strict;
use vars qw(@ISA);
use Pod::Man;
if pod2man pod1/afs.pod > /dev/null ; then
:
else
echo 'pod2man not found, skipping man page generation' >&2
exit 1
fi
if perl -e 'use Pod::Man 2.04' > /dev/null 2>&1 ; then
:
else
echo 'Pod::Man is older than the recommended version of 2.04 or later' >&2
echo 'Continuing with man page generation anyway' >&2
fi
@ISA = qw(Pod::Man);
# Create the directories. We generate each section into its own directory
# to make installation rules easier.
[ -d man1 ] || mkdir man1
[ -d man3 ] || mkdir man3
[ -d man5 ] || mkdir man5
[ -d man8 ] || mkdir man8
# Subclass Pod::Man to support OpenAFS man page generation.
sub new {
my $class = shift;
my $self = Pod::Man->new('release' => 'OpenAFS', 'errors' => 'die');
bless ($self, 'OpenAFS::Man');
return $self;
}
# Generate each set of man pages. For each, allow for the case of the
# directory being empty. In that case, for won't expand the wildcard, and
# we want to avoid running pod2man with a wildcard as an argument.
pod1=`ls pod1`
if [ -n "$pod1" ] ; then
cd pod1
for f in *.pod ; do
pod2man -c 'AFS Command Reference' -r 'OpenAFS' -s 1 "$f" \
> ../man1/`echo "$f" | sed 's/\.pod$//'`.1
done
cd ..
fi
pod3=`ls pod3`
if [ -n "$pod3" ] ; then
cd pod3
for f in *.pod ; do
name=`echo "$f" | sed -e 's/\.pod$//' -e 's/^AFS\./AFS::/'`
pod2man -c 'AFS Library Reference' -r 'OpenAFS' -s 3 "$f" -n "$name" \
> ../man3/${name}.3
# Perl module manpages are named AFS.foo instead of AFS::foo, since
# we cannot have colons in filenames on Windows. So here, we assume
# anything with "AFS." should be "AFS::" instead.
done
cd ..
fi
pod5=`ls pod5`
if [ -n "$pod5" ] ; then
cd pod5
for f in *.pod ; do
pod2man -c 'AFS File Reference' -r 'OpenAFS' -s 5 "$f" \
> ../man5/`echo "$f" | sed 's/\.pod$//'`.5
done
cd ..
fi
pod8=`ls pod8`
if [ -n "$pod8" ] ; then
cd pod8
for f in *.pod ; do
pod2man -c 'AFS Command Reference' -r 'OpenAFS' -s 8 "$f" \
> ../man8/`echo "$f" | sed 's/\.pod$//'`.8
done
cd ..
fi
# Set the current section number and heading text.
sub start_section {
my ($self, $section, $heading) = @_;
$self->{'section'} = $section;
$self->{'center'} = $heading;
}
# Set the man page title based on the file name and section number.
#
# Unless the current section number is 3, the title is taken from the
# name of the pod file being converted.
#
# Special handing is required for man pages in section 3 (libraries). The
# title of these pages are like "AFS::foo.3", but Windows does not support
# colon characters (:) in filenames, so we use dot '.' as a placeholder
# and convert the '.' found to '::' to create the title string set in
# the generated man page.
#
# In the future, the pod files may be organized in subdirectories, like
# pod3/lib/AFS/foo.pod (instead of pod3/AFS.foo.pod), which would be more
# conventional solution.
sub set_name {
my ($self, $pod) = @_;
my $name = $pod;
$name =~ s/\.pod$//;
if ($self->{'section'} == 3) {
$name =~ s/\./::/g;
}
$self->{'name'} = $name;
return $self->{'name'};
}
# Required for Pod::Simple::version_report()
sub VERSION () { '1.0' }
package main;
use strict;
use File::Spec;
my %HEADINGS = (
1 => 'AFS Command Reference',
3 => 'AFS Library Reference',
5 => 'AFS File Reference',
8 => 'AFS Command Reference'
);
# Create the parser object and generate the man pages for each pod file
# in each section.
#
# Remove partially generated files if an error is encountered.
sub generate_man_pages {
my $parser = OpenAFS::Man->new();
for my $section (sort(keys(%HEADINGS))) {
if (! -d "man${section}") {
mkdir("man${section}", 0755) or
die "Cannot create man${section} directory: $!\n";
}
$parser->start_section($section, $HEADINGS{$section});
opendir(D, "pod${section}") or die "Cannot open pod${section}: $!\n";
for my $file (readdir(D)) {
next unless $file =~ /\.pod$/; # Skip non-pod files.
my $name = $parser->set_name($file);
my $input = File::Spec->join("pod${section}", $file);
my $output = File::Spec->join("man${section}", "${name}.${section}");
eval {
$parser->parse_from_file($input, $output);
};
if ($@) {
if (-s $output) {
unlink($output); # Remove partially created file.
}
die "Unable to parse $input: $@";
}
}
}
}
generate_man_pages();