Perl 打包

跳转至: 导航, 搜索

Perl 打包 是一个手把手的教你如何使用 openSUSE 构建服务 为 openSUSE 和其他发行版打包 Perl 软件包的教学。

编译工具

cpanspec

cpanspec 或许是最好的把 CPAN 上的软件包移植到 openSUSE 构建服务上的工具。下面是一个实例流程,在构建服务上打包 perl 模块 File::LibMagic :

# add devel:languages:perl
zypper ar http://download.opensuse.org/repositories/devel:/languages:/perl/openSUSE_11.2/devel:languages:perl.repo
zypper in cpanspec

cd your_checked_out_build_service_project
osc mkpac perl-File-LibMagic
cd perl-File-LibMagic

cpanspec -v File::LibMagic
osc build --local
# vi *.spec
...
 BuildRequires: perl(Other::Package)
 BuildRequires: libfile-devel
ZZ
osc ci

cpanspec 会从 CPAN 网络下载 tar 压缩包,分析它,然后创建 perl-File-LibMagic.spec 和一个初始的 *.changes 文件。cpanspec 在 devel:languages:perl 源中维护。

和原始的潘多拉版本 [1] 不同,这个版本被补缀以适应我们的构建服务的需要。

cpanspec_obs

一个额外的多了点东西的脚本,cpanspec_obs 可以用来全自动化一些简单的 perl 模块包。你只需要导航到 obs 项目的本地目录中,然后调用 cpanspec_obs 带上准确的 CPAN 包名,接着使用 osc 提交即可。

cpan2spec

常见错误叫法。人们说 'cpan2spec',实际上他们的意思是说 'cpanspec'。

cpan2dist

打包 perl 模块的 spec 范式文件书写这个过程可以被 cpan2dist 简化。它和例如 perl-5.10.0 捆绑分发,可以在 CPAN 获得升级。开发环境架设在 github

这是个为构建服务打包 perl 模块 File::LibMagic 的实例流程:

# add devel:languages:perl
zypper ar http://download.opensuse.org/repositories/devel:/languages:/perl/openSUSE_11.2/devel:languages:perl.repo
zypper in perl-CPANPLUS-Dist-RPM
zypper in perl-CPANPLUS-Dist-SUSE
cd your_checked_out_build_service_project
osc mkpac perl-File-LibMagic
cd perl-File-LibMagic
cpan2dist --format CPANPLUS::Dist::SUSE File::LibMagic
osc vc perl-File-LibMagic
osc add File-LibMagic*.tar.* perl-File-LibMagic.{changes,spec}
osc build --local-package
# vi *.spec
osc ci

