4 use vars qw(%Build %Targets %Pragmata %Modules $Verbose $Quiet $Test);
16 # Assumption is that we're either already being run from the top level (*nix,
17 # VMS), or have absolute paths in @INC (Win32, pod/Makefile)
19 my $Top = File::Spec->catdir($FindBin::Bin, File::Spec->updir);
20 chdir $Top or die "Can't chdir to $Top: $!";
21 require 'Porting/pod_lib.pl';
24 # Generate any/all of these files
25 # --verbose gives slightly more output
26 # --quiet suppresses routine warnings
27 # --build-all tries to build everything
28 # --build-foo updates foo as follows
29 # --showfiles shows the files to be changed
30 # --test exit if perl.pod, pod.lst, MANIFEST are consistent, and regenerated
31 # files are up to date, die otherwise.
35 toc => 'pod/perltoc.pod',
36 manifest => 'MANIFEST',
37 perlpod => 'pod/perl.pod',
38 vms => 'vms/descrip_mms.template',
39 nmake => 'win32/Makefile',
40 dmake => 'win32/makefile.mk',
41 podmak => 'win32/pod.mak',
42 # plan9 => 'plan9/mkfile'),
43 unix => 'Makefile.SH',
47 # process command-line switches
50 my @files = keys %Targets;
51 my $filesopts = join(" | ", map { "--build-$_" } "all", sort @files);
55 $0: Usage: $0 [--verbose] [--showfiles] $filesopts
58 && GetOptions (verbose => \$Verbose,
60 showfiles => \$showfiles,
62 map {+"build-$_", \$build_these{$_}} @files, 'all');
63 if ($build_these{all}) {
66 while (my ($file, $want) = each %build_these) {
67 $Build{$file} = $Targets{$file} if $want;
71 print join(" ", sort { lc $a cmp lc $b } values %Build), "\n";
77 print "I will be building $_\n" foreach keys %Build;
80 my $state = get_pod_metadata(values %Build);
84 printf "1..%d\n", 1 + scalar keys %Build;
85 if (@{$state->{inconsistent}}) {
87 die @{$state->{inconsistent}};
92 warn @{$state->{inconsistent}} if @{$state->{inconsistent}};
96 # Find all the modules
99 find \&getpods => 'lib';
103 my $file = $File::Find::name;
104 return if $file =~ qr!/lib/Pod/Functions.pm\z!; # Used only by pod itself
105 return if $file =~ m!(?:^|/)t/!;
106 return if $file =~ m!lib/Attribute/Handlers/demo/!;
107 return if $file =~ m!lib/Net/FTP/.+\.pm!; # Hi, Graham! :-)
108 return if $file =~ m!lib/Math/BigInt/t/!;
109 return if $file =~ m!/Devel/PPPort/[Hh]arness|lib/Devel/Harness!i;
110 return if $file =~ m!XS/(?:APItest|Typemap)!;
112 return if $pod =~ s/pm$/pod/ && -e $pod;
113 unless (open my $f, '<', $_) {
114 warn "$0: bogus <$file>: $!";
115 system "ls", "-l", $file;
119 while ($line = <$f>) {
120 if ($line =~ /^=head1\s+NAME\b/) {
121 push @modpods, $file;
125 warn "$0: NOTE: cannot find '=head1 NAME' in:\n $file\n" unless $Quiet;
130 my_die "Can't find any pods!\n" unless @modpods;
135 $name =~ s/\.p(m|od)$//;
136 $name =~ s-.*?/lib/--;
138 next if $done{$name}++;
140 if ($name =~ /^[a-z]/) {
141 $Pragmata{$name} = $_;
143 $Modules{$name} = $_;
148 # OK. Now a lot of ancillary function definitions follow
149 # Main program returns at "Do stuff"
154 my $filename = shift;
156 ($_= <<"EOPOD2B") =~ s/^\t//gm and $OUT .= $_;
158 # !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
159 # This file is autogenerated by buildtoc from all the other pods.
160 # Edit those files and run buildtoc --build-toc to effect changes.
164 perltoc - perl documentation table of contents
168 This page provides a brief table of contents for the rest of the Perl
169 documentation set. It is meant to be scanned quickly or grepped
170 through to locate the proper section you're looking for.
172 =head1 BASIC DOCUMENTATION
177 # All the things in the master list that happen to be pod filenames
178 foreach (grep {defined $_ && @$_ == 3 && !$_->[0]{toc_omit}} @{$state->{master}}) {
183 ($_= <<"EOPOD2B") =~ s/^\t//gm and $OUT .= $_;
187 =head1 PRAGMA DOCUMENTATION
191 foreach (sort keys %Pragmata) {
192 podset($_, $Pragmata{$_});
195 ($_= <<"EOPOD2B") =~ s/^\t//gm and $OUT .= $_;
199 =head1 MODULE DOCUMENTATION
203 foreach (sort keys %Modules) {
204 podset($_, $Modules{$_});
210 =head1 AUXILIARY DOCUMENTATION
212 Here should be listed all the extra programs' documentation, but they
213 don't all have manual pages yet:
219 $_ .= join "\n", map {"\t=item $_\n"} sort keys %{$state->{aux}};
226 Larry Wall <F<larry\@wall.org>>, with the help of oodles
235 $OUT =~ s/\n\s+\n/\n\n/gs;
236 $OUT =~ s/\n{3,}/\n\n/g;
238 $OUT =~ s/([^\n]+)/wrap('', '', $1)/ge;
243 # Below are all the auxiliary routines for generating perltoc.pod
245 my ($inhead1, $inhead2, $initem);
248 my ($pod, $file) = @_;
252 open my $fh, '<', $file or my_die "Can't open file '$file' for $pod: $!";
256 if (s/^=head1 (NAME)\s*/=head2 /) {
258 $OUT .= "\n\n=head2 ";
260 # Remove svn keyword expansions from the Perl FAQ
261 s/ \(\$Revision: \d+ \$\)//g;
262 if ( /^\s*\Q$pod\E\b/ ) {
263 s/$pod\.pm/$pod/; # '.pm' in NAME !?
268 elsif (s/^=head1 (.*)/=item $1/) {
270 $OUT .= "=over 4\n\n" unless $inhead1;
274 elsif (s/^=head2 (.*)/=item $1/) {
276 $OUT .= "=over 4\n\n" unless $inhead2;
280 elsif (s/^=item ([^=].*)/$1/) {
281 next if $pod eq 'perldiag';
282 s/^\s*\*\s*$// && next;
287 next if $pod eq 'perlmodlib' && /^ftp:/;
288 $OUT .= ", " if $initem;
294 unhead1() if /^=cut\s*\n/;
304 $OUT .= "\n\n=back\n\n";
312 $OUT .= "\n\n=back\n\n";
324 # End of original buildtoc. From here on are routines to generate new sections
325 # for and inplace edit other files
327 sub generate_perlpod {
330 foreach (@{$state->{master}}) {
332 next if $flags->{aux};
333 next if $flags->{perlpod_omit};
337 push @output, "=head2 $_->[1]\n";
340 my $start = " " x (4 + $flags->{indent}) . $_->[4];
341 $maxlength = length $start if length ($start) > $maxlength;
342 push @output, [$start, $_->[3]];
347 my_die "Illegal length " . scalar @$_;
350 # want at least 2 spaces padding
352 $maxlength = ($maxlength + 3) & ~3;
353 # sprintf gives $1.....$2 where ... are spaces:
354 return unexpand (map {ref $_ ? sprintf "%-${maxlength}s%s\n", @$_ : $_}
359 sub generate_manifest {
360 # Annoyingly, unexpand doesn't consider it good form to replace a single
361 # space before a tab with a tab
362 # Annoyingly (2) it returns read only values.
363 my @temp = unexpand (map {sprintf "%-32s%s", @$_} @_);
364 map {s/ \t/\t\t/g; $_} @temp;
366 sub generate_manifest_pod {
367 generate_manifest map {["pod/$_.pod", $state->{pods}{$_}]}
369 !$state->{copies}{"$_.pod"} && !$state->{generated}{"$_.pod"} && !-e "$_.pod"
370 } keys %{$state->{pods}};
372 sub generate_manifest_readme {
373 generate_manifest sort {$a->[0] cmp $b->[0]}
374 ["README.vms", "Notes about installing the VMS port"],
375 map {["README.$_", $state->{readmes}{$_}]} keys %{$state->{readmes}};
378 sub generate_roffitall {
379 (map ({"\t\$maindir/$_.1\t\\"}sort keys %{$state->{pods}}),
381 map ({"\t\$maindir/$_.1\t\\"}sort keys %{$state->{aux}}),
383 map ({"\t\$libdir/$_.3\t\\"}sort keys %Pragmata),
385 map ({"\t\$libdir/$_.3\t\\"}sort keys %Modules),
389 sub generate_nmake_1 {
390 # XXX Fix this with File::Spec
391 (map {sprintf "\tcopy ..\\README.%-8s ..\\pod\\perl$_.pod\n", $_}
392 sort keys %{$state->{readmes}}),
393 (map {"\tcopy ..\\pod\\$state->{copies}{$_} ..\\pod\\$_\n"} sort keys %{$state->{copies}});
396 # This doesn't have a trailing newline
397 sub generate_nmake_2 {
398 # Spot the special case
399 local $Text::Wrap::columns = 76;
400 my $line = wrap ("\t ", "\t ",
401 join " ", sort keys %{$state->{copies}}, keys %{$state->{generated}},
402 map {"perl$_.pod"} keys %{$state->{readmes}});
408 sub generate_pod_mak {
409 my $variable = shift;
411 my $line = "\U$variable = " . join "\t\\\n\t",
412 map {"$_.$variable"} sort grep { $_ !~ m{/} } keys %{$state->{pods}};
414 $line =~ s/.*perltoc.html.*\n//m;
418 sub verify_contiguous {
419 my ($name, $content, $what) = @_;
420 my $sections = () = $content =~ m/\0+/g;
421 croak("$0: $name contains no $what") if $sections < 1;
422 croak("$0: $name contains discontiguous $what") if $sections > 1;
426 my ($name, $prev) = @_;
428 grep {! m!^pod/[^.]+\.pod.*!}
429 grep {! m!^README\.(\S+)! || $state->{ignore}{$1}} split "\n", $prev;
431 # Dictionary order - fold and handle non-word chars as nothing
433 sort { $a->[1] cmp $b->[1] || $a->[0] cmp $b->[0] }
434 map { my $f = lc $_; $f =~ s/[^a-z0-9\s]//g; [ $_, $f ] }
436 &generate_manifest_pod(),
437 &generate_manifest_readme()), '';
441 my ($name, $makefile) = @_;
442 $makefile =~ s/^\tcopy \.\.\\README.*\n/\0/gm;
443 verify_contiguous($name, $makefile, 'README copies');
444 # Now remove the other copies that follow
445 1 while $makefile =~ s/\0\tcopy .*\n/\0/gm;
446 $makefile =~ s/\0+/join ("", &generate_nmake_1)/se;
448 $makefile =~ s{(-cd \$\(PODDIR\) && del /f[^\n]+).*?(-cd \.\.\\utils && del /f)}
449 {"$1\n" . &generate_nmake_2."\n\t$2"}se;
453 # shut up used only once warning
454 *do_dmake = *do_dmake = \&do_nmake;
457 my ($name, $pod) = @_;
459 unless ($pod =~ s{(For\ ease\ of\ access,\ .*\n)
460 (?:\s+[a-z]{4,}.*\n # fooo
461 |=head.*\n # =head foo
465 {$1 . join "", &generate_perlpod}mxe) {
466 my_die "Failed to insert amendments in do_perlpod";
472 my ($name, $body) = @_;
473 foreach my $variable (qw(pod man html tex)) {
474 my_die "could not find $variable in $name"
475 unless $body =~ s{\n\U$variable\E = (?:[^\n]*\\\n)*[^\n]*}
476 {"\n" . generate_pod_mak ($variable)}se;
482 my ($name, $makefile) = @_;
484 # Looking for the macro defining the current perldelta:
485 #PERLDELTA_CURRENT = [.pod]perl5139delta.pod
487 $makefile =~ s/\nPERLDELTA_CURRENT\s+=\s+\Q[.pod]perl\E\d+delta\.pod\n
489 verify_contiguous($name, $makefile, 'current perldelta macro');
490 $makefile =~ s/\0+/join "\n", '', "PERLDELTA_CURRENT = [.pod]$state->{delta_target}", ''/se;
496 my ($name, $makefile_SH) = @_;
498 $makefile_SH =~ s{^(perltoc_pod_prereqs = extra.pods).*}
499 {join ' ', $1, map "pod/$_",
500 sort keys %{$state->{copies}}, grep {!/perltoc/} keys %{$state->{generated}}
503 # pod/perl511delta.pod: pod/perldelta.pod
504 # cd pod && $(LNS) perldelta.pod perl511delta.pod
507 pod/perl[a-z0-9_]+\.pod: pod/perl[a-z0-9_]+\.pod
508 \$\(LNS\) perl[a-z0-9_]+\.pod pod/perl[a-z0-9_]+\.pod
511 verify_contiguous($name, $makefile_SH, 'copy rules');
513 my @copy_rules = map "
514 pod/$_: pod/$state->{copies}{$_}
515 \$(LNS) $state->{copies}{$_} pod/$_
516 ", keys %{$state->{copies}};
518 $makefile_SH =~ s/\0+/join '', @copy_rules/se;
526 while (my ($target, $name) = each %Targets) {
527 print "Working on target $target\n" if $Verbose;
528 next unless $Build{$target};
531 print "Now processing $name\n" if $Verbose;
532 if ($target ne "toc") {
534 my $thing = open_or_die($name);
537 my_die "$name contains NUL bytes" if $orig =~ /\0/;
542 &{"do_$target"}($target, $orig);
548 printf "ok %d # $name is up to date\n", $built + 1;
550 print "Was not modified\n";
554 printf "not ok %d # $name is up to date\n", $built + 1;
557 $mode = (stat $name)[2] // my_die "Can't stat $name: $!";
558 rename $name, "$name.old" or my_die "Can't rename $name to $name.old: $!";
561 open my $thing, '>', $name or my_die "Can't open $name for writing: $!";
563 print $thing $new or my_die "print to $name failed: $!";
564 close $thing or my_die "close $name failed: $!";
566 chmod $mode & 0777, $name or my_die "can't chmod $mode $name: $!";
570 warn "$0: was not instructed to build anything\n" unless $built || $Test;