2 ################################################################################
4 # mktodo.pl -- generate baseline and todo files
6 # It makes the todo file for the single passed in perl binary. If --base is
7 # not specified it compiles with ppport.h.
8 ################################################################################
10 # Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
11 # Version 2.x, Copyright (C) 2001, Paul Marquess.
12 # Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
14 # This program is free software; you can redistribute it and/or
15 # modify it under the same terms as Perl itself.
17 ################################################################################
25 use Time::HiRes qw( gettimeofday tv_interval );
27 require './devel/devtools.pl';
30 blead => 0, # ? Is this perl blead
31 debug => 0, # Adding --verbose increases the detail
32 base => 0, # Don't use ppport.h when generating
35 todo => "", # If no --todo, this is a blead perl
36 shlib => 'blib/arch/auto/Devel/PPPort/PPPort.so',
40 perl=s todo=s blead version=s shlib=s debug base verbose check!
45 print "\n", ident_str(), "\n\n";
47 my $fullperl = `which $opt{perl}`;
50 $ENV{SKIP_SLOW_TESTS} = 1;
52 # Generate the Makefile using the passed in perl
55 # List of functions that are never considered undefined. Add to as necessary
56 my %stdsym = map { ($_ => 1) } qw (
72 # Initialize %sym so that the keys are all the Text symbols for this perl,
73 # output from the system's 'nm'
75 for (`$Config{nm} $fullperl`) {
77 /\s+T\s+(\w+)\s*$/ and $sym{$1}++;
79 keys %sym >= 50 or die "less than 50 symbols found in $fullperl\n";
81 # %todo is initialized to be the symbols in the current todo file, like so:
83 # 'UTF8_SAFE_SKIP' => 'U',
84 # 'newSVsv_flags' => 'U',
85 # 'newSVsv_nomg' => 'U',
88 # The values are the outputs from nm, plus 'E' from us, for Error
89 my %todo = %{load_todo($opt{todo}, $opt{version})} if $opt{todo};
93 # Get an exhaustive list from apicheck.i of symbols, what functions contain
94 # them, and how many in each function.
99 # 'toFOLD_uvchr' => 2,
100 # 'sv_uni_display' => 1,
103 my $symmap = get_apicheck_symbol_map();
105 # In each iteration of the loop we create an apicheck.c. This will contain a
106 # generated wrapper function for each API function and macro. The wrapper
107 # contains one or more calls to its API element. Then we attempt to compile
108 # apicheck.c into apicheck.o. If it compiles, then every API element exists
109 # in this version of perl. If not, we figure out which ones were undefined,
110 # and set things up so that in the next iteration of the loop, the wrappers
111 # for those elements are #ifdef'd out.
119 my(@new, @already_in_sym, %seen);
121 my $r = run(qw(make));
122 $r->{didnotrun} and die "couldn't run make: $!\n" .
123 join('', @{$r->{stdout}})."\n---\n".join('', @{$r->{stderr}});
125 # If there were warnings, we ask the user before continuing when creating
126 # the base files of blead. This leads to a potential early exit when things
127 # aren't working right.
128 if ($opt{blead} && $opt{base}) {
129 undef $opt{blead}; # Only warn once.
130 if (@{$r->{stderr}}) {
131 print STDERR "Warnings and errors from compiling blead:\n";
132 print STDERR @{$r->{stderr}};
133 ask_or_quit("\nUnexpected warnings when compiling blead can lead to"
134 . " wrong results. Please examine the above list.\n"
135 . "Shall I proceed?");
138 print STDERR "blead compiled without warnings nor errors.\n"
139 . "Proceeding with everything else\n";
143 # Examine stderr. For each wrapper function listed in it, we create an
144 # 'E' (for error) entry. If the function (possibly prefixed by '[Pp]erl')
145 # is in %sym, it is added to @already_in_sym. Otherwise, @new.
146 for my $l (@{$r->{stderr}}) {
147 if ($l =~ /_DPPP_test_(\w+)/) {
149 my @s = grep { exists $sym{$_} } $1, "Perl_$1", "perl_$1";
151 push @already_in_sym, [$1, "E (@s)"];
154 push @new, [$1, "E"];
160 if ($r->{status} == 0) {
164 # Here, apicheck.o was successfully created. It likely will need
165 # functions from outside it in order to form a complete executable a.out.
166 # In the first iteration, look to see if all needed externs are available.
167 # (We don't actually try to create an a.out)
169 @u = eval { find_undefined_symbols($fullperl, $opt{shlib}) };
170 warn "warning: $@" if $@;
174 # If it didn't find any undefined symbols, everything should be working.
175 # Run the test suite.
177 $r = run(qw(make test));
178 $r->{didnotrun} and die "couldn't run make test: $!\n" .
179 join('', @{$r->{stdout}})."\n---\n".join('', @{$r->{stderr}});
181 $r->{status} == 0 and last; # It worked!!
183 # Alas, something was wrong. Add any undefined symbols listed in the
185 for my $l (@{$r->{stderr}}) {
186 if ($l =~ /undefined symbol: (\w+)/) {
192 # For each undefined symbol
195 # If this is an API symbol, $symmap->{$u} will exist and be a hash of
196 # keys, being all the symbols referred to within it (with their values
197 # btw being the count of occurrences in the element).
198 for my $m (keys %{$symmap->{$u}}) {
201 $pl =~ s/^[Pp]erl_//;
202 my @s = grep { exists $sym{$_} } $pl, "Perl_$pl", "perl_$pl";
204 # The comment for this entry that goes into the file that gets
205 # written includes any [Pp]erl prefix.
206 push @new, [$m, @s ? "U (@s)" : "U"];
212 # Remove from @new all the current todo symbols
213 @new = grep !$todo{$_->[0]}, @new;
215 # If none remain, start over with those we know about, minus the todo
216 # symbols. khw doesn't understand why this is necessary
218 @new = grep !$todo{$_->[0]}, @already_in_sym;
221 # This retries once if nothing new was found (khw guesses that is just to
222 # be sure, or maybe it's because we ran nm the first time through)
230 die "no new TODO symbols found...";
233 # recheck symbols except undefined ones reported by the dynamic linker
234 push @recheck, map { $_->[0] } grep { $_->[1] !~ /^U/ } @new;
236 # Display each newly found undefined symbol, and add it to the list of todo
239 display_sym('new', @$_);
240 $todo{$_->[0]} = $_->[1];
243 # Write the revised todo, so that apicheck.c when generated in the next
244 # iteration will have these #ifdef'd out
245 write_todo($opt{todo}, $opt{version}, \%todo);
248 # If we are to check our work, do so. This verifies that each symbol
249 # identified above is really a problem in this version. (khw doesn't know
250 # under what circumstances this becomes an issue)
252 # We go through each symbol on the @recheck list, and create an apicheck.c
256 # Create something like '%3d'
257 my $ifmt = '%' . length(scalar @recheck) . 'd';
259 my $t0 = [gettimeofday];
261 RECHECK: for my $i (0 .. $#recheck) {
262 my $sym = $recheck[$i];
264 # Assume it will work
265 my $cur = delete $todo{$sym};
267 # Give a progress report
268 display_sym('chk', $sym, $cur, sprintf(" [$ifmt/$ifmt, ETA %s]",
269 $i + 1, scalar @recheck, eta($t0, $i, scalar @recheck)));
271 # Write out the todo file without this symbol, meaning it will be enabled
272 # in the generated apicheck.c file
273 write_todo($opt{todo}, $opt{version}, \%todo);
275 # E is not an nm symbol, but was added by us to indicate 'Error'
276 if ($cur eq "E (Perl_$sym)") {
278 # We can try a shortcut here. Create an apicheck.c file for just this
280 regen_apicheck($sym);
282 my $r = run(qw(make test));
284 if (!$r->{didnotrun} && $r->{status} == 0) {
286 # Shortcut indicated that this function compiles..
287 display_sym('del', $sym, $cur);
291 # Here, the api file with just this entry failed to compile. (khw
292 # doesn't know why we just don't give up on it now, but we don't.) We
293 # drop down below to generate and compile a full apicheck.c with this
294 # symbol enabled. (XXX Perhaps we could look at stderr and if it
295 # contained things about parameter mismatch, (which is a common
296 # occurrence), we could skip the steps below.)
299 # Either can't shortcut, or the shortcut indicated that the function
300 # doesn't compile in isolation. Create, compile and test with this
301 # function/symbol enabled. (Remember that this should have succeeded
302 # above to get to here when this symbol was disabled, so enabling just
303 # this one will tell us for sure that it works or doesn't work. (khw
304 # wonders if this is actually a DAG, or perhaps with cycles, so this is
305 # under it all, insufficient.)
308 my $r = run(qw(make test));
310 # This regenerated apicheck.c
311 dump_apicheck() if $opt{debug};
313 $r->{didnotrun} and die "couldn't run make test: $!\n" .
314 join('', @{$r->{stdout}})."\n---\n".join('', @{$r->{stderr}});
316 if ($r->{status} == 0) { # This symbol compiles and tests ok, so retain
318 display_sym('del', $sym, $cur);
320 else { # Revert to this symbol is bad in this version
324 } # End of checking our work
326 write_todo($opt{todo}, $opt{version}, \%todo);
328 # Clean up after ourselves
329 run(qw(make realclean));
335 my($what, $sym, $reason, $extra) = @_;
339 'chk' => 'bold magenta',
340 'del' => 'bold green',
342 $what = colored("$what symbol", $col{$what});
344 printf "[%s] %s %-30s # %s%s\n",
345 $opt{version}, $what, $sym, $reason, $extra;
350 # We make sure to add rules for creating apicheck.c
351 my @mf_arg = ('--with-apicheck', 'OPTIMIZE=-O0 -w');
353 # It doesn't include ppport.h if generating the base files.
354 push @mf_arg, qw( DEFINE=-DDPPP_APICHECK_NO_PPPORT_H ) if $opt{base};
357 run(qw(make realclean));
359 my $r = run($fullperl, "Makefile.PL", @mf_arg);
360 unless ($r->{status} == 0) {
361 die "cannot run Makefile.PL: $!\n" .
362 join('', @{$r->{stdout}})."\n---\n".join('', @{$r->{stderr}});
366 sub regen_apicheck # Regeneration can also occur by calling 'make'
368 unlink qw(apicheck.c apicheck.o);
369 runtool({ out => '/dev/null' }, $fullperl, 'apicheck_c.PL', map { "--api=$_" } @_)
370 or die "cannot regenerate apicheck.c\n";
371 dump_apicheck() if $opt{debug};
376 my $apicheck = "apicheck.c";
377 my $f = new IO::File $apicheck or die "cannot open $apicheck: $!\n";
379 print STDERR __FILE__, ": ", __LINE__, ": $apicheck (",
381 " lines) for $fullperl";
382 print STDERR " and '" if @_;
383 print STDERR join "', '", @_;
384 print STDERR "'" if @_;
387 print STDERR $n++, " ", $_ for @lines;
390 sub load_todo # Return entries from $file; skip if the first line
391 # isn't $expver (expected version)
393 my($file, $expver) = @_;
396 my $f = new IO::File $file or die "cannot open $file: $!\n";
399 if ($ver eq $expver) {
403 /^(\w+)\s+#\s+(.*)/ or goto nuke_file;
404 exists $sym{$1} and goto nuke_file;
412 unlink $file or die "cannot remove $file: $!\n";
418 sub write_todo # Write out the todo file. The keys of %sym are known to not
419 # be in this version, hence are 'todo'
421 my($file, $ver, $sym) = @_;
424 $f = new IO::File ">$file" or die "cannot open $file: $!\n";
427 # Dictionary ordering, with only alphanumerics
428 for (sort dictionary_order keys %$sym) {
429 $f->print(sprintf "%-30s # %s\n", $_, $sym->{$_});
433 sub find_undefined_symbols
435 # returns a list of undefined symbols in $shlib. To be considered
436 # undefined, it must also not be defined in $perl. Symbols that begin with
437 # underscore, or contain '@', or are some libc ones are not returned.
438 # Presumably, the list of libc could be expanded if necessary.
440 my($perl, $shlib) = @_;
442 my $ps = read_sym(file => $perl, options => [qw( --defined-only )]);
443 my $ls = read_sym(file => $shlib, options => [qw( --undefined-only )]);
447 for my $sym (keys %$ls) {
448 next if $sym =~ /\@/ or $sym =~ /^_/ or exists $stdsym{$sym};
449 unless (exists $ps->{$sym}) {
450 push @undefined, $sym;
459 my %opt = ( options => [], @_ );
461 my $r = run($Config{nm}, @{$opt{options}}, $opt{file});
463 if ($r->{didnotrun} or $r->{status}) {
464 die "cannot run $Config{nm}" .
465 join('', @{$r->{stdout}})."\n---\n".join('', @{$r->{stderr}});
470 for (@{$r->{stdout}}) {
472 my($adr, $fmt, $sym) = /^\s*([[:xdigit:]]+)?\s+([ABCDGINRSTUVW?-])\s+(\S+)\s*$/i
473 or die "cannot parse $Config{nm} output:\n[$_]\n";
474 $sym{$sym} = { format => $fmt };
475 $sym{$sym}{address} = $adr if defined $adr;
481 sub get_apicheck_symbol_map
488 $r = run(qw(make apicheck.i));
490 # Quit the loop if it succeeded
491 last unless $r->{didnotrun} or $r->{status};
493 # Get the list of macros that had parameter issues. These are marked as
494 # A, for absolute in nm terms
495 my %sym = map { /error: macro "(\w+)" (?:requires|passed) \d+ argument/ ? ($1 => 'A') : () }
498 # Display these, and add them to the global %todo.
500 for my $s (sort dictionary_order keys %sym) {
501 display_sym('new', $s, $sym{$s});
502 $todo{$s} = $sym{$s};
505 # And rewrite the todo file, including these new symbols.
506 write_todo($opt{todo}, $opt{version}, \%todo);
508 # Regenerate apicheck.c for the next iteration
511 else { # It failed for some other reason than parameter issues: give up
512 die "cannot run make apicheck.i ($r->{didnotrun} / $r->{status}):\n".
513 join('', @{$r->{stdout}})."\n---\n".join('', @{$r->{stderr}});
517 # Here, have an apicheck.i. Read it in
518 my $fh = IO::File->new('apicheck.i')
519 or die "cannot open apicheck.i: $!";
528 # We only care about lines within one of our _DPPP_test_ functions. If
529 # we're in one, $cur is set to the name of the current one.
530 if (! defined $cur) { # Not within such a function; see if this starts
532 /_DPPP_test_(\w+)/ and $cur = $1;
536 # For anything that looks like a symbol, note it as a key, and as its
537 # value, the name of the function. Actually the value is another key,
538 # whose value is the count of this symbol's occurrences, so it looks
542 # 'toFOLD_uvchr' => 2,
543 # 'sv_uni_display' => 1,
546 for my $sym (/\b([A-Za-z_]\w+)\b/g) {
547 $symmap{$sym}{$cur}++;
550 # This line marks the end of this function, as constructed by us.