This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Convert to using Test::More. Provide each test with a description.
[perl5.git] / autodoc.pl
1 #!/usr/bin/perl -w
2
3 # Unconditionally regenerate:
4 #
5 #    pod/perlintern.pod
6 #    pod/perlapi.pod
7 #
8 # from information stored in
9 #
10 #    embed.fnc
11 #    plus all the .c and .h files listed in MANIFEST
12 #
13 # Has an optional arg, which is the directory to chdir to before reading
14 # MANIFEST and *.[ch].
15 #
16 # This script is invoked as part of 'make all'
17 #
18 # '=head1' are the only headings looked for.  If the next line after the
19 # heading begins with a word character, it is considered to be the first line
20 # of documentation that applies to the heading itself.  That is, it is output
21 # immediately after the heading, before the first function, and not indented.
22 # The next input line that is a pod directive terminates this heading-level
23 # documentation.
24
25 use strict;
26
27 if (@ARGV) {
28     my $workdir = shift;
29     chdir $workdir
30         or die "Couldn't chdir to '$workdir': $!";
31 }
32 require 'regen/regen_lib.pl';
33 require 'regen/embed_lib.pl';
34
35 #
36 # See database of global and static function prototypes in embed.fnc
37 # This is used to generate prototype headers under various configurations,
38 # export symbols lists for different platforms, and macros to provide an
39 # implicit interpreter context argument.
40 #
41
42 my %docs;
43 my %funcflags;
44 my %macro = (
45              ax => 1,
46              items => 1,
47              ix => 1,
48              svtype => 1,
49             );
50 my %missing;
51
52 my $curheader = "Unknown section";
53
54 sub autodoc ($$) { # parse a file and extract documentation info
55     my($fh,$file) = @_;
56     my($in, $doc, $line, $header_doc);
57 FUNC:
58     while (defined($in = <$fh>)) {
59         if ($in =~ /^#\s*define\s+([A-Za-z_][A-Za-z_0-9]+)\(/ &&
60             ($file ne 'embed.h' || $file ne 'proto.h')) {
61             $macro{$1} = $file;
62             next FUNC;
63         }
64         if ($in=~ /^=head1 (.*)/) {
65             $curheader = $1;
66
67             # If the next line begins with a word char, then is the start of
68             # heading-level documentation.
69             if (defined($doc = <$fh>)) {
70                 if ($doc !~ /^\w/) {
71                     $in = $doc;
72                     redo FUNC;
73                 }
74                 $header_doc = $doc;
75                 $line++;
76
77                 # Continue getting the heading-level documentation until read
78                 # in any pod directive (or as a fail-safe, find a closing
79                 # comment to this pod in a C language file
80 HDR_DOC:
81                 while (defined($doc = <$fh>)) {
82                     if ($doc =~ /^=\w/) {
83                         $in = $doc;
84                         redo FUNC;
85                     }
86                     $line++;
87
88                     if ($doc =~ m:^\s*\*/$:) {
89                         warn "=cut missing? $file:$line:$doc";;
90                         last HDR_DOC;
91                     }
92                     $header_doc .= $doc;
93                 }
94             }
95             next FUNC;
96         }
97         $line++;
98         if ($in =~ /^=for\s+apidoc\s+(.*?)\s*\n/) {
99             my $proto = $1;
100             $proto = "||$proto" unless $proto =~ /\|/;
101             my($flags, $ret, $name, @args) = split /\|/, $proto;
102             my $docs = "";
103 DOC:
104             while (defined($doc = <$fh>)) {
105                 $line++;
106                 last DOC if $doc =~ /^=\w+/;
107                 if ($doc =~ m:^\*/$:) {
108                     warn "=cut missing? $file:$line:$doc";;
109                     last DOC;
110                 }
111                 $docs .= $doc;
112             }
113             $docs = "\n$docs" if $docs and $docs !~ /^\n/;
114
115             # Check the consistency of the flags
116             my ($embed_where, $inline_where);
117             my ($embed_may_change, $inline_may_change);
118
119             my $docref = delete $funcflags{$name};
120             if ($docref and %$docref) {
121                 $embed_where = $docref->{flags} =~ /A/ ? 'api' : 'guts';
122                 $embed_may_change = $docref->{flags} =~ /M/;
123                 $flags .= 'D' if $docref->{flags} =~ /D/;
124             } else {
125                 $missing{$name} = $file;
126             }
127             if ($flags =~ /m/) {
128                 $inline_where = $flags =~ /A/ ? 'api' : 'guts';
129                 $inline_may_change = $flags =~ /x/;
130
131                 if (defined $embed_where && $inline_where ne $embed_where) {
132                     warn "Function '$name' inconsistency: embed.fnc says $embed_where, Pod says $inline_where";
133                 }
134
135                 if (defined $embed_may_change
136                     && $inline_may_change ne $embed_may_change) {
137                     my $message = "Function '$name' inconsistency: ";
138                     if ($embed_may_change) {
139                         $message .= "embed.fnc says 'may change', Pod does not";
140                     } else {
141                         $message .= "Pod says 'may change', embed.fnc does not";
142                     }
143                     warn $message;
144                 }
145             } elsif (!defined $embed_where) {
146                 warn "Unable to place $name!\n";
147                 next;
148             } else {
149                 $inline_where = $embed_where;
150                 $flags .= 'x' if $embed_may_change;
151                 @args = @{$docref->{args}};
152                 $ret = $docref->{retval};
153             }
154
155             $docs{$inline_where}{$curheader}{$name}
156                 = [$flags, $docs, $ret, $file, @args];
157
158             # Create a special entry with an empty-string name for the
159             # heading-level documentation.
160             if (defined $header_doc) {
161                 $docs{$inline_where}{$curheader}{""} = $header_doc;
162                 undef $header_doc;
163             }
164
165             if (defined $doc) {
166                 if ($doc =~ /^=(?:for|head)/) {
167                     $in = $doc;
168                     redo FUNC;
169                 }
170             } else {
171                 warn "$file:$line:$in";
172             }
173         }
174     }
175 }
176
177 sub docout ($$$) { # output the docs for one function
178     my($fh, $name, $docref) = @_;
179     my($flags, $docs, $ret, $file, @args) = @$docref;
180     $name =~ s/\s*$//;
181
182     if ($flags =~ /D/) {
183         $docs = "\n\nDEPRECATED!  It is planned to remove this function from a
184 future release of Perl.  Do not use it for new code; remove it from
185 existing code.\n\n$docs";
186     }
187     else {
188         $docs = "\n\nNOTE: this function is experimental and may change or be
189 removed without notice.\n\n$docs" if $flags =~ /x/;
190     }
191     $docs .= "NOTE: the perl_ form of this function is deprecated.\n\n"
192         if $flags =~ /p/;
193     $docs .= "NOTE: this function must be explicitly called as Perl_$name with an aTHX_ parameter.\n\n"
194         if $flags =~ /o/;
195
196     print $fh "=item $name\nX<$name>\n$docs";
197
198     if ($flags =~ /U/) { # no usage
199         # nothing
200     } elsif ($flags =~ /s/) { # semicolon ("dTHR;")
201         print $fh "\t\t$name;\n\n";
202     } elsif ($flags =~ /n/) { # no args
203         print $fh "\t$ret\t$name\n\n";
204     } else { # full usage
205         my $p            = $flags =~ /o/; # no #define foo Perl_foo
206         my $n            = "Perl_"x$p . $name;
207         my $large_ret    = length $ret > 7;
208         my $indent_size  = 7+8 # nroff: 7 under =head + 8 under =item
209                           +8+($large_ret ? 1 + length $ret : 8)
210                           +length($n) + 1;
211         my $indent;
212         print $fh "\t$ret" . ($large_ret ? ' ' : "\t") . "$n(";
213         my $long_args;
214         for (@args) {
215             if ($indent_size + 2 + length > 79) {
216                 $long_args=1;
217                 $indent_size -= length($n) - 3;
218                 last;
219             }
220         }
221         my $args = '';
222         if ($p) {
223             $args = @args ? "pTHX_ " : "pTHX";
224             if ($long_args) { print $fh $args; $args = '' }
225         }
226         $long_args and print $fh "\n";
227         my $first = !$long_args;
228         while () {
229             if (!@args or
230                  length $args
231                  && $indent_size + 3 + length($args[0]) + length $args > 79
232             ) {
233                 print $fh
234                   $first ? '' : (
235                     $indent //=
236                        "\t".($large_ret ? " " x (1+length $ret) : "\t")
237                       ." "x($long_args ? 4 : 1 + length $n)
238                   ),
239                   $args, (","x($args ne 'pTHX_ ') . "\n")x!!@args;
240                 $args = $first = '';
241             }
242             @args or last;
243             $args .= ", "x!!(length $args && $args ne 'pTHX_ ')
244                    . shift @args;
245         }
246         if ($long_args) { print $fh "\n", substr $indent, 0, -4 }
247         print $fh ")\n\n";
248     }
249     print $fh "=for hackers\nFound in file $file\n\n";
250 }
251
252 sub output {
253     my ($podname, $header, $dochash, $missing, $footer) = @_;
254     my $fh = open_new("pod/$podname.pod", undef,
255                       {by => "$0 extracting documentation",
256                        from => 'the C source files'}, 1);
257
258     print $fh $header;
259
260     my $key;
261     # case insensitive sort, with fallback for determinacy
262     for $key (sort { uc($a) cmp uc($b) || $a cmp $b } keys %$dochash) {
263         my $section = $dochash->{$key}; 
264         print $fh "\n=head1 $key\n\n";
265
266         # Output any heading-level documentation and delete so won't get in
267         # the way later
268         if (exists $section->{""}) {
269             print $fh $section->{""} . "\n";
270             delete $section->{""};
271         }
272         print $fh "=over 8\n\n";
273
274         # Again, fallback for determinacy
275         for my $key (sort { uc($a) cmp uc($b) || $a cmp $b } keys %$section) {
276             docout($fh, $key, $section->{$key});
277         }
278         print $fh "\n=back\n";
279     }
280
281     if (@$missing) {
282         print $fh "\n=head1 Undocumented functions\n\n";
283     print $fh $podname eq 'perlapi' ? <<'_EOB_' : <<'_EOB_';
284 The following functions have been flagged as part of the public API,
285 but are currently undocumented. Use them at your own risk, as the
286 interfaces are subject to change.  Functions that are not listed in this
287 document are not intended for public use, and should NOT be used under any
288 circumstances.
289
290 If you use one of the undocumented functions below, you may wish to consider
291 creating and submitting documentation for it. If your patch is accepted, this
292 will indicate that the interface is stable (unless it is explicitly marked
293 otherwise).
294
295 =over
296
297 _EOB_
298 The following functions are currently undocumented.  If you use one of
299 them, you may wish to consider creating and submitting documentation for
300 it.
301
302 =over
303
304 _EOB_
305     for my $missing (sort @$missing) {
306         print $fh "=item $missing\nX<$missing>\n\n";
307     }
308     print $fh "=back\n\n";
309 }
310     print $fh $footer, "=cut\n";
311
312     read_only_bottom_close_and_rename($fh);
313 }
314
315 foreach (@{(setup_embed())[0]}) {
316     next if @$_ < 2;
317     my ($flags, $retval, $func, @args) = @$_;
318     s/\b(?:NN|NULLOK)\b\s+//g for @args;
319
320     $funcflags{$func} = {
321                          flags => $flags,
322                          retval => $retval,
323                          args => \@args,
324                         };
325 }
326
327 # glob() picks up docs from extra .c or .h files that may be in unclean
328 # development trees.
329 open my $fh, '<', 'MANIFEST'
330     or die "Can't open MANIFEST: $!";
331 while (my $line = <$fh>) {
332     next unless my ($file) = $line =~ /^(\S+\.[ch])\t/;
333
334     open F, "< $file" or die "Cannot open $file for docs: $!\n";
335     $curheader = "Functions in file $file\n";
336     autodoc(\*F,$file);
337     close F or die "Error closing $file: $!\n";
338 }
339 close $fh or die "Error whilst reading MANIFEST: $!";
340
341 for (sort keys %funcflags) {
342     next unless $funcflags{$_}{flags} =~ /d/;
343     warn "no docs for $_\n"
344 }
345
346 foreach (sort keys %missing) {
347     next if $macro{$_};
348     # Heuristics for known not-a-function macros:
349     next if /^[A-Z]/;
350     next if /^dj?[A-Z]/;
351
352     warn "Function '$_', documented in $missing{$_}, not listed in embed.fnc";
353 }
354
355 # walk table providing an array of components in each line to
356 # subroutine, printing the result
357
358 # List of funcs in the public API that aren't also marked as experimental nor
359 # deprecated.
360 my @missing_api = grep $funcflags{$_}{flags} =~ /A/ && $funcflags{$_}{flags} !~ /[MD]/ && !$docs{api}{$_}, keys %funcflags;
361 output('perlapi', <<'_EOB_', $docs{api}, \@missing_api, <<'_EOE_');
362 =head1 NAME
363
364 perlapi - autogenerated documentation for the perl public API
365
366 =head1 DESCRIPTION
367 X<Perl API> X<API> X<api>
368
369 This file contains the documentation of the perl public API generated by
370 F<embed.pl>, specifically a listing of functions, macros, flags, and variables
371 that may be used by extension writers.  L<At the end|/Undocumented functions>
372 is a list of functions which have yet to be documented.  The interfaces of
373 those are subject to change without notice.  Any functions not listed here are
374 not part of the public API, and should not be used by extension writers at
375 all.  For these reasons, blindly using functions listed in proto.h is to be
376 avoided when writing extensions.
377
378 Note that all Perl API global variables must be referenced with the C<PL_>
379 prefix.  Some macros are provided for compatibility with the older,
380 unadorned names, but this support may be disabled in a future release.
381
382 Perl was originally written to handle US-ASCII only (that is characters
383 whose ordinal numbers are in the range 0 - 127).
384 And documentation and comments may still use the term ASCII, when
385 sometimes in fact the entire range from 0 - 255 is meant.
386
387 Note that Perl can be compiled and run under EBCDIC (See L<perlebcdic>)
388 or ASCII.  Most of the documentation (and even comments in the code)
389 ignore the EBCDIC possibility.  
390 For almost all purposes the differences are transparent.
391 As an example, under EBCDIC,
392 instead of UTF-8, UTF-EBCDIC is used to encode Unicode strings, and so
393 whenever this documentation refers to C<utf8>
394 (and variants of that name, including in function names),
395 it also (essentially transparently) means C<UTF-EBCDIC>.
396 But the ordinals of characters differ between ASCII, EBCDIC, and
397 the UTF- encodings, and a string encoded in UTF-EBCDIC may occupy more bytes
398 than in UTF-8.
399
400 The listing below is alphabetical, case insensitive.
401
402 _EOB_
403
404 =head1 AUTHORS
405
406 Until May 1997, this document was maintained by Jeff Okamoto
407 <okamoto@corp.hp.com>.  It is now maintained as part of Perl itself.
408
409 With lots of help and suggestions from Dean Roehrich, Malcolm Beattie,
410 Andreas Koenig, Paul Hudson, Ilya Zakharevich, Paul Marquess, Neil
411 Bowers, Matthew Green, Tim Bunce, Spider Boardman, Ulrich Pfeifer,
412 Stephen McCamant, and Gurusamy Sarathy.
413
414 API Listing originally by Dean Roehrich <roehrich@cray.com>.
415
416 Updated to be autogenerated from comments in the source by Benjamin Stuhl.
417
418 =head1 SEE ALSO
419
420 L<perlguts>, L<perlxs>, L<perlxstut>, L<perlintern>
421
422 _EOE_
423
424 # List of non-static internal functions
425 my @missing_guts =
426  grep $funcflags{$_}{flags} !~ /[As]/ && !$docs{guts}{$_}, keys %funcflags;
427
428 output('perlintern', <<'END', $docs{guts}, \@missing_guts, <<'END');
429 =head1 NAME
430
431 perlintern - autogenerated documentation of purely B<internal>
432                  Perl functions
433
434 =head1 DESCRIPTION
435 X<internal Perl functions> X<interpreter functions>
436
437 This file is the autogenerated documentation of functions in the
438 Perl interpreter that are documented using Perl's internal documentation
439 format but are not marked as part of the Perl API. In other words,
440 B<they are not for use in extensions>!
441
442 END
443
444 =head1 AUTHORS
445
446 The autodocumentation system was originally added to the Perl core by
447 Benjamin Stuhl. Documentation is by whoever was kind enough to
448 document their functions.
449
450 =head1 SEE ALSO
451
452 L<perlguts>, L<perlapi>
453
454 END