13

有没有人对找到定制开发项目中可能出现的所有 CPAN 依赖项的好方法有任何建议。通常情况下,您的本地开发环境很少与您的实际环境相匹配,并且随着您构建越来越多的项目,您倾向于构建本地已安装模块的库。然后,这些会导致您不一定注意到您的最新项目对非核心模块有要求。由于通常需要将整个项目打包以部署到另一个组(在我们的例子中是我们的运营团队),因此了解包中应包含哪些模块非常重要。

有没有人对这个问题有任何见解。

谢谢

彼得

4

8 回答 8

14

我自己也遇到过这个问题。 Devel::Modlist(如this answer建议)采用动态方法。它报告在脚本的特定运行期间实际加载的模块。这会捕获以任何方式加载的模块,但它可能无法捕获条件需求。也就是说,如果你有这样的代码:

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 看到它们。)

于 2008-10-27T20:57:38.290 回答
11

您可以使用deps.cpantesters.org上的在线网络服务,该服务将为您提供许多有用的依赖数据。CPAN 上的所有模块都已经有到依赖站点的链接(在模块页面的右侧)。

于 2008-10-27T17:39:43.977 回答
10

过去我使用过Devel::Modlist,它相当不错,可以让你去

perl -d:Modlist script.pl

获取所需模块的列表。

于 2008-10-27T17:25:59.183 回答
5

我的所有 C/C++ 应用程序(基于 PC 和各种嵌入式项目)都有一个基于 Make 的构建系统,虽然我喜欢能够在新机器上进行顶级构建并验证所有依赖项都在地方(我将我的工具链检查到版本控制中:D),我对没有为我的构建系统中当前没有生成文件的解释语言做同样的事情感到沮丧。

我很想写一个脚本:

  • 在我的修订控制存储库中搜索扩展名为 .pl 或 .pm 的文件
  • perl -d:Modlist在他们身上(感谢瓦格纳!)
  • 将其连接到所需模块的列表
  • 最后将其与已安装模块的列表进行比较。

然后我会将该脚本作为我的顶级构建的一部分来执行,这样任何构建任何东西的人都会知道他们是否拥有运行从修订控制获得的每个 perl 脚本所需的一切。如果有一些 perl 脚本他们从不运行并且不想 CPAN 安装运行它所需的内容,他们必须从硬盘驱动器中删除不需要的脚本,因此依赖项检查器无法找到它们。我知道如何修改 perforce 客户端以在您进行“同步”时省略某些子目录,我必须弄清楚这一点以进行颠覆......

我建议将依赖项检查器制作为搜索 pl 文件的单个脚本,而不是单独的 makefile 来检查每个脚本的依赖项,或者基于脚本名称的硬编码列表。如果您选择的方法需要用户操作以检查脚本的依赖关系,人们将忘记执行该操作,因为即使他们不进行依赖关系检查,他们也能够运行脚本。

就像我说的,我还没有实现上述内容,但是这个问题促使我尝试这样做。完成后我会回贴我的经验。

于 2008-10-27T18:19:17.203 回答
3

“显而易见”的方式——痛苦但适度有效——是在一些偏僻的位置安装一个全新的基础 Perl 构建(你不会在生产中使用它),然后尝试使用它来安装你的模块Perl 的“原始”版本。您将找到所有缺少的依赖项。第一次,这可能会很痛苦。在第一次之后,您将已经涵盖了大部分依赖项,并且痛苦会大大减少。

考虑运行您自己的 CPAN 模块本地存储库 - 这样您就不必总是下载代码。还要考虑如何清理过时的模块。

于 2008-10-27T17:40:01.510 回答
3
use Acme::Magic::Pony;

严重地。如果 Perl 模块丢失,它将自动安装它们。请参阅 CPAN 中的Acme::Magic::Pony页面。

于 2008-10-28T22:43:18.200 回答
2

这是一个“被拴住的马”的答案,但我已经养成了用我所有的依赖项创建一个 Bundle 文件的习惯。因此,当我进入新环境时,我只需将其复制并安装即可。

例如。我有一个 Baz.pm

package Bundle::Baz;
$VERSION = '0.1';
1;
__END__
=head1 NAME
Bundle::Baz
=head1 SYNOPSIS
perl -MCPAN -e 'install Bundle::Baz'
=head1 CONTENTS
# Baz's modules
XML::Twig
XML::Writer
Perl6::Say
Moose

把它放在 ~/.cpan/Bundle/ (或者你的 .cpan 所在的任何地方),然后像普通的 CPAN 模块一样安装 'Bundle::Baz'。然后安装“=head1 CONTENTS”下列出的所有模块。

于 2008-10-27T19:08:43.610 回答
1

这是一个快速的 bash 函数(使用出色的ack):

# find-perl-module-use <directory> (lib/ by default)
function find-perl-module-use() {
    dir=${1:-lib}
    ack '^\s*use\s+.*;\s*$' $dir | awk '{ print $2 }' | sed 's/();\?$\|;$//' | sort | uniq
    ack '^\s*use\s+base\s+.*;\s*$' $dir | awk '{ print $3 }' | sed 's/();\?$\|;$//' | sort | uniq
}
于 2008-10-28T22:48:53.073 回答