Commit 55f19d3e1bd23728769803999139b276aca1da57
1 parent
70949cb4
Windows: find DLLs recursively at installation
Showing
1 changed file
with
50 additions
and
97 deletions
copy_dlls
100755 → 100644
| @@ -11,33 +11,6 @@ usage() unless @ARGV == 4; | @@ -11,33 +11,6 @@ usage() unless @ARGV == 4; | ||
| 11 | my ($file, $destdir, $objdump, $windows_wordsize) = @ARGV; | 11 | my ($file, $destdir, $objdump, $windows_wordsize) = @ARGV; |
| 12 | my $filedir = dirname($file); | 12 | my $filedir = dirname($file); |
| 13 | 13 | ||
| 14 | -my %dlls = (); | ||
| 15 | -my $format = undef; | ||
| 16 | -open(O, "$objdump -p $file|") or die "$whoami: can't run objdump\n"; | ||
| 17 | -while (<O>) | ||
| 18 | -{ | ||
| 19 | - if (m/^\s+DLL Name:\s+(.+\.dll)/i) | ||
| 20 | - { | ||
| 21 | - my $dll = $1; | ||
| 22 | - $dll =~ tr/A-Z/a-z/; | ||
| 23 | - next if $dll =~ m/^(kernel32|user32|msvcrt|advapi32)\.dll$/; | ||
| 24 | - $dlls{$dll} = 1; | ||
| 25 | - } | ||
| 26 | - elsif (m/^Magic.*\((PE.+?)\)/) | ||
| 27 | - { | ||
| 28 | - $format = $1; | ||
| 29 | - } | ||
| 30 | -} | ||
| 31 | -close(O); | ||
| 32 | -if (! defined $format) | ||
| 33 | -{ | ||
| 34 | - die "$whoami: can't determine format of $file\n"; | ||
| 35 | -} | ||
| 36 | - | ||
| 37 | -# Search the directories named in the file's manifest (if present), | ||
| 38 | -# the file's directory, the current directory, and the path for dlls | ||
| 39 | -# since that's what Windows does. Be sure to only capture compatible | ||
| 40 | -# DLLs. | ||
| 41 | my $sep = ($^O eq 'MSWin32' ? ';' : ':'); | 14 | my $sep = ($^O eq 'MSWin32' ? ';' : ':'); |
| 42 | my @path = ($filedir, '.', split($sep, $ENV{'PATH'})); | 15 | my @path = ($filedir, '.', split($sep, $ENV{'PATH'})); |
| 43 | foreach my $var (qw(LIB)) | 16 | foreach my $var (qw(LIB)) |
| @@ -47,10 +20,6 @@ foreach my $var (qw(LIB)) | @@ -47,10 +20,6 @@ foreach my $var (qw(LIB)) | ||
| 47 | push(@path, split($sep, $ENV{$var})); | 20 | push(@path, split($sep, $ENV{$var})); |
| 48 | } | 21 | } |
| 49 | } | 22 | } |
| 50 | -if (-f "$file.manifest") | ||
| 51 | -{ | ||
| 52 | - unshift(@path, get_manifest_dirs("$file.manifest")); | ||
| 53 | -} | ||
| 54 | my $redist_suffix = (($windows_wordsize eq '64') ? "x64" : "x86"); | 23 | my $redist_suffix = (($windows_wordsize eq '64') ? "x64" : "x86"); |
| 55 | if (exists $ENV{'VCINSTALLDIR'}) | 24 | if (exists $ENV{'VCINSTALLDIR'}) |
| 56 | { | 25 | { |
| @@ -68,46 +37,82 @@ if (exists $ENV{'VCINSTALLDIR'}) | @@ -68,46 +37,82 @@ if (exists $ENV{'VCINSTALLDIR'}) | ||
| 68 | } | 37 | } |
| 69 | } | 38 | } |
| 70 | } | 39 | } |
| 71 | -if (exists $ENV{'UNIVERSALCRTSDKDIR'}) | 40 | +if (exists $ENV{'UniversalCRTSdkDir'}) |
| 72 | { | 41 | { |
| 73 | - my $redist = $ENV{'UNIVERSALCRTSDKDIR'} . "/Redist/ucrt/DLLs/$redist_suffix"; | 42 | + my $redist = $ENV{'UniversalCRTSdkDir'} . "/Redist/ucrt/DLLs/$redist_suffix"; |
| 74 | unshift(@path, $redist); | 43 | unshift(@path, $redist); |
| 75 | } | 44 | } |
| 76 | -my @final = (); | 45 | + |
| 46 | +my $format = undef; | ||
| 47 | +my @to_find = get_dlls($file); | ||
| 48 | + | ||
| 49 | +my %final = (); | ||
| 77 | my @notfound = (); | 50 | my @notfound = (); |
| 78 | -dll_loop: | ||
| 79 | -foreach my $dll (sort keys %dlls) | 51 | + |
| 52 | +while (@to_find) | ||
| 80 | { | 53 | { |
| 54 | + my $dll = shift(@to_find); | ||
| 81 | my $found = 0; | 55 | my $found = 0; |
| 82 | foreach my $dir (@path) | 56 | foreach my $dir (@path) |
| 83 | { | 57 | { |
| 84 | - if ((-f "$dir/$dll") && is_format("$dir/$dll", $format)) | ||
| 85 | - { | ||
| 86 | - push(@final, "$dir/$dll"); | ||
| 87 | - $found = 1; | ||
| 88 | - last; | ||
| 89 | - } | 58 | + if ((-f "$dir/$dll") && is_format("$dir/$dll", $format)) |
| 59 | + { | ||
| 60 | + if (! exists $final{$dll}) | ||
| 61 | + { | ||
| 62 | + $final{$dll} = "$dir/$dll"; | ||
| 63 | + push(@to_find, get_dlls("$dir/$dll")); | ||
| 64 | + } | ||
| 65 | + $found = 1; | ||
| 66 | + last; | ||
| 67 | + } | ||
| 90 | } | 68 | } |
| 91 | if (! $found) | 69 | if (! $found) |
| 92 | { | 70 | { |
| 93 | - push(@notfound, $dll); | 71 | + push(@notfound, $dll); |
| 94 | } | 72 | } |
| 95 | } | 73 | } |
| 96 | - | ||
| 97 | if (@notfound) | 74 | if (@notfound) |
| 98 | { | 75 | { |
| 99 | die "$whoami: can't find the following dlls: " . | 76 | die "$whoami: can't find the following dlls: " . |
| 100 | join(', ', @notfound), "\n"; | 77 | join(', ', @notfound), "\n"; |
| 101 | } | 78 | } |
| 102 | 79 | ||
| 103 | -foreach my $f (@final) | 80 | +foreach my $dll (sort keys (%final)) |
| 104 | { | 81 | { |
| 82 | + my $f = $final{$dll}; | ||
| 105 | $f =~ s,\\,/,g; | 83 | $f =~ s,\\,/,g; |
| 106 | print "Copying $f to $destdir\n"; | 84 | print "Copying $f to $destdir\n"; |
| 107 | system("cp -p '$f' '$destdir'") == 0 or | 85 | system("cp -p '$f' '$destdir'") == 0 or |
| 108 | die "$whoami: copy $f to $destdir failed\n"; | 86 | die "$whoami: copy $f to $destdir failed\n"; |
| 109 | } | 87 | } |
| 110 | 88 | ||
| 89 | +sub get_dlls | ||
| 90 | +{ | ||
| 91 | + my @result = (); | ||
| 92 | + my $exe = shift; | ||
| 93 | + open(O, "$objdump -p $exe|") or die "$whoami: can't run objdump\n"; | ||
| 94 | + while (<O>) | ||
| 95 | + { | ||
| 96 | + if (m/^\s+DLL Name:\s+(.+\.dll)/i) | ||
| 97 | + { | ||
| 98 | + my $dll = $1; | ||
| 99 | + $dll =~ tr/A-Z/a-z/; | ||
| 100 | + next if $dll =~ m/^(kernel32|user32|msvcrt|advapi32)\.dll$/; | ||
| 101 | + push(@result, $dll); | ||
| 102 | + } | ||
| 103 | + elsif (m/^Magic.*\((PE.+?)\)/) | ||
| 104 | + { | ||
| 105 | + $format = $1; | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + close(O); | ||
| 109 | + if (! defined $format) | ||
| 110 | + { | ||
| 111 | + die "$whoami: can't determine format of $exe\n"; | ||
| 112 | + } | ||
| 113 | + @result; | ||
| 114 | +} | ||
| 115 | + | ||
| 111 | sub is_format | 116 | sub is_format |
| 112 | { | 117 | { |
| 113 | my ($file, $format) = @_; | 118 | my ($file, $format) = @_; |
| @@ -133,58 +138,6 @@ sub is_format | @@ -133,58 +138,6 @@ sub is_format | ||
| 133 | $result; | 138 | $result; |
| 134 | } | 139 | } |
| 135 | 140 | ||
| 136 | -sub get_manifest_dirs | ||
| 137 | -{ | ||
| 138 | - # Find all system directories in which to search for DLLs based on | ||
| 139 | - # the contents of a Visual Studio manifest file. | ||
| 140 | - | ||
| 141 | - my $manifest_file = shift; | ||
| 142 | - | ||
| 143 | - require XML::Parser; | ||
| 144 | - my $sysroot = $ENV{'SYSTEMROOT'} or die "$whoami: can't get \$SYSTEMROOT\n"; | ||
| 145 | - $sysroot =~ s,\\,/,g; | ||
| 146 | - if ($^O eq 'cygwin') | ||
| 147 | - { | ||
| 148 | - chop($sysroot = `cygpath $sysroot`); | ||
| 149 | - die "$whoami: can't get system root" unless $? == 0; | ||
| 150 | - } | ||
| 151 | - my $winsxs = "$sysroot/WinSxS"; | ||
| 152 | - opendir(D, $winsxs) or die "$whoami: can't opendir $winsxs: $!\n"; | ||
| 153 | - my @entries = readdir(D); | ||
| 154 | - closedir(D); | ||
| 155 | - | ||
| 156 | - my @candidates = (); | ||
| 157 | - | ||
| 158 | - my $readAssemblyIdentity = sub | ||
| 159 | - { | ||
| 160 | - my ($parser, $element, %attrs) = @_; | ||
| 161 | - return unless $element eq 'assemblyIdentity'; | ||
| 162 | - my $type = $attrs{'type'}; | ||
| 163 | - my $name = $attrs{'name'}; | ||
| 164 | - my $version = $attrs{'version'}; | ||
| 165 | - my $processorArchitecture = $attrs{'processorArchitecture'}; | ||
| 166 | - my $publicKeyToken = $attrs{'publicKeyToken'}; | ||
| 167 | - | ||
| 168 | - my $dir_start = join('_', | ||
| 169 | - $processorArchitecture, | ||
| 170 | - $name, | ||
| 171 | - $publicKeyToken, | ||
| 172 | - $version); | ||
| 173 | - push(@candidates, $dir_start); | ||
| 174 | - }; | ||
| 175 | - | ||
| 176 | - my $p = new XML::Parser(Handlers => {'Start' => $readAssemblyIdentity}); | ||
| 177 | - $p->parsefile($manifest_file); | ||
| 178 | - | ||
| 179 | - my @dirs = (); | ||
| 180 | - foreach my $c (@candidates) | ||
| 181 | - { | ||
| 182 | - push(@dirs, map { "$winsxs/$_" } (grep { m/^\Q$c\E/i } @entries)); | ||
| 183 | - } | ||
| 184 | - | ||
| 185 | - @dirs; | ||
| 186 | -} | ||
| 187 | - | ||
| 188 | sub usage | 141 | sub usage |
| 189 | { | 142 | { |
| 190 | die "Usage: $whoami {exe|dll} destdir\n"; | 143 | die "Usage: $whoami {exe|dll} destdir\n"; |