CPANPLUS::Dist::SUSE 的 0.01 版本里,以下条目需要注意:

  • Group 应该是: Development/Libraries/Perl (还行,只丢了 "/Perl").
  • BuildRequires: 那些非 perl 的编译依赖软件包 (通常是 *-devel) 被省略掉了。
  • Requires: perl 软件包经常丢这些。检查 Makefile.PL 并补上。
  •  %install 章节不应该做 rm -rf %{buildroot}
  •  %install 章节触摸了一个空的 %{buildroot}/%{_mandir}/man3/*.3pm.gz
  •  %install 章节没有 %perl_process_packlist
  •  %install 章节没有 %perl_gen_filelist
  •  %files 章节应该使用 %files -f %{name}.files
  •  %bcond_with 检测
    %if %{with test}
    可以用来触发一个非常非常慢的编译检查,可选。(osc build --with test)
  •  %description: 没有描述
  • License: GPL-2.0 或 GPL-3.0

非官方版本 0.01.3 可从 devel:languages:perl 获取,修复了以上大多数。 它也添加了一些 BuildRequires: 和实验性的标签:

BuildRecommends: perl(Test::CheckManifest)

这不会造成错误,但我从来没见过它把 Test::CheckManifest 拉进编译过程。 如何写 Requires: 标签呢?如果它在编译时需要?


与添加一份 tar 压缩包的副本作为源代码不同,而是会使用一个链接到 CPAN 网络上的原始 tar 压缩包地址的 _service 链接。参考 devel:languages:perl:CPAN 获得更多信息。

Perl 模块使用什么授权协议

绝大部分 perl 模块都在 README 文件的底部带了关于授权协议的简短说明。有时候授权协议也写在源代码包里的 LICENSE 或 COPYING 文件中。如果 perl 模块的协议说明中说 与 Perl 自身的协议相同 (这种情况很常见),那么 spec 范式文件中的 License 标签应该写成:

License: Artistic-1.0 or GPL-1.0+

如果授权协议不是 “和 Perl 自身的协议相同” 那么应去 openSUSE:Accepted_licences#Good_Licenses 页面选择正确的 spec 文件 License 标签授权协议缩写 ( Short Name 分类提供了在 spec 文件中使用的协议缩写)。 更复杂的授权协议情况可以到 the Fedora Packager's Licensing Guidelines 页面找到更多的指导。

这里有 openSUSE 支持的所有授权协议: license.opensuse.org

用于 perl 模块的 RPM 宏

(来自 en.opensuse.org/Packaging/SUSE_Package_Conventions/RPM_Macros)

%if 0%{?suse_version} < 1120 
BuildRequires: perl-macros 
%endif

如果是针对 openSUSE 1120 以下的版本打包,请添加上述的 BuildRequires,不然你会得到如下错误提示

+ %perl_gen_filelist
/var/tmp/rpm-tmp.6091: line 40: fg: no job control

%perl_archlib

许多 perl 软件包都是纯 perl 的。因此可以用 noarch 编译。如果可能的话,添加下面这个标签和值到你的 spec 范式文件:

 BuildArch: noarch

如果不能用 noarch 编译的话, %perl_archlib 宏就对你很有用了。 该宏最终会被 perl 模块中和操作系统架构相关的部分的安装路径替代,例如, /usr/lib/perl5/5.8.5/i586-linux-thread-multi

它通常只用于 perl 软件包本身,和 %perl_process_packlist 宏。并不直接使用,也就是说它是一个供机器阅读的中间宏,详情见下。

%perl_make_install

该宏能在各种不同的 openSUSE 产品上正确的调用 make install 操作。在 SL 9.0 之前,通常调用它的方法是:

make PREFIX=$RPM_BUILD_ROOT/%_prefix \
INSTALLMAN1DIR=$RPM_BUILD_ROOT/%_mandir/man1 \
INSTALLMAN3DIR=$RPM_BUILD_ROOT/%_mandir/man3 \
install

针对 9.0 和之后版本:

make DESTDIR=$RPM_BUILD_ROOT install_vendor

使用 %perl_make_install 宏,会根据系统版本相应的使用上面做法中的一种。

下面实例出自 perl-URI:

%install
%perl_make_install

%perl_process_packlist

该宏为最终的软件包准备了与 perl 模块相关的一些文件。它做了如下操作:


  • %perl_archlib/perllocal.pod 中删除了 $RPM_BUILD_ROOT 并把该文件重命名为软件包相关的名字。详情见下。
  • 搜索安装的 .packlist 文件并从它们当中删除 $RPM_BUILD_ROOT 字段。


每个提供 perl 模块的软件包都应该在 %install 章节调用此宏。

%perl_archlib/perllocal.pod 文件必须被重命名因为它包含了额外安装的 perl 模块的相关信息,因此不能被多个软件包共用。所以,它被重命名了,并且一个特殊的 SuSEconfig 模块, /sbin/conf.d/SuSEconfig.perl, 会在软件包安装完成后链接重命名的文件里面的信息到系统的 %perl_archlib/perllocal.pod 文件。

实例出自 perl_URI:

%install
%perl_make_install
%perl_process_packlist

%files
[...]
/var/adm/perl-modules/%{name}

%perl_gen_filelist

自 11.2 起(或者使用 BuildRequires: perl-macros 标签),可以实现更多的自动化操作:

%install
%perl_make_install
%perl_process_packlist
%perl_gen_filelist

%files -f %{name}.files
%defattr(-,root,root,-)
%doc README CHANGES COPYING

注意该宏不能找到没安装的文件。因此我们依然使用 %doc 来直接从源代码树里搜集重要的文档文件。

%perl_sitearch

该宏最终被本地系统管理员把 perl 模块的操作系统架构相关的文件安装到的路径所替代 (/usr/lib/perl5/site_perl/5.8.5/i586-linux-thread-multi)。openSUSE 分发的软件包则使用了 %perl_vendorarch 宏定义的路径。见下。

%perl_sitelib

该宏指代了本地管理员将 Perl 模块中与操作系统架构无关的部分安装到的路径 (/usr/lib/perl5/site_perl/5.8.5)。 随 SuSE 分发的软件包应该使用 %perl_vendorlib 宏定义的路径。(见下)

%perl_vendorarch

该宏最终被 LINUX 发行版将 perl 模块的操作系统架构相关部分安装到的路径所替代 (/usr/lib/perl5/vendor_perl/5.8.5/i586-linux-thread-multi)。该宏的典型使用是在文件列表章节。实例来自 perl-URI:

%files
[...]
%{perl_vendorarch}/auto/URI

该路径自 SL 9.0 起使用。在那以前,perl 模块是被 %perl_sitearch 宏安装到 /usr/lib/perl5/site_perl 路径的。site_perl 目录目前被用于由本地系统管理员安装的模块 (参见上面的 %perl_sitearch)。

%perl_vendorlib

该宏最终被 Linux 发行版将 Perl 模块与操作系统架构无关的部分安装到的路径所替换(/usr/lib/perl5/vendor_perl/5.8.5)。该宏的典型使用在文件列表中。实例来自 perl-URI:

%files
[...]
%{perl_vendorlib}/URI.pm
%{perl_vendorlib}/URI

该路径自 SL 9.0 起使用。之前,Perl 模块被安装到 %perl_sitearch 宏安装到 /usr/lib/perl5/site_perl 目录。该目录 site_perl 目前用于那些被本地系统管理员安装的模块 (见上述的 %perl_sitelib)。

%perl_version

该宏会返回编译软件包使用的 Perl 的版本,例如 5.8.5。它被用于在那些提供 perl 模块的软件包中定义对 Perl 的依赖。

在老旧的软件源中,一般是 SLES,它通常这样使用:

Requires:     perl = %{perl_version}            # 现在已被 %perl_requires 宏替代

%perl_requires

自从 11.4 起,对目前版本的 perl 的运行依赖应该这样子写:

%if 0%{?suse_version} < 1140
Requires:     perl = %{perl_version}
%else
%{perl_requires}
%endif

在 11.4 以前,我们需要一个额外的使用了 %perl_version 宏的 'Requires' 标签。

请注意,使用该宏时,11.3 上的 quilt 就不好使了。

%__perl

返回系统上安装的 perl 的名称。

%perl_gen_filelist

生成一个名为 %[name}.files 的包含了所有被安装到 %{buildroot} 的文件的列表。这可以用来简化后面的 %files 文件列表章节。在 %install 章节的末尾调用该宏:

 %install
 [...]
 %perl_process_packlist
 %perl_gen_filelist

注意该宏并不能探测到没有被安装的文件。因此我们依然需要使用 %doc 标签来从源代码目录里搜集那些比较重要的文档文件。你的 %files 章节可能和下面的这个一样简单:

 %files -f %{name}.files
 %defattr(-, root, root, -)
 %doc Changes README COPYING

这是很有帮助的。如果你的 %{perl_vendorarch}/* 的写法实际上包含了 '*/auto' 目录,用 %exclude 又会使用大量的匹配,那么请看下面的例子。

