|
Packit |
82cce8 |
#!/usr/bin/perl
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
# recursively find NEEDED (in the ELF sense) shared libraries
|
|
Packit |
82cce8 |
# for a given share library or for all installed Perl "glue" libraries
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
use strict;
|
|
Packit |
82cce8 |
use warnings;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
use File::Spec;
|
|
Packit |
82cce8 |
use File::Find;
|
|
Packit |
82cce8 |
use File::Basename;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
package DLL
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
use strict;
|
|
Packit |
82cce8 |
use warnings;
|
|
Packit |
82cce8 |
use Capture::Tiny qw(:all);
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
our ($show_system_libs, $show_perl_libs); # default: don't show
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
my @dll_path = File::Spec->path; # Windows
|
|
Packit |
82cce8 |
# my @dll_path = qw(/lib /lib/x86_64-linux-gnu /usr/lib /usr/lib/x86_64-linux-gnu);
|
|
Packit |
82cce8 |
# + $ENV{LD_LIBRARY_PATH} if set
|
|
Packit |
82cce8 |
# Linux (Debian multi-arch)
|
|
Packit |
82cce8 |
# maybe use "gcc -print-search-dirs" (pathnames may need canonicalization)
|
|
Packit |
82cce8 |
# install: /usr/lib/gcc/x86_64-linux-gnu/4.9/
|
|
Packit |
82cce8 |
# programs: =/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/bin/
|
|
Packit |
82cce8 |
# libraries: =/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib/:/lib/x86_64-linux-gnu/4.9/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/4.9/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../:/lib/:/usr/lib/
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
require Tie::CPHash;
|
|
Packit |
82cce8 |
tie my %cache, "Tie::CPHash";
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub name { shift->{name} }
|
|
Packit |
82cce8 |
sub path { shift->{path} }
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub find # class method
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($class, $name) = @_;
|
|
Packit |
82cce8 |
unless ($cache{$name})
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my $found;
|
|
Packit |
82cce8 |
foreach (@dll_path)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my $path = File::Spec->catfile($_, $name);
|
|
Packit |
82cce8 |
$found = $path, last if -e $path;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
$cache{$name} = bless {
|
|
Packit |
82cce8 |
name => $name,
|
|
Packit |
82cce8 |
path => $found,
|
|
Packit |
82cce8 |
}, $class;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
return $cache{$name};
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub needed
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($self, $path) = @_;
|
|
Packit |
82cce8 |
if (ref $self)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
return @{ $self->{needed} } if $self->{needed};
|
|
Packit |
82cce8 |
$path = $self->{path};
|
|
Packit |
82cce8 |
die "can't find DLL $self->{name}" unless defined $path;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
else
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
die __PACKAGE__."->needed: argument PATH missing" unless defined $path;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
my ($out, $err, $exit) = capture { system(qw( objdump -ax ), $path) };
|
|
Packit |
82cce8 |
die qq["objdump -ax $path" failed: $err] unless $exit == 0;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
my @needed = map { __PACKAGE__->find($_) }
|
|
Packit |
82cce8 |
$out =~ /^\s*DLL Name:\s*(\S+)/gm; # Windows
|
|
Packit |
82cce8 |
# $out =~ /^\s*NEEDED\s+(\S+)/gm; # Linux
|
|
Packit |
82cce8 |
$self->{needed} = \@needed if ref $self;
|
|
Packit |
82cce8 |
return @needed;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub depends
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($self, $path) = @_;
|
|
Packit |
82cce8 |
if (ref $self)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
$path = $self->{path};
|
|
Packit |
82cce8 |
die "can't find DLL $self->{name}" unless defined $path;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
else
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
die __PACKAGE__."->depends argument PATH missing" unless defined $path;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
tie my %seen, "Tie::CPHash";
|
|
Packit |
82cce8 |
$seen{$self->name} = $self if ref $self;
|
|
Packit |
82cce8 |
_depends(\%seen, $self->needed($path));
|
|
Packit |
82cce8 |
return values %seen;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub _depends
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($seen, @needed) = @_;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
foreach (@needed)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
next if $seen->{$_->name};
|
|
Packit |
82cce8 |
if (defined $_->path)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
next if $_->is_system_lib && !$show_system_libs;
|
|
Packit |
82cce8 |
next if $_->is_perl_lib && !$show_perl_libs;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
$seen->{$_->name} = $_;
|
|
Packit |
82cce8 |
_depends($seen, $_->needed) if defined $_->path;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub canon_path
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($self) = @_;
|
|
Packit |
82cce8 |
return unless defined $_->path;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
return $_->{canon_path} ||= _canon_path($_->path);
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub _canon_path
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($path, $no_file) = @_;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
my ($vol, $dirs, $file) = File::Spec->splitpath($path, $no_file);
|
|
Packit |
82cce8 |
$dirs =~ s{[/\\]$}{};
|
|
Packit |
82cce8 |
my $foo = join("/", $vol, File::Spec->splitdir($dirs), $file);
|
|
Packit |
82cce8 |
return lc $foo;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
my $system_root = _canon_path($ENV{SystemRoot}, 1);
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub is_system_lib
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($self) = @_;
|
|
Packit |
82cce8 |
my $canon_path = $_->canon_path or return;
|
|
Packit |
82cce8 |
return length $canon_path > length $system_root
|
|
Packit |
82cce8 |
&& substr($canon_path, 0, length $system_root) eq $system_root;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
tie my %perl_libs, "Tie::CPHash";
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
local $show_system_libs = 0;
|
|
Packit |
82cce8 |
local $show_perl_libs = 1;
|
|
Packit |
82cce8 |
$perl_libs{$_->name} = $_ foreach __PACKAGE__->depends($^X);
|
|
Packit |
82cce8 |
};
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
sub is_perl_lib { $perl_libs{shift->name} ? 1 : 0 }
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
# return a list of installed (ie. found below some directory in @INC) glue DLLs
|
|
Packit |
82cce8 |
sub find_all_installed_glue_dlls
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my @dlls;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
find(sub { push @dlls, $File::Find::name if /\.dll/i; },
|
|
Packit |
82cce8 |
grep { my $auto;
|
|
Packit |
82cce8 |
!ref $_ && -d ($auto = File::Spec->catdir($_, "auto")) ?
|
|
Packit |
82cce8 |
$auto : ()
|
|
Packit |
82cce8 |
} @INC);
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
return @dlls;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
# guess the Perl module from the pathname of a glue DLL
|
|
Packit |
82cce8 |
sub guess_module_from_glue_dll
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($path) = @_;
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
# module Foo::Bar::Quux typically installs its glue DLL as
|
|
Packit |
82cce8 |
# .../auto/Foo/Bar/Quux/Quux.dll or
|
|
Packit |
82cce8 |
# .../auto/Foo/Bar/Quux/Quux.xs.dll
|
|
Packit |
82cce8 |
my ($vol, $dirs, $file) = File::Spec->splitpath($path);
|
|
Packit |
82cce8 |
$dirs =~ s{[/\\]$}{};
|
|
Packit |
82cce8 |
$dirs =~ s{^(?:.*?[/\\])?auto[/\\]}{}
|
|
Packit |
82cce8 |
or warn(qq[DLL "$path": path doesn't contain "auto"\n]), return;
|
|
Packit |
82cce8 |
return join("::", File::Spec->splitdir($dirs));
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
my $show_lib_path = 0;
|
|
Packit |
82cce8 |
sub show_lib
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my ($dll) = @_;
|
|
Packit |
82cce8 |
if ($show_lib_path)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
printf "\t%s => %s\n", $dll->name, $dll->path || "(not found)";
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
else
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
printf "\t%s\n", $dll->name;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
if (@ARGV)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
foreach (@ARGV)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
print $_, "\n";
|
|
Packit |
82cce8 |
show_lib($_) foreach DLL->depends($_);
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
else
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my %mod2dll;
|
|
Packit |
82cce8 |
my @non_mod_dlls;
|
|
Packit |
82cce8 |
foreach (find_all_installed_glue_dlls())
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my $mod = guess_module_from_glue_dll($_);
|
|
Packit |
82cce8 |
push(@non_mod_dlls, $_), next unless $mod;
|
|
Packit |
82cce8 |
$mod2dll{$mod} = $_;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
foreach my $mod (sort keys %mod2dll)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
my $dll = $mod2dll{$mod};
|
|
Packit |
82cce8 |
my @deps = DLL->depends($dll) or next; # suppress glue DLLs w/o dependencies
|
|
Packit |
82cce8 |
print "$mod ($dll)\n";
|
|
Packit |
82cce8 |
show_lib($_) foreach @deps;
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
|
|
Packit |
82cce8 |
print "\n";
|
|
Packit |
82cce8 |
foreach my $dll (sort @non_mod_dlls)
|
|
Packit |
82cce8 |
{
|
|
Packit |
82cce8 |
print "$dll\n";
|
|
Packit |
82cce8 |
show_lib($_) foreach DLL->depends($dll);
|
|
Packit |
82cce8 |
}
|
|
Packit |
82cce8 |
}
|