Perl模块发布与测试全流程指南
1. 构建与测试发行版
在发布Perl模块之前,需要确保其能通过一系列测试。使用命令
% ./Build disttest
可以构建一个包含
MANIFEST
中所有内容的发行版存档,将该存档解压到一个单独的目录,然后对发行版进行测试。如果这个测试都无法通过,那么就不能期望从CPAN下载该发行版的其他用户能够正常使用。
2. 上传发行版
当发行版准备好分享时(甚至在准备过程中),可以通过PAUSE页面(https://pause.perl.org/pause/authenquery?ACTION=add_uri )进行上传。具体步骤如下:
1. 使用PAUSE用户名和密码登录。
2. 选择上传选项:可以上传文件、指定获取发行版的URL,或者认领通过匿名FTP上传的文件。
3. 无论使用哪种方式上传文件,它都应该出现在页面底部的上传文件列表中。对于远程文件,可能需要等待一段时间,不过通常会在一小时内显示出来。如果在发行版名称旁边看不到自己的PAUSE名称,就无法认领该文件。
4. 由于是上传到一个匿名但公开的FTP站点,其他Perl程序员可能会直接从传入目录下载代码,甚至在PAUSE处理之前就开始使用。
5. 一旦PAUSE获取到文件并知道其归属,就会对其进行索引。此时会收到PAUSE索引器发送的电子邮件,告知处理结果。之后,发行版就会进入CPAN。需要注意的是,CPAN中的“N”代表“Network”,发行版可能需要数小时甚至数天才能到达所有的CPAN镜像站点,但通常不会超过几天,使用PAUSE工作人员开发的快速rsync工具,有时几分钟就能完成。
6. 如果遇到问题或认为某些操作没有按预期进行,可以通过发送电子邮件至
modules@perl.org
向PAUSE管理员咨询。
上传发行版的流程可以用以下mermaid流程图表示:
graph LR
A[登录PAUSE页面] --> B[选择上传方式]
B --> C{上传文件类型}
C -->|文件| D[上传本地文件]
C -->|URL| E[指定URL获取文件]
C -->|匿名FTP| F[认领已上传文件]
D --> G[等待文件显示在列表]
E --> G
F --> G
G --> H{是否看到PAUSE名称}
H -->|是| I[PAUSE索引文件]
H -->|否| J[无法认领文件]
I --> K[收到索引结果邮件]
K --> L[发行版进入CPAN]
3. 多平台测试
CPAN Testers(http://cpantesters.org/ )会自动对几乎所有上传到CPAN的发行版进行测试。世界各地的志愿者会自动下载发行版,并在他们各自的环境中进行测试。他们会在各种重要的平台、操作系统和Perl版本(以及许多我们可能不太关注的版本)上测试模块。测试完成后,会向模块作者发送电子邮件告知结果,并自动更新测试数据库。可以通过Testers网站(http://www.cpantesters.org/ )或MetaCPAN(https://www.metacpan.org/ )查看任何模块的测试结果。
虽然测试失败的报告可能会令人沮丧,但要记住,通常这不是测试人员的错。他们可以帮助我们找出在无法访问的平台上出现的问题。
4. 宣布模块发布
发布模块的目的之一是让其他人能够使用它,另一个目的是让其他人对其进行改进。为了实现这两个目标,人们需要了解这个模块。模块会在很多地方自动被关注到,包括:
- CPAN Search的“Recent modules”页面(http://search.cpan.org/recent )
- MetaCPAN的“new modules”部分(https://www.metacpan.org/recent )
- “Perl news”邮件列表的每日公告
在Perl会议上,很多简短的演讲都是由发行版的作者介绍他们的工作。毕竟,没有人比我们自己更适合帮助他人使用我们的模块。其他人对我们的模块越感兴趣,他们就会发送更多的错误报告、功能请求和补丁,模块也会变得更好。
如果想成为演讲者,可以参考 “Nobody is a good speaker when they start”(http://blog.yapcna.org/post/17253936133/nobody-is-a-good-speaker-when-they-start )。如果提出会议演讲的想法让你有些胆怯,或者不想等那么久,可以考虑加入当地的Perl用户组。他们通常会寻找演讲者(通常是为接下来一两周的会议),而且小组规模通常较小,氛围轻松。可以通过Perl Mongers网站(http://www.pm.org/ )找到附近的Perl用户组。如果找不到当地的小组,也可以自己创建一个。
5. 相关练习
以下是一些相关的练习,可以帮助你更好地掌握上述流程:
1. [5分钟] 如果还没有PAUSE账户,进行注册。虽然不会立即获得账户,但不影响开始工作。
2. [10分钟] 确保你的
Animal
发行版能够通过
disttest
检查。首先检查它是否能通过测试检查,必要时进行调整以通过
disttest
。
3. [45分钟] 使用
Acme::*
命名空间创建一个以你的PAUSE名称命名的新发行版。例如,如果你的PAUSE名称是 “GILLIGAN”,则创建
Acme::GILLIGAN::Utils
模块。创建一个
sum
函数用于相加数字,并为该函数创建测试。准备好发行版后上传到PAUSE。
4. [10分钟] 将
lib/Tie/Cycle.pm
添加到练习3中创建的发行版中,可能需要从
Tie::Cycle
发行版中复制该文件。准备好发行版并上传到PAUSE,确保
lib/Tie/Cycle.pm
出现在
MANIFEST
中。等待PAUSE对其进行索引,查看因未授权命名空间而导致的失败结果。
5. [20分钟] 修改练习3中创建的
Acme::*
模块的
sum
函数,使其将数字相乘而不是相加。此时测试应该失败,让测试失败以查看上传有问题的发行版时会发生什么。
6. [5分钟] 在MetaCPAN(https://www.metacpan.org/ )上查看你的发行版页面。该网站会快速更新PAUSE的信息,通常一小时内就能看到你的模块。
7. [10分钟] 使用CPAN客户端从CPAN安装你的
Acme::*
发行版。可能需要等待发行版到达你配置的CPAN镜像站点。
6. 练习答案
以下是部分练习的答案:
6.1 第1章练习答案
-
练习1
:要获得PAUSE账户,访问 http://pause.perl.org/ 并点击 “Request PAUSE account” 链接。填写信息后,申请会由PAUSE管理员进行人工审核。不需要证明自己的价值,只需看起来像人类(而不是公司或角色)且没有已有账户。申请通常会在一两天内得到批准,以便后续使用。账户批准后,会获得一个以
@cpan.org结尾的邮箱地址。可以使用该地址创建一个gravatar头像,CPAN搜索界面在显示作者图片时会查找与该邮箱地址关联的gravatar头像。 - 练习2 :http://www.intermediateperl.com/ 网站提供了相关额外材料和羊驼图片。
6.2 第2章练习答案
-
练习1
:可以使用
Cwd模块获取当前工作目录,使用File::Spec模块构建文件路径。示例代码如下:
use Cwd;
use File::Spec;
my $cwd = getcwd;
foreach my $file ( glob( ".* *" ) ) {
print " ", File::Spec->catfile( $cwd, $file ), "\n";
}
如果使用
File::Spec::Functions
,代码会更简洁:
use Cwd;
use File::Spec::Functions;
my $cwd = getcwd;
foreach my $file ( glob( ".* *" ) ) {
print " ", catfile( $cwd, $file ), "\n";
}
-
练习2
:安装
local::lib可以使用以下步骤:
1. 下载local::lib发行版(https://www.metacpan.org/module/local::lib )。
2. 解压发行版,使用--bootstrap选项运行Makefile.PL:
% perl Makefile.PL --bootstrap
% make install
- 安装完成后,可以使用CPAN工具安装其他模块:
% cpan -I Module::CoreList
% cpanm Module::CoreList
-
在程序中加载
local::lib以使用相同的设置:
use local::lib;
use Module::CoreList;
my @modules = sort keys $Module::CoreList::version{5.014002};
my $max_length = 0;
foreach my $module ( @modules ) {
$max_length = length $module if
length $module > $max_length;
}
foreach my $module ( @modules ) {
printf "%*s %s\n",
- $max_length,
$module,
Module::CoreList->first_release( $module );
}
如果使用
map
和
List::Util
的
max
函数,可以简化代码:
use List::Util qw(max);
my $max_length = max map { length } @modules;
-
练习3
:安装
Business::ISBN模块:
% cpan -I Business::ISBN
% cpanm Business::ISBN
使用该模块处理ISBN的示例代码如下:
use Business::ISBN;
my $isbn = Business::ISBN->new( $ARGV[0] );
print "ISBN is " . $isbn->as_string . "\n";
print "Country code: " . $isbn->country_code . "\n";
print "Publisher code: " . $isbn->publisher_code . "\n";
6.3 第3章练习答案
- 练习1 :筛选出命令行参数中大小小于1000字节的文件并输出:
my @smaller_than_1000 = grep { -s < 1000 } @ARGV;
print map { " $_\n" } @smaller_than_1000;
通常可以不使用中间数组:
print map { " $_\n" } grep { -s < 1000 } @ARGV;
- 练习2 :在当前用户主目录中根据用户输入的正则表达式查找文件:
chdir; # go to our home directory
while( 1 ) {
print "Please enter a regular expression> ";
chomp( my $regex = <STDIN> );
last unless( defined $regex && length $regex );
print
map { " $_\n" }
grep { eval{ /$regex/ } }
glob( ".* *" );
}
6.4 第4章练习答案
-
练习1
:分析四个表达式
$ginger->[2][1]、${$ginger[2]}[1]、$ginger->[2]->[1]和${$ginger->[2]}[1],除了${$ginger[2]}[1]外,其他三个表达式引用的是同一个内容。${$ginger[2]}[1]等同于$ginger[2][1],其基础是数组@ginger,而不是标量$ginger。 - 练习2 :检查人员物品是否齐全的示例代码:
my @gilligan = qw(red_shirt hat lucky_socks water_bottle);
my @professor = qw(sunscreen water_bottle slide_rule batteries radio);
my @skipper = qw(blue_shirt hat jacket preserver sunscreen);
my %all = (
"Gilligan" => \@gilligan,
"Skipper" => \@skipper,
"Professor" => \@professor,
);
check_items_for_all(\%all);
sub check_items_for_all {
my $all = shift;
for my $person (sort keys %$all) {
check_required_items($person, $all->{$person});
}
}
sub check_required_items {
my $who = shift;
my $items = shift;
my @required = qw(preserver sunscreen water_bottle jacket);
my @missing = ( );
for my $item (@required) {
unless (grep $item eq $_, @$items) { # not found in list?
print "$who is missing $item.\n";
push @missing, $item;
}
}
if (@missing) {
print "Adding @missing to @$items for $who.\n";
push @$items, @missing;
}
}
- 练习3 :更新船员位置信息并输出报告的示例代码:
my %gilligan_info = (
name => 'Gilligan',
hat => 'White',
shirt => 'Red',
position => 'First Mate',
);
my %skipper_info = (
name => 'Skipper',
hat => 'Black',
shirt => 'Blue',
position => 'Captain',
);
my %mr_howell = (
name => 'Mr. Howell',
hat => undef,
shirt => 'White',
position => 'Passenger',
);
my @castaways = (\%gilligan_info, \%skipper_info, \%mr_howell);
foreach my $person (@castaways) {
$person->{location} = 'The Island';
}
foreach my $person (@castaways) {
next unless $person->{name} =~ /Howell/;
$person->{location} = 'The Island Country Club';
}
foreach my $person (@castaways) {
print "$person->{name} at $person->{location}\n";
}
也可以简化为:
foreach my $person (@castaways) {
$person->{location} = ( $person->{name} =~ /Howell/ ) ?
'The Island Country Club' : 'The Island';
print "$person->{name} at $person->{location}\n";
}
6.5 第5章练习答案
- 练习1 :对于匿名哈希构造函数的问题,其花括号创建的是哈希引用,是一个标量,不适合单独作为哈希的值,因为哈希期望的是元素对。可以将两对花括号改为圆括号来解决问题。
如果运行代码时没有收到警告信息,可能是没有开启警告,可以使用
-w
开关或
warnings
编译指令。如果收到警告信息但不理解其含义,可以使用
perldiag
查看详细解释。如果想偷懒,可以在程序开头添加
use diagnostics;
,但不建议在生产代码中使用,因为会消耗大量CPU资源。
Perl模块发布与测试全流程指南(续)
7. 练习答案总结
为了更清晰地展示各练习答案的要点,我们将其整理成表格形式:
| 章节 | 练习 | 答案要点 | 代码示例 |
| ---- | ---- | ---- | ---- |
| 第1章 | 练习1 | 访问 http://pause.perl.org/ 注册PAUSE账户,批准后获
@cpan.org
邮箱,可用于创建gravatar头像 | 无 |
| 第1章 | 练习2 | http://www.intermediateperl.com/ 提供额外材料和羊驼图片 | 无 |
| 第2章 | 练习1 | 使用
Cwd
和
File::Spec
模块获取当前目录并构建文件路径 |
perl<br>use Cwd;<br>use File::Spec;<br>my $cwd = getcwd;<br>foreach my $file ( glob( ".* *" ) ) {<br> print " ", File::Spec->catfile( $cwd, $file ), "\n";<br>}<br>
|
| 第2章 | 练习2 | 下载
local::lib
发行版,用
--bootstrap
选项安装,使用CPAN工具安装其他模块,程序中加载
local::lib
|
perl<br>use local::lib;<br>use Module::CoreList;<br>my @modules = sort keys $Module::CoreList::version{5.014002};<br>my $max_length = 0;<br>foreach my $module ( @modules ) {<br> $max_length = length $module if<br> length $module > $max_length;<br>}<br>foreach my $module ( @modules ) {<br> printf "%*s %s\n",<br> - $max_length,<br> $module,<br> Module::CoreList->first_release( $module );<br>}<br>
|
| 第2章 | 练习3 | 安装
Business::ISBN
模块,使用该模块处理ISBN |
perl<br>use Business::ISBN;<br>my $isbn = Business::ISBN->new( $ARGV[0] );<br>print "ISBN is " . $isbn->as_string . "\n";<br>print "Country code: " . $isbn->country_code . "\n";<br>print "Publisher code: " . $isbn->publisher_code . "\n";<br>
|
| 第3章 | 练习1 | 筛选命令行参数中小于1000字节的文件并输出 |
perl<br>print map { " $_\n" } grep { -s < 1000 } @ARGV;<br>
|
| 第3章 | 练习2 | 在主目录根据用户输入正则表达式查找文件 |
perl<br>chdir; # go to our home directory<br>while( 1 ) {<br> print "Please enter a regular expression> ";<br> chomp( my $regex = <STDIN> );<br> last unless( defined $regex && length $regex );<br> print<br> map { " $_\n" }<br> grep { eval{ /$regex/ } }<br> glob( ".* *" );<br>}<br>
|
| 第4章 | 练习1 | 分析四个表达式,除
${$ginger[2]}[1]
外,其他三个引用同一内容 | 无 |
| 第4章 | 练习2 | 检查人员物品是否齐全,缺失则添加 |
perl<br>my @gilligan = qw(red_shirt hat lucky_socks water_bottle);<br>my @professor = qw(sunscreen water_bottle slide_rule batteries radio);<br>my @skipper = qw(blue_shirt hat jacket preserver sunscreen);<br>my %all = (<br> "Gilligan" => \@gilligan,<br> "Skipper" => \@skipper,<br> "Professor" => \@professor,<br>);<br>check_items_for_all(\%all);<br><br>sub check_items_for_all {<br> my $all = shift;<br> for my $person (sort keys %$all) {<br> check_required_items($person, $all->{$person});<br> }<br>}<br><br>sub check_required_items {<br> my $who = shift;<br> my $items = shift;<br> my @required = qw(preserver sunscreen water_bottle jacket);<br> my @missing = ( );<br> for my $item (@required) {<br> unless (grep $item eq $_, @$items) { # not found in list?<br> print "$who is missing $item.\n";<br> push @missing, $item;<br> }<br> }<br> if (@missing) {<br> print "Adding @missing to @$items for $who.\n";<br> push @$items, @missing;<br> }<br>}<br>
|
| 第4章 | 练习3 | 更新船员位置信息并输出报告 |
perl<br>foreach my $person (@castaways) {<br> $person->{location} = ( $person->{name} =~ /Howell/ ) ?<br> 'The Island Country Club' : 'The Island';<br> print "$person->{name} at $person->{location}\n";<br>}<br>
|
| 第5章 | 练习1 | 将匿名哈希构造函数的花括号改为圆括号,开启警告,使用
perldiag
解释警告信息 | 无 |
8. 总结与注意事项
通过上述内容,我们详细了解了Perl模块从构建、测试、上传到宣布发布的全流程,以及相关的练习和答案。在实际操作过程中,还需要注意以下几点:
-
测试的重要性
:多平台测试能够帮助我们发现不同环境下的问题,确保模块的稳定性和兼容性。即使测试失败,也不要责怪测试人员,应积极分析问题并解决。
-
命名空间问题
:在创建发行版时,要注意命名空间的使用,避免使用未授权的命名空间,否则可能导致上传失败。
-
警告信息的利用
:在编写代码时,开启Perl的警告功能,利用
perldiag
工具解释警告信息,有助于快速定位和解决问题。
9. 未来展望
随着Perl社区的不断发展,模块的发布和测试流程可能会更加自动化和高效。可以预见,未来可能会有更多的工具和服务来帮助开发者更轻松地完成模块的开发和发布工作。例如,可能会出现更智能的测试框架,能够自动检测并修复一些常见的问题;或者有更便捷的发布平台,简化上传和索引的过程。
同时,作为开发者,我们也应该积极参与到社区中,分享自己的经验和成果,为Perl社区的发展贡献力量。通过参加Perl会议、加入用户组等方式,与其他开发者交流合作,共同推动Perl技术的进步。
10. 流程图回顾
为了再次强调模块上传的关键流程,我们重新展示上传发行版的mermaid流程图:
graph LR
A[登录PAUSE页面] --> B[选择上传方式]
B --> C{上传文件类型}
C -->|文件| D[上传本地文件]
C -->|URL| E[指定URL获取文件]
C -->|匿名FTP| F[认领已上传文件]
D --> G[等待文件显示在列表]
E --> G
F --> G
G --> H{是否看到PAUSE名称}
H -->|是| I[PAUSE索引文件]
H -->|否| J[无法认领文件]
I --> K[收到索引结果邮件]
K --> L[发行版进入CPAN]
这个流程图清晰地展示了从登录PAUSE页面到发行版进入CPAN的整个过程,希望能帮助你更好地理解和掌握模块上传的流程。
通过本文的学习,相信你已经对Perl模块的发布和测试有了全面的了解。按照上述流程和注意事项进行操作,你可以更顺利地将自己的模块分享给其他开发者,为Perl社区的发展做出贡献。
超级会员免费看
1

被折叠的 条评论
为什么被折叠?



