构建 RPM 包
1. RPM 简介
RPM (Resd HatPackage Manager ),顾名思义是 Red Hat 的软件包管理。RPM 可以让用户直接以 binary 方式安装软件包,并且可以在安装、更新和删除的时候自动解决软件包的依赖。
2. 构建 RPM 包
2.1. 安装工具
首先需要安装一些必要的打包工具。
yum install rpmdevtools
2.2. 创建工作空间
工作空间
是 RPM 存放源码、编译、打包的一个场所。可以通过以下命令来创建一套标准化的工作空间
。
rpmdev-setuptree
之后,就会得到这样这样一个目录
~/rpmbuild/
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
其中各个目录的作用及宏定义如下,宏定义将会在之后具体构建 RPM 包的时候,用来指代具体的路径。
路径 | 宏定义 | 说明 |
---|---|---|
~/rpmbuild/SPECS | %_specdir | 保存 RPM 包配置(.spec)文件 |
~/rpmbuild/SOURCES | %_sourcedir | 保存源码包(如 .tar 包)和所有 patch 补丁 |
~/rpmbuild/BUILD | %_builddir | 源码包被解压至此,并在该目录的子目录完成编译 |
~/rpmbuild/BUILDROOT | %_buildrootdir | 保存 %install 阶段安装的文件 |
~/rpmbuild/RPMS | %_rpmdir | 生成/保存二进制 RPM 包 |
~/rpmbuild/SRPMS | %_srcrpmdir | 生成/保存源码 RPM 包(SRPM) |
2.3. 创建 Spec 文件
有了工作空间
,将源码包和补丁文件等原材料放入 %_sourcedir
目录,便可以通过 spec 文件的指导,生成需要的 RPM 包。因此最为关键的就是如何根据需求编写一个 spec 文件。
3. Spec 文件组成
可以通过命令 rpmdev-newspec
来产生一个典型的 spec 模板。如下:
Name: lq-app
Version:
Release:
...
BuildRequires:
Requires:
%prep
%autosetup
%build
%configure
%make_build
%install
rm -rf $RPM_BUILD_ROOT
%make_install
%files
...
其中包含了构成 spec 文件的大部分条目,可以将其分为以下几种类型:
- 注释 -- 被 RPM 忽略的可读性说明
- 标签 -- 定义数据
- 脚本标识 -- 其后包含可执行的 shell 命令
- 宏定义 -- 包含多行 shell 命令的宏
- 文件清单 -- 指导哪些文件最终要包含在包中
- 指示符 -- 修饰文件清单中的文件
- 条件判断 -- 面向操作系统或者特定硬件体系的预处理
3.1. 注释
以 #
开头的行为注释。但是需要注意宏定义会在任何地方展开,因此需要在多行宏之前加 %
转义字符。比如 # %%configure
。
3.2. 标签
标签定义的格式为 <tag>:<value>
。所支持标签都有特殊含义。标签是大小写不敏感的。以下列出一些常见的标签,详看Tags: Data Definitions。
标签 | 说明 |
---|---|
Name | 软件名称,详看注解 1 |
Version | 软件版本 |
Release | 发布次数 |
Summary | 简介 |
BuildArch | 构建体系,默认为编译机体系,可指定 noarch 编译体系架构无关包。 |
License | 证书 |
URL | 网址 |
Packager | 打包人的姓名和邮箱 |
Group | 软件包所属类别 |
Source<num> | 源码包,可通过改变 num 指定多个。 |
Patch<num> | 补丁包,可通过改变 num 指定多个。 |
Provides | 提供的能力 |
BuildRequires | 编译依赖 |
Requires | 安转生成的 rpm 包时的依赖,详看注解 2 |
Conflicts | 与当前软件包正常运行有冲突的包 |
Obsoletes | 此软件的安转会导致过时的包,详看注解 3 |
Description | 详细描述 |
注解:
name
最终生成的软件包名称为
<name>-<version>-<release>
。BuildRequires
手动添加包需求的能力,详看下一章 包的依赖信息。
Obsoletes
表示安装当前包会导致过时的包。当更新(rpm -U)当前软件包时,会删除所有
Obsoletes
的软件包。当安装(rpm -i)当前软件时,会将Obsolietes:
当做Conflicts:
,因此不会更新。详看 rpm.org - Obsoletes。
3.3. 脚本标识
此类 spec 条目,跟随其后的文本会被 RPM 传递给 shell 解释器直接执行,详看RPM:Scripts。主要分为两类:
-
Build-time : 表示构建包时执行的脚本。构建包时会依次执行以下条目内的脚本:
%prep
、%build
、%install
、%check
、%clean
。 -
Install/Erase-time :表示安装、更新、删除包时执行的脚本。有
%pre
、%post
、%preun
、%postun
、%pretrans
、%posttrans
。具体如下:动作 说明 安装 依次执行 %pretrans
、%pre
、%post
、%posttrans
下的脚本。删除 依次执行 %preun
、%postun
下的脚本。更新 依次执行 %pretrans
、%pre
、%post
、%preun
(旧包)、%postun
(旧包)、%posttrans
下的脚本。
3.3.1. %prep
RPM 在看到此字段后,首先会自动进行初步检查,比如标签 source
指定的文件是否存在,然后进入 RPM 构建目录 %{_builddir}
,然后将 %prep
后文本传递给 shell 解释器直接执行。这些命令通常会选择做一些编译前的准备工作,比如解压源码包,应用补丁等。因此 RPM 提供了一些 shell
命令的宏定义来简化操作。比如 %setup
、%autosetup
、%patch
。
3.3.2. %build
RPM 在看到此字段后,会自动进入到软件构建顶层目录 RPM_BUILD_DIR
,默认是 %{_builddir}/%{name}-%{version}
,然后将 %build
后文本传递给 shell 解释器直接执行。这些命令通常为配置、编译源码包。此阶段常用宏有 %configure
、%make_build
等。
3.3.3. %install
RPM 在看到此字段后,和 %build
相似,会自动进入到软件构建顶层目录,然后将 %install
后文本传递给 shell 解释器直接执行。这些命令通常为直接执行 make install
,或执行一些复制文件和创建目录的操作。此阶段常用宏有 %make_install
等。
3.3.4. %check
RPM 在看到此字段后,和 %build
相似,会自动进入到软件构建顶层目录 ,然后将 %check
后文本传递给 shell 解释器直接执行。这些命令通常会选择执行一些测试程序,确认二进制程序可以正确工作,往往直接执行 make test
或者 make check
。在 RPM 版本 4.2 之后开始支持 %check
。
3.3.5. %clean
RPM 在看到此字段后,和 %build
相似,会自动进入到软件构建顶层目录 ,然后将 %clean
后文本传递给 shell 解释器直接执行。这些命令往往用于清除构建软件的一些文件,通常是直接执行 rm -rf $RPM_BUILD_ROOT
。
3.3.6. %pre
安装软件包前,执行的脚本命令。
3.3.7. %post
安装软件包后,执行的脚本命令。比如,新安装一个共享库之后,执行 ldconfig
更新相关信息。
3.3.8. %preun
卸载软件前,执行的脚本命令。
3.3.9. %postun
卸载软件包后,执行的脚本命令。比如,卸载一个共享库之后,执行 ldconfig
删除相关信息。
3.3.8. %pretrans
在安转或更新所有事务前,执行的脚本命令。
3.3.9. %posttrans
在安转或更新所有事务后,执行的脚本命令。
3.4. 宏定义
可以通过 %define
来定义宏,比如:
%define myecho() echo %1 %2
%myecho first second
RPM 有一些预定义的用于简化 spec 文件编写的宏,存在于文件 /usr/lib/rpm/macros
下。常用的宏如下:
宏名 | 值 | 说明 |
---|---|---|
%_builddir | %{_topdir}/BUILD | 构建顶层目录 |
%_buildshell | /bin/sh | 默认脚本解释器 |
%_defaultdocdir | %{_datadir}/doc | 文档安装目录 |
%_defaultlicensedir | %{_datadir}/licenses | 许可安装目录 |
%_rpmdir | %{_topdir}/RPMS | 生成二进制包存在位置 |
%_sourcedir | %{_topdir}/SOURCES | 源码即补丁存在位置 |
%_specdir | %{_topdir}/SPECS | spec 文件存在位置 |
%_srcrpmdir | %{_topdir}/SRPMS | 生成源码包存在位置 |
%_buildrootdir | %{_topdir}/BUILDROOT | 所有包的虚拟安装根目录 |
%buildroot | %{_buildrootdir}/%{NAME}-%{VERSION}-%{RELEASE}.% | 虚拟安装根目录,详看注解 1 |
%_topdir | %{getenv:HOME}/rpmbuild | rpm 根目录,详看注解 2 |
%_prefix | /usr | 安转文件的路径前缀,用于重定位安装文件 |
%_datadir | %{_prefix}/share | 数据文件安装位置 |
%configure | ... | 配置宏 |
%make_install | %{__make} install DESTDIR=%{?buildroot} INSTALL="%{__install} -p" | 安装宏 |
%setup | ... | 解压宏,详看注解 3 |
%patch | ... | 打补丁宏,详看注解 4 |
%autopatch | ... | 自动打补丁宏 |
%autosetup | ... | 自动解压宏 |
注解:
%buildroot
在生成 rpm 包的
%install
阶段,需要以此标签的值作为虚拟安装根目录。后面可直接引用,或使用$RPM_BUILD_ROOT
的方式引用。%_topdir
rpm 根目录,一般通过命令行传参方式覆盖此默认值。如
rpmbuild --define="_topdir <topdir>"
。%setup
主要作用是解压标签
%{source}
指定的源码包,并进入目录%{_builddir}/%{name}-%{version}
下。其扩展后的详细代码,和具体说明详看Macros: %setup,常用选项如下:
选项 说明 -n <dir> 指定自动进入目录名称。用于当源码解压后目录名不规范。 -c 解压之前先创建默认目录,用于压缩文件未包含在一个顶层目录下。 -q 抑制输出,可以减少日志的输出。 -D 解压前不删除目录。 -T 不执行默认解压动作。 -b <num> 切换目录前,解压 %{source<num>}
指定的包。-a <num> 切换目录后,解压 %{source<num>}
指定的包。注解:
-P <num>
表示应用标签
%{patch<num>}
指定的补丁。也可以直接在宏定义后面指定,如%patch -P 1
等价于%patch1
。%patch
应用标签
%{patch}
指定的补丁到已解压的源码上。其扩展后的详细代码,和具体说明详看Macros:patch,常用选项如下:
选项 说明 -p<num> 表示忽略路径层数,将直接传递给命令 patch。 -P <num> (大写 P
)表示应用的补丁序号,详看注解 1-b<suffix> 指定备份文件的后缀。 -s 不显示详细信息。 注解:
-P <num>
表示应用标签
%{patch<num>}
指定的补丁。也可以直接在宏定义后面指定,如%patch -P 1
等价于%patch1
。
3.5. 文件清单
文件清单为条目 %files
后的文本,每一行都被解释为一个文件的路径。表示为所有要被打包的文件清单。
一般情况下,此路径为全路径,即表示在构建系统中的定位,也表示安装包时的位置。
当路径是一个目录时,RPM 自动递归的包含此目录下所有文件。路径可以使用 shell-style
的通配符。
3.6. 指示符
指示条目用于修饰文件清单。详看Directives For the %files list。常用指示如下:
指示符 | 说明 |
---|---|
%doc | 标记文件是文档。详看注解 1 |
%config | 标记文件是配置文件。详看注解 2 |
%attr | 标记文件的属性,包括许可、用户和用户组。 |
%defattr | 标记文件的默认属性。 |
%ghost | 指示文件包含在包中,但不进行安装。 |
%verify | 指示验证文件的各种属性以保证完整性。 |
注解:
%doc
RPM 会在文档目录(默认为
/usr/share/doc
)下创建一个与包同名的目录,并将此指示符修饰的文件复制到此目录中。%config
标识文件是配置文件。如果文件在安装后发生修改,则在更新软件时应保留此文件。
3.7. 条件判断
面向操作系统或者特定硬件体系的预处理,详看Conditionals。常用条目如下:
%ifarch
%ifnarch
%ifos
%ifnos
%else
%endif
4. 包的依赖信息
包的依赖关系通过包提供的能力(capabilities)和包需求的能力来解决。
- 需求的能力
- 依赖的共享库,由 RPM 调用命令
ldd
自动生成。 - 由标签
requires
手动添加。通常是其他软件包名称,可包含版本信息。
- 依赖的共享库,由 RPM 调用命令
- 提供的能力
- 提供的共享库
soname
,由 RPM 自动生成。 - 由标签
provides
手动添加。用于可替代其他软件包时。 - 包的名称和版本号,由 RPM 自动添加。
- 提供的共享库