Added ability to turn on/off debug channels.
[wine.git] / tools / winapi / winapi_extract
blobbb509e918cc83669ddc894a92b02c46be3efa01a
1 #!/usr/bin/perl -w
3 # Copyright 2001 Patrik Stridvall
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 use strict;
22 BEGIN {
23 $0 =~ m%^(.*?/?tools)/winapi/winapi_extract$%;
24 require "$1/winapi/setup.pm";
27 use config qw(
28 &file_type &files_skip &files_filter &get_spec_files
29 $current_dir $wine_dir $winapi_dir $winapi_check_dir
31 use output qw($output);
32 use winapi_extract_options qw($options);
34 if($options->progress) {
35 $output->enable_progress;
36 } else {
37 $output->disable_progress;
40 use function;
41 use type;
42 use winapi_function;
43 use winapi_parser;
44 use winapi qw(@winapis);
46 my %module2entries;
47 my %module2spec_file;
48 my %module2type;
49 my %module2filename;
50 if($options->spec_files || $options->winetest) {
51 local $_;
53 foreach my $spec_file (get_spec_files("winelib")) {
54 my $entries = [];
56 my $module;
57 my $type;
59 open(IN, "< $wine_dir/$spec_file");
61 my $header = 1;
62 my $lookahead = 0;
63 while($lookahead || defined($_ = <IN>)) {
64 $lookahead = 0;
66 s/^\s*?(.*?)\s*$/$1/; # remove whitespace at begining and end of line
67 s/^(.*?)\s*#.*$/$1/; # remove comments
68 /^$/ && next; # skip empty lines
70 if($header) {
71 if(/^name\s+(.*?)$/) {
72 $module = $1;
73 $module2spec_file{$module} = $spec_file;
74 } elsif(/^file\s+(.*?)$/) {
75 my $filename = $1;
76 $module2filename{$module} = $filename;
77 } elsif(/^type\s+(.*?)$/) {
78 $type = $1;
79 $module2type{$module} = $type;
80 } elsif(/^\d+|@/) {
81 $header = 0;
82 $lookahead = 1;
84 next;
87 if(/^(@|\d+)\s+stdcall\s+(\w+)\s*\(\s*([^\)]*)\s*\)/) {
88 my $ordinal = $1;
89 my $name = $2;
90 my @args = split(/\s+/, $3);
92 push @$entries, [$name, "undef", \@args];
95 close(IN);
97 $module2entries{$module} = $entries;
101 my %specifications;
103 sub documentation_specifications {
104 my $function = shift;
106 my @debug_channels = @{$function->debug_channels};
107 my $documentation = $function->documentation;
108 my $documentation_line = $function->documentation_line;
109 my $return_type = $function->return_type;
110 my $linkage = $function->linkage;
111 my $internal_name = $function->internal_name;
113 if($linkage eq "static") {
114 return;
117 local $_;
118 foreach (split(/\n/, $documentation)) {
119 if(/^\s*\*\s*(\S+)\s*[\(\[]\s*(\w+)\s*\.\s*(\S+)\s*[\)\]]/) {
120 my $external_name = $1;
121 my $module = lc($2);
122 my $ordinal = $3;
124 if($ordinal eq "@") {
125 if(1 || !exists($specifications{$module}{unfixed}{$external_name})) {
126 $specifications{$module}{unfixed}{$external_name}{ordinal} = $ordinal;
127 $specifications{$module}{unfixed}{$external_name}{external_name} = $external_name;
128 $specifications{$module}{unfixed}{$external_name}{function} = $function;
129 } else {
130 $output->write("$external_name ($module.$ordinal) already exists\n");
132 } elsif($ordinal =~ /^\d+$/) {
133 if(1 || !exists($specifications{$module}{fixed}{$ordinal})) {
134 $specifications{$module}{fixed}{$ordinal}{ordinal} = $ordinal;
135 $specifications{$module}{fixed}{$ordinal}{external_name} = $external_name;
136 $specifications{$module}{fixed}{$ordinal}{function} = $function;
137 } else {
138 $output->write("$external_name ($module.$ordinal) already exists\n");
140 } elsif($ordinal eq "init") {
141 if(!exists($specifications{$module}{init})) {
142 $specifications{$module}{init}{function} = $function;
143 } else {
144 $output->write("$external_name ($module.$ordinal) already exists\n");
146 } else {
147 if(!exists($specifications{$module}{unknown}{$external_name})) {
148 $specifications{$module}{unknown}{$external_name}{ordinal} = $ordinal;
149 $specifications{$module}{unknown}{$external_name}{external_name} = $external_name;
150 $specifications{$module}{unknown}{$external_name}{function} = $function;
151 } else {
152 $output->write("$external_name ($module.$ordinal) already exists\n");
156 if($options->debug) {
157 $output->write("$external_name ($module.$ordinal)\n");
163 my %module_pseudo_stub_count16;
164 my %module_pseudo_stub_count32;
166 sub statements_stub {
167 my $function = shift;
169 my $statements = $function->statements;
170 if(defined($statements) && $statements =~ /FIXME[^;]*stub/s) {
171 if($options->win16) {
172 foreach my $module16 ($function->modules16) {
173 $module_pseudo_stub_count16{$module16}++;
176 if($options->win32) {
177 foreach my $module32 ($function->modules32) {
178 $module_pseudo_stub_count32{$module32}++;
184 my @c_files = $options->c_files;
185 @c_files = files_skip(@c_files);
186 @c_files = files_filter("winelib", @c_files);
188 my $progress_output;
189 my $progress_current = 0;
190 my $progress_max = scalar(@c_files);
192 foreach my $file (@c_files) {
193 my %functions;
195 $progress_current++;
196 $output->progress("$file (file $progress_current of $progress_max)");
198 my $create_function = sub {
199 if($options->stub_statistics) {
200 return 'winapi_function'->new;
201 } else {
202 return 'function'->new;
206 my $found_function = sub {
207 my $function = shift;
209 my $internal_name = $function->internal_name;
210 $functions{$internal_name} = $function;
212 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
213 $output->prefix_callback(sub { return $function->prefix; });
215 my $documentation_line = $function->documentation_line;
216 my $documentation = $function->documentation;
217 my $function_line = $function->function_line;
218 my $linkage = $function->linkage;
219 my $return_type = $function->return_type;
220 my $calling_convention = $function->calling_convention;
221 my $statements = $function->statements;
223 if($options->spec_files || $options->winetest) {
224 documentation_specifications($function);
227 if($options->stub_statistics) {
228 statements_stub($function);
231 $output->prefix("");
234 my $create_type = sub {
235 return 'type'->new;
238 my $found_type = sub {
239 my $type = shift;
242 my $found_preprocessor = sub {
243 my $directive = shift;
244 my $argument = shift;
247 &winapi_parser::parse_c_file($file, $create_function, $found_function, $create_type, $found_type, $found_preprocessor);
249 my @internal_names = keys(%functions);
250 if($#internal_names < 0) {
251 $output->write("$file: doesn't contain any functions\n");
255 sub output_function {
256 local *OUT = shift;
257 my $type = shift;
258 my $ordinal = shift;
259 my $external_name = shift;
260 my $function = shift;
262 my $internal_name = $function->internal_name;
264 my $return_kind;
265 my $calling_convention;
266 my $refargument_kinds;
267 if($type eq "win16") {
268 $return_kind = $function->return_kind16 || "undef";
269 $calling_convention = $function->calling_convention16 || "undef";
270 $refargument_kinds = $function->argument_kinds16;
271 } elsif($type eq "win32") {
272 $return_kind = $function->return_kind32 || "undef";
273 $calling_convention = $function->calling_convention32 || "undef";
274 $refargument_kinds = $function->argument_kinds32;
277 if(defined($refargument_kinds)) {
278 my @argument_kinds = map { $_ || "undef"; } @$refargument_kinds;
279 print OUT "$ordinal $calling_convention $external_name(@argument_kinds) $internal_name\n";
280 } else {
281 print OUT "$ordinal $calling_convention $external_name() $internal_name # FIXME: arguments undefined\n";
285 if($options->spec_files) {
286 foreach my $module (keys(%specifications)) {
287 my $spec_file = $module2spec_file{$module};
288 my $type = $module2type{$module};
290 if(!defined($spec_file) || !defined($type)) {
291 $output->write("$module: doesn't exist\n");
292 next;
295 $spec_file .= "2";
297 $output->progress("$spec_file");
298 open(OUT, "> $wine_dir/$spec_file");
300 print OUT "name $module\n";
301 print OUT "type $type\n";
302 if(exists($specifications{$module}{init})) {
303 my $function = $specifications{$module}{init}{function};
304 print OUT "init " . $function->internal_name . "\n";
306 print OUT "\n";
308 my %debug_channels;
309 if(exists($specifications{$module}{init})) {
310 my $function = $specifications{$module}{init}{function};
311 foreach my $debug_channel (@{$function->debug_channels}) {
312 $debug_channels{$debug_channel}++;
315 foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
316 my $function = $specifications{$module}{fixed}{$ordinal}{function};
317 foreach my $debug_channel (@{$function->debug_channels}) {
318 $debug_channels{$debug_channel}++;
321 foreach my $name (sort(keys(%{$specifications{$module}{unfixed}}))) {
322 my $function = $specifications{$module}{unfixed}{$name}{function};
323 foreach my $debug_channel (@{$function->debug_channels}) {
324 $debug_channels{$debug_channel}++;
327 foreach my $name (sort(keys(%{$specifications{$module}{unknown}}))) {
328 my $function = $specifications{$module}{unknown}{$name}{function};
329 foreach my $debug_channel (@{$function->debug_channels}) {
330 $debug_channels{$debug_channel}++;
334 my @debug_channels = sort(keys(%debug_channels));
335 if($#debug_channels >= 0) {
336 print OUT "debug_channels (" . join(" ", @debug_channels) . ")\n";
337 print OUT "\n";
340 my $empty = 1;
342 if(!$empty) {
343 print OUT "\n";
344 $empty = 1;
346 foreach my $external_name (sort(keys(%{$specifications{$module}{unknown}}))) {
347 my $entry = $specifications{$module}{unknown}{$external_name};
348 my $ordinal = $entry->{ordinal};
349 my $function = $entry->{function};
350 print OUT "# ";
351 output_function(\*OUT, $type, $ordinal, $external_name, $function);
352 $empty = 0;
355 if(!$empty) {
356 print OUT "\n";
357 $empty = 1;
359 foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
360 my $entry = $specifications{$module}{fixed}{$ordinal};
361 my $external_name = $entry->{external_name};
362 my $function = $entry->{function};
363 output_function(\*OUT, $type, $ordinal, $external_name, $function);
364 $empty = 0;
367 if(!$empty) {
368 print OUT "\n";
369 $empty = 1;
371 foreach my $external_name (sort(keys(%{$specifications{$module}{unfixed}}))) {
372 my $entry = $specifications{$module}{unfixed}{$external_name};
373 my $ordinal = $entry->{ordinal};
374 my $function = $entry->{function};
375 output_function(\*OUT, $type, $ordinal, $external_name, $function);
376 $empty = 0;
379 close(OUT);
383 if($options->stub_statistics) {
384 foreach my $winapi (@winapis) {
385 if($winapi->name eq "win16" && !$options->win16) { next; }
386 if($winapi->name eq "win32" && !$options->win32) { next; }
388 my %module_stub_count;
389 my %module_total_count;
391 foreach my $internal_name ($winapi->all_internal_functions,$winapi->all_functions_stub) {
392 foreach my $module (split(/ \& /, $winapi->function_internal_module($internal_name))) {
393 if($winapi->is_function_stub_in_module($module, $internal_name)) {
394 $module_stub_count{$module}++;
396 $module_total_count{$module}++;
400 foreach my $module ($winapi->all_modules) {
401 my $pseudo_stubs;
402 if($winapi->name eq "win16") {
403 $pseudo_stubs = $module_pseudo_stub_count16{$module};
404 } elsif($winapi->name eq "win32") {
405 $pseudo_stubs = $module_pseudo_stub_count32{$module};
408 my $real_stubs = $module_stub_count{$module};
409 my $total = $module_total_count{$module};
411 if(!defined($real_stubs)) { $real_stubs = 0; }
412 if(!defined($pseudo_stubs)) { $pseudo_stubs = 0; }
413 if(!defined($total)) { $total = 0;}
415 my $stubs = $real_stubs + $pseudo_stubs;
417 $output->write("*.c: $module: ");
418 $output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo)\n");
423 if($options->winetest) {
424 foreach my $module (sort(keys(%specifications))) {
425 my $type = $module2type{$module};
426 my $filename = $module2filename{$module} || $module;
427 my $modulename = $filename;
428 $modulename =~ s/\./_/g;
430 next unless $type eq "win32";
432 my @entries;
434 foreach my $external_name (sort(keys(%{$specifications{$module}{unknown}}))) {
435 my $entry = $specifications{$module}{unknown}{$external_name};
436 push @entries, $entry;
439 foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
440 my $entry = $specifications{$module}{fixed}{$ordinal};
441 push @entries, $entry;
444 foreach my $external_name (sort(keys(%{$specifications{$module}{unfixed}}))) {
445 my $entry = $specifications{$module}{unfixed}{$external_name};
446 push @entries, $entry;
449 my $n = 0;
450 foreach my $entry (@entries) {
451 my $external_name = $entry->{external_name};
452 my $ordinal = $entry->{ordinal};
453 my $function = $entry->{function};
455 my $return_kind;
456 my $calling_convention;
457 my $refargument_kinds;
458 if($type eq "win16") {
459 $return_kind = $function->return_kind16 || "undef";
460 $calling_convention = $function->calling_convention16 || "undef";
461 $refargument_kinds = $function->argument_kinds16;
462 } elsif($type eq "win32") {
463 $return_kind = $function->return_kind32 || "undef";
464 $calling_convention = $function->calling_convention32 || "undef";
465 $refargument_kinds = $function->argument_kinds32;
468 my @argument_kinds;
469 if(defined($refargument_kinds)) {
470 @argument_kinds = map { $_ || "undef"; } @$refargument_kinds;
473 next if $calling_convention ne "stdcall";
474 next if $external_name eq "\@";
476 if($n == 0) {
477 open(OUT, "> $wine_dir/programs/winetest/include/${modulename}.pm");
479 print OUT "package ${modulename};\n";
480 print OUT "\n";
482 print OUT "use strict;\n";
483 print OUT "\n";
485 print OUT "require Exporter;\n";
486 print OUT "\n";
488 print OUT "use wine;\n";
489 print OUT "use vars qw(\@ISA \@EXPORT \@EXPORT_OK);\n";
490 print OUT "\n";
492 print OUT "\@ISA = qw(Exporter);\n";
493 print OUT "\@EXPORT = qw();\n";
494 print OUT "\@EXPORT_OK = qw();\n";
495 print OUT "\n";
497 print OUT "my \$module_declarations = {\n";
498 } elsif($n > 0) {
499 print OUT ",\n";
502 print OUT " \"\Q$external_name\E\" => [\"$return_kind\", [";
503 my $m = 0;
504 foreach my $argument_kind (@argument_kinds) {
505 if($m > 0) {
506 print OUT ", ";
508 print OUT "\"$argument_kind\"";
509 $m++;
511 print OUT "]]";
512 $n++;
515 if($n > 0) {
516 print OUT "\n";
517 print OUT "};\n";
518 print OUT "\n";
519 print OUT "&wine::declare(\"$filename\",\%\$module_declarations);\n";
520 print OUT "push \@EXPORT, map { \"&\" . \$_; } sort(keys(\%\$module_declarations));\n";
521 print OUT "1;\n";
522 close(OUT);