Skip to content

Instantly share code, notes, and snippets.

@gregorg
Created January 16, 2014 10:47
Show Gist options
  • Save gregorg/8452941 to your computer and use it in GitHub Desktop.
Save gregorg/8452941 to your computer and use it in GitHub Desktop.
Find Perl dependencies for Debian packages. find_perldeps.pl -d script.pl
#!/usr/bin/perl -w
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
if 0; # not running under some shell
$VERSION = '0.06';
use strict;
use Config;
use Getopt::Std;
use Module::ScanDeps;
use ExtUtils::MakeMaker;
use subs qw( _name _modtree );
my %opts;
getopts('dBVxce:', \%opts);
my (%map, %skip);
my $core = $opts{B};
my $verbose = $opts{V};
my $eval = $opts{e};
my $debian = $opts{d};
if ($eval) {
require File::Temp;
my ($fh, $filename) = File::Temp::tempfile( UNLINK => 1 );
print $fh $eval, "\n" or die $!;
close $fh;
push @ARGV, $filename;
}
die "Usage: $0 [ -B ] [ -V ] [ -x | -c ] [ -d ] [ -e STRING | FILE ... ]\n" unless @ARGV;
my @files = @ARGV;
while (<>) {
next unless /^package\s+([\w:]+)/;
$skip{$1}++;
}
my $map = scan_deps(
files => \@files,
recurse => 1,
$opts{x} ? ( execute => 1 ) :
$opts{c} ? ( compile => 1 ) : (),
);
my $len = 0;
my @todo;
my (%seen, %dist, %core, %bin);
foreach my $key (sort keys %$map) {
my $mod = $map->{$key};
my $name = $mod->{name} = _name($key);
print "# $key [$mod->{type}]\n" if $verbose;
if ($mod->{type} eq 'shared') {
$key =~ s!auto/!!;
$key =~ s!/[^/]+$!!;
$key =~ s!/!::!;
$bin{$key}++;
}
next unless $mod->{type} eq 'module';
next if $skip{$name};
if ($mod->{file} eq "$Config::Config{privlib}/$key"
or $mod->{file} eq "$Config::Config{archlib}/$key") {
next unless $core;
$core{$name}++;
}
elsif (my $dist = _modtree->{$name}) {
$seen{$name} = $dist{$dist->package}++;
}
$len = length($name) if $len < length($name);
$mod->{used_by} ||= [];
push @todo, $mod;
}
$len += 2;
warn "# Legend: [C]ore [X]ternal [S]ubmodule [?]NotOnCPAN\n" if $verbose;
sub in_array
{
my $val = shift;
my $ref = shift;
for ( @$ref ) { return 1 if $_ eq $val }
return 0;
}
my @packages;
my $deb_all_found = 1;
foreach my $mod (sort {
"@{$a->{used_by}}" cmp "@{$b->{used_by}}" or
$a->{key} cmp $b->{key}
} @todo) {
my $version = MM->parse_version($mod->{file});
if ($debian)
{
my $dpkg = `dpkg -S $mod->{file} &&>&1`;
chomp($dpkg);
if ($dpkg !~ /not found/ and $dpkg !~ /^dpkg:/)
{
$dpkg =~ /(.+?): .*/;
if ($1 and !in_array($1, \@packages))
{
push @packages, $1;
}
}
else
{
$deb_all_found = 0;
warn "Not found: $mod->{name}\n";
}
}
elsif (!$verbose) {
printf "%-${len}s => '$version',", "'$mod->{name}'" if $version;
print "\n";
} else {
printf "%-${len}s => '0', # ", "'$mod->{name}'";
my @base = map(_name($_), @{$mod->{used_by}});
print $seen{$mod->{name}} ? 'S' : ' ';
print $bin{$mod->{name}} ? 'X' : ' ';
print $core{$mod->{name}} ? 'C' : ' ';
print _modtree && !_modtree->{$mod->{name}} ? '?' : ' ';
print " # ";
print "@base" if @base;
print "\n";
}
}
if ($debian)
{
print "perllibs:Depends=";
print join(", ", @packages);
print "\n";
unless ($deb_all_found)
{
warn "WARNING: Not all packages were found.\n";
#warn "Not all packages were found, continue ? [yY] ";
#$_ = <>;
#chomp;
#exit 1 if (/n/i);
}
}
warn "No modules found!\n" unless @todo;
sub _name {
my $str = shift;
$str =~ s!/!::!g;
$str =~ s!.pm$!!i;
$str =~ s!^auto::(.+)::.*!$1!;
return $str;
}
my $modtree;
sub _modtree {
$modtree ||= eval {
require CPANPLUS::Backend;
CPANPLUS::Backend->new->module_tree;
} || {};
}
1;
__END__
=head1 NAME
find_perldeps.pl - Scan file prerequisites
=head1 SYNOPSIS
% find_perldeps.pl *.pm # Print PREREQ_PM section for *.pm
% find_perldeps.pl -e 'STRING' # Scan an one-liner
% find_perldeps.pl -B *.pm # Include core modules
% find_perldeps.pl -V *.pm # Show autoload/shared/data files
=head1 DESCRIPTION
F<find_perldeps.pl> is a simple-minded utility that prints out the
C<PREREQ_PM> section needed by modules.
If you have B<CPANPLUS> installed, modules that are part of an
earlier module's distribution with be denoted with C<S>; modules
without a distribution name on CPAN are marked with C<?>.
Also, if the C<-B> option is specified, module belongs to a perl
distribution on CPAN (and thus uninstallable by C<CPAN.pm> or
C<CPANPLUS.pm>) are marked with C<C>.
Finally, modules that has loadable shared object files (usually
needing a compiler to install) are marked with C<X>; with the
C<-V> flag, those files (and all other files found) will be listed
before the main output.
=head1 OPTIONS
=over 4
=item -e STRING
Scan I<STRING> as a string containing perl code.
=item -c
Compiles the code and inspects its C<%INC>, in addition to static scanning.
=item -x
Executes the code and inspects its C<%INC>, in addition to static scanning.
=item -B
Include core modules in the output and the recursive search list.
=item -V
Verbose mode: Output all files found during the process;
show dependencies between modules and availability.
=back
=head1 SEE ALSO
L<Module::ScanDeps>, L<CPANPLUS::Backend>, L<PAR>
=cut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment