Commit | Line | Data |
---|---|---|
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 | ||
21 | use strict; | |
49ef49fe CBW |
22 | use Getopt::Long; |
23 | ||
3d7c117d | 24 | require './parts/ppptools.pl'; |
55179e46 | 25 | require './parts/inc/inctools'; |
97b9d11f | 26 | require './devel/devtools.pl'; |
adfe19db | 27 | |
49ef49fe CBW |
28 | our %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 | 36 | GetOptions(\%opt, qw( install=s mode=s blead=s debug=i debug-start=s)) or die; |
49ef49fe CBW |
37 | |
38 | my $write = $opt{mode} eq 'write'; | |
adfe19db MHM |
39 | |
40 | my %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 |
45 | my @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. | |
51 | my $hard_to_test_ref = known_but_hard_to_test_for(); | |
52 | push @provided, keys %$hard_to_test_ref; | |
53 | ||
54 | my $base_dir = 'parts/base'; | |
55 | my $todo_dir = 'parts/todo'; | |
56 | ||
57 | if ($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. | |
124 | my $perls_ref = get_and_sort_perls(\%opt); | |
97b9d11f KW |
125 | |
126 | die "Couldn't find any perls" unless @$perls_ref > 1; | |
adfe19db | 127 | |
a3c49949 KW |
128 | find_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 | |
135 | sub 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 | |
143 | sub 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 | } |