RPM(RPM Package Manager)是一种用于在Linux系统上管理软件包的工具。它最初由Red Hat开发,现已成为许多Linux发行版(包括openEuler、CentOS、Fedora等)的标准包管理工具。RPM主要用于安装、卸载、更新和查询软件包。
本文章将基于openEuler24.03LTS操作系统完成对一个应用程序(feh)的RPM打包,涉及到RPM打包的相关概念、流程以及问题的发现与解决,适用于有一定LINUX操作命令与编程语言基础的读者朋友。
因本人水平及时间有限,文中难免有疏漏之处,请各位读者海涵并不吝指正。
注意:①# - 涉及到的commands需要sudo权限;
②$ - 涉及到的commands需要普通非特权用户权限;
③rpmbuild命令应在普通用户权限下执行,否则一点小错误可能危害整个操作系统,因此本文章建议在普通用户而非root用户界面完成;
④文章中出现的/egdoc或/zwy应替换为读者实际的用户名。
一、rpm building process涉及到的相关概念
安装、删除、更新(一句话,管理)软件是每个操作系统的基本任务。当包管理器还不存在时,安装程序的唯一方法是编译其源代码,并将生成的文件放在文件系统的适当位置。跟踪每段代码的依赖关系是非常困难和耗时的,在引入包管理器后一切都变得容易了。
如今,每个现代Linux发行版都有自己的包管理器:Debian及其衍生版本使用dpkg,而Red Hat系列发行版使用rpm。软件以包的形式提供预编译,包基本上是压缩的存档,包含有关软件版本、其依赖关系以及与其他包可能发生冲突的元数据。
在本文章中,我们将看到如何从应用程序源代码开始创建rpm包。我们将打包的应用程序是feh,它是一个简单的命令行图像查看器:它非常小,几乎没有依赖项。然而,在开始构建第一个包之前,我们应该掌握一些基本概念。
1. build environment相关
rpm构建环境树的根目录是rpmbuild目录,它包含6个子目录:BUILD、BUILDROOT、RPMS、SOURCES、SPECS和SRPMS。
在下文我们将看到如何通过启动一个简单的命令来生成这个环境,现在,我们只讨论这些目录的作用。下面是工作树的表示:
这些目录中的每一个在构建过程中都有其特定的作用:
①BUILD目录是构建我们想要打包的程序的源代码的地方。
②BUILDROOT目录是复制BUILD目录中软件编译所产生的文件的地方,反映了目标系统在包名称子目录中的结构:在我们的例子中,将安装在/usr/bin中的“feh”二进制文件将报告为BUILDROOT/feh-3.0-1.fc29.x86_64/usr/bin。
③RPMS目录是生成rpm包的地方:每个rpm将被放置在以其体系结构命名的子目录中,如果不是特定于体系结构(architecture),则放置在noarch目录中。(是的,这里的 architecture 指的是软件包(RPM)的目标硬件架构。在 Linux 系统中,不同的硬件架构需要不同版本的软件包,因为这些软件包是为特定的处理器架构编译的。)
④SOURCES目录存放着我们想要打包的软件的压缩源代码,通常以压缩文件或压缩文件的形式存在。
⑤SPECS目录是我们放置.spec文件的地方,其中包含构建包的指令:稍后我们将分析该文件的结构。
⑥SRPMS目录相当于RPMS,但用于源RPMS。这个特殊的包包含应用程序的原始源代码、最终补丁和用于构建包的指定文件。
现在各位可能对每个目录内容仍有疑惑,不必担忧,在第二部分的具体操作过程中各位将对其有更深刻的理解。
2. .spec文件相关
定义构建rpm包所需的所有指令和信息的文件是.spec文件。一个specfile包含了build dependencies(构建依赖项,编译我们想要打包的程序所需的软件)、runtime dependencies(运行时依赖项,程序正确运行所需的库)和编译软件时应该执行的命令。
该文件由两个宏节组成:preamble(序言,绪论;前言,开场白)和body(主体部分)。
(1)preamble部分可以包含下列说明:
- Name: The basename of the package (需要匹配 the spec file)
- Version: The upstream version of the packaged software
- Release: The release number of the package(第二章涉及到细节)
- License: The license used for the software we want to package(第二章涉及到细节)
- Url: The upstream URL of the software(资源的主页,可以是.org主页)
- Source0: The direct URL or the path of the software’s compressed source code (tarball or zipped file)(直接源)
- BuildArch: The architecture of the package: if no architecture is specified the one of the host system will be used
- BuildRequires: The dependencies needed to build the software(构建依赖项)
- Requires: The dependencies needed to run the software(运行依赖项)
(2)body部分可以包含下列说明:
- %description: An optionally multi-line description of the software packaged(描述)
- %prep: The command(s) needed to prepare the source code (for example, the commands needed to extract a tarball)
- %build: The command(s) needed to build the software
- %install: The command(s) needed to copy the file resulting from the build process to the BUILDROOT directory
- %files: The list of the files provided by the package, which will be installed on the system
另外,在zh/contributors/packaging.md · openEuler/community - Gitee.com中涉及到了软件拆分规则,感兴趣的读者可以参考学习。
“但是对于很多简单的软件来说,可以将libs包,devel包和主包合并,不单独拆分。目的是保持包的简洁,易于理解,不会将包的粒度做的过细而导致后续更新,维护困难,并且增加依赖关系的复杂性。”
由于本文章涉及到的软件不复杂,采用不单独拆分的形式。
3. .spec文件中的marcos(宏)的使用
为了简化我们的工作,在指定文件中,我们可以使用一些宏,这些宏允许我们引用许多有用的东西并自动执行某些任务。
首先,我们有RPM directory macros,它允许我们引用构建环境的目录;我们应该总是使用它们而不是直接路径(相当于将tree中直接路径定义好):
- %{_topdir}: This macro references the rpmbuild directory
- %{_builddir}: References the BUILD directory inside our build tree
- %{_rpmdir}: References the path of the RPMS directory
- %{_sourcedir}: This macro is evaluated to the path of the SOURCES directory
- %{_specdir}: A macro which represents the path of the SPECS directory
- %{_srcrpmdir}: References the path of SRPMS directory
- %{_buildrootdir}: References the path of the BUILDROOT directory
不难看出,上述为工作树的上级目录rpmbuild以及下属的六个子目录的直接路径的宏。
还有一些宏可以让我们引用我们的操作系统中最重要的目录,例如:
- %{_sysconfigdir}: The /etc directory
- %{_prefix}: The /usr directory
- %{_bindir}: The /usr/bin directory
- %{_mandir}: The path to the /usr/share/man directory
在.spec文件的body部分,我们同样使用到了marcos,如:
(1)The %setup macro, is used in the %config section of the specfile,
基本上执行以下操作:
- ①提取我们要打包到BUILDDIR目录的程序的源代码
- ②切换到解压的目录
- ③在其中设置适当的文件权限
(2)The %{make_build} macro is used in the %build section of the specfile,基本上运行the make command with a predefined sets of options(一组预定义选项的make命令),来编译软件的源代码。
(3)The %{make_install} macro, instead, is used in the %install section of the file, and runs make install with the DESTDIR parameter, used to instruct the command to install the compiled files relatively to a given directory instead of the real system /(安装到指定的路径而不是根路径)
二、完成RPM打包的具体步骤及问题解决
在学习了包构建过程的基本概念后,接下来我们可以看到如何创建构建环境和第一个rpm包。
1. 安装构建依赖项(build dependencies)
首先,我们需要安装rpmdevtools,以及构建feh所需的依赖项:
$ sudo dnf install rpmdevtools gcc make imlib2-devel libjpeg-devel libpng-devel libXt-devel libXinerama-devel libexif-devel perl-Test-Command perl-Test-Harness libcurl-devel
命令中install后用空格分开的各项为并列关系,均需要安装,这些依赖项根据是.spec文件中的BuildRequires和Requires,对于不同的软件依赖项需要针对性调整,是一项比较重要的内容。
问题:在CENTOS系统(在该系统中同样进行了测试)中出现部分包不在仓库中的问题,通过启用 EPEL(Extra Packages for Enterprise Linux)仓库解决:
sudo yum install epel-release
sudo yum clean all
sudo yum makecache
安装这些包后,我们可以生成构建环境,运行以下命令:
$ rpmdev-setuptree
此时,应该在/home/user目录创建好了工作树的上级目录rpmbuild以及下属的五个子目录(无BUILDROOT)
(此时应暂时无BUILDROOT)
2. 编写.spec文件
命名为feh.spec,存放在~/rpmbuild/SPECS目录,以下为文件内容:
3. .spec文件分析
.spec文件来源于https://linuxconfig.org/how-to-create-an-rpm-package,在实际使用过程中出现一些问题,结合个人实践与查阅资料补充以下内容/问题解决方案:
#Release相关
使用1%{?dist}而非1的好处
#License相关
由于另一项目许可证涉及到GPLv3+,于是对该字段标准进行补充。
编写完成后可以使用rpmlint命令检查.spec文件。
关于%prep %build %install %files
%prep:提供%setup -q宏就足够了:如前所述,这个宏将运行解压缩源tarball所需的命令,并将解压缩的目录放入BUILD文件夹中。
%build:我们指定构建源代码时应该运行的命令。在这里,我们所要使用的也只是%{make_build}宏,它使用我们之前看到的选项将make命令运行到存放我们想要打包的应用程序的未打包源代码的目录中。
%install:我们使用了另一个宏%{make_install},它也提供了PREFIX参数,将其设置为%{_prefix},它将扩展到/usr。生成的命令将导致编译源代码生成的文件被放置在“假根”中,并使用宏中包含的DESTDIR参数进行设置。因为在%{make_install}宏中,“DESTDIR”被设置为/home/egdoc/rpmbuild/BUILDROOT/%{NAME}-%{VERSION}-%{RELEASE}。x86_64时,文件将安装在:/home/egdoc/rpmbuild/ buildroot /%{NAME}-%{VERSION}-%{RELEASE}.x86_64/usr下。
%files:a list of the files which will be installed by our package我们的包将要安装的文件列表。
#关于自动解压失败问题
在%prep阶段出现了自动解压失败问题
解决:手动解压 tar -xjvf ~/rpmbuild/SOURCES/feh-3.0.tar.bz2 -C /tmp注意选项对应压缩文件类型
tar -xjvf ~/rpmbuild/SOURCES/feh-3.0.tar.bz2 -C /tmp
该问题应该有更好的解决方法,后续将检查%prep内含命令使其可以自动化解压,mark。
#关于.debug文件的问题
第二行:/usr/lib/debug/usr/bin/feh-3.0-1.fc29.x86_64.debug(出现报错)
解决方法:取消手动创建文件,删去此行系统自动创建后问题解决。
4. 获取sources压缩包
下载“feh”的源代码tarball:不需要手动下载,因为我们可以使用spectool命令:
$ spectool -g -R ~/rpmbuild/SPECS/feh.spec
相当于Getting http://feh.finalrewind.org/feh-3.0.tar.bz2 to /home/zwy/rpmbuild/SOURCES/feh-3.0.tar.bz2
当spectool有问题时,可以考虑替代的手动下载命令curl/wget,如:
curl -o /home/zwy/rpmbuild/SOURCES/feh-3.0.tar.bz2 http://feh.finalrewind.org/feh-3.0.tar.bz2
上述命令将使用spec文件中的SOURCE0下载我们引用的源代码,下载到工作树的适当目录中:~/rpmbuild/SOURCES。
5. rpmbuild完成打包
我们所要做的就是启动rpmbuild命令,并提供指定文件的路径。当使用-bb选项启动时,rpmbuild将只构建一个二进制包,如果我们也想生成一个源rpm,我们必须使用-ba来代替。
以下为-bb 选项示例:
$ rpmbuild -bb ~/rpmbuild/SPECS/feh.spec
所执行操作的输出将打印在屏幕上,请注意rpmbuild操作可能会有各种各样的报错,但是报错提示是很详细的,可以根据报错提示逐步完善依赖项或修改.spec文件,或将一些自动化操作改为手动操作,希望读者可以顺利解决遇到的问题。
如果一切正常,rpm包将在RPMS目录中生成。
6. RPM包的安装与校验
执行以下命令将安装feh的rpm包:
sudo rpm -i RPMS/x86_64/feh-3.0-1.x86_64.rpm
man feh指令将显示feh的说明书:
为openEuler系统安装GNOME桌面环境,以运行feh-3.0 :
sudo dnf install gnome-shell gdm gnome-session gnome-terminal -y
root权限设置默认启动环境:
systemctl enable gdm.service
systemctl set-default graphical.target
重启后生效,运行feh打开一张图片:
至此本文章已将至尾声,作者也是正处于LINUX操作系统的学习阶段,文章可能有所纰漏,希望本文能对各位有所帮助,祝愿共同进步。
refer:①https://linuxconfig.org/how-to-create-an-rpm-package
②zh/contributors/packaging.md · openEuler/community - Gitee.com -源于openEuler开源社区