SPEC 基础知识

写 RPM 包的核心,不是命令多复杂,而是把“构建过程”和“最终安装内容”写清楚。

这篇文章以 rpmbuild 为主线,整理一份可直接落地的 SPEC 入门指南。

1. SPEC 文件是什么

SPEC 是 RPM 的打包脚本与元数据描述文件。
它决定了:

  1. 包叫什么、版本是多少。
  2. 构建时需要什么依赖。
  3. 代码如何编译、安装到临时目录。
  4. 最终哪些文件进 RPM 包。

可以理解成:元信息 + 构建脚本 + 打包清单

2. 最小可用 SPEC 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Name:           hello
Version: 1.0.0
Release: 1%{?dist}
Summary: A simple hello package
License: MIT
URL: https://example.com/hello
Source0: %{name}-%{version}.tar.gz

BuildRequires: gcc, make
Requires: bash

%description
A simple hello package for RPM packaging demo.

%prep
%autosetup

%build
%configure
make %{?_smp_mflags}

%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}

%files
%license LICENSE
%doc README.md
%{_bindir}/hello

%changelog
* Tue Mar 24 2026 Your Name <you@example.com> - 1.0.0-1
- Initial package

如果你刚接触 SPEC,先把这个模板跑通,再做高级改造。

3. 头部字段(Header)

常见核心字段:

  1. Name:包名。
  2. Version:上游软件版本。
  3. Release:打包发布号(同一版本改 SPEC 通常加这个)。
  4. Summary:一句话描述。
  5. License:许可证。
  6. Source0:源码包。
  7. BuildRequires:构建依赖。
  8. Requires:运行依赖。

说明:

  1. VersionRelease 组合决定升级比较顺序。
  2. Release 常用 1%{?dist},便于不同发行版区分。

4. 各阶段脚本做什么

4.1. %prep

准备源码,解压、打补丁。

  1. 现代写法优先 %autosetup
  2. 需要补丁时在头部加 Patch0Patch1,再由 %autosetup%patch 应用。

4.2. %build

执行编译,常见是:

  1. ./configure / %configure
  2. make %{?_smp_mflags}

%{?_smp_mflags} 通常会展开成 -jN,用于并行编译。

4.3. %install

把产物安装到 虚拟根目录 %{buildroot},不是直接装系统。

典型模式:

1
2
3
%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}

4.4. %files

声明“最终打包进 RPM 的文件”。

注意:

  1. 这里写的是最终系统路径(如 %{_bindir}/hello)。
  2. %install 里必须先把这些文件放进 %{buildroot} 对应位置。

5. %files 常用标记

  1. %doc:文档文件。
  2. %license:许可证文件。
  3. %config:配置文件(升级可能覆盖,视场景)。
  4. %config(noreplace):配置文件升级时尽量不覆盖本地修改,生成 .rpmnew
  5. %dir:声明目录。
  6. %attr(mode,user,group):指定权限和属主属组。

一个常见示例:

1
2
3
4
5
6
%files
%license LICENSE
%doc README.md
%config(noreplace) %{_sysconfdir}/hello/hello.conf
%{_bindir}/hello
%{_unitdir}/hello.service

6. 脚本钩子(可选)

需要安装/卸载动作时可用:

  1. %pre:安装前。
  2. %post:安装后。
  3. %preun:卸载前。
  4. %postun:卸载后。
  5. %posttrans:事务完成后。

示例(systemd 服务):

1
2
3
4
5
6
7
8
%post
%systemd_post hello.service

%preun
%systemd_preun hello.service

%postun
%systemd_postun_with_restart hello.service

没必要时不要加脚本钩子,保持简单。

7. 宏与路径(高频)

常用宏:

  1. %{_bindir}/usr/bin
  2. %{_sbindir}/usr/sbin
  3. %{_libdir}/usr/lib64(64 位常见)
  4. %{_sysconfdir}/etc
  5. %{_unitdir}:systemd unit 目录(发行版相关)
  6. %{buildroot}:打包虚拟根
  7. %{?_smp_mflags}:并行编译参数

查看宏值:

1
2
rpm --eval '%{_bindir}'
rpm --eval '%{buildroot}'

8. BuildRequires 与 Requires 的区别

  1. BuildRequires:只在构建机需要。
  2. Requires:用户安装包时需要。

典型误区:

  1. 把构建工具误写进 Requires
  2. 漏掉运行时依赖,导致安装成功但运行失败。

9. 从 0 到 1 的打包流程

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1) 准备目录(通常是 ~/rpmbuild)
rpmdev-setuptree

# 2) 放源码和 spec
# SOURCES: hello-1.0.0.tar.gz
# SPECS: hello.spec

# 3) 构建二进制包和源码包
rpmbuild -ba ~/rpmbuild/SPECS/hello.spec

# 4) 查看产物
ls ~/rpmbuild/RPMS
ls ~/rpmbuild/SRPMS

验证:

1
2
3
rpm -qpi xxx.rpm      # 看包头信息
rpm -qpl xxx.rpm # 看文件列表
rpm --scripts -qp xxx.rpm # 看脚本钩子

10. 常见报错与排查

  1. File not found by glob%files 写了不存在的路径,先检查 %install 是否真的安装到 %{buildroot}
  2. Installed (but unpackaged) file(s) found:有文件进了 %{buildroot} 但没在 %files 声明。
  3. 依赖报错:区分是 BuildRequires 缺失还是 Requires 缺失。
  4. 宏路径不一致:用 rpm --eval 先确认宏值,再写路径。

11. 我的建议

  1. 先保证“能稳定构建 + 文件列表准确”,再做宏优化。
  2. %install%files 要一一对应,这是 SPEC 最容易踩坑的地方。
  3. 先写最小模板跑通,再逐步引入 %config、脚本钩子和子包拆分。

当你能稳定回答“这个文件为什么会进包、为什么会装到这个目录”,SPEC 就算入门了。