rpmbuild打包
目标是把 rpmbuild 的核心概念、标准流程、常见命令、服务类软件打包方式和排错方法串成一份可直接上手的教程。
1. 什么是 RPM 和 rpmbuild
- RPM:Linux 下常见的软件包格式与底层包管理机制。
- rpm 命令:负责安装、卸载、查询、校验本地
.rpm包。 - dnf/yum:更上层的包管理工具,负责仓库管理、依赖解析,并最终调用 rpm。
- rpmbuild:用来把源码、脚本、配置文件、systemd 服务文件等内容构建成 RPM 包。
可以简单理解为:
dnf/yum 负责“拿包并解决依赖” rpm 负责“安装和管理包” rpmbuild 负责“制作包”2. 先建立正确认知:RPM 包里到底装的是什么
一个 RPM 包通常包含:
- 可执行文件,如
/usr/bin/* - 配置文件,如
/etc/* - 服务文件,如
/usr/lib/systemd/system/*.service - 文档、license、man 手册
- 安装/升级/卸载脚本
- 依赖关系、架构信息、校验信息等元数据
最终哪些文件会进 RPM 包,不是看你复制了什么,而是看%files里声明了什么。
- 在
%install里放进%{buildroot},但没写到%files:不会被打进包 - 写到了
%files,但实际文件不存在:打包会失败
3. rpm 与 dnf/yum 的关系
用户 ↓ dnf / yum ← 自动处理仓库和依赖 ↓ rpm ← 执行本地包安装、查询、卸载 ↓ 文件系统 + RPM 数据库什么时候优先用哪个
- 安装本地包做测试:
rpm -ivh或dnf install ./xxx.rpm - 正式部署,且希望自动补依赖:优先
dnf install - 查看包内容、脚本、元数据:用
rpm -q*系列命令
4. 安装打包工具
在 RPM 系发行版上安装:
sudodnfinstall-yrpm-build rpmdevtools rpmlint适用于:
- RHEL
- CentOS Stream
- Fedora
- openEuler
- Rocky Linux
- AlmaLinux
- 部分麒麟/统信 RPM 环境
说明:
rpmdevtools并不是所有 Debian/Ubuntu 环境都适用,本文教程以 RPM 系系统为主。
5. 初始化 rpmbuild 目录结构
执行:
rpmdev-setuptree默认会在当前用户家目录下生成:
~/rpmbuild/ ├── BUILD/ # 解压源码、执行编译的工作目录 ├── BUILDROOT/ # 模拟安装根目录 ├── RPMS/ # 生成的二进制 RPM 包 ├── SOURCES/ # 源码包、补丁、配置源文件 ├── SPECS/ # .spec 文件 └── SRPMS/ # 生成的源码 RPM 包后续最常打交道的是:
SOURCES/SPECS/RPMS/BUILDROOT/
6.%{buildroot}是什么
%{buildroot}是RPM 构建时的虚拟根目录,用来模拟真实系统的/。
你在%install阶段不能直接把文件安装到真实系统路径,而是必须先装进%{buildroot},最后再由 rpmbuild 打包。
例如:
%install mkdir -p %{buildroot}/usr/bin install -m 0755 myapp %{buildroot}/usr/bin/这表示:
- 构建阶段先把
myapp放到临时根目录里 - 最终用户安装 RPM 后,它才会出现在真实系统的
/usr/bin/myapp
为什么一定要这样做
- 避免构建过程污染当前系统
- 可以重复构建和检查包内容
- 能清楚区分“构建机环境”和“安装目标环境”
常见错误
错误写法:
install -m 0755 myapp /usr/bin/正确写法:
install -m 0755 myapp %{buildroot}/usr/bin/7..spec文件是什么
.spec是 RPM 打包的核心控制文件。它定义:
- 包名、版本、依赖、描述
- 源码从哪里来
- 怎么解压、怎么编译、怎么安装
- 最终打哪些文件进包
- 安装和卸载时需要执行哪些脚本
可以把它理解成 RPM 的“打包配方”。
8..spec文件基本结构
一个最小化的.spec文件通常长这样:
Name: hello-kernel Version: 1.0 Release: 1%{?dist} Summary: A dummy package for learning rpmbuild License: GPL-3.0-or-later Source0: %{name}-%{version}.tar.gz BuildArch: noarch %description A dummy package for learning rpmbuild. %prep %setup -q %build : %install mkdir -p %{buildroot}/usr/share/hello printf 'Hello from kernel!\n' > %{buildroot}/usr/share/hello/message %files /usr/share/hello/message %changelog * Sun May 25 2026 Your Name <you@example.com> - 1.0-1 - Initial package9..spec中最重要的字段
9.1 头部字段
| 字段 | 作用 |
|---|---|
Name | 包名 |
Version | 软件版本 |
Release | 打包发布号 |
Summary | 简短描述 |
License | 许可证 |
URL | 项目主页,可选 |
Source0 | 源码压缩包 |
BuildRequires | 构建依赖 |
Requires | 运行依赖 |
BuildArch | 架构,如x86_64、noarch |
9.2 常见区段
| 区段 | 作用 |
|---|---|
%description | 详细描述 |
%prep | 解压源码、打补丁 |
%build | 编译 |
%install | 安装到%{buildroot} |
%files | 声明最终进包的文件 |
%pre | 安装前脚本 |
%post | 安装后脚本 |
%preun | 卸载前脚本 |
%postun | 卸载后脚本 |
%changelog | 变更记录 |
10. rpmbuild 的标准构建流程
rpmbuild 一般遵循下面这条主线:
准备源码 → %prep → %build → %install → %files 校验 → 生成 RPM10.1%prep
负责准备源码,一般会解压Source0:
%prep %setup -q -n %{name}-%{version}其中:
-q:安静模式-n:指定解压后的顶层目录名
10.2%build
如果需要编译,就在这里执行编译命令:
%build gcc -g -O2 -Wall -o myprogram src/myprogram.c如果是不需要编译的纯脚本/纯配置包,也可以留空或写:
%build :10.3%install
把产物安装到%{buildroot}:
%install mkdir -p %{buildroot}%{_bindir} install -m 0755 myprogram %{buildroot}%{_bindir}/10.4%files
列出最终打包的路径:
%files %{_bindir}/myprogram11. 可以编译,也可以不编译
这是很多人最容易混淆的点。
rpmbuild 只负责执行 spec 里的步骤,并不强制要求你一定要编译。
11.1 场景一:源码编译型打包
适合:
- C/C++ 程序
- 需要针对目标平台构建的软件
- 从源码生成二进制
示例:
%build make %{?_smp_mflags} %install make install DESTDIR=%{buildroot}11.2 场景二:预编译/脚本型打包
适合:
- shell/python/perl 脚本
- 配置文件包
- 已提前在 CI 中产出的二进制
- 闭源程序分发
示例:
%build : %install mkdir -p %{buildroot}%{_bindir} install -m 0755 myapp %{buildroot}%{_bindir}/12. 最小可运行示例:打一个最简单的 RPM 包
12.1 准备源码目录
cd~mkdir-phello-kernel-1.0echo'Hello from kernel!'>hello-kernel-1.0/message12.2 制作源码包
tar-czf~/rpmbuild/SOURCES/hello-kernel-1.0.tar.gz-C~ hello-kernel-1.0注意这里的压缩包名和顶层目录名要匹配:
- 压缩包:
hello-kernel-1.0.tar.gz - 顶层目录:
hello-kernel-1.0/
12.3 编写 spec 文件
保存到~/rpmbuild/SPECS/hello-kernel.spec:
Name: hello-kernel Version: 1.0 Release: 1%{?dist} Summary: A dummy package for learning rpmbuild License: GPL-3.0-or-later Source0: %{name}-%{version}.tar.gz BuildArch: noarch %description A dummy package for learning rpmbuild. %prep %setup -q %build : %install mkdir -p %{buildroot}/usr/share/hello install -m 0644 message %{buildroot}/usr/share/hello/message %files /usr/share/hello/message %changelog * Sun May 25 2026 Your Name <you@example.com> - 1.0-1 - Initial package12.4 开始构建
rpmbuild-bb~/rpmbuild/SPECS/hello-kernel.spec12.5 成功后产物位置
~/rpmbuild/RPMS/noarch/hello-kernel-1.0-1.noarch.rpm修正说明:如果
BuildArch: noarch,最终包名通常是noarch.rpm,不是x86_64.rpm。
12.6 安装验证
sudodnfinstall-y~/rpmbuild/RPMS/noarch/hello-kernel-1.0-1.noarch.rpmcat/usr/share/hello/message13. 常用 rpmbuild 命令
13.1 构建二进制包
rpmbuild-bbyour-package.spec13.2 构建源码包
rpmbuild-bsyour-package.spec13.3 同时构建源码包和二进制包
rpmbuild-bayour-package.spec13.4 查看已安装包信息
rpm-qiyour-package-namerpm-qlyour-package-namerpm-q--scriptsyour-package-name13.5 查询某个文件属于哪个包
rpm-qf/path/to/file13.6 查看 rpm 包内容但不安装
rpm-qplyour-package.rpmrpm-qpiyour-package.rpm14. 常用宏
| 宏 | 含义 | 常见值 |
|---|---|---|
%{_topdir} | rpmbuild 根目录 | ~/rpmbuild |
%{_sourcedir} | 源文件目录 | ~/rpmbuild/SOURCES |
%{_specdir} | spec 目录 | ~/rpmbuild/SPECS |
%{_builddir} | 编译目录 | ~/rpmbuild/BUILD |
%{_buildrootdir} | buildroot 根目录 | ~/rpmbuild/BUILDROOT |
%{buildroot} | 当前包的临时安装根目录 | 动态生成 |
%{_bindir} | 可执行文件目录 | /usr/bin |
%{_sbindir} | 管理命令目录 | /usr/sbin |
%{_unitdir} | systemd unit 目录 | 通常是/usr/lib/systemd/system |
%{_sysconfdir} | 配置目录 | /etc |
%{_datadir} | 数据目录 | /usr/share |
建议优先写宏,不要硬编码路径。
例如:
install -m 0755 myapp %{buildroot}%{_bindir}/ install -m 0644 myapp.service %{buildroot}%{_unitdir}/15. RPM 安装过程到底发生了什么
执行下面命令时:
sudorpm-ivhpkg.rpm或:
sudodnfinstall./pkg.rpm大致流程如下:
1. 读取 RPM 头部信息 2. 检查架构、依赖、签名 3. 执行 %pre 4. 解包并写入文件系统 5. 处理 %config 配置文件策略 6. 更新 RPM 数据库 7. 执行 %post如果是卸载:
%preun → 删除文件 → %postun16. 安装/升级/卸载脚本怎么写
服务类软件经常会用到脚本区段。
16.1 安装后脚本%post
%post if [ $1 -eq 1 ]; then systemctl daemon-reload systemctl enable myapp.service >/dev/null 2>&1 || : systemctl start myapp.service >/dev/null 2>&1 || : fi16.2 卸载后脚本%postun
%postun if [ $1 -eq 0 ]; then systemctl stop myapp.service >/dev/null 2>&1 || : systemctl disable myapp.service >/dev/null 2>&1 || : systemctl daemon-reload fi16.3$1的常见含义
不同脚本阶段对$1的语义会有差异,但在实际打包里,最常见判断方式是:
$1 -eq 1:通常表示安装后首次配置场景$1 -eq 0:通常表示彻底卸载场景
实际项目里如果脚本逻辑比较敏感,建议结合发行版文档和测试结果确认。
17. 配置文件与日志文件要区别处理
17.1 配置文件
如果文件允许用户改动,建议使用:
%files %config(noreplace) /etc/myapp/myapp.conf这样升级时:
- 用户改过的配置尽量保留
- 不会被新包直接覆盖
17.2 日志文件
日志文件一般不建议直接作为普通静态文件打进包,更常见做法是:
- 由程序自己创建
- 或通过
tmpfiles.d、logrotate、首次启动逻辑创建
如果只是教学演示,也可以在%post中用touch创建:
%post install -d -m 0755 /var/log/myapp >/dev/null 2>&1 || : touch /var/log/myapp/myapp.log || : chmod 0644 /var/log/myapp/myapp.log || :说明:生产环境更推荐让应用本身或日志系统接管日志文件,而不是在
%files里长期维护一个空日志文件。
18. 实战示例:把一个 systemd 服务打成 RPM
下面用你文档里的sdet-monitor作为完整示例。
目标:
- 安装一个监控程序
det-monitor - 安装对应的 systemd 服务
- 安装后自动启用并启动服务
- 卸载时自动停止并禁用服务
19. 项目目录建议
先准备项目目录:
mkdir-p~/det-monitor/{src,systemd}cd~/det-monitor目录结构建议如下:
det-monitor/ ├── src/ │ └── det-monitor.c └── systemd/ └── det-monitor.service修正说明:源码压缩包里通常不必再额外塞一个
rpm/目录,只要最终把 spec 复制到~/rpmbuild/SPECS/即可。
20. 编写 systemd 服务文件
文件:systemd/det-monitor.service
[Unit] Description=Monitor for det command execution After=network.target [Service] Type=simple ExecStart=/usr/bin/det-monitor Restart=always RestartSec=5 User=root Group=root StandardOutput=journal StandardError=journal Nice=19 IOSchedulingClass=idle [Install] WantedBy=multi-user.target说明
Restart=always:异常退出后自动重启RestartSec=5:5 秒后重启Nice=19:降低 CPU 调度优先级IOSchedulingClass=idle:尽量减少 I/O 干扰
如果程序不必须以 root 运行,生产环境建议尽量使用专用低权限用户。
21. 编写服务类软件的 spec 文件
文件:~/rpmbuild/SPECS/det-monitor.spec
Name: det-monitor Version: 1.0 Release: 1%{?dist} Summary: Monitor for execution of det command License: GPL-3.0-or-later Source0: %{name}-%{version}.tar.gz BuildRequires: gcc, systemd Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %description This service monitors the execution of the det command. %prep %setup -q -n %{name}-%{version} %build gcc -g -O2 -Wall -o det-monitor src/det-monitor.c %install rm -rf %{buildroot} install -d %{buildroot}%{_bindir} install -d %{buildroot}%{_unitdir} install -m 0755 det-monitor %{buildroot}%{_bindir}/det-monitor install -m 0644 systemd/det-monitor.service %{buildroot}%{_unitdir}/det-monitor.service %files %{_bindir}/det-monitor %{_unitdir}/det-monitor.service %post if [ $1 -eq 1 ]; then systemctl daemon-reload >/dev/null 2>&1 || : systemctl enable det-monitor.service >/dev/null 2>&1 || : systemctl start det-monitor.service >/dev/null 2>&1 || : fi %preun if [ $1 -eq 0 ]; then systemctl stop det-monitor.service >/dev/null 2>&1 || : systemctl disable det-monitor.service >/dev/null 2>&1 || : fi %postun systemctl daemon-reload >/dev/null 2>&1 || : %changelog * Sun May 25 2026 Your Name <you@example.com> - 1.0-1 - Initial package这个版本相比原文修正了什么
- 去掉了把日志文件直接塞进
%files的做法 - 对 systemd 脚本依赖加了更清晰的
Requires(post/preun/postun) - 使用
%{_bindir}、%{_unitdir}代替硬编码路径 - 在
%install前显式rm -rf %{buildroot},避免脏目录残留 - 将停止/禁用服务放进
%preun,把daemon-reload放进%postun,更符合常见实践
22. 制作标准源码包
源码包必须满足两个要求:
- 压缩包文件名与
Source0对应 - 解压后的顶层目录名与
%setup期望一致
在项目目录执行:
cd~/det-monitormkdir-pdet-monitor-1.0cp-asrc systemd det-monitor-1.0/tar-czf~/rpmbuild/SOURCES/det-monitor-1.0.tar.gz det-monitor-1.0rm-rfdet-monitor-1.0也可以先检查压缩包内容:
tar-tf~/rpmbuild/SOURCES/det-monitor-1.0.tar.gz|head你应该看到类似:
det-monitor-1.0/ det-monitor-1.0/src/ det-monitor-1.0/src/det-monitor.c det-monitor-1.0/systemd/ det-monitor-1.0/systemd/det-monitor.service23. 构建 RPM 包
执行:
rpmbuild-ba~/rpmbuild/SPECS/det-monitor.spec成功后通常会生成:
~/rpmbuild/RPMS/x86_64/det-monitor-1.0-1.x86_64.rpm ~/rpmbuild/SRPMS/det-monitor-1.0-1.src.rpm注意:这里是
x86_64还是aarch64,取决于你的构建机架构以及包本身是否声明为noarch。
24. 安装与验证
24.1 安装
sudodnfinstall-y~/rpmbuild/RPMS/*/det-monitor-1.0-1.*.rpm也可以用:
sudorpm-ivh~/rpmbuild/RPMS/*/det-monitor-1.0-1.*.rpm24.2 检查服务状态
systemctl status det-monitor.service systemctl is-enabled det-monitor.service预期:
- 状态为
active (running)或至少已成功启动 is-enabled输出enabled
24.3 查看包内容
rpm-qldet-monitorrpm-q--scriptsdet-monitor25. 卸载与验证
卸载:
sudorpm-edet-monitor或:
sudodnf remove-ydet-monitor验证:
rpm-qdet-monitor systemctl status det-monitor.servicels-l/usr/bin/det-monitorls-l/usr/lib/systemd/system/det-monitor.service预期:
- 包查询不到
- 服务单元文件已移除
- 可执行文件已移除
26. 常见错误与排查方法
26.1File not found或%files报错
原因通常是:
%install没把文件放进%{buildroot}%files路径写错- 目标文件名和实际文件名不一致
检查思路:
find~/rpmbuild/BUILDROOT-typef|sort26.2%prep阶段解压失败
常见原因:
Source0文件名不匹配- 压缩包顶层目录名与
%setup -n ...不一致
26.3 构建依赖缺失
比如:
error: Failed build dependencies: gcc is needed by xxx处理:
sudodnfinstall-ygccmakesystemd-rpm-macros26.4 服务安装后没有启动
检查:
systemctl status det-monitor.service journalctl-udet-monitor.service-xerpm-q--scriptsdet-monitor26.5 日志或配置文件被覆盖
检查是否正确使用:
%config(noreplace)- 应用自身日志目录策略
- 是否误把运行时文件直接打进
%files
27. 调试技巧
27.1 检查 spec 质量
rpmlint ~/rpmbuild/SPECS/det-monitor.spec rpmlint ~/rpmbuild/RPMS/*/det-monitor-*.rpm27.2 查看构建目录
ls-l~/rpmbuild/BUILD/ls-l~/rpmbuild/BUILDROOT/27.3 查看最终 buildroot 内容
tree ~/rpmbuild/BUILDROOT27.4 查看包内文件列表
rpm-qpl~/rpmbuild/RPMS/*/det-monitor-*.rpm27.5 查看安装/卸载脚本
rpm-qp--scripts~/rpmbuild/RPMS/*/det-monitor-*.rpm28. 最佳实践
28.1 压缩包命名规范
推荐:
<name>-<version>.tar.gz例如:
det-monitor-1.0.tar.gz28.2 顶层目录名保持一致
例如压缩包内应是:
det-monitor-1.0/28.3 能用宏就用宏
不要硬编码:
/usr/bin /usr/lib/systemd/system推荐:
%{_bindir} %{_unitdir}28.4 不要在%install中改真实系统
所有写入都应进入%{buildroot}。
28.5 区分构建依赖和运行依赖
BuildRequires:编译时依赖Requires:安装运行时依赖
28.6 尽量不用 root 做构建
构建阶段通常应在普通用户下执行,安装阶段再使用sudo。
28.7 服务包优先考虑 systemd 生命周期
至少要考虑:
- 安装后
daemon-reload - 首次安装后
enable/start - 卸载前
stop/disable - 卸载后
daemon-reload
29. 一份从零到打包成功的最短操作清单
如果你只想快速上手,按下面执行即可。
29.1 初始化环境
sudodnfinstall-yrpm-build rpmdevtools rpmlint rpmdev-setuptree29.2 准备源码包
mkdir-p~/demo-1.0echo'hello rpm'>~/demo-1.0/READMEtar-czf~/rpmbuild/SOURCES/demo-1.0.tar.gz-C~ demo-1.029.3 写 spec
Name: demo Version: 1.0 Release: 1%{?dist} Summary: Simple demo rpm License: MIT Source0: %{name}-%{version}.tar.gz BuildArch: noarch %description Simple demo rpm. %prep %setup -q %build : %install mkdir -p %{buildroot}/usr/share/demo install -m 0644 README %{buildroot}/usr/share/demo/README %files /usr/share/demo/README保存到:
~/rpmbuild/SPECS/demo.spec29.4 构建
rpmbuild-bb~/rpmbuild/SPECS/demo.spec29.5 安装验证
sudodnfinstall-y~/rpmbuild/RPMS/noarch/demo-1.0-1.noarch.rpmrpm-qldemo30. 总结
你可以把 RPM 打包记成三句话:
- **源码、脚本、服务文件先准备好,放进 **
SOURCES/ - 用
.spec明确描述“怎么解压、怎么编译、怎么安装、打哪些文件” - 用
rpmbuild构建,再用rpm/dnf安装验证
真正掌握 rpmbuild,关键不是死记命令,而是理解下面这条链路:
Source0 → %prep → %build → %install → %{buildroot} → %files → RPM 包 → rpm/dnf 安装