Skip to content

从源码包构建 RPM 包

字数: 6448 字 时长: 24 分钟

从源码包构建 RPM 包指的是将软件的源代码转换成一个 RPM 格式的软件包的过程。构建 RPM 包不仅能够简化软件的安装和卸载过程,还能自动处理依赖关系,确保系统的稳定性和安全性。此外,RPM 包的标准化特性使得软件能够在不同的 RPM 兼容系统间轻松移植,极大地方便了软件的分发和维护。

将一个源码包转换为 RPM 包这个过程,这不仅涉及到编译源代码,还包括编写 SPEC 文件、配置构建环境、以及使用 rpmbuild 工具来生成最终的 RPM 包。

首先,需要安装了 rpmdevtools,它包括了创建 RPM 包所需的工具和文档:

shell
[root@localhost ~]# dnf -y install rpmdevtools

rpmdevtools 是 RPM 打包工作的重要工具集,它为开发者提供了创建、测试和维护 RPM 包所需的各种工具和脚本。通过这些工具,开发者可以更高效地进行 RPM 包的开发和管理工作。

RPM 构建环境

RPM 构建环境是指一组目录和工具的集合,它们被配置和用来构建 RPM 软件包。这个环境包括了所有必要的文件和脚本,以确保源代码可以被正确地编译、打包,并最终生成可以在 RPM 兼容的 Linux 发行版上安装的二进制包。

当运行 rpmdev-setuptree 命令时,它会在当前目录下创建一个名为 rpmbuild 的目录,并在其中创建几个子目录每个目录都有特定的用途:

shell
[root@localhost ~]# rpmdev-setuptree
[root@localhost ~]# ll rpmbuild/
total 0
drwxr-xr-x. 2 root root 6 Nov 14 11:17 BUILD
drwxr-xr-x. 2 root root 6 Nov 14 11:17 RPMS
drwxr-xr-x. 2 root root 6 Nov 14 11:17 SOURCES
drwxr-xr-x. 2 root root 6 Nov 14 11:17 SPECS
drwxr-xr-x. 2 root root 6 Nov 14 11:17 SRPMS
文件名作用
BUILDBUILD 目录是 RPM 构建过程中的临时工作区,用于存放构建过程中生成的所有中间文件。在这个目录中,rpmbuild 会解压 SOURCES 目录中的源代码包,编译源代码,并执行构建过程中的所有步骤,如编译、链接和安装。构建过程中,BUILD 目录会被 rpmbuild 工具自动管理,用户通常不需要直接操作此目录。构建完成后,其中的文件通常会被清理,但可以通过配置 rpmbuild 来保留日志文件等信息。
RPMSRPMS 目录用于存放最终构建生成的二进制 RPM 包。这个目录包含了为特定硬件架构编译的二进制 RPM 包。例如,RPMS/x86_64 目录下存放的是为 x86_64 架构编译的 RPM 包。构建完成后,用户可以在此目录找到生成的 RPM 包,并可以将其分发给其他用户安装。这些包可以直接通过 rpm 命令安装到系统中。
SOURCESSOURCES 目录用于存放软件包的源代码文件或源代码压缩包。这个目录包含了构建 RPM 包所需的所有源代码文件,通常是 .tar.gz.tgz.zip 等格式的压缩文件。这些文件是软件的原始代码,用于编译生成二进制文件。开发者需要将源代码包或源代码压缩文件放置于此目录,以便在构建过程中被引用和解压。
SPECSSPECS 目录用于存放 SPEC 文件。SPEC 文件是构建 RPM 包的配方,包含了软件包的元数据和构建指令,如软件包的名称、版本、依赖关系、源代码位置、构建脚本等。开发者需要在 SPECS 目录中创建或复制 SPEC 文件,以便构建相应的 RPM 包。SPEC 文件定义了如何从源代码构建出 RPM 包。
SRPMSSRPMS 目录用于存放源 RPM 包(Source RPMs)。源 RPM 包含软件的源代码和 SPEC 文件,可以被用来在任何支持 RPM 的系统上重新构建 RPM 包,无论目标系统的架构如何。开发者可以在此目录找到生成的源 RPM 包,这些包可以用于在其他系统上重新构建,或者作为软件分发的中间产品。

