【Linux】从零开始:RPM 打包全流程实战万字指南(含目录结构、spec 编写、分步调试)

【Linux】从零开始:RPM 打包全流程实战万字指南(含目录结构、spec 编写、分步调试)

引言

RPM(Red Hat Package Manager)文件是一种用于Linux系统的软件包管理格式,主要用于基于Red Hat的Linux发行版,如Fedora、CentOS和Redhat等。一个RPM软件包实际上是一个包含了已编译的软件以及有关该软件的元数据(如名称、版本号、依赖关系等)的压缩归档文件。

目录

引言

一、环境准备

二、RPM文件的组成

1、根据RPM的目录结构分类

2、根据RPM文件内容分类

三、SPEC文件

[1、SPEC 文件结构](#1、SPEC 文件结构)

[2、SPEC 文件语法](#2、SPEC 文件语法)

3、SPEC模板创建

4、SPEC文件示例

四、完整打包流程

1、准备自己的布局文件

2、创建RPM目录结构

3、将源文件放入SOURCES目录

4、SPEC文件

5、生成RPM包

6、测试验证

五、高级调试与优化

1、分步调试

2、依赖分析

3、构建问题诊断

4、依赖分析工具

5、质量检查

6、性能优化技巧

六、应用场景

参考

一、环境准备

RPM 包构建需要依赖**rpmdevtools工具包,安装命令如下。**

bash

复制代码

# 安装必要工具

sudo yum install rpmdevtools rpmlint rpmdev-setuptree

rpmdevtools :一个工具包,包含多个命令,帮你方便地制作 RPM 软件包。其中就包括 rpmdev-setuptree。

rpmdev-setuptree :是 rpmdevtools 里的一个命令,运行它会自动在你家目录下创建好打包所需的文件夹结构(如 ~/rpmbuild/SPECS, ~/rpmbuild/SOURCES 等)。

rpmlint :一个检查工具。用来检查你的 RPM 包或 .spec 文件有没有错误或不符合规范的地方,确保打包质量。

二、RPM文件的组成

1、根据RPM的目录结构分类

bash

复制代码

~/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 文件结构

bash

复制代码

# 基础元数据 (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模板创建

通用模板

bash

复制代码

rpmdev-newspec -o ~/rpmbuild/SPECS/myapp.spec

标准 C/C++ 项目模板

bash

复制代码

rpmdev-newspec -t lib ~/rpmbuild/SPECS/mylibrary.spec

python项目模板

bash

复制代码

rpmdev-newspec -t python ~/rpmbuild/SPECS/mypythonapp.spec

systemd服务模板

bash

复制代码

rpmdev-newspec -t service ~/rpmbuild/SPECS/mydaemon.spec

4、SPEC文件示例

bash

复制代码

# ===================================================================

# 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 - 1.0.0-1

- 初始版本打包

# 使用自动化工具生成 changelog:

# rpmdev-bumpspec --comment="更新说明" --user="Your Name " SPECS/%{name}.spec

四、完整打包流程

这里为了方便, 通过一个具体的例子演示。

**开发环境:**Linux Centos 6.12.0-105.el10.x86_64 GNU/Linux

1、准备自己的布局文件

1)新建一个工程文件夹,如my_test。

bash

复制代码

mkdir my_test

2)创建一个myTest.c文件,内容为打印一下"hello world"。

cpp

复制代码

//myTest.c文件

#include

int main(){

printf("hello world!\n");

return 0;

}

3)为myTest.c写一个Makefile用来编译

bash

复制代码

#Makefile文件

myTest: myTest.c

gcc myTest.c -o myTest

clean:

rm myTest

4)测试验证程序

提示:打包前一定要测试基本程序的正确性,这是一个好习惯!

bash

复制代码

#编译及测试验证

[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目录结构

bash

复制代码

#创建rpm目录结构

rpmdev-setuptree

#查看rpm目录结构

tree rpmbuild/

#显示结果

# rpmbuild/

# ├── BUILD

# ├── RPMS

# ├── SOURCES

# ├── SPECS

# └── SRPMS

3、将源文件放入SOURCES目录

bash

复制代码

#将工程文件压缩打包,并放进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模板

bash

复制代码

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文件内容:

bash

复制代码

# 禁用 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 - 1.0.0-1

- Initial build

5、生成RPM包

如果上面步骤都没问题,那么直接执行下面就会生成rpm包了。

bash

复制代码

rpmbuild -ba ~/rpmbuild/SPECS/myTest.spec

成功后会在RPMS下生成最终的目标RPM包。

bash

复制代码

[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)验证是否安装成功

bash

复制代码

[root@Centos ~]# rpm -ql myTest

#成功显示已安装的文件,如下:

#/usr/bin/myTest

#/usr/lib/.build-id

#/usr/lib/.build-id/3c

#/usr/lib/.build-id/3c/3412eaf27ce1deb57e230a1a4fb7c2191a945e

2)测试程序是否可以使用

bash

复制代码

[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、依赖分析

bash

复制代码

# 检查未打包文件

find %{buildroot} -type f | grep -vFf <(rpm -qlp app.rpm)

# 依赖分析

rpm -qpR app.rpm # 查看依赖

3、构建问题诊断

bash

复制代码

# 详细构建日志

rpmbuild -ba --define='_verbose 10' package.spec 2>&1 | tee build.log

# 检查未打包文件

find %{buildroot} -type f | grep -vFf <(rpm -qlp package.rpm)

4、依赖分析工具

bash

复制代码

# 生成依赖图

rpm -qpR package.rpm | xargs rpmdep -o deps.dot

dot -Tpng deps.dot -o deps.png

# 或使用rpmlint自动检测缺失依赖

rpmlint -i package.spec

5、质量检查

bash

复制代码

​​​​​​​# 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