zhangguanzhang's Blog

rpm包的制作笔记

字数统计: 5.3k阅读时长: 25 min
2017/05/15 Share

文章写于 2021-08-02,防止排列在前面,所以 markdown 里的时间改成 2017-05-15

rpm 一些知识

建议起个 centos:7 的容器里去制作

Linux 里的 so

用白话说,就是类似于 windows 的 dll 文件,通常是底层库,或者经常用到的 c 函数。比如说 curl 命令和 wget 命令,他们很多的底层例如 http 请求,dns 解析,hosts 解析这块逻辑是一样的。如果他们全部打包在二进制文件里(也就是静态编译)会造成二进制体积很大(golang 一般都是静态编译的)。如果共用(共用的一个其中原因是因为早期的系统硬盘容量很小)的话体积会很小,所以安装 rpm 的时候一般会检查依赖。

rpm 制作目录

rpm 包分为两种,rpm 和 srpm。前者基本是打包文件(编译好的二进制文件例如/usr/sbin/nginx、lograte、conf、和 systemd 文件),后者里面有源码包,安装的时候会编译安装,通常的 rpm 包是代指前者。

制作 rpm 包需要以下目录:

1
2
3
4
5
6
BUILD       # 编译 rpm 包的临时目录
BUILDROOT # 虚拟安装目录,即在整个 install 的过程中临时安装到这个目录,把这个目录当作根来用的,所以在这个目录下的文件,才是真正的目录文件。最终, Spec 文件中最后有清理阶段,这个目录中的内容将被删除
RPMS # 制作完成后的rpm包存放目录,会以架构名存放,例如 RPMS/{aarch64,x86_64}/xxx.rpm
SOURCES # 存放源文件,配置文件,补丁文件等放置的目录,如果是 srpm 的话,一般这个目录是有源码,不是 srpm 的话,里面一般有编译好的二进制文件。
SPECS # 核心文件,里面的文件描述 rpm 包的制作和每个步骤怎么执行
SRPMS # srpm 包生成存放文件

这些目录可以通过 rpmdevtools 包里的命令 rpmdev-setuptree 创建,rpmdev-setuptree 实际上是一个脚本。它会生成文件 ~/.rpmmacros (如果文件里没有定义 %_topdir 则写入%_topdir %(echo $HOME)/rpmbuild)然后在 %_topdir 目录下创建上面的制作目录。也可以手动创建目录。安装 rpmbuild 的包:

1
2
yum install -y rpm-build \
rpmdevtools

创建目录:

1
2
3
4
mkdir -p /root/rpmbuild/
cd /root/rpmbuild/

mkdir -p {BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}

另外后文的 spec 文件里用了很多 __xxx 的,可以通过默认的宏文件查看,例如

1
2
3
4
5
6
7
8
$ rpmbuild --showrc | grep topdir 
-14: _builddir %{_topdir}/BUILD
-14: _buildrootdir %{_topdir}/BUILDROOT
-14: _rpmdir %{_topdir}/RPMS
-14: _sourcedir %{_topdir}/SOURCES
-14: _specdir %{_topdir}/SPECS
-14: _srcrpmdir %{_topdir}/SRPMS
-14: _topdir %{getenv:HOME}/rpmbuild

一个下划线是环境有关,两个下划线一般是命令。也推荐使用内置的宏,因为每个系统的命令路径和存放路径不同。

实践

最小模板讲解

比如说文件 SPECS/xxx.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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Name:           hellorpm           #名字为源码tar.gz 包的名字 
Version: 1.0.0 #版本号,一定要与tar.gz包的一致哦
Release: 1%{?dist} #释出号,也就是第几次制作rpm
Summary: helloword #软件包简介,最好不要超过50字符

License: GPL #许可,GPL还是BSD等
URL: #可以写一个网址
Packager: abel
Source0: %{name}-%{version}.tar.gz
#定义用到的source,也就是你的源码

BuildRoot: %_topdir/BUILDROOT
#这个是软件make install 的测试安装目录.

BuildRequires: gcc,make #制作过程中用到的软件包
Requires: python-apscheduler >= 2.1.2-1.el7,python-daemon >= 1.6-1.el7 #软件运行依赖的软件包,也可以指定最低版本如 bash >= 1.1.1