SPEC 文件

SPEC 文件是用于描述如何构建 RPM 软件包的脚本文件。它详细指定了构建过程中的各个步骤,包括源代码的获取、预处理、编译、安装以及最终的打包。SPEC 文件是 RPM 包构建系统的核心,它告诉构建工具如何操作。

SPEC 文件通常包含以下几个部分:

  • 头部信息:包括软件包的名称、版本、发布号、摘要、许可证、URL 等元数据。
  • 宏定义:使用 %define 来定义一些在构建过程中会用到的变量或宏。
  • 构建依赖:使用 BuildRequires 指定构建过程中需要的软件包。
  • 源代码:指定源代码文件的位置,通常是通过 Source 标签指定 URL 或本地路径。
  • 补丁:如果需要对源代码进行修改,可以使用 Patch 标签指定补丁文件。
  • 准备阶段(%prep):这个阶段通常包含解压缩源代码和应用补丁的命令。
  • 构建阶段(%build):包含编译源代码的命令,如 ./configuremake
  • 安装阶段(%install):在这个阶段,构建系统会将编译好的程序安装到一个临时的安装目录中,通常是 %{buildroot}。
  • 清理阶段(%clean):可选阶段,用于清理构建过程中产生的临时文件。
  • 文件列表(%files):指定最终 RPM 包中应该包含哪些文件。
  • 变更日志(%changelog):记录软件包的变更历史,每次构建新版本时都会在这里添加一条记录。
  • 预安装和后安装脚本(%pre 和 %post):在软件包安装前后执行的脚本。
  • 卸载前和卸载后脚本(%preun 和 %postun):在软件包卸载前后执行的脚本。

SPEC 文件的语法和结构对于构建 RPM 包至关重要,它需要精确地描述构建过程,以确保软件包能够正确构建和安装。SPEC 文件通常以 .spec 为文件扩展名。

在 RPM 包的 SPEC 文件中,「宏」(Macros)是一种特殊的变量,它们在构建过程中被扩展为具体的值。宏可以用来简化 SPEC 文件的编写,提高可读性和可维护性,同时允许 SPEC 文件在不同的构建环境中使用相同的代码来执行不同的操作。

宏在 SPEC 文件中以 % 符号开头,可以包含在 SPEC 文件的任何部分,包括头部字段、宏定义、构建指令等。它们可以被看作是变量,但与传统变量不同,宏通常在构建过程中由 rpmbuild 工具或其他外部因素(如构建环境)控制和扩展。

宏的类型可以大致分为:

  • 预定义宏:RPM 工具链提供了一些预定义的宏,如 %{_target_cpu}, %{_target_os}, %{_prefix} 等,这些宏在构建时自动被替换为特定的值。
  • 条件宏:以 %{?macro_name} 格式出现的条件宏,如果 macro_name 被定义,则替换为 macro_name 的值,否则不替换。
  • 用户定义宏:用户可以在 SPEC 文件中自定义宏,通过 %define macro_name value 定义,然后在 SPEC 文件的其他部分使用 %{macro_name} 引用。

编写 SPEC 示例

