构建 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 文件的大部分条目,可以将其分为以下几种类型:

  1. 注释 -- 被 RPM 忽略的可读性说明
  2. 标签 -- 定义数据
  3. 脚本标识 -- 其后包含可执行的 shell 命令
  4. 宏定义 -- 包含多行 shell 命令的宏
  5. 文件清单 -- 指导哪些文件最终要包含在包中
  6. 指示符 -- 修饰文件清单中的文件
  7. 条件判断 -- 面向操作系统或者特定硬件体系的预处理

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 详细描述

注解:

  1. name

    最终生成的软件包名称为 <name>-<version>-<release>

  2. BuildRequires

    手动添加包需求的能力,详看下一章 包的依赖信息

  3. Obsoletes

    表示安装当前包会导致过时的包。当更新(rpm -U)当前软件包时,会删除所有 Obsoletes 的软件包。当安装(rpm -i)当前软件时,会将 Obsolietes: 当做 Conflicts:,因此不会更新。详看 rpm.org - Obsoletes

3.3. 脚本标识

此类 spec 条目,跟随其后的文本会被 RPM 传递给 shell 解释器直接执行,详看RPM:Scripts。主要分为两类:

  1. Build-time : 表示构建包时执行的脚本。构建包时会依次执行以下条目内的脚本:%prep%build%install%check%clean

  2. 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 ... 自动解压宏

注解:

  1. %buildroot

    在生成 rpm 包的 %install 阶段,需要以此标签的值作为虚拟安装根目录。后面可直接引用,或使用 $RPM_BUILD_ROOT 的方式引用。

  2. %_topdir

    rpm 根目录,一般通过命令行传参方式覆盖此默认值。如 rpmbuild --define="_topdir <topdir>"

  3. %setup

    主要作用是解压标签 %{source} 指定的源码包,并进入目录 %{_builddir}/%{name}-%{version} 下。其扩展后的详细代码,和具体说明详看Macros: %setup,常用选项如下:

    选项 说明
    -n <dir> 指定自动进入目录名称。用于当源码解压后目录名不规范。
    -c 解压之前先创建默认目录,用于压缩文件未包含在一个顶层目录下。
    -q 抑制输出,可以减少日志的输出。
    -D 解压前不删除目录。
    -T 不执行默认解压动作。
    -b <num> 切换目录前,解压 %{source<num>} 指定的包。
    -a <num> 切换目录后,解压 %{source<num>} 指定的包。

    注解:

    1. -P <num>

      表示应用标签 %{patch<num>} 指定的补丁。也可以直接在宏定义后面指定,如%patch -P 1 等价于 %patch1

  4. %patch

    应用标签 %{patch} 指定的补丁到已解压的源码上。其扩展后的详细代码,和具体说明详看Macros:patch,常用选项如下:

    选项 说明
    -p<num> 表示忽略路径层数,将直接传递给命令 patch。
    -P <num> (大写 P)表示应用的补丁序号,详看注解 1
    -b<suffix> 指定备份文件的后缀。
    -s 不显示详细信息。

    注解:

    1. -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 指示验证文件的各种属性以保证完整性。

注解:

  1. %doc

    RPM 会在文档目录(默认为 /usr/share/doc)下创建一个与包同名的目录,并将此指示符修饰的文件复制到此目录中。

  2. %config

    标识文件是配置文件。如果文件在安装后发生修改,则在更新软件时应保留此文件。

3.7. 条件判断

面向操作系统或者特定硬件体系的预处理,详看Conditionals。常用条目如下:

  1. %ifarch
  2. %ifnarch
  3. %ifos
  4. %ifnos
  5. %else
  6. %endif

4. 包的依赖信息

包的依赖关系通过包提供的能力(capabilities)和包需求的能力来解决。

  • 需求的能力
    1. 依赖的共享库,由 RPM 调用命令 ldd 自动生成。
    2. 由标签 requires 手动添加。通常是其他软件包名称,可包含版本信息。
  • 提供的能力
    1. 提供的共享库 soname,由 RPM 自动生成。
    2. 由标签 provides 手动添加。用于可替代其他软件包时。
    3. 包的名称和版本号,由 RPM 自动添加。
posted @ 2021-08-23 16:13  fluidog  阅读(1115)  评论(0编辑  收藏  举报