У меня самого была эта проблема. Devel :: Modlist (согласно предложению этот ответ) использует динамический подход. Он сообщает о модулях, которые были фактически загружены во время определенного запуска вашего скрипта. Это улавливает модули, которые загружаются любым способом, но может не улавливать условные требования. То есть, если у вас есть такой код:
if ($some_condition) { require Some::Module }
и $some_condition
оказывается ложным, Devel::Modlist
не будет указывать Some::Module
как требование.
Вместо этого я решил использовать Module :: ExtractUse. Он выполняет статический анализ, что означает, что он всегда будет ловить Some::Module
в приведенном выше примере. С другой стороны, он ничего не может сделать с таким кодом, как:
my $module = "Other::Module";
eval "use $module;";
Конечно, вы можете использовать оба подхода, а затем объединить два списка.
В любом случае, вот решение, которое я придумал:
#! /usr/bin/perl
#---------------------------------------------------------------------
# Copyright 2008 Christopher J. Madsen <perl at cjmweb.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
# This program 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 either the
# GNU General Public License or the Artistic License for more details.
#
# Recursively collect dependencies of Perl scripts
#---------------------------------------------------------------------
use strict;
use warnings;
use File::Spec ();
use Module::CoreList ();
use Module::ExtractUse ();
my %need;
my $core = $Module::CoreList::version{'5.008'};
# These modules have lots of dependencies. I don't need to see them now.
my %noRecurse = map { $_ => 1 } qw(
Log::Log4perl
XML::Twig
);
foreach my $file (@ARGV) {
findDeps($file);
}
foreach my $module (sort keys %need) {
print " $module\n";
}
#---------------------------------------------------------------------
sub findDeps
{
my ($file) = @_;
my $p = Module::ExtractUse->new;
$p->extract_use($file);
foreach my $module ($p->array) {
next if exists $core->{$module};
next if $module =~ /^5[._\d]+/; # Ignore "use MIN-PERL-VERSION"
next if $module =~ /\$/; # Run-time specified module
if (++$need{$module} == 1 and not $noRecurse{$module}) {
my $path = findModule($module);
if ($path) { findDeps($path) }
else { warn "WARNING: Can't find $module\n" }
} # end if first use of $module
} # end foreach $module used
} # end findDeps
#---------------------------------------------------------------------
sub findModule
{
my ($module) = @_;
$module =~ s!::|\'!/!g;
$module .= '.pm';
foreach my $dir (@INC) {
my $path = File::Spec->catfile($dir, $module);
return $path if -f $path;
}
return;
} # end findModule
Вы бы запустили это так:
perl finddeps.pl scriptToCheck.pl otherScriptToCheck.pl
Он печатает список всех дополнительных модулей, необходимых для запуска перечисленных сценариев. (Если только они не проделывают хитроумные трюки с загрузкой модуля, которые не позволяют Module :: ExtractUse их видеть.)
person
cjm
schedule
27.10.2008