%description #描述,随便写

%prep #打包开始
%setup -q #这个作用静默模式解压所有 Source 并cd

%build #编译制作阶段,主要目的就是编译,如果不用编译就为空
./configure \
%{?_smp_mflags} #make后面的意思是:如果就多处理器的话make时并行编译

%install #安装阶段
rm -rf %{buildroot} #先删除原来的安装的,如果你不是第一次安装的话
cp -rp %_topdir/BUILD/%{name}-%{version}/* $RPM_BUILD_ROOT
#将需要需要打包的文件从BUILD 文件夹中拷贝到BUILDROOT文件夹下。

#下面的几步pre、post、preun、postun 没必要可以不写
%pre #rpm安装前制行的脚本

%post #安装后执行的脚本

%preun #卸载前执行的脚本

%postun #卸载后执行的脚本

%clean #清理段,删除buildroot
rm -rf %{buildroot}


%files #rpm要包含的文件
%defattr (-,root,root,-) #设定默认权限,如果下面没有指定权限,则继承默认
/etc/hello/word/helloword.c #将你需要打包的文件或目录写下来

### 7.chagelog section 改变日志段
%changelog

打包

1
rpmbuild -bb SPECS/xxx.spec

单步执行选项。可以先 rpmbuild -bp ,再 -bc-bi 如果没问题,rpmbuild -ba 生成 src 包与二进制包。

1
2
3
4
5
6
7
-ba 既生成src.rpm又生成二进制rpm 
-bs 只生成src的rpm
-bb 只生二进制的rpm
-bp 执行到pre
-bc 执行到 build段
-bi 执行install段 ,有 %files 则也会处理 files, %doc 同理
-bl 检测有文件没包含

nginx 做示例

大多数参考来源于 nginx-1.19.10-1.el7.ngx.src.rpm 使用 rpm2cpio ../nginx-1.19.10-1.el7.ngx.src.rpm | cpio -div 解开后里面的 nginx.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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
cat > SOURCES/nginx.conf << 'EOF'
user nginx;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;


events {
worker_connections 1024;
}


http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

include /etc/nginx/conf.d/*.conf;
}
EOF

cat > SOURCES/nginx.default.conf << 'EOF'
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
EOF

下载源码文件:

1
2
3
cd SOURCES
wget https://nginx.org/download/nginx-1.19.10.tar.gz
cd -

开始编写:

1
vi SPECS/nginx.spec

内容为下面的,一般最开始是定义一些变量。所有 %define xxx yyy aaa 都可以通过运行 rpmbuild 的时候使用 --define 'xxx yyy aaa' 表示 xxx='yyy aaa',然后可以使用 %{xxx} 即可,也可以调用 shell, 就像下面的 %define _topdir %(echo $PWD) , 也可以获取环境变量 %define _topdir %{getenv:HOME}/rpmbuild

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# 定义目录的变量,因为我这里不是`~/rpmbuild`。也可以用 rpmbuild 运行时候 rpmbuild --define '_topdir `pwd`'
# 定义为 pwd 的话这个遇到问题,Dest dir longer than base dir is not supported , ref: https://bugzilla.redhat.com/show_bug.cgi?id=1427970
# %define _topdir %(echo $PWD)

%define _prefix /usr/local/nginx //预定义的prefix目录
%define nginx_home %{_localstatedir}/cache/nginx
%define nginx_user nginx
%define nginx_group nginx
%define nginx_loggroup adm
%define nginx_debug 0

%if 0%{?rhel} == 7
%define epoch 1
Epoch: %{epoch}
Requires(pre): shadow-utils
Requires: openssl >= 1.0.2
BuildRequires: openssl-devel >= 1.0.2
%define dist .el7
%endif

%if 0%{?rhel} == 8
%define epoch 1
Epoch: %{epoch}
Requires(pre): shadow-utils
BuildRequires: openssl-devel >= 1.1.1
%define _debugsource_template %{nil}
%endif


%define base_version 1.19.10
%define base_release 1%{?dist}.ngx

%define bdir %{_builddir}/%{name}-%{base_version}

%define WITH_CC_OPT $(echo %{optflags} $(pcre-config --cflags)) -fPIC
%define WITH_LD_OPT -Wl,-z,relro -Wl,-z,now -pie

# http://nginx.org/en/docs/configure.html
%define BASE_CONFIGURE_ARGS $(echo "--prefix=%{_sysconfdir}/nginx --sbin-path=%{_sbindir}/nginx --modules-path=%{_libdir}/nginx/modules --conf-path=%{_sysconfdir}/nginx/nginx.conf --error-log-path=%{_localstatedir}/log/nginx/error.log --http-log-path=%{_localstatedir}/log/nginx/access.log --pid-path=%{_localstatedir}/run/nginx.pid --lock-path=%{_localstatedir}/run/nginx.lock --http-client-body-temp-path=%{_localstatedir}/cache/nginx/client_temp --http-proxy-temp-path=%{_localstatedir}/cache/nginx/proxy_temp --http-fastcgi-temp-path=%{_localstatedir}/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=%{_localstatedir}/cache/nginx/uwsgi_temp --http-scgi-temp-path=%{_localstatedir}/cache/nginx/scgi_temp --user=%{nginx_user} --group=%{nginx_group} --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module")

# 介绍,看到有人说建议不超过 50 字符
Summary: High performance web server
# 软件包的名字,后面可以使用 %{name} 的方式引用
Name: nginx
# 后面可使用 %{version} 引用
Version: %{base_version}
# 标明第几次打包,后面可使用 %{release} 引用
Release: %{base_release}
# 打包组织或者人员
Vendor: Nginx, Inc.
Group: System Environment/Daemons # 软件分组,具体类别有: Amusements/Games (娱乐/游戏) Amusements/Graphics(娱乐/图形) Applications/Archiving (应用/文档) Applications/Communications(应用/通讯) Applications/Databases (应用/数据库) Applications/Editors (应用/编辑器) Applications/Emulators (应用/仿真器) Applications/Engineering (应用/工程) Applications/File (应用/文件) Applications/Internet (应用/因特网) Applications/Multimedia(应用/多媒体) Applications/Productivity (应用/产品) Applications/Publishing(应用/印刷) Applications/System(应用/系统) Applications/Text (应用/文本) Development/Debuggers (开发/调试器) Development/Languages (开发/语言) Development/Libraries (开发/函数库) Development/System (开发/系统) Development/Tools (开发/工具) Documentation (文档) System Environment/Base(系统环境/基础) System Environment/Daemons (系统环境/守护) System Environment/Kernel (系统环境/内核) System Environment/Libraries (系统环境/函数库) System Environment/Shells (系统环境/接口) User Interface/Desktops(用户界面/桌面) User Interface/X (用户界面/X窗口) User Interface/X Hardware Support (用户界面/X硬件支持)
Packager: xxx@xxx.com # 打包的人

License: GPLv2 # 软件授权方式,通常是GPL(自由软件)或GPLv2、BSD
URL: https://nginx.org/

# 指编译的目标处理器架构,noarch 标识不指定,但通常都是以 /usr/lib/rpm/marcros 中的内容为默认值
BuildArch: x86_64
Prefix: %{_prefix} //这个主要是为了解决今后安装rpm包时,并不一定把软件安装到rpm中打包的目录的情况。这样,必须在这里定义该标识,并在编写%install脚本的时候引用,才能实现rpm安装时重新指定位置的功能

# 一般是设置为 %_topdir/BUILDROOT 不建议声明,除非你有子目录需求。后面可使用 $RPM_BUILD_ROOT 方式引用
# 测试了下,好像没啥用,可能废弃了?
BuildRoot: %{_tmppath}/%{name}-%{base_version}-%{base_release}-root

# 单行或者多行,也就是你 rpmbuild 的时候提醒需要啥包
BuildRequires: gcc,make
BuildRequires: zlib-devel,pcre-devel
# 版本号要求的话,符号左右有空格
BuildRequires: openssl-devel >= 1.0.2
# yum install centos-release-scl -y && yum install devtoolset-8-gcc devtoolset-8-gcc-c++ -y
BuildRequires: centos-release-scl
BuildRequires: devtoolset-8-gcc,devtoolset-8-gcc-c++

# 安装 rpm 的时候前置依赖
Requires: openssl >= 1.0.2
# 还有例如PreReq、Requires(pre)、Requires(post)、Requires(preun)、Requires(postun)、BuildRequires等都是针对不同阶段的依赖指定。

#Conflicts: xxx # 互斥的包


# 可以带多个用 Source1、Source2 等源,后面也可以用 %{source1}、%{source2} 引用
# 可以使用 URL
Source0: https://nginx.org/download/%{name}-%{version}.tar.gz
Source1: nginx.conf
Source2: nginx.default.conf

%description
nginx [engine x] is an HTTP and reverse proxy server, as well as
a mail proxy server.

# 预处理通常用来执行一些解开源程序包的命令,为下一步的编译安装作准备。prep和下面的build,install段一样,除了可以执行RPM所定义的宏命令(以%开头)以外,还可以执行SHELL命令。功能上类似于./configure。用来准备要编译的软件。通常,这一段落将归档中的源代码解压,并应用补丁。这些可以用标准的 shell 命令完成,但是更多地使用预定义的宏。它一般包含%setup与%patch两个命令。%setup用于将软件包打开,执行%patch可将补丁文件加入解开的源程序中。
%prep
%setup -q -T -c -b 0
# %{__cp} %{SOURCE1} %{_builddir}/%{name}-%{version}/%{name}-%{version}/conf/ # 前面定义了宏,使用下面的
# 拷贝自定义的 nginx 文件
#%{__cp} %{SOURCE1} %{bdir}/%{name}-%{version}/conf/

# 默认解压到 %{_builddir}/%{name}-%{version}目录,此处加了-c,保持了源目录。nginx的源码目录为 %{_builddir}/%{name}-%{version}/nginx-1.19.0/
# %setup 指令可以自动提取 tar、zip、gzip、bzip2、pack、compress 和 lzh 压缩文件。不过,tar-gzip 格式使用最广泛。
# http://ftp.rpm.org/max-rpm/s1-rpm-inside-macros.html#S2-RPM-INSIDE-SETUP-MACRO
# %setup -q #将 tar 命令的输出关闭。
# %setup -a 切换目录前,解压指定 Source 文件,例如 `-a 0` 表示解压 `Source0`
# %setup -n newdir 将软件包解压在newdir目录。
# %setup -c 解压缩之前先产生目录。 如果压缩包里有目录,用了 -c 就两层目录了。如果没目录,则是BUILD/nginx-1.19.0
# %setup -b num 将第 num 个 source 文件解压缩。
# %setup -D 解压前不删除目录 # SOURCE 下面有多个.tar.gz 需要解压的时候需要使用这个
# %setup -T 不使用default的解压缩操作。
#
# %setup -T -b 0 将第 0 个源代码文件解压缩。
# %setup -c -n newdir 指定目录名称 newdir,并在此目录产生 rpm 套件。
# 假如 有多个Source 的压缩包,单独解压 Source1,可以下面这样写。%setup -q -b 1 则会解压所有的
# %setup -q -T -b 1 # 但是这样缺少目录,所以加上-c : %setup -q -T -c -b 1 但是多个同时解压因为解压之前会删除目录,所以我们加 -D 选项不删除,也就是下面的多个
#
# %{__rm} -rf %{_builddir}/%{name}-%{version}
# %setup -q -T -D -c -b 0
# %setup -q -T -D -c -b 1


%build
#此阶段会在BUILD目录的, 我们需要cd %{_builddir}/%{name}-%{version}

cd %{bdir}/%{name}-%{version}

# gcc8
source /opt/rh/devtoolset-8/enable

%if %{nginx_debug}
./configure %{BASE_CONFIGURE_ARGS} \
--with-cc-opt="%{WITH_CC_OPT}" \
--with-ld-opt="%{WITH_LD_OPT}" \
--with-debug

make %{?_smp_mflags}
%{__mv} %{bdir}/objs/nginx \
%{bdir}/objs/nginx-debug
%endif

./configure %{BASE_CONFIGURE_ARGS} \
--with-cc-opt="%{WITH_CC_OPT}" \
--with-ld-opt="%{WITH_LD_OPT}"
make %{?_smp_mflags}

%install
%define _build_id_links none

#此阶段会在 %{_builddir}目录 BUILD/

cd %{bdir}/%{name}-%{version}

[ "$RPM_BUILD_ROOT" != "/" ] && %{__rm} -rf $RPM_BUILD_ROOT
%{__make} DESTDIR=$RPM_BUILD_ROOT INSTALLDIRS=vendor install

%if %{nginx_debug}
%{__install} -m755 %{bdir}/%{name}-%{version}/objs/nginx-debug \
$RPM_BUILD_ROOT%{_sbindir}/nginx-debug
%endif

%{__mkdir} -p $RPM_BUILD_ROOT%{_datadir}/nginx
%{__mv} $RPM_BUILD_ROOT%{_sysconfdir}/nginx/html $RPM_BUILD_ROOT%{_datadir}/nginx/

%{__rm} -f $RPM_BUILD_ROOT%{_sysconfdir}/nginx/*.default
%{__rm} -f $RPM_BUILD_ROOT%{_sysconfdir}/nginx/fastcgi.conf

%{__mkdir} -p $RPM_BUILD_ROOT%{_localstatedir}/log/nginx
%{__mkdir} -p $RPM_BUILD_ROOT%{_localstatedir}/run/nginx
%{__mkdir} -p $RPM_BUILD_ROOT%{_localstatedir}/cache/nginx

%{__mkdir} -p $RPM_BUILD_ROOT%{_libdir}/nginx/modules
cd $RPM_BUILD_ROOT%{_sysconfdir}/nginx && \
%{__ln_s} ../..%{_libdir}/nginx/modules modules && cd -

%{__mkdir} -p $RPM_BUILD_ROOT%{_sysconfdir}/nginx/conf.d
%{__rm} $RPM_BUILD_ROOT%{_sysconfdir}/nginx/nginx.conf
%{__install} -m 644 -p %{SOURCE1} \
$RPM_BUILD_ROOT%{_sysconfdir}/nginx/nginx.conf
%{__install} -m 644 -p %{SOURCE2} \
$RPM_BUILD_ROOT%{_sysconfdir}/nginx/conf.d/default.conf


#%clean
#[ "$RPM_BUILD_ROOT" != "/" ] && %{__rm} -rf "$RPM_BUILD_ROOT"
#%{__rm} -rf $RPM_BUILD_DIR/%{name}-%{version}


# 任何打包的文件,都需要在这个包的详细的文件列表中
%files
# 设定默认权限,(文件权限,用户名,组名,目录权限)
%defattr(-,root,root)

%{_sbindir}/nginx

%if %{nginx_debug}
%{_sbindir}/nginx-debug
%endif

%dir %{_sysconfdir}/nginx
%dir %{_sysconfdir}/nginx/conf.d
%{_sysconfdir}/nginx/modules

%config(noreplace) %{_sysconfdir}/nginx/nginx.conf
%config(noreplace) %{_sysconfdir}/nginx/conf.d/default.conf
%config(noreplace) %{_sysconfdir}/nginx/mime.types
%config(noreplace) %{_sysconfdir}/nginx/fastcgi_params
%config(noreplace) %{_sysconfdir}/nginx/scgi_params
%config(noreplace) %{_sysconfdir}/nginx/uwsgi_params
%config(noreplace) %{_sysconfdir}/nginx/koi-utf
%config(noreplace) %{_sysconfdir}/nginx/koi-win
%config(noreplace) %{_sysconfdir}/nginx/win-utf


%dir %{_datadir}/nginx/html
%{_datadir}/nginx/html/*

%attr(0755,root,root) %dir %{_localstatedir}/cache/nginx
%attr(0755,root,root) %dir %{_localstatedir}/log/nginx

# rpm -q --scripts packagename # 可以看到脚本的信息

# 安装之前,通常用户和目录创建之类的
%pre
# 这里的1为安装;0为卸载
if [ $1 == 1 ]; then
# Add the "nginx" user
getent group %{nginx_group} >/dev/null || groupadd -r %{nginx_group}
getent passwd %{nginx_user} >/dev/null || \
useradd -r -g %{nginx_group} -s /sbin/nologin \
-d %{nginx_home} -c "nginx user" %{nginx_user}
exit 0
fi


#安装后的工作,如设置服务的开机启动,定时任务的设定,启动服务等等
%post
# Register the nginx service
# 这里的1为安装
if [ $1 -eq 1 ]; then

# print site info
cat <<BANNER
----------------------------------------------------------------------

Thanks for using nginx!

Please find the official documentation for nginx here:
* https://nginx.org/en/docs/

Please subscribe to nginx-announce mailing list to get
the most important news about nginx:
* https://nginx.org/en/support.html

Commercial subscriptions for nginx are available on:
* https://nginx.com/products/

----------------------------------------------------------------------
BANNER

# Touch and set permisions on default log files on installation

if [ -d %{_localstatedir}/log/nginx ]; then
if [ ! -e %{_localstatedir}/log/nginx/access.log ]; then
touch %{_localstatedir}/log/nginx/access.log
%{__chmod} 640 %{_localstatedir}/log/nginx/access.log
%{__chown} nginx:%{nginx_loggroup} %{_localstatedir}/log/nginx/access.log
fi

if [ ! -e %{_localstatedir}/log/nginx/error.log ]; then
touch %{_localstatedir}/log/nginx/error.log
%{__chmod} 640 %{_localstatedir}/log/nginx/error.log
%{__chown} nginx:%{nginx_loggroup} %{_localstatedir}/log/nginx/error.log
fi
fi
fi

# 卸载前执行的脚本,在升级的时候会执行。$1 -eq 0 表示卸载
%preun
#if [ $1 -eq 0 ]; then
#%if %use_systemd
# /usr/bin/systemctl --no-reload disable nginx.service >/dev/null 2>&1 ||:
# /usr/bin/systemctl stop nginx.service >/dev/null 2>&1 ||:
#%else
# /sbin/service nginx stop > /dev/null 2>&1
# /sbin/chkconfig --del nginx
# /sbin/chkconfig --del nginx-debug
#%endif
#fi


# 卸载后执行的脚本,在升级的时候不会执行
%postun
#%if %use_systemd
#/usr/bin/systemctl daemon-reload >/dev/null 2>&1 ||:
#%endif
#if [ $1 -ge 1 ]; then
# /sbin/service nginx status >/dev/null 2>&1 || exit 0
# /sbin/service nginx upgrade >/dev/null 2>&1 || echo \
# "Binary upgrade failed, please check nginx's error.log"
#fi


%changelog
* Tue Apr 13 2021 Andrei Belov <defan@nginx.com> - 1.19.10-1%{?dist}.ngx
- 1.19.10-1

* Tue Mar 30 2021 Konstantin Pavlov <thresh@nginx.com> - 1.19.9-1%{?dist}.ngx
- 1.19.9-1

注意上面一些变量目录的清理,判断下是不是 / 。如果这样制作出来的 rpm 包有问题,也就是同一个机器上都无法安装,报错 glibc 版本依赖的话。需要在上面添加下面属性,参考困扰一天的rpm打包问题

1
AutoReqProv: no

制作 rpm

1
rpmbuild -bb SPECS/nginx.spec

起个容器进去安装,先安装 openssl 依赖,然后安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ rpm -ivh nginx-1.19.10-1.el7.ngx.x86_64.rpm 
Preparing... ################################# [100%]
Updating / installing...
1:nginx-1:1.19.10-1.el7.ngx ################################# [100%]
----------------------------------------------------------------------

Thanks for using nginx!

Please find the official documentation for nginx here:
* https://nginx.org/en/docs/

Please subscribe to nginx-announce mailing list to get
the most important news about nginx:
* https://nginx.org/en/support.html

Commercial subscriptions for nginx are available on:
* https://nginx.com/products/

----------------------------------------------------------------------

# 启动
$ nginx -g 'daemon off;'

容器里 curl 下:

1
2
3
4
5
6
7
8
9
10
$ docker exec -ti d4a curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.19.10
Date: Tue, 03 Aug 2021 02:08:51 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 03 Aug 2021 02:07:29 GMT
Connection: keep-alive
ETag: "6108a4e1-264"
Accept-Ranges: bytes

官方的 SPEC 文件里实际还处理了 systemd 之类的,我这里就不搞了,还有一些宏之类的也不讲解了,基本够用了。

一些后话

nginx 这种依赖 openssl-libs的,可以把依赖打包到非根目录里进去,然后用变量 LD_LIBRARY_PATH

1
2
3
4
5
6
7
ls -l lib64/
total 2924
lrwxrwxrwx 1 root root 19 Aug 6 11:09 libcrypto.so.10 -> libcrypto.so.1.0.2k
-rwxr-xr-x 1 root root 2520768 Aug 6 10:22 libcrypto.so.1.0.2k
lrwxrwxrwx 1 root root 16 Aug 6 11:09 libssl.so.10 -> libssl.so.1.0.2k
-rwxr-xr-x 1 root root 470376 Aug 6 10:22 libssl.so.1.0.2k

%files

文件带标记和各种情况总结

文件标记 在update包中是否更新 本地磁盘没有被修改 本地磁盘被修改过
无论有无更新,反正都是替换
%config 没更新 替换(旧文件删除) 保留修改过的文件,不替换
%config 更新 替换(旧文件删除) 旧文件被保存为 xxx.rpmsave,新文件替换旧文件
%config(noreplace) 没更新 替换(旧文件删除) 保留修改过的文件,不替换
%config(noreplace) 更新 替换(旧文件删除) 旧文件保持不变,新文件重命名为xxx.rpmnew

当rpm包中该文件的标记方法发生改变的两个场景:

文件标记 在update包中是否更新 本地磁盘被修改过
%config(noreplace) -> %config 更新 文件被替换,旧文件被保存为xxx.rpmsave
%config -> %config(noreplace) 更新 旧文件保持不变,update包中的新文件重命名为xxx.rpmnew

常用变量

可以使用 rpm 命令行查看变量的值:

1
2
$ rpm --eval '%{_arch}'
x86_64
macro definition comment
%{_sysconfdir} /etc ———-
%{_prefix} /usr can be defined to /app for flatpak builds
%{_exec_prefix} %{_prefix} default: /usr
%{_includedir} %{_prefix}/include default: /usr/include
%{_bindir} %{_exec_prefix}/bin default: /usr/bin
%{_libdir} %{_exec_prefix}/%{_lib} default: /usr/%{_lib}
%{_libexecdir} %{_exec_prefix}/libexec default: /usr/libexec
%{_sbindir} %{_exec_prefix}/sbin default: /usr/sbin
%{_datadir} %{_datarootdir} default: /usr/share
%{_infodir} %{_datarootdir}/info default: /usr/share/info
%{_mandir} %{_datarootdir}/man default: /usr/share/man
%{_docdir} %{_datadir}/doc default: /usr/share/doc
%{_rundir} /run
%{_localstatedir} /var
%{_sharedstatedir} /var/lib
%{_lib} lib64 lib` on 32bit platforms

遇到的问题

1
2
3
4
+ /usr/lib/rpm/find-debuginfo.sh --strict-build-id -m --run-dwz --dwz-low-mem-die-limit 10000000 --dwz-max-die-limit 110000000 /test/BUILD/nginx-1.19.10
extracting debug info from /test/BUILDROOT/nginx-1.19.10-1.el7.ngx.x86_64/usr/sbin/nginx
Dest dir longer than base dir is not supported
error: Bad exit status from /var/tmp/rpm-tmp.NhXfFj (%install)

就是第一行的 %define _topdir %(echo $PWD) ,设置了就有问题,不设置老老实实在 ~/rpmbuild 目录里就没问题。是 debugedit 引起的,这个 说了个解决办法但是没看懂咋搞。

参考

CATALOG
  1. 1. rpm 一些知识
    1. 1.1. Linux 里的 so
    2. 1.2. rpm 制作目录
  2. 2. 实践
    1. 2.1. 最小模板讲解
    2. 2.2. 打包
    3. 2.3. nginx 做示例
    4. 2.4. 一些后话
    5. 2.5. %files
    6. 2.6. 常用变量
  3. 3. 遇到的问题
  4. 4. 参考