这篇文介绍了如何使用 ** 最简单 ** 的方法,为 Fedora 或者其他基于 RPM 的发行版创建 ** 合法 (未必符合规范)** 的 rpm 格式的软件包。
概述
Feodra 中的软件包格式为 rpm。它由 rpmbuild 软件和一个名为 package-name.spec 的文件创建的。"package-name"是你所需要打包的软件的名字。package-name.spec 文件中包含了很多信息,大致分为软件包的介绍信息、软件包的依赖信息、控制打包流程的信息。
运行 rpmbuild 时,必须指定 spec 文件的路径,然后 rpmbuild 根据 spec 文件的指导,编译、打包软件,最后把二进制文件、软件依赖等信息写入 rpm 包。
得到的 rpm 软件包可以直接通过 dnf localinstall /path/to/package 命令安装。
准备工作
必需的软件包
打包之前,你需要安装一些必要的软件。
- gcc、clang、make、cmake 等软件,用来编译源代码
- rpmbuild 等软件,用来将编译出的软件打包成 rpm 包
上述软件可以通过如下命令安装:
dnf groupinstall -y "C Development Tools and Libraries"
dnf groupinstall -y "RPM Development Tools"
此外,你所打包的软件可能还需要一些其他软件,根据软件说明,下载相应的软件包。
下载并测试安装
下载你想打包的软件的源代码,解压,按照作者所说的步骤安装它。记录下在编译和安装软件过程中需要的所有命令或步骤,它们在编写 package-name.spec 文件中要用到。
大部分软件的安装需要 3 步:
./configure
make
make install
安装完记得检查软件是否能正常使用。
创建 package-name.spec 文件
rpmbuild 打包时需要一个标准的目录树,源码和 spec 文件放在各自的目录,打包出来的 rpm 包则放在另一些目录。具体的格式如下:
/home/username/
└── rpmbuild
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
开始打包之前,首先需要切换到一个非 root 用户,然后执行 rpmdev-setuptree 命令。该命令会自动在你的 home 目录创建上述文件夹。
详解 rpmbuild 目录树
/home/username/
└── rpmbuild
├── BUILD //编译文件的临时目录
│ └── hello-2.10 //解压源码产生的目录
├── BUILDROOT
│ └── hello-2.10-1.fc33.x86_64 //虚假的根目录
├── RPMS //包含生成的 rpm 软件包
│ └── x86_64
├── SOURCES //放源代码的目录
│ └── hello-2.10.tar.gz
├── SPECS //放 spec 文件的目录
│ └── hello.spec
└── SRPMS //包含生成的包含源码的 rpm 包
└── hello-2.10-1.fc33.src.rpm
上面的目录树是打包 “GNU hello” 软件时生成的目录树,那些目录在编译都要用到,每个目录都有相应的宏与之对应,对应关系如下表所示:
宏 | 宏定义 | 实际路径 |
%{_topdir} | ~/rpmbuild | ~/rpmbuild |
%{_buildrootdir} | %{_topdir}/BUILDROOT | ~/rpmbuild/BUILDROOT |
%{buildroot} | %{_buildrootdir}/%{name}-%{version}-%{release}.%{_arch} | 比如说 ~/rpmbuild/BUILDROOT/hello-2.10-1.fc33.x86_64/ |
%{_sourcedir} | %{_topdir}/SOURCE/ | ~/rpmbuild/SOURCES/ |
%{_builddir} | %{_topdir}/BUILD/ | ~/rpmbuild/BUILD/ |
%{_rpmdir} | %{_topdir}/RPMS | ~/rpmbuild/RPMS/ |
%{_specdir} | %{_topdir}/SPECS | ~/rpmbuild/SPECS/ |
%{_srcrpmdir} | %{_topdir}/SRPMS | ~/rpmbuild/SRPMS/ |
宏 - 路径映射表
简要介绍 spec 文件
以 Fedora 官方教程中为 GNU hello 打包的 hello.spec 文件为例,具体文件如下:
Name: hello //包名
Version: 2.10 //版本号
Release: 1%{?dist} //release 号,第几次编译
Summary: The "Hello World" program from GNU //简要介绍
License: GPLv3+ //软件许可证
URL: http://ftp.gnu.org/gnu/%{name} //软件的官方网站
Source0: http://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.gz //源码包的下载链接,如果存在多个源码包,依次列出。这个链接只是说明性质的,你仍然需要手动下载源码,放到源码对应的文件夹。
BuildRequires: gettext //编译时依赖,如果依赖多个软件包,则分别写出来,每行一个。
Requires(post): info //运行时依赖
Requires(preun): info
%description //详细介绍
The "Hello World" program, done with all bells and whistles of a proper FOSS
project, including configuration, build, internationalization, help files, etc.
//准备阶段
%prep
%autosetup
//编译阶段
%build
%configure
%make_build
//安装阶段(安装到虚拟根目录)
%install
%make_install
%find_lang %{name}
rm -f %{buildroot}/%{_infodir}/dir
//软件安装到用户系统之前执行的动作
%post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :
//软件从用户系统卸载前执行的动作
%preun
if [ $1 = 0 ] ; then
/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi
//打包阶段
%files -f %{name}.lang
%{_mandir}/man1/hello.1.*
%{_infodir}/hello.info.*
%{_bindir}/hello
//其他一些元信息
%doc AUTHORS ChangeLog NEWS README THANKS TODO
%license COPYING
//软件包的修改日志
%changelog
* Tue Sep 06 2011 The Coon of Ty <Ty@coon.org> 2.10-1
- Initial version of the package
spec 打包步骤
正如上面文件中展示的一样,package-name.spec 文件只是一个说明性的文档,它包含了一些代码段,但它本身不是。
每一段代码都是一些 bash 命令,其中夹杂着一些宏。这些代码段经过 rpmbuild 预处理之后,替换掉代码中的宏,生成真正的可执行脚本,交给 bash 执行。
spec 文件中,前面的包名、版本号、依赖、源码等比较重要,其他部分则只有下面 4 部分比较重要,剩下的都是可有可无的东西,可以直接删掉。
下面 4 个阶段按照执行顺序依次列出,你需要在每一部分下面写一段脚本。
%prep
准备阶段。这一阶段主要用来预处理源代码,比如解压软件包,对代码打补丁等。
我们需要把源代码解压到 BUILD 目录中,如下:
tar -xf %{_sourcedir}/name-version.tar.gz -C %{_builddir}
%build
编译阶段。在这个阶段你需要将源代码编译成真正的可执行文件。
编译第一步就是进入由解压生成的源码目录,因此, 第一条命令一般是这样的:
cd %{_builddir}/name-version/
具体的目录取决于你打包的软件。
然后,就需要执行当初安装软件所执行的命令了。根据 Fedora 的打包规范,所有的软件都需要安装到 /usr/
目录,因此 configure 脚本都需要加上 --prefix=/usr/
./configure --prefix=/usr
make
因为我们需要打包软件,而不是真正的安装,所以执行到这里就可以了。
如果软件不需要编译,这一步可以直接留空。
%install
这里的安装指的是将编译出的源码和文档放在虚拟的根目录,也就是 ~/rpmbuild/buildroot/name-version-arch/
目录。
一般来说只需要执行如下命令即可:
make prefix=%{buildroot}%/usr/ install
如果还有其他的文件需要手动安装,比如某个文件需要安装到/usr/bin/,那么将它复制到 %{buildroot}%/usr/bin 即可。
对了,每一阶段的代码都会生成单独的 bash 脚本,所以你可能在这一阶段开始时,先 cd 到正确的目录。
%files
这一阶段很简单,就是把上一阶段中安装到 %{buildroot}%
目录中的文件一行行地列出即可。记住,%{buildroot}% 前缀需要去掉,比如:
/usr/bin/hello
/usr/share/locale/*
/usr/share/man/man1/hello.1.*
/usr/share/info/*
记得尽量写详细一些,不要偷懒一个通配符完事。
测试 package-name.spec 文件
接下来就是真正的打包了,将编写好的 spec 文件交给 rpmbuild 命令执行。
rpmbuild ~/rpmbuild/SPECS/package-name.spec
如果 spec 文件编写无误,RPMS 和 SRPMS 目录下会生成相应的 rpm 包。如果 spec 文件存在错误,那么 rpmbuild 将会报错。仔细查看是哪一阶段出现的错误,然后依据错误提示进行修改即可。
对于生成的 rpm 包,你可以直接 dnf localinstall ~/rpmbuild/RPMS/xxx.rpm 进行安装。
如果你只是拿来自己使用,那么执行到这里就可以了。
如果你打算分发给其他人,或者提交到某一个软件仓库,那么你就需要仔细确认软件包的依赖关系了。还记得在打包之前你安装了多少软件包吧!软件的依赖分为两种,编译时依赖 (BuildRequires) 和运行时依赖 (Requires)。编译时依赖就是那么编译时用到的软件包,比如编译器、库的头文件等等;运行时依赖就是平时安装软件所看到的软件依赖关系了,此处不再详述。
检查包是否符合规范
确定包可以正常使用后,再使用 rpmlint 来检查错误:
rpmlint ~/rpmbuild/SPECS/package-name.spec
rpmlint ~/rpmbuild/RPMS/package-name*.rpm
rpmlint ~/rpmbuild/SRPMS/package-name*.rpm
rpmlint 可以检查 spec 文件、RPM 包、RPM 源码包文件,并发现其中的错误。一个合格的 rpm 包应该没有错误,甚至没有警告。
还有,假如你是根据这篇文章进行的打包,那么不用检查了,你 1000% 会得到错误提示。这里为了简化打包流程,除了用到了几个宏,其他时候都是在写脚本,根本没有遵守任何打包规范(虽然包确实已经可以使用了)。
将软件包提交到软件仓库
软件仓库有很多,比如 Fedora 的官方仓库、rpmfusion 仓库,它们都是官方仓库,对于包的质量要求很高。
还有一些服务,比如 Fedora 的 corp 网站、openSUSE 的 obs 网站,它们对所有的用户开放,允许用户创建私有仓库,它们的用法是另一个话题了。
甚至你可以使用 createrepo 工具自行创建软件仓库,放在自己的服务器上,具体用法自行探索。
总结
- 下载你希望打包的软件源代码
- 试着编译安装包到任意目录
- 从网上找到一个 spec 文件作为模板,重命名为 package-name.spec。
- 根据具体情况修改 package-name.spec 文件
- 运行 rpmbuild 看看输出的打包结果是否正确
- 如果不正确,重复前两个步骤
注意事项
- 在开始自动打包之前,请确保你至少已成功手动打包一次,除非你 “很清楚” 你正在做什么。不幸的是,虽然大多数软件作者遵循了三步走的安装惯例:./configure;make;
make
install,但事情并不都是这样的,有时候你不得不自己打补丁才能安装成功。如果你手动无法编译成功或者无法将软件安装到指定子目录下,那你就不必费心打包了。rpmbuild 无法帮你自动解决源码问题。 - 在一些情况下,你可能无法直接得到包的源码,可能需要使用 sh
installer.run 这样的东西来工作。这时就需要你自己做很多工作了(比如读 READMEs,安装指导,手册,或者 Gentoo 的 ebuilds 等等)。在一些特殊的情况下,你需要自己编辑源码才能正常安装。但是,rpmbuild 需要完全自主运行,不能有用户的干预。因此,如果你想修改 makefiles,你需要随 spec 附上一个定制的补丁,放在源码目录中,然后在%prep 阶段安装这个补丁;或者你可以在%prep 阶段通过 sed 来修改。