极简 RPM 格式软件包打包指南

这篇文介绍了如何使用 ** 最简单 ** 的方法,为 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 命令安装。

准备工作

必需的软件包

打包之前,你需要安装一些必要的软件。

  1. gcc、clang、make、cmake 等软件,用来编译源代码
  2. 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 来修改。

参考

5赞

我还以为有什么方法可以让用户更加简单快捷的打包了呢.
简单的打个 Deb 或者 RPM 包可以用 checkinstall.