1*9a4edab6Sbluhm#!/usr/bin/perl 2*9a4edab6Sbluhm 3de8cc8edSafresh1################################################################################ 4de8cc8edSafresh1# 5de8cc8edSafresh1# mkapidoc.pl -- generate apidoc.fnc from scanning the Perl source 6de8cc8edSafresh1# 7de8cc8edSafresh1# Should be called from the base directory for Devel::PPPort. 8de8cc8edSafresh1# If that happens to be in the /dist directory of a perl build structure, and 9de8cc8edSafresh1# you're doing the standard thing, no parameters are required. Otherwise 10de8cc8edSafresh1# (again with the standard things, its single parameter is the base directory 11de8cc8edSafresh1# of the perl source tree to be used. 12de8cc8edSafresh1# 13de8cc8edSafresh1################################################################################ 14de8cc8edSafresh1# 15de8cc8edSafresh1# Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz. 16de8cc8edSafresh1# Version 2.x, Copyright (C) 2001, Paul Marquess. 17de8cc8edSafresh1# Version 1.x, Copyright (C) 1999, Kenneth Albanowski. 18de8cc8edSafresh1# 19de8cc8edSafresh1# This program is free software; you can redistribute it and/or 20de8cc8edSafresh1# modify it under the same terms as Perl itself. 21de8cc8edSafresh1# 22de8cc8edSafresh1################################################################################ 23de8cc8edSafresh1 24de8cc8edSafresh1use warnings; 25de8cc8edSafresh1use strict; 26*9a4edab6Sbluhmuse File::Find; 27*9a4edab6Sbluhmuse re '/aa'; 28de8cc8edSafresh1 29de8cc8edSafresh1my $PERLROOT = $ARGV[0]; 30*9a4edab6Sbluhmunless ($PERLROOT) { 31*9a4edab6Sbluhm $PERLROOT = '../..'; 32*9a4edab6Sbluhm print STDERR "$0: perl directory root argument not specified. Assuming '$PERLROOT'\n"; 33*9a4edab6Sbluhm} 34de8cc8edSafresh1 35de8cc8edSafresh1die "'$PERLROOT' is invalid, or you haven't successfully run 'make' in it" 36de8cc8edSafresh1 unless -e "$PERLROOT/warnings.h"; 37*9a4edab6Sbluhmmy $maindir = '.'; 38*9a4edab6Sbluhmrequire "$maindir/parts/ppptools.pl"; 39de8cc8edSafresh1 40de8cc8edSafresh1my %seen; 41de8cc8edSafresh1 42de8cc8edSafresh1# Find the files in MANIFEST that are core, but not embed.fnc, nor .t's 43de8cc8edSafresh1my @files; 44de8cc8edSafresh1open(my $m, '<', "$PERLROOT/MANIFEST") || die "MANIFEST:$!"; 45de8cc8edSafresh1while (<$m>) { # In embed.fnc, 46de8cc8edSafresh1 chomp; 47de8cc8edSafresh1 next if m! ^ embed \. fnc \t !x; 48de8cc8edSafresh1 next if m! ^ ( cpan | dist | t) / !x; 49de8cc8edSafresh1 next if m! [^\t]* \.t \t !x; 50*9a4edab6Sbluhm s/\t.*//; 51*9a4edab6Sbluhm push @files, "$PERLROOT/$_"; 52*9a4edab6Sbluhm} 53*9a4edab6Sbluhmclose $m or die "Can't close $m: $!"; 54*9a4edab6Sbluhm 55*9a4edab6Sbluhm# Here, we have the lists of doc files and root First, get the known macros 56*9a4edab6Sbluhm# and functions from embed.fnc, converting from an array into a hash (for 57*9a4edab6Sbluhm# convenience) 58*9a4edab6Sbluhmmy %embeds; 59*9a4edab6Sbluhmmy %apidoc; 60*9a4edab6Sbluhm 61*9a4edab6Sbluhmforeach my $entry (parse_embed("$maindir/parts/embed.fnc")) { 62*9a4edab6Sbluhm my $name = $entry->{'name'}; 63*9a4edab6Sbluhm my $cond = $entry->{'cond'}; 64*9a4edab6Sbluhm 65*9a4edab6Sbluhm my $flags = join "", sort { lc $a cmp lc $b or $a cmp $b } 66*9a4edab6Sbluhm keys $entry->{flags}->%*; 67*9a4edab6Sbluhm my @arg_pairs; 68*9a4edab6Sbluhm foreach my $pair ($entry->{args}->@*) { 69*9a4edab6Sbluhm push @arg_pairs, join " ", $pair->@*; 70*9a4edab6Sbluhm } 71*9a4edab6Sbluhm my $args = join "|", @arg_pairs; 72*9a4edab6Sbluhm 73*9a4edab6Sbluhm die "Multiple entries for $embeds{$name}{$cond}" 74*9a4edab6Sbluhm if defined $embeds{$name}{$cond}; 75*9a4edab6Sbluhm 76*9a4edab6Sbluhm # Save the embed.fnc entry 77*9a4edab6Sbluhm $embeds{$name}{$cond} = "$flags|$entry->{'ret'}|$name|$args"; 78de8cc8edSafresh1} 79de8cc8edSafresh1 80de8cc8edSafresh1 81*9a4edab6Sbluhm# Examine the SEE ALSO section of perlapi which should contain links to all 82*9a4edab6Sbluhm# the pods with apidoc entries in them. Add them to the MANIFEST list. 83*9a4edab6Sbluhmmy $file; 84de8cc8edSafresh1 85*9a4edab6Sbluhmsub callback { 86*9a4edab6Sbluhm return unless $_ eq $file; 87*9a4edab6Sbluhm return if $_ eq 'config.h'; # We don't examine this one 88*9a4edab6Sbluhm return if $_ eq 'perlintern.pod'; # We don't examine this one 89*9a4edab6Sbluhm return if $File::Find::dir =~ / \/ ( cpan | dist | t ) \b /x; 90*9a4edab6Sbluhm push @files, $File::Find::name; 91*9a4edab6Sbluhm} 92*9a4edab6Sbluhm 93*9a4edab6Sbluhmopen my $a, '<', "$PERLROOT/pod/perlapi.pod" 94*9a4edab6Sbluhm or die "Can't open perlapi.pod ($PERLROOT needs to have been built): $!"; 95*9a4edab6Sbluhmwhile (<$a>) { 96*9a4edab6Sbluhm next unless / ^ =head1\ SEE\ ALSO /x; 97*9a4edab6Sbluhm while (<$a>) { 98*9a4edab6Sbluhm # The lines look like: 99*9a4edab6Sbluhm # F<config.h>, L<perlintern>, L<perlapio>, L<perlcall>, L<perlclib>, 100*9a4edab6Sbluhm last if /^=/; 101*9a4edab6Sbluhm 102*9a4edab6Sbluhm my @tags = split /, \s* | \s+ /x; # Allow comma- or just space-separated 103*9a4edab6Sbluhm 104*9a4edab6Sbluhm foreach my $tag (@tags) { 105*9a4edab6Sbluhm if ($tag =~ / ^ F< (.*) > $ /x) { 106*9a4edab6Sbluhm $file = $1; 107*9a4edab6Sbluhm } 108*9a4edab6Sbluhm elsif ($tag =~ / ^ L< (.*) > $ /x) { 109*9a4edab6Sbluhm $file = "$1.pod"; 110*9a4edab6Sbluhm } 111*9a4edab6Sbluhm else { 112*9a4edab6Sbluhm die "Unknown tag '$tag'"; 113*9a4edab6Sbluhm } 114*9a4edab6Sbluhm 115*9a4edab6Sbluhm find(\&callback, $PERLROOT); 116*9a4edab6Sbluhm } 117*9a4edab6Sbluhm } 118*9a4edab6Sbluhm} 119*9a4edab6Sbluhm 120*9a4edab6Sbluhmmy ($controlling_flags, $controlling_ret_type, $controlling_args); 121*9a4edab6Sbluhm 122*9a4edab6Sbluhm# Look through all the files that potentially have apidoc entries 123*9a4edab6Sbluhm# These may be associated with embed.fnc, in which case we do nothing; 124*9a4edab6Sbluhm# otherwise, we output them to apidoc.fnc, potentially modified. 125*9a4edab6Sbluhmfor my $file (@files) { 126*9a4edab6Sbluhm 127*9a4edab6Sbluhm $file =~ s/ \t .* //x; # Trim all but first column 128*9a4edab6Sbluhm open my $f, '<', "$file" or die "Can't open $file: $!"; 129de8cc8edSafresh1 130de8cc8edSafresh1 my $line; 131de8cc8edSafresh1 while (defined ($line = <$f>)) { 132de8cc8edSafresh1 chomp $line; 133*9a4edab6Sbluhm next unless $line =~ / ^ =for \s+ apidoc ( _item )? \s+ 134*9a4edab6Sbluhm (?: 135*9a4edab6Sbluhm ( [^|]*? ) # flags, backoff trailing 136*9a4edab6Sbluhm # white space 137*9a4edab6Sbluhm \s* \| \s* 138de8cc8edSafresh1 139*9a4edab6Sbluhm ( [^|]*? ) # return type 140*9a4edab6Sbluhm 141*9a4edab6Sbluhm \s* \| \s* 142*9a4edab6Sbluhm 143*9a4edab6Sbluhm )? # flags and ret type are all 144*9a4edab6Sbluhm # or nothing 145*9a4edab6Sbluhm 146*9a4edab6Sbluhm ( [^|]+? ) # name 147*9a4edab6Sbluhm 148*9a4edab6Sbluhm \s* 149*9a4edab6Sbluhm 150*9a4edab6Sbluhm (?: \| \s* ( .* ) \s* )? # optional args 151*9a4edab6Sbluhm 152*9a4edab6Sbluhm $ 153*9a4edab6Sbluhm /x; 154*9a4edab6Sbluhm my $item = $1 // 0; 155*9a4edab6Sbluhm my $flags = $2 // ""; 156*9a4edab6Sbluhm my $ret_type = $3 // ""; 157*9a4edab6Sbluhm my $name = $4; 158*9a4edab6Sbluhm my $args = $5 // ""; 159*9a4edab6Sbluhm 160*9a4edab6Sbluhm next unless $name; # Not an apidoc line 161*9a4edab6Sbluhm 162*9a4edab6Sbluhm # If embed.fnc already contains this name, this better be an empty 163*9a4edab6Sbluhm # entry, unless it has the M flag, meaning there is another macro 164*9a4edab6Sbluhm # defined for it. 165*9a4edab6Sbluhm if (defined $embeds{$name}) { 166*9a4edab6Sbluhm my @conds = keys $embeds{$name}->%*; 167*9a4edab6Sbluhm 168*9a4edab6Sbluhm # If this is just the anchor for where the pod is in the source, 169*9a4edab6Sbluhm # the entry is already fully in embed.fnc. 170*9a4edab6Sbluhm if ("$flags$ret_type$args" eq "") { 171*9a4edab6Sbluhm if (! $item) { 172*9a4edab6Sbluhm foreach my $cond (@conds) { 173*9a4edab6Sbluhm # For a plain apidoc entry, save the inputs, so as to apply them 174*9a4edab6Sbluhm # to any following apidoc_item lines. 175*9a4edab6Sbluhm ($controlling_flags, $controlling_ret_type, $controlling_args) 176*9a4edab6Sbluhm = $embeds{$name}{$cond} =~ / ( [^|]* ) \| ( [^|]* ) \| (?: [^|]* ) \| (.*) /x; 177*9a4edab6Sbluhm $controlling_flags =~ s/[iMpb]//g; 178*9a4edab6Sbluhm $controlling_flags .= 'm' unless $controlling_flags =~ /m/; 179*9a4edab6Sbluhm last; 180*9a4edab6Sbluhm } 181de8cc8edSafresh1 } 182de8cc8edSafresh1 next; 183de8cc8edSafresh1 } 184de8cc8edSafresh1 185*9a4edab6Sbluhm # And the only reason we should have something with other 186*9a4edab6Sbluhm # information than what's in embed.fnc is if it is an M flag, 187*9a4edab6Sbluhm # meaning there is an extra macro for this function, and this is 188*9a4edab6Sbluhm # documenting that. 189*9a4edab6Sbluhm my $msg; 190*9a4edab6Sbluhm foreach my $cond (@conds) { 191*9a4edab6Sbluhm if ($embeds{$name}{$cond} !~ / ^ [^|]* M /x) { 192*9a4edab6Sbluhm $msg = "Specify only name when main entry is in embed.fnc"; 193*9a4edab6Sbluhm last; 194*9a4edab6Sbluhm } 195*9a4edab6Sbluhm } 196*9a4edab6Sbluhm 197*9a4edab6Sbluhm if (! defined $msg) { 198*9a4edab6Sbluhm if ($flags !~ /m/) { 199*9a4edab6Sbluhm $msg = "Must have 'm' flag for overriding 'M' embed.fnc entry"; 200*9a4edab6Sbluhm } 201*9a4edab6Sbluhm elsif ($flags =~ /p/) { 202*9a4edab6Sbluhm $msg = "Must not have 'p' flag for overriding 'M' embed.fnc entry"; 203*9a4edab6Sbluhm } 204*9a4edab6Sbluhm } 205*9a4edab6Sbluhm 206*9a4edab6Sbluhm die "$msg: $file: $.: \n'$line'\n" if defined $msg; 207*9a4edab6Sbluhm } 208*9a4edab6Sbluhm 209*9a4edab6Sbluhm # Here, we have an entry for apidoc.fnc, one that isn't in embed.fnc. 210*9a4edab6Sbluhm 211*9a4edab6Sbluhm # If this is an apidoc_item line, there was a plain apidoc line 212*9a4edab6Sbluhm # earlier, and we saved the values from that to use here (if here is 213*9a4edab6Sbluhm # empty). 214*9a4edab6Sbluhm if ($item) { 215*9a4edab6Sbluhm $flags = $controlling_flags unless $flags ne ""; 216*9a4edab6Sbluhm $ret_type = $controlling_ret_type unless $ret_type ne ""; 217*9a4edab6Sbluhm $args = $controlling_args unless $args ne ""; 218*9a4edab6Sbluhm } 219*9a4edab6Sbluhm else { 220*9a4edab6Sbluhm # For a plain apidoc entry, save the inputs, so as to apply them 221*9a4edab6Sbluhm # to any following apidoc_item lines. 222*9a4edab6Sbluhm $controlling_flags = $flags; 223*9a4edab6Sbluhm $controlling_ret_type = $ret_type; 224*9a4edab6Sbluhm $controlling_args = $args; 225*9a4edab6Sbluhm } 226de8cc8edSafresh1 227de8cc8edSafresh1 # Many of the entries omit the "d" flag to indicate they are 228*9a4edab6Sbluhm # documented, but we got here because of an apidoc line, which 229*9a4edab6Sbluhm # indicates it is documented in the source 230*9a4edab6Sbluhm $flags .= 'd' unless $flags =~ /d/; 231de8cc8edSafresh1 232*9a4edab6Sbluhm # We currently don't handle typedefs, nor this special case 233*9a4edab6Sbluhm next if $flags =~ /y/; 234*9a4edab6Sbluhm next if $name eq 'JMPENV_PUSH'; 235*9a4edab6Sbluhm 236*9a4edab6Sbluhm my $entry = "$flags|$ret_type|$name"; 237*9a4edab6Sbluhm $entry .= "|$args" if $args ne ""; 238*9a4edab6Sbluhm $apidoc{$name}{entry} = $entry; 239de8cc8edSafresh1 } 240de8cc8edSafresh1} 241de8cc8edSafresh1 242*9a4edab6Sbluhmmy $outfile = "$maindir/parts/apidoc.fnc"; 243*9a4edab6Sbluhmopen my $out, ">", $outfile 244*9a4edab6Sbluhm or die "Can't open '$outfile' for writing: $!"; 245*9a4edab6Sbluhmrequire "$maindir/parts/inc/inctools"; 246de8cc8edSafresh1print $out <<EOF; 247de8cc8edSafresh1:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 248de8cc8edSafresh1: 249de8cc8edSafresh1: !!!! Do NOT edit this file directly! -- Edit devel/mkapidoc.sh instead. !!!! 250de8cc8edSafresh1: 251de8cc8edSafresh1: This file was automatically generated from the API documentation scattered 252de8cc8edSafresh1: all over the Perl source code. To learn more about how all this works, 253de8cc8edSafresh1: please read the F<HACKERS> file that came with this distribution. 254de8cc8edSafresh1: 255de8cc8edSafresh1:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 256de8cc8edSafresh1 257de8cc8edSafresh1: 258de8cc8edSafresh1: This file lists all API functions/macros that are documented in the Perl 259de8cc8edSafresh1: source code, but are not contained in F<embed.fnc>. 260de8cc8edSafresh1: 261de8cc8edSafresh1EOF 262*9a4edab6Sbluhmprint $out join "\n", sort sort_api_lines map { $apidoc{$_}{entry} } keys %apidoc; 263de8cc8edSafresh1close $out or die "Close failed: $!"; 264*9a4edab6Sbluhmprint "$outfile regenerated\n"; 265