编写 SPEC 文件是一个详细且具体的过程,需要根据要打包的软件的特定需求来定制。以下是编写 SPEC 文件的一般步骤和指南:

  1. 确定基本信息:在编写 SPEC 文件之前,需要收集一些基本信息,包括软件的名称、版本、许可证、源代码的下载地址等。
  2. 创建 SPEC 文件:在 ~/rpmbuild/SPECS 目录下创建一个新的 SPEC 文件,文件名通常与软件包的名称相对应,例如 nginx.spec。
  3. 填写头部信息:在 SPEC 文件的顶部填写软件包的基本信息,如 Name、Version、Release、Summary、License、URL 和 Source0。
  4. 指定构建依赖:使用 BuildRequires 或 Requires 标签指定构建过程中需要的依赖项。
  5. 编写描述:在 SPEC 文件中添加一个 %description 部分,提供软件包的详细描述。
  6. 准备阶段:编写 %prep 部分,通常包含解压缩源代码的命令。
  7. 构建软件:在 %build 部分,编写编译和构建软件的命令。这可能包括运行 ./configure 脚本和 make 命令。
  8. 安装软件:在 %install 部分,编写将软件安装到构建根目录(%{buildroot})的命令。
  9. 列出文件:在 %files 部分,列出最终 RPM 包中应该包含的文件和目录。
  10. 记录变更日志:在 %changelog 部分,记录软件包版本变更的历史。
  11. 测试 SPEC 文件:在完成 SPEC 文件的编写后,使用 rpmbuild 命令测试构建过程,确保没有错误。
  12. 调整和优化:根据测试结果和软件的特定需求,调整 SPEC 文件中的配置选项和构建指令。

通过构建一个 RPM 包来安装 Nginx,用户可以非常方便地安装最新版本的 Nginx,而无需手动下载源代码并编译安装。用户只需使用 rpm 或包管理器(如 yumdnf)安装 RPM 包,即可自动处理依赖关系并完成安装。

确认基本信息

首先,需要下载最新的 Nginx 源码并将其放置在 ~/rpmbuild/SOURCES 目录下:

shell
[root@localhost ~]# wget http://nginx.org/download/nginx-1.27.2.tar.gz
[root@localhost ~]# mv nginx-1.27.2.tar.gz ~/rpmbuild/SOURCES/

注意,当在构建 RPM 包时,构建系统会检查机器上是否安装了构建该软件包所需的依赖项,也就是说需要安装 Nginx 相关的依赖:

shell
[root@localhost ~]# dnf -y install gcc make openssl-devel  pcre-devel zlib-devel

创建 SPEC 文件

shell
[root@localhost ~]# vim ~/rpmbuild/SPECS/nginx.spec

头部信息

shell
Name:           nginx
Version:        1.27.2
Release:        1%{?dist}
Summary:        A high performance web server and reverse proxy server

License:        BSD
URL:            https://nginx.org/
Source0:        nginx-%{version}.tar.gz

Release 用来指定软件包的发布版本号:

  • 1 表示这是该版本软件的第一个发布版本。
  • %{?dist} 是一个宏,它会根据 Linux 发行版自动替换成特定的发行版标识。这个宏是可选的,意味着如果构建环境提供了 %dist 变量的值,它就会被替换;如果没有提供,它就不会被替换。

也就是说,如果在 Rocky Linux 9 上构建一个 RPM 包,那么 %{?dist} 会被替换为 .el9,从而 Release 字段变成 1.el9,主要用于生成与特定发行版相关的后缀。

Source0 指定构建这个软件包所需的主要源代码文件:

  • %{version} 是一个宏,它会被替换为实际的版本号,例如如果 Version: 1.27.2,则 %{version} 会被替换为 1.27.2,文件名就变成了 nginx-1.27.2.tar.gz。

指定构建依赖

shell
BuildRequires:  gcc
BuildRequires:  make
BuildRequires:  openssl-devel
BuildRequires:  pcre-devel
BuildRequires:  zlib-devel

Requires: openssl
Requires: pcre
Requires: zlib

BuildRequires 用于指定构建过程中所需的依赖。这些依赖是在编译和构建软件包时必须安装在系统上的软件包。它们通常包括编译器、库文件和开发工具等。在构建完成后,这些依赖不一定需要保留在最终用户的系统上,因为它们只在编译过程中使用。

Requires 用于指定软件包在运行时所需的依赖。这些依赖是在软件包安装后,为了使软件正常运行而必须安装在系统上的软件包。这些依赖通常包括运行时需要的库文件和其他软件包,它们是软件运行所必需的。

编写描述

shell
%description
Nginx is a HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server.

宏定义部分

在 SPEC 文件中,%define 是用来定义宏的命令,这些宏可以在 SPEC 文件的其他部分被引用:

