xref: /openbsd-src/gnu/usr.bin/perl/dist/Devel-PPPort/devel/mkapidoc.pl (revision 9a4edab696eab16ea2f4a08ac420f1101492473d)
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