This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
devel/scanprov: Extract code into a function
[perl5.git] / dist / Devel-PPPort / devel / scanprov
CommitLineData
adfe19db
MHM
1#!/usr/bin/perl -w
2################################################################################
3#
daa565d0 4# scanprov -- scan Perl headers for provided macros, and add known exceptions
adfe19db 5#
daa565d0
KW
6# The lines added have a code to signify they are added by us:
7# M means it is a macro
8# X means it is a known exceptional item
a7971112 9#
adfe19db
MHM
10################################################################################
11#
b2049988 12# Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
adfe19db
MHM
13# Version 2.x, Copyright (C) 2001, Paul Marquess.
14# Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
15#
16# This program is free software; you can redistribute it and/or
17# modify it under the same terms as Perl itself.
18#
19################################################################################
20
21use strict;
49ef49fe
CBW
22use Getopt::Long;
23
3d7c117d 24require './parts/ppptools.pl';
55179e46 25require './parts/inc/inctools';
97b9d11f 26require './devel/devtools.pl';
adfe19db 27
49ef49fe
CBW
28our %opt = (
29 mode => 'check',
30 install => '/tmp/perl/install/default',
31 blead => 'bleadperl',
97b9d11f
KW
32 debug => 0,
33 'debug-start' => "",
49ef49fe
CBW
34);
35
0f50f756 36GetOptions(\%opt, qw( install=s mode=s blead=s debug=i debug-start=s)) or die;
49ef49fe
CBW
37
38my $write = $opt{mode} eq 'write';
adfe19db
MHM
39
40my %embed = map { ( $_->{name} => 1 ) }
679ad62d 41 parse_embed(qw(parts/embed.fnc parts/apidoc.fnc parts/ppport.fnc ));
adfe19db 42
c48f5549
KW
43# @provided is set to the elements that are provided, but not functions in the
44# .fnc files
adfe19db
MHM
45my @provided = grep { !exists $embed{$_} }
46 map { /^(\w+)/ ? $1 : () }
47 `$^X ppport.h --list-provided`;
48
daa565d0
KW
49# There are a few exceptions that have to be dealt with specially. Add these
50# to the list of things to scan for.
51my $hard_to_test_ref = known_but_hard_to_test_for();
52push @provided, keys %$hard_to_test_ref;
53
54my $base_dir = 'parts/base';
55my $todo_dir = 'parts/todo';
56
57if ($write) {
58
59 # Get the list of files, which are returned sorted, and so the min version
60 # is in the 0th element
61 my @files = all_files_in_dir($base_dir);
62 my $file = $files[0];
63 my $min_perl = $file;
64 $min_perl =~ s,.*/,,; # The name is the integer of __MIN_PERL__
65
66 # There are a very few special cases that we may not find in scanning, but
67 # exist all the way back. Add them now to avoid throwing later things
68 # off.
69 print "-- $file --\n";
70 open F, ">>$file" or die "$file: $!\n";
71 for (qw(RETVAL CALL THIS)) { # These are also in hard_to_test_for(),
72 # so can't be in blead, as they are skipped
73 # in testing, so no real need to check that
74 # they aren't dups.
75 print "Adding $_ to $file\n";
76 print F format_output_line($_, 'X');
77 }
78 close F;
79
80 # Now we're going to add the hard to test symbols. The hash has been
81 # manually populated and commited, with the version number ppport supports
82 # them to.
83 #
84 # This is a hash ref with the keys being all symbols found in all the
85 # files in the directory, and the values being the perl versions of each
86 # symbol.
87 my $todo = parse_todo($todo_dir);
adfe19db 88
daa565d0
KW
89 # The keys of $hard_to_test_ref are the symbols, and the values are
90 # subhashes, with each 'version' key being its proper perl version.
91 # Below, we invert %hard_to_test, so that the keys are the version, and
92 # the values are the symbols that go in that version
93 my %add_by_version;
94 for my $hard (keys %$hard_to_test_ref) {
95
96 # But if someone ups the min version we support, we don't want to add
97 # something less than that.
98 my $version = int_parse_version($hard_to_test_ref->{$hard});
99 $version = $min_perl if $version < $min_perl;
100 $version = format_version_line($version);
101
102 push @{$add_by_version{$version}}, $hard
103 unless grep { $todo->{$_}->{version} eq $hard } keys %$todo;
104 }
105
106 # Only a few files will have exceptions that apply to them. Rewrite each
107 foreach my $version (keys %add_by_version) {
108 my $file = "$todo_dir/" . int_parse_version($version);
109 print "-- Adding known exceptions to $file --\n";
110 my $need_version_line = ! -e $file;
111 open F, ">>$file" or die "$file: $!\n";
112 print F format_version_line($version) . "\n" if $need_version_line;
113 foreach my $symbol (sort dictionary_order @{$add_by_version{$version}})
114 {
115 print "adding $symbol\n";
116 print F format_output_line($symbol, 'X');
117 }
118 close F;
119 }
120}
121
122# Now that we've added the exceptions to a few files, we can parse
123# and deal with all of them.
124my $perls_ref = get_and_sort_perls(\%opt);
97b9d11f
KW
125
126die "Couldn't find any perls" unless @$perls_ref > 1;
adfe19db 127
a3c49949
KW
128find_first_mentions($perls_ref, # perls to look in
129 \@provided, # List of symbol names to look for
130 '*.h', # Look in all hdrs.
131 1, # Strip comments
132 'M'
133 );
502d9f8e
KW
134
135sub format_output_line
136{
137 my $sym = shift;
a7971112 138 my $code = shift;
502d9f8e 139
a7971112 140 return sprintf "%-30s # $code added by $0\n", $sym;
502d9f8e 141}
a3c49949
KW
142
143sub find_first_mentions
144{
145 my $perls_ref = shift; # List of perls to look in
146 my $look_for_ref = shift; # List of symbol names to look for
147 my $hdrs = shift; # Glob of hdrs to look in
148 my $strip_comments = shift;
149 my $code = shift; # Mark entries as having this type
150
151 $hdrs = [ $hdrs ] unless ref $hdrs;
152
153 my @remaining = @$look_for_ref;
154
155 my %v;
156
157 # We look in descending order of perl versions. Each time through the
158 # loop @remaining is narrowed.
159 for my $p (@$perls_ref) {
160 print "checking perl $p->{version}...\n";
161
162 # Get the hdr files associated with this version
163 my $archlib = `$p->{path} -MConfig -l -e 'print \$Config{archlib}'`;
164 chomp $archlib;
165 local @ARGV;
166 push @ARGV, glob "$archlib/CORE/$_" for @$hdrs;
167
168 my %sym;
169
170 # %sym's keys are every single thing that looks like an identifier
171 # (beginning with a non-digit \w, followed by \w*) that occurs in all
172 # the headers, regardless of where (outside of comments).
173 local $/ = undef;
174 while (<>) { # Read in the next file
175
176 # Strip comments, from perl faq
177 if ($strip_comments) {
178 s#/\*[^*]*\*+([^/*][^*]*\*+)*/|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $2 ? $2 : ""#gse;
179 }
180
181 $sym{$_}++ for /(\b[^\W\d]\w*)/g;
182 }
183
184 # @remaining is narrowed to include only those identifier-like things
185 # that are mentioned in some hdr in this release. (If it isn't even
186 # mentioned, it won't exist in the release.) For those not mentioned,
187 # a key is added of the identifier-like thing in %v. It is a subkey
188 # of this release's "todo" release, which is the next higher one. If
189 # we are at version n, we have already done version n+1 and the
190 # provided element was mentioned there, and now it no longer is. We
191 # take that to mean that to mean that the element became provided for
192 # in n+1.
193 @remaining = map { $sym{$_} or $v{$p->{todo}}{$_}++;
194 $sym{$_} ? $_ : ()
195 } @remaining;
196
197 }
198
199 $v{$perls_ref->[-1]{file}}{$_}++ for @remaining;
200
201 # Read in the parts/base files. The hash ref has keys being all symbols
202 # found in all the files in base/, which are all we are concerned with
203 # became defined in.
204 my $base_ref = parse_todo($base_dir);
205
206
207 # Now add the results from above. At this point, The keys of %v are the 7
208 # digit BCD version numbers, and their subkeys are the symbols provided by
209 # D:P that are first mentioned in this version, like this:
210 # '5009002' => {
211 # 'MY_CXT_CLONE' => 1,
212 # 'SV_NOSTEAL' => 1,
213 # 'UTF8_MAXBYTES' => 1
214 # },
215
216 for my $v (keys %v) {
217
218 # Things listed in blead (the most recent file) are special. They are
219 # there by default because we haven't found them anywhere, so they
220 # don't really exist as far as we can determine, so shouldn't be
221 # listed as existing.
222 next if $v > $perls_ref->[0]->{file};
223
224 # @new becomes the symbols for version $v not already in the file for
225 # $v
226 my @new = sort dictionary_order grep { !exists $base_ref->{$_} }
227 keys %{$v{$v}};
228 @new or next; # Nothing new, skip writing
229
230 my $file = $v;
231 $file =~ s/\.//g;
232 $file = "$base_dir/$file";
233 -e $file or die "non-existent: $file\n";
234 print "-- $file --\n";
235 $write and (open F, ">>$file" or die "$file: $!\n");
236 for (@new) {
237 print "adding $_\n";
238 $write and print F format_output_line($_, $code);
239 }
240 $write and close F;
241 }
242}