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 | 11 | my ($file, $destdir, $objdump, $windows_wordsize) = @ARGV; |
| 12 | 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 | 14 | my $sep = ($^O eq 'MSWin32' ? ';' : ':'); |
| 42 | 15 | my @path = ($filedir, '.', split($sep, $ENV{'PATH'})); |
| 43 | 16 | foreach my $var (qw(LIB)) |
| ... | ... | @@ -47,10 +20,6 @@ foreach my $var (qw(LIB)) |
| 47 | 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 | 23 | my $redist_suffix = (($windows_wordsize eq '64') ? "x64" : "x86"); |
| 55 | 24 | if (exists $ENV{'VCINSTALLDIR'}) |
| 56 | 25 | { |
| ... | ... | @@ -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 | 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 | 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 | 55 | my $found = 0; |
| 82 | 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 | 69 | if (! $found) |
| 92 | 70 | { |
| 93 | - push(@notfound, $dll); | |
| 71 | + push(@notfound, $dll); | |
| 94 | 72 | } |
| 95 | 73 | } |
| 96 | - | |
| 97 | 74 | if (@notfound) |
| 98 | 75 | { |
| 99 | 76 | die "$whoami: can't find the following dlls: " . |
| 100 | 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 | 83 | $f =~ s,\\,/,g; |
| 106 | 84 | print "Copying $f to $destdir\n"; |
| 107 | 85 | system("cp -p '$f' '$destdir'") == 0 or |
| 108 | 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 | 116 | sub is_format |
| 112 | 117 | { |
| 113 | 118 | my ($file, $format) = @_; |
| ... | ... | @@ -133,58 +138,6 @@ sub is_format |
| 133 | 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 | 141 | sub usage |
| 189 | 142 | { |
| 190 | 143 | die "Usage: $whoami {exe|dll} destdir\n"; | ... | ... |