1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
|
#!/usr/bin/perl
use 5.14.0;
use warnings;
use Carp;
use File::Spec;
use Getopt::Long;
use Module::Metadata;
require "./Porting/manifest_lib.pl";
=head1 NAME
add-pod-file - Utility to add new F<pod/*.pod> file to core distribution
=head1 USAGE
After C<make test_prep> has been run, call from top level of Perl 5 core
distribution:
perl Porting/add-pod-file \
--stub=<XXX> --section=<Z> --verbose
=head1 DESCRIPTION
This is a program which I<may> be helpful when a committer has to add a new
F<*.pod> file in the F<pod/> directory.
=head2 Prerequisites
This program assumes that committer has taken the following steps (in the
order listed):
=over 4
=item 1 You have run F<make test_prep>.
This is to guarantee that all files are properly positioned.
=item 2 You have placed a well-formatted F<.pod> file into the F<pod/> directory.
In the C<NAME> section of this file there is a single non-blank line which
consists of a string in the format C<STUB - ABSTRACT>, where C<STUB> is the
basename of the file without the C<.pod> suffix and C<ABSTRACT> is the short
description of the file. For example, a new file whose path is
F<pod/perlphonypod.pod> must have a C<NAME> section like this:
=head1 NAME
perlphonypod - This is phony POD
=back
F<pod/*.pod> files need entries in multiple locations to keep F<make
test_porting> happy. This program automates the formulation of I<most> of
those entries, but will need some assistance from the committer to work
properly. The committer will have to make a reasonable choice as to which
section of F<pod/perl.pod> the new F<.pod> file should be listed under.
The eligible sections are shown in the following table:
Command-Line Value Section in pod/perl.pod
O => 'Overview',
T => 'Tutorials',
R => 'Reference Manual',
I => 'Internals and C Language Interface',
H => 'History',
M => 'Miscellaneous',
L => 'Language-Specific',
P => 'Platform-Specific',
For a first pass, we'll put the new entry at the end of the C<^=head2> section
specified by the committer with the single-initial provided for command-line
switch C<section>.
=head2 Testing this program
=over 4
=item 1 Run F<configure> and F<make> in the source tree.
=item 2 Create a well formatted F<.pod> file somewhere on your system.
=item 3 Copy it into the source tree under F<pod>.
=item 4 Call the program as in L</USAGE> above.
=item 5 Call F<git diff> and examine results.
=item 6 Run F<make test_porting>.
=back
=head1 BUGS
When the argument provided to the C<--section> command-line switch is C<P> (for platform-specific), F<win32/pod.mak> is not getting updated -- but it's not clear whether it I<ought> to be updated.
=cut
my @man_sections = (
O => 'Overview',
T => 'Tutorials',
R => 'Reference Manual',
I => 'Internals and C Language Interface',
H => 'History',
M => 'Miscellaneous',
L => 'Language-Specific',
P => 'Platform-Specific',
);
my @man_section_abbrevs = ();
my $man_sections_str = '';
for (my $i=0; $i<= $#man_sections; $i+=2) {
my $j = $i+1;
push @man_section_abbrevs, $man_sections[$i];
$man_sections_str .= "\t$man_sections[$i] => $man_sections[$j]\n";
}
my %man_sections_seen = map { $_ => 1 } @man_section_abbrevs;
my $man_sections = { @man_sections };
my ($stub, $section, $verbose) = ('') x 3;
GetOptions(
"stub=s" => \$stub,
"section=s" => \$section,
"verbose" => \$verbose,
) or croak("Error in command line arguments to add-pod-file.pl\n");
croak "$0: Must provide value for command-line switch 'stub'"
unless length($stub);
croak "$0: Must provide value for command-line switch 'section'"
unless length($section);
my $section_croak = "$0: Value for command-line switch must be one of @man_section_abbrevs\n";
$section_croak .= " Select one initial from:\n$man_sections_str";
croak $section_croak unless $man_sections_seen{$section};
my $newpodfile = "$stub.pod";
my $newpodpath = File::Spec->catfile('pod', $newpodfile);
croak "Unable to locate new file '$newpodpath'" unless -f $newpodpath;
my $thispodchecker = File::Spec->catfile(qw|cpan Pod-Checker podchecker|);
croak "Cannot locate 'podchecker' within this checkout; have you called 'make'?"
unless -f $thispodchecker;
say "Step 1: Basic test of validity of POD in $newpodpath" if $verbose;
system(qq|$^X $thispodchecker $newpodpath|)
and croak "$newpodpath has POD errors; correct before proceeding further";
my $data = Module::Metadata->new_from_file($newpodpath, collect_pod => 1, decode_pod => 1);
my $regex = qr/\A\s*(?:\S+\s+)+?-+\s+(.+?)\s*\z/s;
my ($abstract) = ($data->pod('NAME') // '') =~ $regex
or croak "Could not parse abstract from `=head1 NAME` in $newpodpath";
system(qq|git add $newpodpath|) and croak "Unable to 'git add'";
# Step 2: Insert entry for $newpodpath into MANIFEST
my $manifest = 'MANIFEST';
say "Step 2: Insert entry for $newpodpath into $manifest" if $verbose;
open(my $IN, '<', $manifest)
or croak "Can't open $manifest for reading";
my @manifest_orig = <$IN>;
close($IN) or croak "Can't close $manifest after reading";
chomp(@manifest_orig);
my (@before_pod, @pod, @after_pod);
my $seen_pod = 0;
while (my $l = shift(@manifest_orig)) {
if (! $seen_pod and $l !~ m{^pod\/}) {
push @before_pod, $l;
}
elsif ($l =~ m{^pod\/}) {
push @pod, $l;
$seen_pod++;
}
else {
push @after_pod, $l;
}
}
say "Inserting entry for '$newpodpath' into $manifest; text will be '$abstract'" if $verbose;
my $new_manifest_entry = "$newpodpath\t\t$abstract";
my @new_pod = sort_manifest(@pod, $new_manifest_entry);
open(my $OUT, '>', $manifest)
or croak "Can't open $manifest for writing";
binmode($OUT);
say $OUT join("\n", @before_pod, @new_pod, @after_pod);
close($OUT) or croak "Can't close $manifest after writing";
my $perlpod = File::Spec->catfile(qw|pod perl.pod|);
say "Step 3: Add entry to $perlpod" if $verbose;
# Read the existing pod/perl.pod into memory.
# Divide it into chunks before the selected section, the head2 of the selected
# section, the selected section, and what comes after the selected section.
# Add the stub and abstract for the new .pod file to the end of the selected
# section. (Manually reposition to taste.)
open(my $IN1, '<', $perlpod)
or croak "Can't open $perlpod for reading";
my $perlpod_str;
{
local $/;
$perlpod_str = <$IN1>;
}
close($IN1) or croak "Can't close $perlpod after reading";
my $section_head = "=head2 $man_sections->{$section}";
my @chunks = split $section_head, $perlpod_str;
chomp $chunks[0]; # So we can use 'say' consistently later on
my @balance = split /\n/, $chunks[1];
shift @balance; # $chunks[1] begins with a newline which we won't need to output
my (@target_section, @after_section);
my $target = \@target_section;
for my $l (@balance) {
$target = \@after_section if $l =~ m/^=(head2|for)/;
push @$target, $l;
}
push @target_section, " $stub\t\t$abstract";
open(my $OUT1, '>', $perlpod)
or croak "Can't open $perlpod for writing";
say $OUT1 $chunks[0];
say $OUT1 $section_head;
say $OUT1 join("\n" => @target_section), "\n";
say $OUT1 join("\n" => @after_section), "\n";
close $OUT1 or croak "Can't close $perlpod after writing";
my $podmak_command = './perl -Ilib Porting/pod_rules.pl --build-podmak --verbose';
say "Step 4: Running '$podmak_command' to update win32/pod.mak."
if $verbose;
system($podmak_command) and croak "'$podmak_command' failed";
system(qq|git add MANIFEST pod/perl.pod win32/pod.mak|)
and croak "Unable to git-add three updated files";
if ($verbose) {
say "Call 'git diff --staged' and inspect modified files; correct as needed.";
say "Then run 'make test_porting'.";
say "Then say 'git commit'.";
}
|