shell
%define _prefix /usr/local
%define nginx_user nginx
%define nginx_group nginx
  • %define _prefix /usr/local:定义了一个宏 _prefix,其值为 /usr/local,用于指定安装路径的前缀。
  • %define nginx_user nginx:定义了一个宏 nginx_user,其值为 nginx,用于指定将要创建的用户的用户名。
  • %define nginx_group nginx:定义了一个宏 nginx_group,其值为 nginx,用于指定将要创建的组的组名。

这些宏在 SPEC 文件中其他地方通过 %{宏名} 的方式被引用,例如 %{_prefix} 会被替换为 /usr/local。

%pre 脚本部分

%pre 是 SPEC 文件中的一个脚本部分,用于在 RPM 包安装之前执行一些命令:

shell
%pre
getent group %{nginx_group} >/dev/null || groupadd -r %{nginx_group}
getent passwd %{nginx_user} >/dev/null || useradd -r -g %{nginx_group} -d /var/lib/nginx -s /sbin/nologin %{nginx_user}
  • getent group %{nginx_group} >/dev/null || groupadd -r %{nginx_group}:检查系统中是否已存在名为 %{nginx_group} 的组,如果不存在,则创建一个受限的同名组。
  • getent passwd %{nginx_user} >/dev/null || useradd -r -g %{nginx_group} -d /var/lib/nginx -s /sbin/nologin %{nginx_user}:检查系统中是否已存在名为 %{nginx_user} 的用户,如果不存在,则创建一个受限的用户,指定其组为 %{nginx_group},主目录为 /var/lib/nginx,并且登录 shell 为 /sbin/nologin。

准备阶段

shell
%prep
%setup -q

%prep 这是一个宏,用于执行准备工作阶段的任务,通常是在实际编译之前。在这个宏中,可以放置任何需要在构建开始之前执行的命令,比如复制补丁文件、设置环境变量等。如果没有特别的准备工作要做,%prep 宏可以为空或者不写。在这里指定在软件包安装之前需要执行的命令:

%setup%prep 宏中常用的一个指令,用于从 SPEC 文件中指定的源代码包中提取源代码。它通常用于解压缩 SOURCES 目录中的源代码包到构建目录(BUILD 目录)。-q 参数:这个参数告诉 %setup 指令在执行时保持安静,即不输出解压缩过程中的详细信息。具体来说,%setup -q 通常用于以下操作:

  • 解压缩 SOURCES 目录下的源代码包到当前构建目录。
  • 如果源代码包是一个 tar 压缩文件,%setup 会使用相应的命令(如 tar、gzip、bzip2、zip)来解压缩。
  • -q 参数确保了解压缩过程不会显示不必要的输出,使得构建日志更加清晰。

构建软件

shell
%build
./configure --prefix=%{_prefix}/nginx --user=nginx --group=nginx
make %{?_smp_mflags}

%build 是 SPEC 文件中的一个宏,用于指定构建(编译)软件的命令序列。在这个宏里,可以放置所有需要的编译命令。%{_prefix} 是 SPEC 文件中的一个宏,是一个安装前缀。

%{?_smp_mflags} 这是一个条件宏,用于在多核处理器系统上加速构建过程。如果 _smp_mflags 变量被设置(通常是 -j 参数,用于指定并行编译的作业数),那么 make 命令将使用这个值来并行编译,以加快构建速度。如果没有设置 _smp_mflags,则 make 将默认为单线程编译。

安装软件

shell
%install
make install DESTDIR=%{buildroot}

%install 宏通常包含实际安装软件到 %{buildroot} 目录的命令。这个目录模拟了最终的文件系统环境,因此在这个目录下的操作可以确保软件包在实际安装到系统中时的正确性。在构建过程中,%{buildroot} 是一个临时目录。构建完成后,这个目录及其内容通常会被删除,以清理构建环境。

DESTDIR 是一个常用的变量,用于指定安装命令的目标根目录。

使用 %{buildroot} 它用于模拟软件包的安装环境,并确保了构建过程中的安装操作不会影响构建系统的实际文件系统,同时也允许在构建过程中验证软件包的内容和结构。

列出文件

