| #!/usr/bin/perl -w | |
| # | |
| # Update spec files across dlls that share an implementation | |
| # | |
| # Copyright 2011 Alexandre Julliard | |
| # | |
| # This library is free software; you can redistribute it and/or | |
| # modify it under the terms of the GNU Lesser General Public | |
| # License as published by the Free Software Foundation; either | |
| # version 2.1 of the License, or (at your option) any later version. | |
| # | |
| # This library is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| # Lesser General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU Lesser General Public | |
| # License along with this library; if not, write to the Free Software | |
| # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| # | |
| use strict; | |
| my %funcs; | |
| my $group_head; | |
| my @dll_groups = | |
| ( | |
| [ | |
| "msvcrt", | |
| "msvcr90", | |
| "msvcirt", | |
| "msvcr100", | |
| "msvcr80", | |
| "msvcr71", | |
| "msvcr70", | |
| "msvcrt40", | |
| "msvcrt20", | |
| "msvcrtd", | |
| "crtdll", | |
| ], | |
| [ | |
| "msvcrt", | |
| "msvcp90", | |
| "msvcp100", | |
| "msvcp80", | |
| "msvcp71", | |
| "msvcp70", | |
| "msvcp60", | |
| ], | |
| [ | |
| "d3dx9_36", | |
| "d3dx9_43", | |
| "d3dx9_42", | |
| "d3dx9_41", | |
| "d3dx9_40", | |
| "d3dx9_39", | |
| "d3dx9_38", | |
| "d3dx9_37", | |
| "d3dx9_35", | |
| "d3dx9_34", | |
| "d3dx9_33", | |
| "d3dx9_32", | |
| "d3dx9_31", | |
| "d3dx9_30", | |
| "d3dx9_29", | |
| "d3dx9_28", | |
| "d3dx9_27", | |
| "d3dx9_26", | |
| "d3dx9_25", | |
| "d3dx9_24", | |
| ], | |
| [ | |
| "d3dx10_43", | |
| "d3dx10_42", | |
| "d3dx10_41", | |
| "d3dx10_40", | |
| "d3dx10_39", | |
| "d3dx10_38", | |
| "d3dx10_37", | |
| "d3dx10_36", | |
| "d3dx10_35", | |
| "d3dx10_34", | |
| "d3dx10_33", | |
| ], | |
| [ | |
| "d3dcompiler_43", | |
| "d3dcompiler_42", | |
| "d3dcompiler_41", | |
| "d3dcompiler_40", | |
| "d3dcompiler_39", | |
| "d3dcompiler_38", | |
| "d3dcompiler_37", | |
| "d3dcompiler_36", | |
| "d3dcompiler_35", | |
| "d3dcompiler_34", | |
| "d3dcompiler_33", | |
| ], | |
| [ | |
| "xinput1_3", | |
| "xinput1_2", | |
| "xinput1_1", | |
| "xinput9_1_0", | |
| ], | |
| [ | |
| "setupapi", | |
| "cfgmgr32", | |
| ], | |
| ); | |
| my $update_flags = 0; | |
| my $show_duplicates = 0; | |
| foreach my $arg (@ARGV) | |
| { | |
| if ($arg eq "-f") { $update_flags = 1; } | |
| elsif ($arg eq "-d") { $show_duplicates = 1; } | |
| } | |
| sub update_file($) | |
| { | |
| my $file = shift; | |
| my $ret = !(-f $file) || system "cmp $file $file.new >/dev/null"; | |
| if (!$ret) | |
| { | |
| unlink "$file.new"; | |
| } | |
| else | |
| { | |
| #system "diff -u $file $file.new"; | |
| rename "$file.new", "$file"; | |
| print "$file updated\n"; | |
| } | |
| return $ret; | |
| } | |
| # parse a spec file line | |
| sub parse_line($$$) | |
| { | |
| my ($name, $line, $_) = @_; | |
| if (/^\s*(\@|\d+)\s+(stdcall|cdecl|varargs|thiscall|stub|extern)\s+((?:-\S+\s+)*)([A-Za-z0-9_\@\$?]+)(?:\s*(\([^)]*\)))?(?:\s+([A-Za-z0-9_\@\$?.]+))?(\s*\#.*)?/) | |
| { | |
| return ( "ordinal" => $1, "callconv" => $2, "flags" => $3, "name" => $4, "args" => $5 || "", | |
| "target" => $6 || $4, "comment" => $7, "spec" => $name ); | |
| } | |
| return () if /^\s*$/; | |
| return () if /^\s*\#/; | |
| printf STDERR "$name.spec:$line: error: Unrecognized line $_\n"; | |
| } | |
| sub read_spec_file($) | |
| { | |
| my $name = shift; | |
| my $file = "dlls/$name/$name.spec"; | |
| my %stubs; | |
| open SPEC, "<$file" or die "cannot open $file"; | |
| while (<SPEC>) | |
| { | |
| chomp; | |
| my %descr = parse_line( $name, $., $_ ); | |
| next unless %descr; | |
| my $func = $descr{name}; | |
| next if defined $funcs{$func}; | |
| $funcs{$func} = \%descr; | |
| } | |
| close SPEC; | |
| } | |
| sub update_spec_file($) | |
| { | |
| my $name = shift; | |
| my $file = "dlls/$name/$name.spec"; | |
| my %stubs; | |
| open SPEC, "<$file" or die "cannot open $file"; | |
| open NEW, ">$file.new" or die "cannot create $file.new"; | |
| while (<SPEC>) | |
| { | |
| chomp; | |
| my $commented_out = 0; | |
| my %descr = parse_line( $name, $., $_ ); | |
| if (!%descr) | |
| { | |
| # check for commented out exports | |
| if (/^\s*\#\s*((?:\@|\d+)\s+)?((?:extern|stub|stdcall|cdecl|varargs|thiscall)\s+.*)/) | |
| { | |
| $commented_out = 1; | |
| %descr = parse_line( $name, $., ($1 || "\@ ") . $2 ); | |
| } | |
| } | |
| goto done unless %descr; | |
| my $func = $descr{name}; | |
| if (!defined $funcs{$func}) | |
| { | |
| $funcs{$func} = \%descr unless $commented_out; | |
| goto done; | |
| } | |
| my %parent = %{$funcs{$func}}; | |
| goto done if $parent{spec} eq $descr{spec}; # the definition is in this spec file | |
| if ($descr{callconv} ne "stub" && $descr{target} !~ /\./ && !$commented_out) | |
| { | |
| printf "%s:%u: note: %s already defined in %s\n", $file, $., $func, $parent{spec} if $show_duplicates; | |
| goto done; | |
| } | |
| my $flags = ($parent{callconv} ne "stub" || $update_flags) ? $parent{flags} : $descr{flags}; | |
| if ($parent{callconv} ne "stub" || $parent{args}) | |
| { | |
| my $callconv = $parent{callconv} ne "stub" ? $parent{callconv} : | |
| $parent{spec} =~ /msvc/ ? "cdecl" : "stdcall"; # hack | |
| $_ = sprintf "$descr{ordinal} %s %s%s", $callconv, $flags, $func; | |
| if ($parent{target} =~ /$group_head\./) # use the same forward as parent if possible | |
| { | |
| $_ .= sprintf "%s %s", $parent{args}, $parent{target}; | |
| } | |
| else | |
| { | |
| $_ .= sprintf "%s %s.%s", $parent{args}, $parent{spec}, $func; | |
| } | |
| } | |
| else | |
| { | |
| $_ = sprintf "$descr{ordinal} stub %s%s", $flags, $func; | |
| } | |
| $_ .= $descr{comment} || ""; | |
| done: | |
| print NEW "$_\n"; | |
| } | |
| close SPEC; | |
| close NEW; | |
| update_file( $file ); | |
| } | |
| sub sync_spec_files(@) | |
| { | |
| %funcs = (); | |
| $group_head = shift; | |
| read_spec_file( $group_head ); | |
| foreach my $spec (@_) { update_spec_file($spec); } | |
| } | |
| foreach my $group (@dll_groups) | |
| { | |
| sync_spec_files( @{$group} ); | |
| } |