4 use vars qw($masterpodfile %Build %Targets $Verbose $Up %Ignore
5 @Master %Readmes %Pods %Aux %Readmepods %Pragmata %Modules
16 $Up = File::Spec->updir;
17 $masterpodfile = File::Spec->catdir($Up, "pod.lst");
19 # Generate any/all of these files
20 # --verbose gives slightly more output
21 # --build-all tries to build everything
22 # --build-foo updates foo as follows
23 # --showfiles shows the files to be changed
28 manifest => File::Spec->catdir($Up, "MANIFEST"),
29 perlpod => "perl.pod",
30 vms => File::Spec->catdir($Up, "vms", "descrip_mms.template"),
31 nmake => File::Spec->catdir($Up, "win32", "Makefile"),
32 dmake => File::Spec->catdir($Up, "win32", "makefile.mk"),
33 podmak => File::Spec->catdir($Up, "win32", "pod.mak"),
34 # plan9 => File::Spec->catdir($Up, "plan9", "mkfile"),
35 unix => File::Spec->catdir($Up, "Makefile.SH"),
39 my @files = keys %Targets;
40 my $filesopts = join(" | ", map { "--build-$_" } "all", sort @files);
43 $0: Usage: $0 [--verbose] [--showfiles] $filesopts
46 && GetOptions (verbose => \$Verbose,
47 showfiles => \$showfiles,
48 map {+"build-$_", \$Build{$_}} @files, 'all');
49 # Set them all to true
50 @Build{@files} = @files if ($Build{all});
54 sort { lc $a cmp lc $b }
56 my ($v, $d, $f) = File::Spec->splitpath($_);
58 @d = defined $d ? File::Spec->splitdir($d) : ();
60 File::Spec->catfile(@d ?
61 (@d == 1 && $d[0] eq '' ? () : @d)
63 } @Targets{grep { $_ ne 'all' && $Build{$_} } keys %Build}),
69 # Don't copy these top level READMEs
77 print "I'm building $_\n" foreach grep {$Build{$_}} keys %Build;
80 chdir $FindBin::Bin or die "$0: Can't chdir $FindBin::Bin: $!";
82 open MASTER, $masterpodfile or die "$0: Can't open $masterpodfile: $!";
84 my ($delta_source, $delta_target);
89 # At least one upper case letter somewhere in the first group
90 if (/^(\S+)\s(.*)/ && $1 =~ tr/h//) {
94 my %flags = (header => 1);
95 $flags{toc_omit} = 1 if $flags =~ tr/o//d;
96 $flags{aux} = 1 if $flags =~ tr/a//d;
97 die "$0: Unknown flag found in heading line: $_" if length $flags;
98 push @Master, [\%flags, $2];
100 } elsif (/^(\S*)\s+(\S+)\s+(.*)/) {
102 my ($flags, $filename, $desc) = ($1, $2, $3);
104 my %flags = (indent => 0);
105 $flags{indent} = $1 if $flags =~ s/(\d+)//;
106 $flags{toc_omit} = 1 if $flags =~ tr/o//d;
107 $flags{aux} = 1 if $flags =~ tr/a//d;
109 if ($flags =~ tr/D//d) {
110 $flags{perlpod_omit} = 1;
111 $delta_source = "$filename.pod";
113 if ($flags =~ tr/d//d) {
114 $flags{manifest_omit} = 1;
115 $delta_target = "$filename.pod";
118 if ($flags =~ tr/r//d) {
119 my $readme = $filename;
120 $readme =~ s/^perl//;
121 $Readmepods{$filename} = $Readmes{$readme} = $desc;
123 } elsif ($flags{aux}) {
124 $Aux{$filename} = $desc;
126 $Pods{$filename} = $desc;
128 die "$0: Unknown flag found in section line: $_" if length $flags;
129 push @Master, [\%flags, $filename, $desc];
133 die "$0: Malformed line: $_" if $1 =~ tr/A-Z//;
136 if (defined $delta_source) {
137 if (defined $delta_target) {
138 # This way round so that keys can act as a MANIFEST skip list
139 # Targets will aways be in the pod directory. Currently we can only cope
140 # with sources being in the same directory. Fix this and do perlvms.pod
142 $Copies{$delta_target} = $delta_source;
144 die "$0: delta source defined but not target";
146 } elsif (defined $delta_target) {
147 die "$0: delta target defined but not target";
154 my (%disk_pods, @disk_pods);
155 my (@manipods, %manipods);
156 my (@manireadmes, %manireadmes);
157 my (@perlpods, %perlpods);
161 # Convert these to a list of filenames.
162 foreach (keys %Pods, keys %Readmepods) {
163 $our_pods{"$_.pod"}++;
166 # None of these filenames will be boolean false
167 @disk_pods = glob("*.pod");
168 @disk_pods{@disk_pods} = @disk_pods;
170 # Things we copy from won't be in perl.pod
171 # Things we copy to won't be in MANIFEST
172 @sources{values %Copies} = ();
174 open(MANI, "../MANIFEST") || die "$0: opening ../MANIFEST failed: $!";
176 if (m!^pod/([^.]+\.pod)\s+!i) {
178 } elsif (m!^README\.(\S+)\s+!i) {
180 push @manireadmes, "perl$1.pod";
184 @manipods{@manipods} = @manipods;
185 @manireadmes{@manireadmes} = @manireadmes;
187 open(PERLPOD, "perl.pod") || die "$0: opening perl.pod failed: $!\n";
189 if (/^For ease of access, /../^\(If you're intending /) {
190 if (/^\s+(perl\S*)\s+\w/) {
191 push @perlpods, "$1.pod";
196 die "$0: could not find the pod listing of perl.pod\n"
198 @perlpods{@perlpods} = @perlpods;
200 foreach my $i (sort keys %disk_pods) {
201 warn "$0: $i exists but is unknown by buildtoc\n"
202 unless $our_pods{$i};
203 warn "$0: $i exists but is unknown by ../MANIFEST\n"
204 if !$manipods{$i} && !$manireadmes{$i} && !$Copies{$i};
205 warn "$0: $i exists but is unknown by perl.pod\n"
206 if !$perlpods{$i} && !exists $sources{$i};
208 foreach my $i (sort keys %our_pods) {
209 warn "$0: $i is known by buildtoc but does not exist\n"
210 unless $disk_pods{$i};
212 foreach my $i (sort keys %manipods) {
213 warn "$0: $i is known by ../MANIFEST but does not exist\n"
214 unless $disk_pods{$i};
216 foreach my $i (sort keys %perlpods) {
217 warn "$0: $i is known by perl.pod but does not exist\n"
218 unless $disk_pods{$i};
222 # Find all the mdoules
225 find \&getpods => qw(../lib ../ext);
229 my $file = $File::Find::name;
230 return if $file eq '../lib/Pod/Functions.pm'; # Used only by pod itself
231 return if $file =~ m!(?:^|/)t/!;
232 return if $file =~ m!lib/Attribute/Handlers/demo/!;
233 return if $file =~ m!lib/Net/FTP/.+\.pm!; # Hi, Graham! :-)
234 return if $file =~ m!lib/Math/BigInt/t/!;
235 return if $file =~ m!/Devel/PPPort/[Hh]arness|lib/Devel/Harness!i;
236 return if $file =~ m!XS/(?:APItest|Typemap)!;
238 return if $pod =~ s/pm$/pod/ && -e $pod;
239 die "$0: tut $File::Find::name" if $file =~ /TUT/;
240 unless (open (F, "< $_\0")) {
241 warn "$0: bogus <$file>: $!";
242 system "ls", "-l", $file;
246 while ($line = <F>) {
247 if ($line =~ /^=head1\s+NAME\b/) {
248 push @modpods, $file;
249 #warn "GOOD $file\n";
253 warn "$0: $file: cannot find =head1 NAME\n";
258 die "$0: no pods" unless @modpods;
262 #($name) = /(\w+)\.p(m|od)$/;
263 my $name = path2modname($_);
264 if ($name =~ /^[a-z]/) {
265 $Pragmata{$name} = $_;
267 if ($done{$name}++) {
268 # warn "already did $_\n";
271 $Modules{$name} = $_;
276 # OK. Now a lot of ancillay function definitions follow
277 # Main program returns at "Do stuff"
291 open(OUT, ">perltoc.pod") || die "$0: creating perltoc.pod failed: $!";
295 ($_= <<"EOPOD2B") =~ s/^\t//gm && output($_);
297 # !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
298 # This file is autogenerated by buildtoc from all the other pods.
299 # Edit those files and run buildtoc --build-toc to effect changes.
303 perltoc - perl documentation table of contents
307 This page provides a brief table of contents for the rest of the Perl
308 documentation set. It is meant to be scanned quickly or grepped
309 through to locate the proper section you're looking for.
311 =head1 BASIC DOCUMENTATION
316 # All the things in the master list that happen to be pod filenames
317 podset(map {"$_->[1].pod"} grep {defined $_ && @$_ == 3 && !$_->[0]{toc_omit}} @Master);
320 ($_= <<"EOPOD2B") =~ s/^\t//gm && output($_);
324 =head1 PRAGMA DOCUMENTATION
328 podset(sort values %Pragmata);
330 ($_= <<"EOPOD2B") =~ s/^\t//gm && output($_);
334 =head1 MODULE DOCUMENTATION
338 podset( @Modules{ sort keys %Modules } );
343 =head1 AUXILIARY DOCUMENTATION
345 Here should be listed all the extra programs' documentation, but they
346 don't all have manual pages yet:
352 $_ .= join "\n", map {"\t=item $_\n"} sort keys %Aux;
359 Larry Wall <F<larry\@wall.org>>, with the help of oodles
367 output "\n"; # flush $LINE
370 # Below are all the auxiliary routines for generating perltoc.pod
372 my ($inhead1, $inhead2, $initem);
380 if (s/^=head1 (NAME)\s*/=head2 /) {
381 $pod = path2modname($ARGV);
383 output "\n \n\n=head2 ";
385 if ( /^\s*$pod\b/ ) {
386 s/$pod\.pm/$pod/; # '.pm' in NAME !?
394 if (s/^=head1 (.*)/=item $1/) {
396 output "=over 4\n\n" unless $inhead1;
398 output $_; nl(); next;
400 if (s/^=head2 (.*)/=item $1/) {
402 output "=over 4\n\n" unless $inhead2;
404 output $_; nl(); next;
406 if (s/^=item ([^=].*)/$1/) {
407 next if $pod eq 'perldiag';
408 s/^\s*\*\s*$// && next;
413 next if $pod eq 'perlmodlib' && /^ftp:/;
414 ##print "=over 4\n\n" unless $initem;
415 output ", " if $initem;
421 if (s/^=cut\s*\n//) {
431 output "\n\n=back\n\n";
439 output "\n\n=back\n\n";
447 ##print "\n\n=back\n\n";
456 my $NEWLINE = 0; # how many newlines have we seen recently
457 my $LINE; # what remains to be printed
460 for (split /(\n)/, shift) {
463 print OUT wrap('', '', $LINE);
466 if (($NEWLINE) < 2) {
471 elsif (/\S/ && length) {
478 # End of original buildtoc. From here on are routines to generate new sections
479 # for and inplace edit other files
481 sub generate_perlpod {
486 next if $flags->{aux};
487 next if $flags->{perlpod_omit};
491 push @output, "=head2 $_->[1]\n";
494 my $start = " " x (4 + $flags->{indent}) . $_->[1];
495 $maxlength = length $start if length ($start) > $maxlength;
496 push @output, [$start, $_->[2]];
501 die "$0: Illegal length " . scalar @$_;
504 # want at least 2 spaces padding
506 $maxlength = ($maxlength + 3) & ~3;
507 # sprintf gives $1.....$2 where ... are spaces:
508 return unexpand (map {ref $_ ? sprintf "%-${maxlength}s%s\n", @$_ : $_}
513 sub generate_manifest {
514 # Annyoingly unexpand doesn't consider it good form to replace a single
515 # space before a tab with a tab
516 # Annoyingly (2) it returns read only values.
517 my @temp = unexpand (map {sprintf "%-32s%s\n", @$_} @_);
518 map {s/ \t/\t\t/g; $_} @temp;
520 sub generate_manifest_pod {
521 generate_manifest map {["pod/$_.pod", $Pods{$_}]}
522 grep {!$Copies{"$_.pod"}} sort keys %Pods;
524 sub generate_manifest_readme {
525 generate_manifest map {["README.$_", $Readmes{$_}]} sort keys %Readmes;
528 sub generate_roffitall {
529 (map ({"\t\$maindir/$_.1\t\\"}sort keys %Pods),
531 map ({"\t\$maindir/$_.1\t\\"}sort keys %Aux),
533 map ({"\t\$libdir/$_.3\t\\"}sort keys %Pragmata),
535 map ({"\t\$libdir/$_.3\t\\"}sort keys %Modules),
539 sub generate_descrip_mms_1 {
540 local $Text::Wrap::columns = 150;
542 my @lines = map {"pod" . $count++ . " = $_"}
543 split /\n/, wrap('', '', join " ", map "[.lib.pod]$_.pod",
544 sort keys %Pods, keys %Readmepods);
545 @lines, "pod = " . join ' ', map {"\$(pod$_)"} 0 .. $count - 1;
548 sub generate_descrip_mms_2 {
549 map {sprintf <<'SNIP', $_, $_ eq 'perlvms' ? 'vms' : 'pod', $_}
550 [.lib.pod]%s.pod : [.%s]%s.pod
551 @ If F$Search("[.lib]pod.dir").eqs."" Then Create/Directory [.lib.pod]
552 Copy/NoConfirm/Log $(MMS$SOURCE) [.lib.pod]
554 sort keys %Pods, keys %Readmepods;
557 sub generate_nmake_1 {
558 # XXX Fix this with File::Spec
559 (map {sprintf "\tcopy ..\\README.%-8s ..\\pod\\perl$_.pod\n", $_}
561 (map {"\tcopy ..\\pod\\$Copies{$_} ..\\pod\\$_\n"} sort keys %Copies);
564 # This doesn't have a trailing newline
565 sub generate_nmake_2 {
566 # Spot the special case
567 local $Text::Wrap::columns = 76;
568 my $line = wrap ("\t ", "\t ",
569 join " ", sort keys %Copies,
570 map {"perl$_.pod"} "vms", keys %Readmes);
575 sub generate_pod_mak {
576 my $variable = shift;
578 my $line = join "\\\n", "\U$variable = ",
579 map {"\t$_.$variable\t"} sort keys %Pods;
581 $line =~ s/.*perltoc.html.*\n//m;
588 grep {! m!^pod/[^.]+\.pod.*\n!}
589 grep {! m!^README\.(\S+)! || $Ignore{$1}} @_;
590 # Dictionary order - fold and handle non-word chars as nothing
592 sort { $a->[1] cmp $b->[1] || $a->[0] cmp $b->[0] }
593 map { my $f = lc $_; $f =~ s/[^a-z0-9\s]//g; [ $_, $f ] }
595 &generate_manifest_pod(),
596 &generate_manifest_readme();
601 my $makefile = join '', @_;
602 die "$0: $name contains NUL bytes" if $makefile =~ /\0/;
603 $makefile =~ s/^\tcopy \.\.\\README.*\n/\0/gm;
604 my $sections = () = $makefile =~ m/\0+/g;
605 die "$0: $name contains no README copies" if $sections < 1;
606 die "$0: $name contains discontiguous README copies" if $sections > 1;
607 # Now remove the other copies that follow
608 1 while $makefile =~ s/\0\tcopy .*\n/\0/gm;
609 $makefile =~ s/\0+/join ("", &generate_nmake_1)/se;
611 $makefile =~ s{(del /f [^\n]+checkpods[^\n]+).*?(pod2html)}
612 {"$1\n" . &generate_nmake_2."\n\t $2"}se;
616 # shut up used only once warning
617 *do_dmake = *do_dmake = \&do_nmake;
621 my $pod = join '', @_;
623 unless ($pod =~ s{(For\ ease\ of\ access,\ .*\n)
624 (?:\s+[a-z]{4,}.*\n # fooo
625 |=head.*\n # =head foo
629 {$1 . join "", &generate_perlpod}mxe) {
630 die "$0: Failed to insert ammendments in do_perlpod";
637 my $body = join '', @_;
638 foreach my $variable (qw(pod man html tex)) {
639 die "$0: could not find $variable in $name"
640 unless $body =~ s{\n\U$variable\E = (?:[^\n]*\\\n)*[^\n]*}
641 {"\n" . generate_pod_mak ($variable)}se;
648 my $makefile = join '', @_;
649 die "$0: $name contains NUL bytes" if $makefile =~ /\0/;
650 $makefile =~ s/\npod\d* =[^\n]*/\0/gs;
651 my $sections = () = $makefile =~ m/\0+/g;
652 die "$0: $name contains no pod assignments" if $sections < 1;
653 die "$0: $name contains $sections discontigous pod assignments"
655 $makefile =~ s/\0+/join "\n", '', &generate_descrip_mms_1/se;
657 die "$0: $name contains NUL bytes" if $makefile =~ /\0/;
659 # Looking for rules like this
660 # [.lib.pod]perl.pod : [.pod]perl.pod
661 # @ If F$Search("[.lib]pod.dir").eqs."" Then Create/Directory [.lib.pod]
662 # Copy/NoConfirm/Log $(MMS$SOURCE) [.lib.pod]
664 $makefile =~ s/\n\Q[.lib.pod]\Eperl[^\n\.]*\.pod[^\n]+\n
665 [^\n]+\n # Another line
666 [^\n]+\Q[.lib.pod]\E\n # ends [.lib.pod]
668 $sections = () = $makefile =~ m/\0+/g;
669 die "$0: $name contains no copy rules" if $sections < 1;
670 die "$0: $name contains $sections discontigous copy rules"
672 $makefile =~ s/\0+/join "\n", '', &generate_descrip_mms_2/se;
678 my $makefile_SH = join '', @_;
679 die "$0: $name contains NUL bytes" if $makefile_SH =~ /\0/;
681 $makefile_SH =~ s/\n\s+-\@test -f \S+ && cd pod && \$\(LNS\) \S+ \S+ && cd \.\. && echo "\S+" >> extra.pods \# See buildtoc\n/\0/gm;
683 my $sections = () = $makefile_SH =~ m/\0+/g;
685 die "$0: $name contains no copy rules" if $sections < 1;
686 die "$0: $name contains $sections discontigous copy rules"
689 my @copy_rules = map "\t-\@test -f pod/$Copies{$_} && cd pod && \$(LNS) $Copies{$_} $_ && cd .. && echo \"pod/$_\" >> extra.pods # See buildtoc",
692 $makefile_SH =~ s/\0+/join "\n", '', @copy_rules, ''/se;
700 while (my ($target, $name) = each %Targets) {
701 next unless $Build{$target};
703 if ($target eq "toc") {
704 print "Now processing $name\n" if $Verbose;
706 print "Finished\n" if $Verbose;
709 print "Now processing $name\n" if $Verbose;
710 open THING, $name or die "Can't open $name: $!";
712 my $orig = join '', @orig;
716 &{"do_$target"}($target, @orig);
718 my $new = join '', @new;
720 print "Was not modified\n" if $Verbose;
723 rename $name, "$name.old" or die "$0: Can't rename $name to $name.old: $!";
724 open THING, ">$name" or die "$0: Can't open $name for writing: $!";
725 print THING $new or die "$0: print to $name failed: $!";
726 close THING or die die "$0: close $name failed: $!";
729 warn "$0: was not instructed to build anything\n" unless $built;