shell
%files
%defattr(-, %{nginx_user}, %{nginx_group}, -)
%{_prefix}/nginx

%files 是 SPEC 文件中的一个宏,它告诉 RPM 构建过程哪些文件应该包含在最终的二进制 RPM 包中。 %defattr 是一个宏,用于定义 %files 宏中列出的文件的默认属性。参数 (-, %{nginx_user}, %{nginx_group}, -) 指定了文件的默认属性:

  1. 第一个参数 - 表示文件类型为普通文件。
  2. 第二个参数 %{nginx_user} 设置文件的默认用户所有者为 nginx 用户。
  3. 第三个参数 %{nginx_group} 设置文件的默认组所有者为 nginx 用户组。
  4. 第四个参数 - 表示不指定默认权限,使用文件系统的默认权限。

%files 宏中,%{_prefix}/nginx 表示 /usr/local/nginx 目录及其内容。

记录变更日志

shell
%changelog
* Fri Nov 15 16:31:20 CST 2024 Tom  <youremail@example.com> - 1.27.2-1
- First package build

在 RPM 规范中,%changelog 是一个非常重要的部分,它记录了软件包在不同版本或发布之间所发生的更改。%changelog 条目的步骤和格式:

  1. 日期:使用完整的日期格式,通常是 星期 月份 日 年份 的格式,例如 Mon Nov 19 2024。
  2. 姓名和电子邮件:更改者的全名和电子邮件地址,两者之间用空格分隔。
  3. 版本号:软件包的版本号和发布号,格式为 版本号-发布号。
  4. 更改描述:对所做的更改进行简洁而详细的描述,每项更改前用减号(-)开头。

具体内容

至此,Nginx 的 SPEC 文件已经编写完成,具体内容如下:

shell
Name:           nginx
Version:        1.27.2
Release:        1%{?dist}
Summary:        A high performance web server and reverse proxy server

License:        BSD
URL:            https://nginx.org/
Source0:        nginx-%{version}.tar.gz

BuildRequires:  gcc
BuildRequires:  make
BuildRequires:  openssl-devel
BuildRequires:  pcre-devel
BuildRequires:  zlib-devel

Requires: openssl
Requires: pcre
Requires: zlib

%description
Nginx is a HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server.

%define _prefix /usr/local
%define nginx_user nginx
%define nginx_group nginx

%pre
getent group %{nginx_group} >/dev/null || groupadd -r %{nginx_group}
getent passwd %{nginx_user} >/dev/null || useradd -r -g %{nginx_group} -d /var/lib/nginx -s /sbin/nologin %{nginx_user}

%prep
%setup -q

%build
./configure --prefix=%{_prefix}/nginx --user=nginx --group=nginx
make %{?_smp_mflags}

%install
make install DESTDIR=%{buildroot}

%files
%defattr(-, %{nginx_user}, %{nginx_group}, -)
%{_prefix}/nginx

%changelog
* Fri Nov 15 16:31:20 CST 2024 Tom  <youremail@example.com> - 1.27.2-1
- First package build

测试 SPEC 文件

在开始构建之前,仔细检查 SPEC 文件中的每个部分,确保所有的路径、依赖关系和命令都是正确的。并且确保 SOURCES 目录中包含了所有需要的源代码包和补丁文件。

然后可以使用 rpmbuild 命令构建 RPM 包。例如:

shell
[root@localhost ~]# rpmbuild -ba ~/rpmbuild/SPECS/nginx.spec
setting SOURCE_DATE_EPOCH=1731688280
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.IegsEI
+ umask 022
+ cd /root/rpmbuild/BUILD
+ rm -rf nginx-1.27.2
+ /usr/bin/gzip -dc /root/rpmbuild/SOURCES/nginx-1.27.2.tar.gz
+ /usr/bin/tar -xof -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd nginx-1.27.2
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ RPM_EC=0
++ jobs -p
+ exit 0
……
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.Tbh55b
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd nginx-1.27.2
+ /usr/bin/rm -rf /root/rpmbuild/BUILDROOT/nginx-1.27.2-1.el9.x86_64
+ RPM_EC=0
++ jobs -p
+ exit 0

