1################################################################################
2#
3#  mkppport_fnc.pl -- generate ppport.fnc
4#
5# This program should be run when regenerating the data for ppport.h
6# (devel/regenerate).  It should be run after parts/embed.fnc is updated, and
7# after mkapidoc.pl has been run.
8#
9# Its purpose is to generate ppport.fnc, a file which has the same syntax as
10# embed.fnc and apidoc.fnc, but contains entries that should only be tested
11# when ppport.h is enabled during the test.
12#
13# Thus it includes items that are Devel::PPPort only, and items that it
14# figures out aren't tested by the other two functions.
15#
16# These otherwise-untested items are those:
17#   1) for which D:P provides and are not found in embed.fnc nor apidoc.fnc,
18#      or aren't listed as public API in those files
19#   2) and for which tests can be automatically generated that they at least
20#      compile.
21#
22# The reason that an item isn't in those two files is that it is an
23# undocumented macro.  (If it's not a macro, it has to be in embed.fnc, and if
24# it's documented, mkapidoc.pl would find it and place it in apidoc.fnc.)
25#
26# And, the reason we can't generate tests for undocumented macros is we don't
27# readily know the types of the parameters, which we need to get a C program
28# to compile.  We could easily discover the number of parameters, but gleaning
29# their types is harder.
30#
31# Instead of expending effort to cope with undocumented items, document them
32# instead, improving the product doubly.
33#
34# However, if the macro has no parameters, there are no types to need to know.
35# And, it turns out, that it may be that many of these macros (which typically
36# just define constants) really don't need to be documented.  They may be
37# something that is considered to be provided, but should actually have been
38# internal constants, not exposed to the outside world.  And they're a lot of
39# them.  So this function was written to handle them.
40#
41# Algorithms could be devised to read the =xsubs sections and associate code
42# found therein with the item, and to include the code as the test for the
43# item, but again, it would be better to just document them.
44#
45# scanprov, run as part of regeneration, will find when all functions, API or
46# not, became defined; but not macros.
47################################################################################
48#
49#  This program is free software; you can redistribute it and/or
50#  modify it under the same terms as Perl itself.
51#
52################################################################################
53
54use strict;
55use warnings;
56use re '/aa';
57
58my $main_dir = $0;
59my $source_dir = $ARGV[0];
60die "Need base directory as argument" unless -e $source_dir;
61
62# Up one level
63$main_dir =~ s;[^/]*$;;;
64$main_dir =~ s;/$;;;
65
66# Up a second level
67$main_dir =~ s;[^/]*$;;;
68$main_dir =~ s;/$;;;
69
70$main_dir = '.' unless $main_dir;
71require "$main_dir/parts/ppptools.pl";
72
73
74my @provided = map { /^(\w+)/ ? $1 : () } `$^X ppport.h --list-provided`;
75die "Nothing provided" unless @provided;
76
77my $api_fnc = "$main_dir/parts/apidoc.fnc";
78my $embed_fnc = "$main_dir/parts/embed.fnc";
79
80# One of the outputs is a known element provided only by us.
81my @out = 'Am|void|sv_magic_portable|NN SV* sv|NULLOK SV* obj|int how|NULLOK const char* name|I32 namlen';
82
83# First, get the known elements
84my @embeds = parse_embed($api_fnc, $embed_fnc);
85
86# Look for %include lines in the ppport.h generator
87my $PPPort = "$main_dir/PPPort_pm.PL";
88open F, "<", $PPPort or die "Can't open $PPPort: $!";
89
90# Now find all the elements furnished by us whose signatures we don't know
91# (hence not in embed.fnc nor apidoc.fnc) and have no parameters.
92my @no_parameters;
93while (<F>) {
94    next unless/^%include (\w+)/;
95    my @implementation = split /\n/,
96                parse_partspec("$main_dir/parts/inc/$1")->{'implementation'};
97    while (defined (my $line = shift @implementation)) {
98        my $var;
99        if ($line =~ /^ \s* __UNDEFINED__ \s+ (\w+) \s /x) {
100            $var = $1;
101        }
102        elsif ($line =~ /^ \s* __NEED_VAR__ \s+ (\w+) \s+ (\w+) /x) {
103            $var = $2;
104        }
105        elsif ($line =~ / ^ \# \s* define \s+ ( \w+ ) \s /x) {
106            $var = $1;
107        }
108
109        next unless defined $var;
110        next if $var =~ / ^ D_PPP_ /x;                  # Skip internal only
111        next if grep { $1 eq $_->{'name'} } @embeds;    # Skip known elements
112        next if grep { $1 eq $_ } @no_parameters;   # Skip if already have it
113        push @no_parameters, $var;
114    }
115}
116
117push @out, map { "AmnT|void|$_" } @no_parameters;
118
119@out = sort sort_api_lines @out;
120
121my $out = "parts/ppport.fnc";
122open OUT, ">", $out or die "Could open '$out' for writing: $!";
123
124print OUT <<EOF;
125::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
126:
127:  !!!! Do NOT edit this file directly! -- Edit devel/mkppport_fnc.pl instead. !!!!
128:
129:  Perl/Pollution/Portability
130:
131::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
132:
133:  Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
134:  Version 2.x, Copyright (C) 2001, Paul Marquess.
135:  Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
136:
137:  This program is free software; you can redistribute it and/or
138:  modify it under the same terms as Perl itself.
139:
140::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
141:
142: This file lists all functions/macros that are provided by Devel::PPPort that
143: would not be tested otherwise; because either they are not public, or they
144: exist only in D:P.  It is in the same format as the F<embed.fnc> that ships
145: with the Perl source code.
146:
147: Since these are used only to provide the argument types, it's ok to have the
148: return value be void for some where it's a potential issue.
149
150EOF
151
152print OUT map { "$_\n" } sort sort_api_lines @out;
153print OUT "\n";
154print "$out regenerated\n";
155
156close OUT;
157