引言
RPM(Red Hat Package Manager)文件是一种用于Linux系统的软件包管理格式,主要用于基于Red Hat的Linux发行版,如Fedora、CentOS和Redhat等。一个RPM软件包实际上是一个包含了已编译的软件以及有关该软件的元数据(如名称、版本号、依赖关系等)的压缩归档文件。
目录
一、环境准备
RPM 包构建需要依赖rpmdevtools工具包,安装命令如下。
# 安装必要工具
sudo yum install rpmdevtools rpmlint rpmdev-setuptree
rpmdevtools
:一个工具包,包含多个命令,帮你方便地制作 RPM 软件包。其中就包括rpmdev-setuptree
。rpmdev-setuptree
:是rpmdevtools
里的一个命令,运行它会自动在你家目录下创建好打包所需的文件夹结构(如~/rpmbuild/SPECS
,~/rpmbuild/SOURCES
等)。rpmlint
:一个检查工具。用来检查你的 RPM 包或.spec
文件有没有错误或不符合规范的地方,确保打包质量。
二、RPM文件的组成
1、根据RPM的目录结构分类
~/rpmbuild/
├── BUILD/ # %prep 阶段会在这里解压源码,%build 和 %install 阶段也在此目录操作
├── BUILDROOT/ # 临时安装根目录,%install 阶段会把编译好的文件“安装”到这里RPM 打包时从这收集
├── RPMS/ # 存放最终生成的 二进制 RPM 包,通常按架构(如 x86_64, aarch64)分目录存放
├── SOURCES/ # 存放所有原始材料:源码压缩包(如 .tar.gz)、补丁文件、图标、配置文件等
├── SPECS/ # 存放 .spec 文件,这是 RPM 构建的“配方”,定义了如何准备、编译、安装和打包软件
└── SRPMS/ # 存放生成的源码 RPM 包(.src.rpm)
2、根据RPM文件内容分类
1)文件系统布局文件 (Payload)
二进制可执行文件:/usr/bin/,/usr/local/bin等
依赖库文件:/usr/lib/,/usr/lib64
配置文件:/etc/ (带有%config标记)
文档文件:/usr/share/doc/
元数据:SPEC文件、changelog、依赖信息
脚本钩子:pre/post install/uninstall脚本
2)元数据 (Metadata):
SPEC 文件: 这是构建 RPM 的“菜谱”,包含了构建指令、文件列表、依赖关系、脚本、变更日志等所有信息。
依赖信息:
Requires: 该 RPM 包运行时依赖的其他包(或库、文件等)。
Provides: 该 RPM 包提供的能力(通常是包名本身,但也可能是虚拟能力如
webserver
或库的soname
)。Conflicts: 与该 RPM 包冲突不能同时安装的其他包。
Obsoletes: 该 RPM 包会取代(并通常卸载)的其他旧包。
BuildRequires: 构建该 RPM 包所需的依赖(编译器、库等)。
变更日志 (
%changelog
): 记录包版本更新历史的条目。这个信息会包含在生成的.rpm
文件中,可以用rpm -q --changelog
查看。其他元数据: 包名、版本号、发行号 (Release)、架构 (Arch)、摘要 (Summary)、描述 (Description)、打包者 (Packager)、供应商 (Vendor)、许可证 (License)、URL 等。这些信息都存储在 RPM 头部 (Header) 中。
3)脚本钩子 (Scriptlets):
%pre
: 在安装包之前运行的脚本。%post
: 在安装包之后运行的脚本(常用于更新ldconfig
, 初始化数据库,启动服务)。%preun
: 在卸载包之前运行的脚本(常用于优雅地停止服务)。%postun
: 在卸载包之后运行的脚本(常用于清理ldconfig
缓存,删除空目录)。%pretrans
/%posttrans
: 在事务(可能涉及多个包的安装/卸载)开始前/结束后运行的脚本(更复杂,较少用)。
rpmbuild
目录是“厨房”,SPEC
是“菜谱”,SOURCES
是“食材”,RPMS
是“做好的菜”,而 RPM 包本身是“带标签的密封餐盒”,里面装着菜(文件)和说明书(元数据)。
三、SPEC文件
SPEC 文件是创建自定义RPM包的基础。它是一个文本文件,定义了如何从源代码构建RPM包。包括了软件的名称、版本、发布信息、构建指令、安装指令、描述、依赖关系等信息。
SPEC 文件是源码,不是 RPM 二进制包的一部分。它本身并不包含在生成的 .rpm
文件中。.rpm
文件包含的是 SPEC 文件编译后的结果。
1、SPEC 文件结构
# 基础元数据 (Basic Metadata)
Name: package-name # 包名(全小写,无空格)
Version: 1.0.0 # 上游版本号
Release: 1%{?dist} # 打包版本号(每次打包递增)
Summary: 单行描述 # 30字以内摘要
License: GPLv3+ # 许可证(SPDX格式)
# 高级元数据 (Advanced Metadata)
URL: https://project.org # 项目主页
Vendor: Company Name # 供应商信息
Group: Development/Tools # 分类(兼容旧系统)
Source0: %{name}-%{version}.tar.gz # 源码URL/路径
Patch1: fix-security.patch # 补丁文件
2、SPEC 文件语法
宏变量:
%{name}
,%{version}
自动填充%{?dist}
处理发行版后缀%{_bindir}
,%{_libdir}
等路径宏
构建阶段:
%prep
:准备源码%build
:编译源码%install
:安装到构建根目录%check
:运行测试
脚本钩子:
%pre
/%post
:安装前后脚本%preun
/%postun
:卸载前后脚本%posttrans
:事务后操作
多包支持:
使用
%package
定义子包%description -n subpackage
子包描述%files -n subpackage
子包文件列表
3、SPEC模板创建
- 通用模板
rpmdev-newspec -o ~/rpmbuild/SPECS/myapp.spec
- 标准 C/C++ 项目模板
rpmdev-newspec -t lib ~/rpmbuild/SPECS/mylibrary.spec
- python项目模板
rpmdev-newspec -t python ~/rpmbuild/SPECS/mypythonapp.spec
- systemd服务模板
rpmdev-newspec -t service ~/rpmbuild/SPECS/mydaemon.spec
4、SPEC文件示例
# ===================================================================
# RPM SPEC 文件通用模板 (优化版)
# 适用于大多数基于源码构建的软件包
# 遵循 Fedora 打包规范和最佳实践
# 注意:根据实际项目替换占位符(如 %{name}, %{version} 等)
# ===================================================================
%global _enable_debug_package 0 # 禁用 debuginfo 包(按需开启)
%global _hardened_build 1 # 启用安全加固编译
Name: your-package-name
Version: 1.0.0
Release: 1%{?dist}
Summary: A brief description of the package (少于 80 字符)
# 使用 SPDX 许可证标识符 (https://spdx.org/licenses/)
License: MIT
URL: https://github.com/yourusername/your-package-name
Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz
# 构建架构(默认自动检测,noarch 适用于脚本/解释型语言)
# BuildArch: noarch
# 构建依赖(使用宏确保兼容性)
BuildRequires: gcc
BuildRequires: make
BuildRequires: pkgconfig
BuildRequires: systemd-rpm-macros
BuildRequires: %{?_python:python%{python_version}-devel} # Python 项目示例
# 运行时依赖(自动依赖检测 + 显式声明)
Requires: bash >= 4.2
Requires: coreutils
Requires: systemd
# 提供虚拟能力(如需要)
Provides: bundled(openssl) = 1.1.1k # 示例:声明捆绑的库
Provides: %{name}-cli = %{version} # 子包提供
%description
A longer description of the package.
Explain what it does, who it's for, and any special features.
This can span multiple lines.
%prep
%autosetup -n %{name}-%{version} -p1 # 自动处理源码和补丁
# 示例补丁应用:
# %patch0 -p1 -b .buildfix # 应用补丁并备份原文件
%build
%set_build_flags # 设置标准构建标志(CFLAGS/LDFLAGS)
# === 根据构建系统选择 ===
# 1. Autotools 项目
%configure \
--disable-static \ # 推荐禁用静态库
--enable-shared \
--with-systemd \
--without-debug
make %{?_smp_mflags} VERBOSE=1
# 2. CMake 项目
# %cmake \
# -DCMAKE_BUILD_TYPE=Release \
# -DENABLE_TESTS=OFF \
# -DINSTALL_DOCS=ON
#
# %cmake_build %{?_smp_mflags}
# 3. 纯 Makefile 项目
# make %{?_smp_mflags} \
# CC="%{__cc}" \
# CFLAGS="%{optflags}" \
# LDFLAGS="%{__global_ldflags}"
%install
# 清理并创建必要目录
rm -rf %{buildroot}
%make_install # 适用于 make install 项目
# === 手动安装示例 ===
# 1. 安装可执行文件
install -d -m 0755 %{buildroot}%{_bindir}
install -m 0755 %{name} %{buildroot}%{_bindir}/
# 2. 配置文件 (noreplace 防止覆盖用户修改)
install -d -m 0755 %{buildroot}%{_sysconfdir}/%{name}
install -m 0644 config.conf %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf
# 3. Systemd 服务文件
install -d -m 0755 %{buildroot}%{_unitdir}
install -m 0644 %{name}.service %{buildroot}%{_unitdir}/
# 4. 文档和许可证
%license LICENSE
%doc README.md CHANGELOG.md CONTRIBUTING.md
# 5. 创建运行时目录
install -d -m 0755 %{buildroot}%{_sharedstatedir}/%{name}
install -d -m 0755 %{buildroot}%{_localstatedir}/log/%{name}
%check
# 构建后测试(确保 BuildRequires 包含测试依赖)
# make test
# 或 ctest -V
# 或 pytest
%post
# 系统用户/组管理
getent group %{name} >/dev/null || groupadd -r %{name}
getent passwd %{name} >/dev/null || \
useradd -r -g %{name} -d %{_sharedstatedir}/%{name} \
-s /sbin/nologin -c "%{name} service user" %{name}
# 设置目录权限
chown -R %{name}:%{name} %{_sharedstatedir}/%{name}
chown -R %{name}:%{name} %{_localstatedir}/log/%{name}
# 服务管理
%systemd_post %{name}.service
%preun
%systemd_preun %{name}.service
%postun
%systemd_postun_with_restart %{name}.service
%posttrans
# 数据库迁移等事务后操作
if [ $1 -eq 1 ] || [ $1 -ge 2 ]; then # 安装或升级后
/usr/bin/%{name}-db-migrate
fi
%files -f %{name}.lang # 如果有多语言文件
%license LICENSE
%doc README.md CHANGELOG.md
%dir %{_sysconfdir}/%{name} # 配置目录
%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf # 配置文件
%{_bindir}/%{name} # 主程序
%{_unitdir}/%{name}.service # systemd 服务
%{_mandir}/man1/%{name}.1.gz # 手册页
%dir %{_sharedstatedir}/%{name} # 数据目录
%ghost %{_localstatedir}/log/%{name} # 日志目录(不打包内容)
# === 子包示例 ===
%package devel
Summary: Development files for %{name}
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
Headers and libraries for developing applications that use %{name}
%files devel
%{_includedir}/%{name}.h
%{_libdir}/lib%{name}.so
%{_libdir}/pkgconfig/%{name}.pc
%changelog
* %(date +"%a %b %d %Y") Your Name <you@example.com> - 1.0.0-1
- 初始版本打包
# 使用自动化工具生成 changelog:
# rpmdev-bumpspec --comment="更新说明" --user="Your Name <email>" SPECS/%{name}.spec
四、完整打包流程
这里为了方便, 通过一个具体的例子演示。
开发环境:Linux Centos 6.12.0-105.el10.x86_64 GNU/Linux
1、准备自己的布局文件
1)新建一个工程文件夹,如my_test。
mkdir my_test
2)创建一个myTest.c文件,内容为打印一下“hello world”。
//myTest.c文件
#include <stdio.h>
int main(){
printf("hello world!\n");
return 0;
}
3)为myTest.c写一个Makefile用来编译
#Makefile文件
myTest: myTest.c
gcc myTest.c -o myTest
clean:
rm myTest
4)测试验证程序
提示:打包前一定要测试基本程序的正确性,这是一个好习惯!
#编译及测试验证
[root@Centos my_test]# make
gcc myTest.c -o myTest
[root@Centos my_test]# ./myTest
hello world!
[root@Centos my_test]# make clean
rm myTest
[root@Centos my_test]# ls
Makefile myTest.c
2、创建RPM目录结构
#创建rpm目录结构
rpmdev-setuptree
#查看rpm目录结构
tree rpmbuild/
#显示结果
# rpmbuild/
# ├── BUILD
# ├── RPMS
# ├── SOURCES
# ├── SPECS
# └── SRPMS
3、将源文件放入SOURCES目录
#将工程文件压缩打包,并放进rpmbuild/SOURCES目录下
tar czvf ~/rpmbuild/SOURCES/my_test.tar.gz my_test
#结果显示:
#my_test/
#my_test/myTest.c
#my_test/Makefile
4、SPEC文件
1)直接生成一个通用SPEC模板
rpmdev-newspec -o ~/rpmbuild/SPECS/myTest.spec
#结果:/root/rpmbuild/SPECS/myTest.spec created; type minimal, rpm version >= 4.19.
2)修改SPEC文件
修改内容:Version、Source0路径、添加%files部分等;
修改后的完整SPEC文件内容:
# 禁用 debuginfo 包生成
%global debug_package %{nil}
Name: myTest
Version: 1.0.0
Release: 1%{?dist}
Summary: A simple C language test program
License: MIT
URL: https://github.com/yourusername/myTest
Source0: myTest-1.0.0.tar.gz
BuildRequires: gcc
%description
A simple C language test program.
%prep
#下面setup是解压压缩包, -n myTest-%{version}是指定解压后的文件夹名称, -q是不显示过程信息
%setup -n myTest-%{version} -q
%build
make
%install
# buildroot是搭建一个虚拟的文件系统目录, _bindir 等价 /usr/bin
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_bindir}
cp myTest %{buildroot}%{_bindir}/myTest
%files
%{_bindir}/myTest
%changelog
* Fri Aug 08 2025 Super User <user@example.com> - 1.0.0-1
- Initial build
5、生成RPM包
如果上面步骤都没问题,那么直接执行下面就会生成rpm包了。
rpmbuild -ba ~/rpmbuild/SPECS/myTest.spec
成功后会在RPMS下生成最终的目标RPM包。
[root@Centos ~]# tree rpmbuild/
#显示结果:
#rpmbuild/
#├── BUILD
#├── BUILDROOT
#├── RPMS
#│ └── x86_64
#│ └── myTest-1.0.0-1.el10.x86_64.rpm #这就是目标RPM包
#├── SOURCES
#│ └── myTest-1.0.0.tar.gz
#├── SPECS
#│ └── myTest.spec
#└── SRPMS
# └── myTest-1.0.0-1.el10.src.rpm
生成时如遇到问题时非常正常的,需要一步一步的调试优化,最终达到效果。
下一章会介绍一些调试和优化的方法。
6、测试验证
1)验证是否安装成功
[root@Centos ~]# rpm -ql myTest
#成功显示已安装的文件,如下:
#/usr/bin/myTest
#/usr/lib/.build-id
#/usr/lib/.build-id/3c
#/usr/lib/.build-id/3c/3412eaf27ce1deb57e230a1a4fb7c2191a945e
2)测试程序是否可以使用
[root@Centos ~]# myTest
hello world! #在任意目录下执行 myTest =, 都可以打印“hello world”
五、高级调试与优化
1、分步调试
rpmbuild
分步调试是排查打包问题的 最有效方法。通过逐步执行 .spec
文件中的各个阶段,你可以精准定位问题出在哪个环节。
RPM打包会按照SPEC文件的配置顺序执行,如果有错误会在终端提示。根据提示信息,我们可以使用对应的命令单步调试,修改SPEC阶段命令。
阶段 | 命令 | 说明 |
---|---|---|
%prep |
rpmbuild -bp |
准备:解压源码、打补丁 |
%build |
rpmbuild -bc |
编译:运行 make 等 |
%install |
rpmbuild -bi |
安装:复制文件到 BUILDROOT |
%check |
rpmbuild -bt |
测试(可选) |
打包 | rpmbuild -bb |
构建二进制 RPM |
全流程 | rpmbuild -ba |
从头到尾构建 |
2、依赖分析
# 检查未打包文件
find %{buildroot} -type f | grep -vFf <(rpm -qlp app.rpm)
# 依赖分析
rpm -qpR app.rpm # 查看依赖
3、构建问题诊断
# 详细构建日志
rpmbuild -ba --define='_verbose 10' package.spec 2>&1 | tee build.log
# 检查未打包文件
find %{buildroot} -type f | grep -vFf <(rpm -qlp package.rpm)
4、依赖分析工具
# 生成依赖图
rpm -qpR package.rpm | xargs rpmdep -o deps.dot
dot -Tpng deps.dot -o deps.png
# 或使用rpmlint自动检测缺失依赖
rpmlint -i package.spec
5、质量检查
# SPEC 文件检查
rpmlint SPECS/%{name}.spec
# RPM 包检查
rpmlint RPMS/x86_64/%{name}-*.rpm
6、性能优化技巧
# 并行编译
%define _smp_mflags -j%(nproc)
# 编译缓存
%define _ccache_path /usr/bin/ccache
export CCACHE_DIR="/var/cache/ccache"
六、应用场景
- 打包复杂的企业级应用
- 设计自动化的构建流水线
- 处理多架构兼容性问题
- 实现安全的软件分发流程
- 解决依赖地狱问题
参考
RPM 官方网站 https://rpm.org/
- man rpm