Perl 包指南
本文档涵盖了通过 CPAN (Comprehensive Perl Authors Network) 分发的 Perl 模块的 PKGBUILD 创建。本文档的目标读者是 Perl 模块的打包者。关于 Perl 策略,请参阅 Perl Policy。
Arch Linux 打包约定
应遵循以下约定,以保持 Perl 模块包的一致性。本节作为 Arch Linux 视角下 Perl 打包概念的介绍;即,包管理和系统管理。为了满足快速阅读的读者,最简单/最流行的内容会放在前面。
包名
对于模块,包名应以 perl- 开头,其余部分应通过将模块名转换为小写,然后用连字符替换冒号来构成。例如,与 HTML::Parser 对应的包名将是 perl-html-parser。Perl 应用程序应与其应用程序名称相同,但全部小写。
包文件存放位置
Perl 包应将模块文件安装到 /usr/lib/perl5/$version/vendor_perl/ (在脚本中使用 perl -V:vendorarch),或 /usr/share/perl5/vendor_perl/。这可以通过将 INSTALLDIRS 命令行参数设置为 vendor 来实现,如下所示。不应将任何文件存储在 /usr/lib/perl5/$version/site_perl/ 中,因为该目录保留给系统管理员用于安装包管理系统之外的 Perl 包。当用户通过 cpan shell 系统范围地安装模块时,模块会进入 site-perl 子目录。
文件 perllocal.pod 和 .packlist 也不应存在;下面的示例 PKGBUILD 已处理了这个问题。
Architecture
在大多数情况下,arch 数组应包含 'any',因为大多数 Perl 包与架构无关。XS 模块被编译成动态加载库(.so 文件),并且在构建时应将其架构显式设置为 ('x86_64') 以指示它们是与架构相关的。XS 模块通常包含一个或多个 .xs 文件,这些文件会动态生成 .c 文件。
自动化
CPANPLUS(第二代 CPAN shell)的一个插件可在 perl-cpanplus-dist-archAUR 包中找到。此插件会在 CPANPLUS 安装发行版时即时打包它们。在线文档可在 https://metacpan.org/release/CPANPLUS-Dist-Arch 找到。
PKGBUILD 示例
示例 PKGBUILD 可在 [1] 找到。
以下两个 PKGBUILD 示例使用了本页介绍的技术,旨在使 PKGBUILD 更能应对更复杂的问题。由于存在两种构建脚本样式,因此有两个示例 PKGBUILD。第一个 PKGBUILD 是一个打包使用 Makefile.PL 的发行版的示例。第二个 PKGBUILD 可用作使用 Build.PL 的发行版的起点。
PKGBUILD
# Contributor: Your Name <youremail@domain.example>
pkgname=perl-foo-bar
pkgver=1.0
pkgrel=1
pkgdesc='This packages the Foo-Bar distribution, containing the Foo::Bar module!'
_dist=Foo-Bar
arch=('any')
url="https://metacpan.org/release/$_dist"
license=('Artistic-1.0-Perl OR GPL-1.0-or-later')
depends=('perl')
options=('!emptydirs' 'purge')
source=("https://cpan.metacpan.org/authors/id/BAZ/$_dist-$pkgver.tar.gz")
b2sums=(...)
build() {
cd $_dist-$pkgver
unset PERL5LIB PERL_MM_OPT PERL_LOCAL_LIB_ROOT
export PERL_MM_USE_DEFAULT=1 PERL_AUTOINSTALL=--skipdeps
/usr/bin/perl Makefile.PL
make
}
check() {
cd $_dist-$pkgver
unset PERL5LIB PERL_MM_OPT PERL_LOCAL_LIB_ROOT
export PERL_MM_USE_DEFAULT=1
make test
}
package() {
cd $_dist-$pkgver
unset PERL5LIB PERL_MM_OPT PERL_LOCAL_LIB_ROOT
make install INSTALLDIRS=vendor DESTDIR="$pkgdir"
}
PKGBUILD
# Contributor: Your Name <youremail@domain.example>
pkgname=perl-foo-bar
pkgver=1.0
pkgrel=1
pkgdesc='This packages the Foo-Bar distribution, containing the Foo::Bar module!'
_dist=Foo-Bar
arch=('any')
url="https://metacpan.org/release/$_dist"
license=('Artistic-1.0-Perl OR GPL-1.0-or-later')
depends=('perl')
makedepends=('perl-module-build')
options=('!emptydirs' 'purge')
source=("https://cpan.metacpan.org/authors/id/BAZ/$_dist-$pkgver.tar.gz")
b2sums=(...)
build() {
cd $_dist-$pkgver
unset PERL5LIB PERL_MM_OPT PERL_MB_OPT PERL_LOCAL_LIB_ROOT
export PERL_MM_USE_DEFAULT=1 MODULEBUILDRC=/dev/null
/usr/bin/perl Build.PL
./Build
}
check() {
cd $_dist-$pkgver
unset PERL5LIB PERL_MM_OPT PERL_MB_OPT PERL_LOCAL_LIB_ROOT
export PERL_MM_USE_DEFAULT=1
./Build test
}
package() {
cd $_dist-$pkgver
unset PERL5LIB PERL_MM_OPT PERL_MB_OPT PERL_LOCAL_LIB_ROOT
./Build install --installdirs=vendor --destdir="$pkgdir"
}
这些 PKGBUILD 复杂性增加的理由将在后面的章节中尝试解释。
CPAN 模块机制
有许多精心设计(或未精心设计)的机制协同工作来创建模块系统。在使用 CPAN 时,必须遵循相应程序来获取模块的源代码,构建获取到的模块,并将其插入系统软件以便稍后执行。为了理解模块应如何打包,了解模块在没有任何 pacman 和 Arch Linux 包干预的情况下如何工作会非常有帮助。我们的最终目标是尽可能地保持不打扰,同时改善最终产品的组织和一致性。
模块
模块使用 package 关键字在 perl 中声明。模块包含在 .pm(“点-pee-em”)文件中。虽然一个文件中可能包含不止一个模块(package)。模块具有由 ::(双冒号)分隔的命名空间,例如:Archlinux::Module。加载模块时,:: 会被替换为目录分隔符。例如:对于模块 Archlinux::Module,将加载 Archlinux/Module.pm。
核心模块随 Perl 安装一起提供。有些核心模块*仅*随 Perl 一起提供。其他模块仍可从 CPAN 单独下载和安装。
发行版
(又称 dist, package) 在 CPAN 术语中,这相当于一个 Arch Linux 包。发行版是包含文件的 .tar.gz 压缩包。这些压缩包主要包含 .pm 模块文件、包含模块的测试、模块的文档以及任何其他认为必要的文件。
通常,一个发行版包含一个同名的主要模块。有时情况并非如此,例如 Template-Toolkit 发行版。Template-Toolkit 发行版的最新包 Template-Toolkit-2.22.tar.gz 不包含 Template::Toolkit 模块!
有时,因为发行版以一个主模块命名,它们的名称被互换使用并且混淆在一起。然而,有时考虑它们是独立实体(如 Template-Toolkit 的情况)是有用的。
CPAN
每个 CPAN 镜像都包含索引,列出了 CPAN 上的发行版、发行版中的模块以及上传发行版的作者姓名。这些只是文本文件。最有用的索引在每个 CPAN 镜像提供的 /modules/02packages.details.txt.gz 文件中。这里的“包”一词指的是 Perl 语言本身的 package 关键字,而不是类似 pacman 包的东西。CPAN shell,通常称为小写、斜体的 _cpan_,只是用于导航索引以查找您要安装的模块的经典 Perl 脚本。
模块可以在 02packages.details.txt.gz 列表中找到。与模块/包名称在同一行的是包含该模块的发行版 tarball 的路径。当您要求 _cpan_ 安装一个模块时,它会查找该模块并安装相关的发行版。在安装发行版时,它会生成一个模块依赖列表。_Cpan_ 将尝试将每个模块依赖加载到 Perl 解释器中。如果一个给定版本的模块无法加载,则会重复此过程。
_cpan_ shell 不必担心安装所需模块的版本。_Cpan_ 可以依赖于最新版本的模块必须满足它最初安装的模块的要求。包详细信息文件中仅列出了模块的最新版本。不幸的是,对于 Perl 包作者来说,我们不能总是依赖我们的包提供最新版本的 Perl 发行版及其包含的模块。Pacman 的依赖检查更具静态性和强制性。
模块依赖
与 Python eggs 和 Ruby gems 等类似系统相比,Perl 有一种独特的定义依赖的方式。Eggs 定义对其他 eggs 的依赖。Gems 依赖于 gems。Perl 发行版依赖于模块。模块仅可从 CPAN 发行版获得,因此在某种程度上,Perl 发行版仅间接依赖于发行版。模块可以独立于发行版定义自己的版本,这在模块源代码中完成。这是通过定义一个名为 $VERSION 的*包*变量来完成的。在使用 strict 和 warnings 时,它使用 our 关键字定义。例如:
package Foo::Module; use warnings; use strict; our $VERSION = '1.00';
模块可以随意更改其版本,甚至可以拥有一个与发行版版本不同的版本。这一点的重要性值得商榷,但要记住这一点很重要。模块版本从 Perl 解释器外部确定更困难,需要解析 Perl 代码本身,甚至可能需要将模块加载到 Perl 中。其优势在于,从 Perl 解释器内部,模块版本很容易确定。例如:
use Foo::Module; print $Foo::Module::VERSION, "\n";
依赖定义
Perl 发行版中的依赖在哪里定义?它们在 Makefile.PL 或 Build.PL 脚本中“定义”。例如,在 Makefile.PL 脚本中,调用 WriteMakeFile 函数来生成 Makefile,如下所示:
use ExtUtils::MakeMaker;
WriteMakeFile(
'NAME' => 'ArchLinux::Module',
'VERSION' => '0.01',
'PREREQ_PM' => { 'POSIX' => '0.01' },
);
这是一个人为的例子,但重要的是要理解依赖关系直到 Makefile.PL 或 Build.PL 脚本运行后才最终确定。依赖关系是在运行时指定的,这意味着可以使用 Perl 的全部功能来更改或修改它们。这意味着模块作者可以在发行版安装前立即添加、删除或更改依赖项的版本。有些模块作者会利用这一点做一些非常聪明的事情,比如仅在安装时依赖模块。一些多平台发行版在不同操作系统上安装时也依赖于特定于系统的模块。
例如,CPANPLUS 发行版会查找当前已安装的 CPANPLUS::Dist 插件。如果当前安装的 CPANPLUS 版本有任何插件已安装,它会将它们添加到新 CPANPLUS 的先决条件中。我不确定为什么。幸运的是,对于 Perl 打包者来说,大多数依赖项都是静态的,就像上面要求 POSIX 模块且最低版本为 0.01 的示例一样。
元信息
最近的发行版中包含元文件,其中包含发行版的元信息,如名称、作者、摘要描述和模块要求。以前有 YAML 格式的 META.yml 文件,但最近已切换为 JSON 格式的 META.json 文件。这些文件可以手动编辑,但更常见的是由 Makefile.PL 或 Build.PL 脚本在打包发行版以供发布时自动生成。最新的规范在 CPAN::Meta::Spec 的在线文档 中进行了描述。
请记住,依赖项可以在运行时更改!因此,在运行构建脚本后会生成另一个元文件。这个第二个元文件称为 MYMETA.json,它反映了脚本在运行时所做的更改,并且可能与发行版打包到 CPAN 时生成的元文件不同。
CPAN 上的旧发行版根本没有元文件。这些旧版本早于 META.yml 文件的概念,并且仅在其 Makefile.PL 中描述了其先决条件。
安装模块
Perl 的最大优势之一是 CPAN 上提供了大量的模块。毫不奇怪,也有几个不同的模块用于安装……嗯……模块!TMTOWTDI!我不知道这些类型的模块的标准名称,所以我只是称它们为“安装模块”。
这些模块负责构建发行版并将模块文件安装到用户喜欢的任何位置。这看起来很简单,但考虑到 Perl 运行的系统数量众多,这可能会变得很复杂。安装模块都会将一个 Perl 代码文件放入发行版 tarball 中。运行此 Perl 脚本将启动构建和安装过程。脚本总是以 .PL 后缀结尾,并在下列表中被称为“构建脚本”。
ExtUtils::MakeMaker
- 构建脚本
Makefile.PL- CPAN 链接
- https://search.cpan.org/dist/ExtUtils-MakeMaker
安装模块最原始、最古老的模块是 ExtUtils::MakeMaker。该模块的主要缺点是它需要 make 程序来构建和安装所有内容。这对于 Linux 用户来说可能没什么大不了的,但对于 Windows 用户来说却是一个真正的麻烦!
Module::Build
- 构建脚本
Build.PL- CPAN 链接
- https://search.cpan.org/dist/Module-Build
Module::Build 的主要优点是它是纯 Perl。这意味着它不需要安装 make 程序就可以构建/安装模块。它的采用过程很艰难,因为如果 Module::Build 尚未安装,您就无法运行自带的 Build.PL 脚本!对于较新版本的 Perl 来说,这不是问题,因为 Module::Build 是一个核心模块。(注意:从 Perl 5.22 开始,Module::Build 将不再是核心模块)
Module::Build::Tiny
- 构建脚本
Build.PL- CPAN 链接
- https://search.cpan.org/dist/Module-Build-Tiny
这是另一个纯 Perl 构建工具。作为接口,它实现了 Module::Build 接口的子集,特别是它要求参数前有连字符(Module::Build 接受带和不带连字符的参数),并且不支持 .modulebuildrc。
Module::Install
- 构建脚本
Makefile.PL- CPAN 链接
- https://search.cpan.org/dist/Module-Install
另一个现代构建/安装模块,Module::Install 仍然需要安装 make 程序才能运行。MI 被设计为 MakeMaker 的一个即插即用替代品,以解决 MakeMaker 的一些缺点。讽刺的是,它依赖于 MakeMaker 才能运行。由 MI 生成的 Makefile.PL 文件看起来差别很大,并且使用简单的领域特定语言实现。
一个非常有趣的特性是 Module::Install 将*完整的* *副本*的自身打包到发行版文件中。因此,与 MakeMaker 或 M::B 不同,您不需要在系统上安装 Module::Install。
另一个非常独特的特性是自动安装。_虽然 Module::Install 的作者不推荐使用_,但这个特性却被频繁使用。_当模块作者为其发行版启用自动安装时,Module::Install 会在执行 Makefile.PL 时搜索并安装任何尚未安装的先决模块。当 Module::Install 检测到它正在被 CPAN 或 CPANPLUS 运行时,会跳过此功能。然而,当在……我不知道……一个 PKGBUILD 中运行时,此功能不会被跳过!我希望你能看到一个恶意的 Perl 程序在 PKGBUILD 中随意下载和安装模块可能会有问题。请参阅 #PERL_AUTOINSTALL 环境变量,了解如何解决此问题。
环境变量
一些环境变量会影响模块的构建或安装方式。有些会产生巨大的影响,如果理解不当可能会导致问题。高级用户可能会使用这些环境变量。其中一些会破坏未经训练的 PKGBUILD 或导致意外行为。
PERL_MM_USE_DEFAULT
当此变量设置为真值时,安装模块将假装已回答了它通常会提出的任何问题。这*不一定*总是有效,但所有安装模块都会遵守它。_但这并不意味着模块作者会!_
PERL_AUTOINSTALL
您可以使用此变量将额外的命令行参数传递给 Module::Install 的 Makefile.PL。为了关闭自动安装(*强烈推荐*),请为此赋值 --skipdeps。
export PERL_AUTOINSTALL='--skipdeps'
PERL_MM_OPT
您可以使用此变量将额外的命令行参数传递给 Makefile.PL 和/或 Build.PL。例如,您可以使用以下方法将模块安装到您的主目录:
export PERL_MM_OPT=INSTALLBASE=~/perl5
PERL_MB_OPT
这与 PERL_MM_OPT 相同,只是它仅适用于 Module::Build。例如,您可以使用以下方法将模块安装到您的主目录:
export PERL_MB_OPT=--install_base=~/perl5
MODULEBUILDRC
Module::Build 允许您通过 rc 文件覆盖其命令行参数。这默认为 ~/.modulebuildrc。这在 Perl 工具链中被认为是已弃用的。您可以通过在 MODULEBUILDRC 中设置 rc 文件路径来覆盖它使用的文件。偏执狂可能会将 MODULEBUILDRC 设置为 /dev/null……以防万一。
PERL5LIB
搜索库的目录可以由用户设置(特别是在使用 Local::Lib 时),方法是设置 PERL5LIB。在构建之前应清除此变量。
PERL_LOCAL_LIB_ROOT
如果用户正在使用 Local::Lib,它会设置 PERL_LOCAL_LIB_ROOT。在构建之前应清除此变量。
用户安装的 Perl 问题
一个微妙的问题是,高级 Perl 程序员可能喜欢安装多个 Perl 版本。这对于测试创建程序的向后兼容性很有用。编译自己的自定义 Perl 解释器(例如,不带线程)也有速度优势。自定义 _perl_ 的另一个原因是官方 Perl Arch Linux 包有时会落后于 Perl 发布。用户可能正在尝试最新的 Perl……谁知道呢?
如果用户在其 $PATH 中拥有自定义 Perl 可执行文件,当用户在 shell 中键入 _perl_ 命令时,将运行自定义 Perl。事实上,自定义 Perl 也会在 PKGBUILD 中运行!这可能导致难以理解的隐蔽问题。
问题在于编译的 XS 模块。这些模块在 Perl 和 C 之间架起了桥梁。因此,它们必须使用 Perl 的内部 C API 来实现此桥梁。Perl 的 C API 会随着不同版本的 Perl 而略有变化。如果用户拥有不同于系统 Perl(/usr/bin/perl)的自定义 Perl 版本,则任何使用用户 Perl 编译的 XS 模块都将与系统范围的 Perl 不兼容。当尝试使用编译的 XS 模块与系统 Perl 一起使用时,该模块将因链接错误而无法加载。
一个简单的解决方案是在 PKGBUILD 中运行 Perl 时,始终使用系统范围的 Perl 解释器的绝对路径(/usr/bin/perl)。