构建RPM包 发表于 2025-07-07 | 分类于 Linux | 暂无评论 ### 构建一个 RPM 包,基本思路: 1. 明确你要构建什么。一个 rpm 包可能只是一个应用程序,也可能是不需要编译的包含一堆网页文件的 rpm 包,也可能是一个包含库的单独的 rpm 包,也可能只是文档性质的 rpm 包,也可能是补丁性质的 rpm 包,也可能只是包含配置文件的 rpm 包。 2. 准备好「原材料」,也就是项目源代码的压缩归档文件,常以 .tar.gz 或 .tar.xz 或 .tar.bz2 等后缀结尾 3. 收集补丁,给 rpm 打补丁 4. 升级,包含兼容性、功能改进和冲突解决等 5. 依赖关系。每个 rpm 都有一个能力,这个能力大多数情况下都与其名字有关。众所周知,rpm 安装后生成一堆文件,释放出的对应文件也是一种能力体现,它们有可能被其他 rpm 包依赖。换言之,这里的能力包括包的名称、包安装后的文件。在制作 rpm 的时候,有两类依赖关系:一个叫编译依赖(BuildRequires);一个叫安装依赖(Requires) 6. 编写一个 spec 文件。spec 是配置规范文件,可理解为打包成 rpm 格式的配方。 7. 开始构建 rpm 包,生成 rpm 包 8. 对 rpm 包进行测试 ### 构建要求 1. 目录结构要求 – rpm 包的构建需要对目录结构有一定的要求,可理解为构建时所必须的「工作目录」 2. 构建时使用的用户 – 推荐使用 root (uid=0),当然普通用户(uid>=1000)也可以进行构建 3. 将「原材料」放入到对应的目录中 4. 编写 spec 文件,编译打包成 rpm 包 ### spec 文件 什么是 spec 文件? 一个包含元数据、构建指令和安装规则的脚本,它驱动整个 RPM 包的生成流程,定义了软件从源码编译到最终打包的所有步骤。spec 的文件内容包含三部分: - 序言项(Preamble items) - 主体项(Body items) - 高级项(Advanced items) 除了序言项,其他部分都需要定义一些指令为构建系统提供必要的信息。 ### 序言项标签 - Name – 软件包的基本名称,应与 spec 文件名匹配 - Version – 软件包的上游版本号,注意!这里的版本号不要带 – ,- 在 spec 中有特殊的意义 - Release – 此软件包的发布次数。通常将初始值设置为 1%{?dist} - Summary – 简短的、单行的说明软件包的摘要信息 - License – 被打包的软件包的开源许可证 - URL – 大多数情况下,这是所打软件包的上游项目网站。 - Source0 – 上游源代码压缩包存档的路径或URL - Patch – 补丁,可以有两种方式应用该指令,在 Patch 后面加或不加数字。也可以使用 %Patch0 这种方式。 - BuildArch – 构建的体系架构,如果软件包不依赖特定的架构,可写为 BuildArch: noarch。如果不写,则自动继承当前机器的体系架构 - BuildRequires – 编译时的依赖包,多个依赖包可用空格或逗号分隔。可以有多个 BuildRequires 条目 - Requires – 软件安装后运行所需的依赖包,多个依赖包可用空格或逗号分隔。可以有多个 Requires 条目 - ExcludeArch – 如果某个软件不能在特定的处理器架构上运行,你可以在此处排除该架构 - Conflicts – 冲突的包,与 Requires 相反,如果匹配到对应的包,则无法安装该包。 - Obsoletes – 废弃的包,也就是其他包提供的功能已经不推荐使用了 rpm 软件包的命名规范: rpm 软件包的命名规范: ```shell [Package_Name]-[Version]-[Release].[OS].[Arch].rpm [Package_Name]-[Version]-[Release].[OS].[Arch].src.rpm ``` Note:通常一个 rpm 软件包的完整名称由 Name、Version、Release 组成,我们称其为 NVR,即 Name-Version-Release.rpm。在上面的命名规范中,有些资料也将 [Release].[OS].[Arch] 这三部分单独划分到 Release 中。 ### 主体项指令 一个软件包能否打包成功,全看主体项的各种步骤。 - %description – rpm 软件包的完整描述,可跨越多行,可分为多列。 - %prep – 预处理(编译前的准备操作),为下一步编译安装做准备。 - %build – 编译位于 BUILD 目录里的文件,类似源代码安装 ./configure && make 的操作。 - %install – 将 BUILD 目录的文件安装至 BUILDROOT 目录下,需要注意的是!这个 BUILDROOT 目录是最终用户安装 rpm 后得到的文件 。类似源代码安装中的 make install - %check – 用于测试软件的一系列命令。类似源代码安装中的 make test - %files – 制作 rpm 包时应该包含哪些文件。注意!这里的文件一定是和 BUILDROOT 目录下是一一对应的关系(除了 debug 目录)。BUILDROOT 是一个模拟操作系统根的临时目录。 - %changelog – 每个 release 所做的变更日志,例如漏洞修复、补丁、额外的功能增强等。 ### 高级项 高级项包含 Scriptlet(程序段)与 Triggers(触发器)。 - Scriptlet 是在安装或删除软件包之前或之后执行的一系列 RPM 指令;Triggers 提供了一种在安装和卸载软件包期间进行交互的方法。Triggers 难以调试,因此要尽量减少使用它。 - %pre – 在安装包之前执行的程序段 - %pretrans – 在安装或删除任何包之前执行的程序段 - %post – 在安装包之后执行的程序段 - %preun – 在卸载包之前执行的程序段,通常在升级的时候会执行 - %postun – 在卸载包之后执行的程序段 - %posttrans – 在事务结束时执行的程序段 关于这部分内容的更多内容,请参阅 [这里](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/packaging_and_distributing_software/advanced-topics#scriptlets-directives_epoch-scriplets-and-triggers "这里")。 ### rpm 软件包构建实验 实验要求:将 Nginx 最新版本构建成 rpm 包 环境: 操作系统:Rocky Linux 8.10 rpm 版本:4.14.3 用户:root (uid=0) 准备好需要的命令行工具: ```shell Shell > dnf -y install rpmdevtools rpm-build ``` 其中 rpmdevtools 软件包包含这些基本的命令行工具: - rpmdev-setuptree 在当前用户家目录中生成 rpm 的构建树(build tree) - rpmdev-diff 比对两个归档的不同内容 - rpmdev-newspec 从模板中创建新的 .spec 文件 - rpmdev-checksig 使用备用的rpm密钥环检查软件包的签名 - rpminfo 打印有关可执行文件和库的信息 - rpmdev-md5 显示 RPM 中所有文件的 md5 校验 步骤一:准备「工作目录」 ```shell Shell > cd ; rpmdev-setuptree Shell > tree rpmbuild/ rpmbuild/ ├── BUILD ├── RPMS ├── SOURCES ├── SPECS └── SRPMS ``` 目录 说明 - BUILD 构建过程中的工作目录 - RPMS 存放生成的二进制 rpm 包 - SOURCES 存放构建所需的资源,包含源码压缩文件、补丁文件、配置文件等 - SPECS 核心目录,存放 spec 文件 - SRPMS 存放生成的源码 RPM 包(.src.rpm),其包含源代码和 .spec 文件,便于分发和重新构建 ### 步骤二:使用 spec 文件 有两种方式创建 spec 文件: 使用者创建一个全新的 spec 文件,文件内容需要自行填写 使用 rpmdev-newspec 命令工具创建一个 spec 模块文件,使用者只需填写必要的指令与字段即可 ```shell Shell > cd /root/rpmbuild/SOURCES ; wget -c https://nginx.org/download/nginx-1.29.0.tar.gz Shell > cd /root/rpmbuild/SPECS ; rpmdev-newspec nginx nginx.spec created; type minimal, rpm version >= 4.14. # 该文件的内容如下: Shell > cat nginx.spec Name: nginx Version: Release: 1%{?dist} Summary: License: URL: Source0: BuildRequires: Requires: %description %prep %autosetup %build %configure %make_build %install rm -rf $RPM_BUILD_ROOT %make_install %files %license add-license-file-here %doc add-docs-here %changelog * Fri Jul 4 2025 root - ```shell 修改之后的内容如下: ```shell Name: nginx Version: 1.29.0 Release: 1%{?dist} Summary: A high performance web server and reverse proxy server License: BSD URL: http://nginx.org/ Source0: %{name}-%{version}.tar.gz BuildRequires: gcc gcc-c++ openssl openssl-devel make pcre pcre-devel gzip tar bzip2 bzip2-devel # Requires: %description Nginx is a web server and a reverse proxy server for HTTP, SMTP, POP3 and \ IMAP protocols, with a strong focus on high concurrency, performance and low \ memory usage. %prep %setup -q %build ./configure \ --sbin-path=/usr/sbin/nginx \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx/nginx.pid \ --lock-path=/var/lock/nginx.lock \ --http-log-path=/var/log/nginx/access.log \ --user=nginx \ --group=nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_stub_status_module \ --with-http_gzip_static_module make %{_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=%{buildroot} %{__install} -p -d -m 0755 %{buildroot}/var/run/nginx %{__install} -p -d -m 0755 %{buildroot}/var/log/nginx %clean rm -rf %{buildroot} %pre # $1表示软件包的执行方式,0表示卸载;1表示第一次安装;2表示升级 if [ $1 == 1 ];then /usr/sbin/groupadd -r nginx 2> /dev/null /usr/sbin/useradd -r -g nginx nginx 2> /dev/null fi %files %defattr(-,root,root,-) %doc LICENSE CHANGES README %{_sbindir}/%{name} %dir /var/run/nginx %dir /var/log/nginx %dir /etc/nginx %config(noreplace) /etc/nginx/fastcgi.conf %config(noreplace) /etc/nginx/fastcgi.conf.default %config(noreplace) /etc/nginx/fastcgi_params %config(noreplace) /etc/nginx/fastcgi_params.default %config(noreplace) /etc/nginx/koi-utf %config(noreplace) /etc/nginx/koi-win %config(noreplace) /etc/nginx/mime.types %config(noreplace) /etc/nginx/mime.types.default %config(noreplace) /etc/nginx/nginx.conf %config(noreplace) /etc/nginx/nginx.conf.default %config(noreplace) /etc/nginx/scgi_params %config(noreplace) /etc/nginx/scgi_params.default %config(noreplace) /etc/nginx/uwsgi_params %config(noreplace) /etc/nginx/uwsgi_params.default %config(noreplace) /etc/nginx/win-utf /usr/local/nginx/html/50x.html /usr/local/nginx/html/index.html %changelog * Fri Jul 4 2025 root - first version - init ``` 稍后我们将说明 spec 文件中的内容。 ### 步骤三:使用 rpmbuild 进行构建 常见选项如下表所示: - -bp 只执行到 %prep 阶段 - -bi 只执行到 %install 阶段 - -bc 只执行到 %build 阶段 - -bb 制作二进制的 rpm 包 - -bs 制作源码格式的 rpm 包 - -bl 检测,检测哪些文件在 BUILDROOT 目录下安装生成了,但是在 spec 文件中的 %files 没有包含进来 - -ba 既制作二进制的 rpm 包,也制作源码格式的 rpm包 使用 -bp 选项时: # 若输出文本的最后一行看到 exit 0 表示无任何问题 ```shell Shell > rpmbuild -bp /root/rpmbuild/SPECS/nginx.spec Shell > ls /root/rpmbuild/BUILD nginx-1.29.0 Shell > ls -lh /root/rpmbuild/BUILD/nginx-1.29.0/ ``` 使用 -bc 选项时: # 检查环境后会执行编译过程 ## 同样的,若输出文本的最后一行看到 exit 0 表示无任何问题 ```shell Shell > rpmbuild -bc /root/rpmbuild/SPECS/nginx.spec ``` 使用 -bi 选项时: # 未出现 error 或 file not found 之类的错误则表示正常 ```shell Shell > rpmbuild -bi /root/rpmbuild/SPECS/nginx.spec ``` 使用 -ba 选项时: ```shell Shell > rpmbuild -ba /root/rpmbuild/SPECS/nginx.spec Shell > tree /root/rpmbuild/RPMS/ ``` 步骤四:安装 我们的 nginx-1.29.0-1.el8.x86_64.rpm 已经制作完成,安装即可: ```shell Shell > rpm -ivh /root/rpmbuild/RPMS/x86_64/nginx-1.29.0-1.el8.x86_64.rpm Verifying... ################################# [100%] Preparing... ################################# [100%] Updating / installing... 1:nginx-1.29.0-1.el8 ################################# [100%] # 释放出的文件 Shell > rpm -ql nginx ``` 请注意!我这里并没有将 nginx 的启动 unit 存放在 /usr/lib/systemd/system/ 目录中。 到这一步,构建 nginx 的简单 rpm 包就完成了。 ### 最后 spec 文件的内容非常丰富,单靠这一篇文档是可能了解完整的,详情参阅官方的 [手册页](https://rpm.org/docs/4.20.x/manual/ "手册页") 对于一般的使用者或运维人员来说,构建考虑全面的 rpm 包是一件非常费时费力的事情,主要原因在于其涉及的多环节精细化管理与技术挑战: - 依赖管理的复杂性。库依赖、依赖冲突等 - spec 文件编写的技术门槛。需要考虑多阶段构建流程、跨平台适配以及相关的脚本逻辑,比如一些关系型数据库的 RPM 包需在spec 中声明 400+ 文件路径及 20+ 依赖项 - 高昂的测试与验证成本。需要考虑卸载后无残留文件、升级时配置文件保留(.rpmnew 机制),还要考虑在不同的 RHEL 衍生版去验证一致性。 转载自: >https://www.rockylinux.cn/notes/rpm-fundamentals-02-building-software-packages.html