可以看到构建过程已经开始,要确定构建是否成功,需要查看完整的构建日志,特别是最后的输出行,以确认是否有错误或警告信息。如果构建成功,可以在 /root/rpmbuild/RPMS/x86_64/ 目录下看到生成的 RPM 包:

shell
[root@localhost ~]# ll ~/rpmbuild/RPMS/x86_64/
total 1316
-rw-r--r--. 1 root root 309362 Nov 15 17:19 nginx-1.27.2-1.el9.x86_64.rpm
-rw-r--r--. 1 root root 654174 Nov 15 17:19 nginx-debuginfo-1.27.2-1.el9.x86_64.rpm
-rw-r--r--. 1 root root 380052 Nov 15 17:19 nginx-debugsource-1.27.2-1.el9.x86_64.rpm

测试 RPM 包

可以看到 RPM 包构建已经成功,可以在另一台虚拟机上测试构建好的 RPM 包,将构建好的 RPM 包 通过 scp 命令复制到测试虚拟机上:

shell
[root@localhost ~]# scp ~/rpmbuild/RPMS/x86_64/nginx-1.27.2-1.el9.x86_64.rpm root@192.168.121.129:/root/
The authenticity of host '192.168.121.129 (192.168.121.129)' can't be established.
ED25519 key fingerprint is SHA256:3schwFBhHjcTA3ZoA9kMLwVDiZ5ElgdppNgkG6raGzk.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.121.129' (ED25519) to the list of known hosts.
root@192.168.121.129's password: 
nginx-1.27.2-1.el9.x86_64.rpm                                                                     100%  302KB  73.0MB/s   00:00

然后另一台虚拟机上进行查看该 RPM 包:

shell
[root@localhost ~]# rpm -qi nginx-1.27.2-1.el9.x86_64.rpm 
Name        : nginx
Version     : 1.27.2
Release     : 1.el9
Architecture: x86_64
Install Date: (not installed)
Group       : Unspecified
Size        : 795591
License     : BSD
Signature   : (none)
Source RPM  : nginx-1.27.2-1.el9.src.rpm
Build Date  : Thu Oct 24 09:12:45 2024
Build Host  : localhost
URL         : https://nginx.org/
Summary     : A high performance web server and reverse proxy server
Description :
Nginx is a HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server.

只需使用 rpmdnf 来安装该 RPM 包即可:

shell
[root@localhost ~]# rpm -ivh nginx-1.27.2-1.el9.x86_64.rpm
Verifying...                          ################################# [100%]
Preparing...                          ################################# [100%]
Updating / installing...
   1:nginx-1.27.2-1.el9               ################################# [100%]

接下来启动 Nginx 测试:

shell
[root@localhost ~]# /usr/local/nginx/sbin/nginx
shell
[root@localhost ~]# curl 127.0.0.0
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

返回的 HTML 内容是 Nginx 的默认欢迎页面,这表明 Nginx 已经成功安装并正在运行。页面上提供了一些基本信息和链接,包括 Nginx 官方文档和支持页面。

SPEC 文件补充

如果希望用户在安装后可以使用 Systemd 管理 Nginx 服务,需要在 SPEC 文件中添加两个主要部分:

  1. 服务文件(通常是 .service 文件)。
  2. 脚本来启动和停止服务的 %post%preun 脚本。

在 RPM 软件包中,%post%preun 是两个特殊的脚本,它们在软件包生命周期的特定时刻被调用。这些脚本允许在软件包安装后(%post)和卸载前(%preun)执行自定义操作。

%post 脚本在软件包安装之后执行。它通常用于执行安装后需要立即完成的任务,%post 脚本对于确保软件包安装后系统处于正确状态非常有用。比如:

  • 启动服务。
  • 创建配置文件。
  • 设置权限。
  • 运行初始化脚本。

%preun 脚本在软件包卸载之前执行。它通常用于执行卸载前需要完成的任务,%preun 脚本有助于确保在软件包被卸载后,系统仍然保持一致和稳定的状态。比如:

  • 停止服务。
  • 备份配置文件。
  • 清理临时文件。
  • 撤销 %post 脚本所做的更改。