rpmlint 的一些相关错误以及误报

auto directory is included

如果编译过程中止在:

... running 04-check-filelist
... checking filelist
perl-File-MMagic-XS: auto directory is included in the perl package

这个错误多见于你的 spec 范式文件中有这样一行:

%files
[...]
%{perl_vendorlib}/*

要是使用 perl-5.10.0 中的 cpan2dist 创建 spec 范式文件,默认的情况下一般都会遇到这个问题。 上述写法占有了 %{perl_vendorlib}/i586-linux-thread-multi/auto 文件夹,如果你的软件包内有一些 .so 的函数库,那么它自身就必须不能占有 '*/auto' 文件夹, 只能拥有里面的内容和子文件夹。

%files
[...]
%{perl_vendorlib}/*/auto/*
%{perl_vendorlib}/*/File
%{perl_vendorlib}/*/File/*

上述改动适用于 File::* 模块。也可以使用上述的 %perl_gen_filelist 宏来获得一个更好的可能的解法。

链接失败,错误: undefined reference to "function name"

如果您得到「未定义的指向(undefined reference)」系列错误,但实际上需要的函数库是定义过的,这就意味着链接顺序不对。openSUSE > 1110 版本中的所有函数库都是在需要时(--as-needed)通过 ld 标记来实时链接的。让我们看一个小例子:

假设你编译了一个静态函数库 libwurst,它的主函数中使用了 pow() 函数。

 $ gcc -Wl,--as-needed --static main.c -o wurstsalat -L. -lm -lwurst
 ./libwurst.a(wurst.o): In function `wurst':
 wurst.c:(.text+0x29): undefined reference to `pow'

这里的问题出在,链接器(ld)不能在 main.c 中找到任何 pow() 的指向。然后第一个函数是 libm,由于它没被用到,链接器忽略了它和它之后的所有 -l 链接。所以你需要在链接 libm 之前链接 libwurst.a。

 $ gcc -Wl,--as-needed --static main.c -o wurstsalat -L. -lwurst -lm

简单总结: 当使用 --as-needed 参数时,命令行中函数库的出现顺序是严格相关的:提供 X 使用的符号的所有函数库 X 必须在所有函数库 Y 之前。

如果你的软件包非常复杂,或者你缺乏制作这类补丁的经验,添加下面一行到你的 %build 章节:

 export SUSE_ASNEEDED=0

这会忽略 --as-needed 参数。

详情参考 http://www.gentoo.org/proj/en/qa/asneeded.xml#doc_chap2