以下是修改后的 SPEC 文件示例:

text
Name:           nginx
Version:        1.27.2
Release:        1%{?dist}
Summary:        A high performance web server and reverse proxy server

License:        BSD
URL:            https://nginx.org/ 
Source0:        nginx-%{version}.tar.gz

BuildRequires:  gcc
BuildRequires:  make
BuildRequires:  openssl-devel
BuildRequires:  pcre-devel
BuildRequires:  zlib-devel

Requires: openssl
Requires: pcre
Requires: zlib

%description
Nginx is a HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server.

%define _prefix /usr/local
%define nginx_user nginx
%define nginx_group nginx
%define _systemd_dir /etc/systemd/system

%pre
getent group %{nginx_group} >/dev/null || groupadd -r %{nginx_group}
getent passwd %{nginx_user} >/dev/null || useradd -r -g %{nginx_group} -d /var/lib/nginx -s /sbin/nologin %{nginx_user}

%prep
%setup -q

%build
./configure --prefix=%{_prefix}/nginx --user=nginx --group=nginx
make %{?_smp_mflags}

%install
make install DESTDIR=%{buildroot}
install -d %{buildroot}%{_systemd_dir} // [!code ++]
cat << 'EOF' > %{buildroot}%{_systemd_dir}/nginx.service // [!code ++]
[Unit] // [!code ++]
Description=The NGINX HTTP and reverse proxy server // [!code ++]
After=network.target remote-fs.target nss-lookup.target // [!code ++]

[Service] // [!code ++]
Type=forking // [!code ++]
ExecStart=%{_prefix}/nginx/sbin/nginx // [!code ++]
ExecReload=%{_prefix}/nginx/sbin/nginx -s reload // [!code ++]
ExecStop=%{_prefix}/nginx/sbin/nginx -s stop // [!code ++]
PrivateTmp=true // [!code ++]

[Install] // [!code ++]
WantedBy=multi-user.target // [!code ++]
EOF // [!code ++]

%post
%systemd_post %{_systemd_dir}/nginx.service

%preun
%systemd_preun %{_systemd_dir}/nginx.service

%files
%defattr(-, %{nginx_user}, %{nginx_group}, -)
%{_prefix}/nginx
%{_systemd_dir}/nginx.service

%changelog
* Fri Nov 15 16:31:20 CST 2024 Tom  <youremail@example.com> - 1.27.2-1
- First package build

绿色高亮表示新增行,其中:

  • install -d %{buildroot}%{_systemd_dir} 用来创建必要的目录结构。相比 mkdirinstall -d 更受推荐,因为它是 install 命令的一部分,而 install 命令是构建 RPM 包的标准工具之一。使用 install -d 可以确保在构建过程中创建目录的一致性和可预测性,特别是在处理文件和目录权限时。
  • cat << 'EOF' > ... EOF 是一个在 Unix 和类 Unix 系统中常用的 shell 命令,用于创建或覆盖文件,并写入文本内容。在这个例子中,cat 命令将读取由 << 'EOF' 开始和 EOF 结束的 here document 中的所有文本,并将这些文本写入到 nginx.service 文件中。如果 nginx.service 文件已经存在,它的内容将被新内容替换;如果不存在,将创建一个新文件。
  • %systemd_post 是 RPM 宏的一部分,它是一个在构建 RPM 包时使用的宏,用于在 %post 脚本中自动添加启用和启动 Systemd 服务的命令。这个宏简化了在软件包安装后配置 Systemd 服务的过程。
  • %systemd_preun 是一个宏,它用于在 %preun 脚本中自动添加代码,以便在软件包卸载之前正确地处理 Systemd 服务。这个宏会生成必要的命令来停止服务,并根据需要禁用服务,以确保在软件包被卸载时服务不会自动启动。

这样用户再使用 RPM 包安装后可以直接使用 systemctl 命令进行管理:

shell
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target
shell
[root@localhost ~]# systemctl enable nginx --now
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service /etc/systemd/system/nginx.service.