手把手教你将tar转rpm包

make编译安装

本节内容来源鸟哥私房菜,第二十二章、软件安装:原始码与 Tarball,有兴趣朋友建议看原版。

gcc相关知识

先编写一个C文件,其第一行表示包含文件在/usr/include/stdio.h下,如果没有申明,则需要使用参数-I /usr/include指定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@home ~]#cat sin.c
#include <stdio.h>
int main(void)
{
float value;
value=sin(3.14/2);
printf("%.2f\n",value);
}

[root@localhost src]# gcc -c sin.c
sina.c: In function ‘main’:
sina.c:5:8: warning: incompatible implicit declaration of built-in function ‘sin’ [enabled by default]
value=sin(3.14/2);
^
[root@7ftbv7mdz7cdpc ~]# gcc -c sin.c -lm -L /lib/64 -L /usr/lib64
sin.c: In function ‘main’:
sin.c:5: warning: incompatible implicit declaration of built-in function ‘sin’
[root@localhost src]# gcc -o sin sina.o
[root@localhost src]# ./sin
1.00

gcc sin.c全产生a.out运行文件,加上参数-c则生成二进制文件sin.o,再加上-o则生成运行档sin。

但是如果出现以下错误,这是因为 C 语言里面的 sin 函示是写在 libm.so 这个函式库中,而我们并没有在原始码里面将这个函式库功能加进去,可以使用以下命令gcc -c sin.c -lm -L /lib/64 -L /usr/lib64

  • gcc -O 为产生最佳化的参数

  • gcc -Wall 为产生更详细的编译过程资讯

  • -l :是『加入某个函式库(library)』的意思,

  • m :则是 libm.so 这个函式库,其中, lib 与扩展名(.a 或 .so)不需要写
  • -I /path 后面接的路径( Path )就是配置要去搜寻相关的 include 文件的目录啦
  • -L 后面接的路径表示函式库 libm.so 请到 /lib 或 /usr/lib 里面搜寻

总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 仅将原始码编译成为目标档,并不制作连结等功能:
[root@www ~]# gcc -c hello.c
# 会自动的产生 hello.o 这个文件,但是并不会产生 binary 运行档。

# 在编译的时候,依据作业环境给予最佳化运行速度
[root@www ~]# gcc -O hello.c -c
# 会自动的产生 hello.o 这个文件,并且进行最佳化喔!

# 在进行 binary file 制作时,将连结的函式库与相关的路径填入
[root@www ~]# gcc sin.c -lm -L/usr/lib -I /usr/include
# 这个命令较常下达在最终连结成 binary file 的时候,
# -lm 指的是 libm.so 或 libm.a 这个函式库文件;
# -L 后面接的路径是刚刚上面那个函式库的搜寻目录;
# -I 后面接的是原始码内的 include 文件之所在目录。

# 将编译的结果输出成某个特定档名
[root@www ~]# gcc -o hello hello.c
# -o 后面接的是要输出的 binary file 档名

# 在编译的时候,输出较多的信息说明
[root@www ~]# gcc -o hello hello.c -Wall
# 加入 -Wall 之后,程序的编译会变的较为严谨一点,
# 所以警告信息也会显示出来!

另外,我们通常称 -Wall 或者 -O 这些非必要的参数为旗标 (FLAGS),因为我们使用的是 C 程序语言,所以有时候也会简称这些旗标为 CFLAGS。

makefile介绍

实例

首先,下载wget http://linux.vbird.org/linux_basic/0520source/main.tgz然后开始编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@localhost main]# cat makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
clean:
rm -f main main.o haha.o sin_value.o cos_value.o

[root@localhost main]# make
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm

[root@localhost main]# ./main
Please input your name: fdm
Please enter the degree angle (ex> 90): 33
Hi, Dear fdm, nice to meet you.
The Sin is: 0.54
The Cos is: 0.84

[root@localhost main]# make clean
rm -f main main.o haha.o sin_value.o cos_value.o

makefile 的基本语法与变量

make 会主动的去判断每个目标档相关的原始码文件,并直接予以编译,最后再直接进行连结的动作。

  • 在 makefile 当中的 # 代表注解;
    • 需要在命令行 (例如 gcc 这个编译器命令) 的第一个字节;
    • 标的 (target) 与相依文件(就是目标档)之间需以『:』隔开。就是可执行程序的文件名。
      1
      2
      标的(target): 目标档1 目标档2
      gcc -o 欲创建的运行档 目标档1 目标档2

如下,makefile 里面就具有至少两个标的,分别是 main 与 clean ,如果我们想要创建 main 的话,输入『make main』,如果想要清除有的没的,输入『make clean』即可!而如果想要先清除目标档再编译 main 这个程序的话,就可以这样输入:『make clean main』

还可以进行简化,使用变量来减少程序的修改量。$@:代表目前的标的(target);gcc 在进行编译的行为时,会主动的去读取 CFLAGS 这个环境变量,可以写到makefile

1
2
3
4
5
6
7
8
[root@www ~]# cat makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o $@ ${OBJS} ${LIBS}
clean:
rm -f $@ ${OBJS}

由於 gcc 在进行编译的行为时,会主动的去读取 CFLAGS 这个环境变量,所以,你可以直接在 shell 定义出这个环境变量,也可以在 makefile 文件里面去定义:

1
2
[root@www ~]# CFLAGS="-Wall" make clean main
# 这个动作在上 make 进行编译时,会去取用 CFLAGS 的变量内容!

变量的基本语法为:

  • 变量与变量内容以『=』隔开,同时两边可以具有空格;
  • 变量左边不可以有 ,例如上面范例的第一行 LIBS 左边不可以是
  • 变量与变量内容在『=』两边不能具有『:』;
  • 在习惯上,变量最好是以『大写字母』为主;
  • 运用变量时,以 ${变量} 或 $(变量) 使用;
  • 在该 shell 的环境变量是可以被套用的,例如提到的 CFLAGS 这个变量!
  • 在命令列模式也可以给予变量。

yum安装相关环境

  • 如果是要安装 gcc 等软件发展工具,请使用『 yum groupinstall “Development Tools” 』
  • 若待安装的软件需要图形介面支持,一般还需要『 yum groupinstall “X Software Development” 』
  • 若安装的软件较旧,可能需要『 yum groupinstall “Legacy Software Development” 』

编译软件步骤

  • ./configure 创建Makefile文件
  • make clean 会读取 Makefile 中关於 clean 的工作,可有可无
  • make make 会依据 Makefile 当中的默认工作进行编译的行为
  • make install

如果未指定安装路径,则编译安装的目录是放在/usr/local/etc、/usr/local/bin、/usr/local/lib、/usr/local/man下。

rpm知识

此内容与编译无关。可以跳过,同时此内容也来自于鸟哥私房菜,是一本好本啊~

RPM 安装 (install)

1
2
3
4
5
6
7
8
9
10
[root@www ~]# rpm -ivh package_name
选项与参数:
-i :install 的意思
-v :察看更细部的安装资讯画面
-h :以安装资讯列显示安装进度

-Uvh 后面接的软件即使没有安装过,则系统将予以直接安装; 若后面接的软件有安装过旧版,则系统自动升级至新版;
-Fvh 如果后面接的软件并未安装到你的 Linux 系统上,则该软件不会被安装;亦即只有已安装至你 Linux 系统内的软件会被『升级』

[root@www ~]# rpm --rebuilddb <==重建数据库

rpm 安装时常用的选项与参数说明

  • —nodeps:当发生软件属性相依问题而无法安装,但你执意安装时
  • —replacefiles:如果在安装的过程当中出现了『某个文件已经被安装在你的系统上面』的资讯,又或许出现版本不合的信息 (confilcting files) 时,可以使用这个参数来直接覆盖文件。
  • —replacepkgs:重新安装某个已经安装过的软件!如果你要安装一堆 RPM 软件文件时,可以使用 rpm -ivh *.rpm ,但若某些软件已经安装过了, 此时系统会出现『某软件已安装』的资讯,导致无法继续安装。此时可使用这个选项来重复安装喔!
  • —force:这个参数其实就是 —replacefiles 与 —replacepkgs 的综合体!
  • —test: 想要测试一下该软件是否可以被安装到使用者的 Linux 环境当中,可找出是否有属性相依的问题。
  • —justdb 使用时机: 由於 RPM 数据库破损或者是某些缘故产生错误时,可使用这个选项来升级软件在数据库内的相关资讯。
  • —nosignature 使用时机: 想要略过数码签章的检查时,可以使用这个选项。
  • —prefix 新路径 使用时机: 要将软件安装到其他非正规目录时。举例来说,你想要将某软件安装到 /usr/local 而非正规的 /bin, /etc 等目录, 就可以使用『 —prefix /usr/local 』来处理了。
  • —noscripts 使用时机:不想让该软件在安装过程中自行运行某些系统命令。

RPM 查询 (query)

RPM 在查询的时候,其实查询的地方是在 /var/lib/rpm/ 这个目录下的数据库文件。

1
2
3
4
[root@www ~]# rpm -qa                           <==已安装软件
[root@www ~]# rpm -q[licdR] 已安装的软件名称 <==已安装软件
[root@www ~]# rpm -qf 存在於系统上面的某个档名 <==已安装软件
[root@www ~]# rpm -qp[licdR] 未安装的某个文件名称 <==查阅RPM文件

  • -q :仅查询,后面接的软件名称是否有安装
  • -qa :列出所有的,已经安装在本机 Linux 系统上面的所有软件名称;
  • -qi :列出该软件的详细资讯 (information),包含开发商、版本与说明等;
  • -ql :列出该软件所有的文件与目录所在完整档名 (list);
  • -qc :列出该软件的所有配置档 (找出在 /etc/ 底下的档名而已)
  • -qd :列出该软件的所有说明档 (找出与 man 有关的文件而已)
  • -qR :列出与该软件有关的相依软件所含的文件 (Required 的意思)
  • -qf :由后面接的文件名称,找出该文件属於哪一个已安装的软件
  • -qp[icdlR]:注意 -qp 后面接的所有参数以上面的说明一致。但用途仅在於找出某个 RPM 文件内的资讯,即未安装的名称

RPM 验证与数码签章

验证

验证 (Verify) 的功能主要在於提供系统管理员一个有用的管理机制!作用的方式是『使用 /var/lib/rpm 底下的数据库内容来比对目前 Linux 系统的环境下的所有软件文件 』也就是说,当你有数据不小心遗失, 或者是因为你误杀了某个软件的文件,或者是不小心不知道修改到某一个软件的文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@www ~]# rpm -Va
[root@www ~]# rpm -V 已安装的软件名称
[root@www ~]# rpm -Vp 某个 RPM 文件的档名
[root@www ~]# rpm -Vf 在系统上面的某个文件

选项与参数:
-V :后面加的是软件名称,若该软件所含的文件被更动过,才会列出来;
-Va :列出目前系统上面所有可能被更动过的文件;
-Vp :后面加的是文件名称,列出该软件内可能被更动过的文件;
-Vf :列出某个文件是否被更动过~

[root@www ~]# rpm -V logrotate
..5....T c /etc/logrotate.conf

第一列的含义有以下几种:

1
2
3
4
5
6
7
8
S :(file Size differs) 文件的容量大小是否被改变
M :(Mode differs) 文件的类型或文件的属性 (rwx) 是否被改变?如是否可运行等参数已被改变
5 :(MD5 sum differs) MD5 这一种指纹码的内容已经不同
D :(Device major/minor number mis-match) 装置的主/次代码已经改变
L :(readLink(2) path mis-match) Link 路径已被改变
U :(User ownership differs) 文件的所属人已被改变
G :(Group ownership differs) 文件的所属群组已被改变
T :(mTime differs) 文件的创建时间已被改变

第二列的含义有以下几种:

1
2
3
4
5
c :配置档 (config file)
d :文件数据档 (documentation)
g :鬼文件~通常是该文件不被某个软件所包含,较少发生!(ghost file)
l :授权文件 (license file)
r :读我文件 (read me)

数码签章

软件开发厂商可以数码签章系统产生一个专属於该软件的签章,并将该签章的公钥 (public key) 释出。 当你要安装一个 RPM 文件时:

首先你必须要先安装原厂释出的公钥文件;实际安装原厂的 RPM 软件时, rpm 命令会去读取 RPM 文件的签章资讯,与本机系统内的签章资讯比对,若签章相同则予以安装,若找不到相关的签章资讯时,则给予警告并且停止安装喔。

1
2
3
4
5
6
7
8
[root@localhost ~]# ll /etc/pki/rpm-gpg/RPM-GPG-KEY-*
-rw-r--r--. 1 root root 1690 Nov 23 08:16 /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
-rw-r--r--. 1 root root 1004 Nov 23 08:16 /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Debug-7
-rw-r--r--. 1 root root 1690 Nov 23 08:16 /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Testing-7
-rw-r--r--. 1 root root 1662 Oct 2 2017 /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
-rw-r--r--. 1 root root 1340 Mar 23 2017 /etc/pki/rpm-gpg/RPM-GPG-KEY-remi
-rw-r--r--. 1 root root 3100 Mar 23 2017 /etc/pki/rpm-gpg/RPM-GPG-KEY-remi2017
-rw-r--r--. 1 root root 3143 Jan 16 2018 /etc/pki/rpm-gpg/RPM-GPG-KEY-remi2018

SRPM相关知识

rpm的全称是Redhat Package Manager,SRPM是指 Source RPM 的意思,这个 RPM 文件里面含有原始码,特别注意的是,这个 SRPM 所提供的软件内容『并没有经过编译』。通常 SRPM 的扩展名是以 *.src.rpm 这种格式来命名的。srpm跟tarball的区别就是srpm提供了这个软件所需要的相依性软件说明、以及所有 RPM 文件所提供的数据。

rebuild直接安装

下载src.rpm包之后,可以在不修改配置的情况下,直接使用以下参数直接操作:

  • —rebuild:这个选项会将后面的SRPM进行『编译』与『打包』的动作,最后会产生RPM的档案,但是产生的RPM档案并没有安装到系统上。当你使用—rebuild的时候,最后通常会发现一行字体:Wrote: /root/rpmbuild/RPMS/x86_64/pkgname.x86_64.rpm这个就是编译完成的RPM档案啰!这个档案就可以用来安装啦!安装的时候请加绝对路径来安装即可
  • —recompile:这个动作会直接的『编译』『打包』并且『安装』啰!请注意, rebuild 仅『编译并打包』而已,而recompile 不但进行编译跟打包,还同时进行『安装』了

示例:

1
2
3
4
5
6
7
8
9
10
先下载软体:
wget http://vault.centos.org/7.1.1503/updates/Source/SPackages/ntp-4.2.6p5-19.el7.centos.1.src.rpm
再尝试直接编译看看:
rpmbuild --rebuild ntp-4.2.6p5-19.el7.centos.1.src.rpm
上面的动作会告诉我还有一堆相依软体没有安装~所以我得要安装起来才行:
yum install libcap-devel openssl-devel libedit-devel pps-tools-devel autogen autogen-libopts-devel
再次尝试编译的行为:
rpmbuild --rebuild ntp-4.2.6p5-19.el7.centos.1.src.rpm
最终的软体就会被放置到:
/root/rpmbuild/RPMS/x86_64/ntp-4.2.6p5-19.el7.centos.1.x86_64.rpm

SPEC编译安装

目录说明

如果你的rpm的版本<=4.4.x,那么rpmbuid工具其默认的工作路径是/usr/src/redhat,这就使得普通用户不能制作rpm包,因为权限的问题,在制作rpm软件包时必须切换到root身份才可以。所以,rpm从4.5.x版本开始,将rpmbuid的默认工作路径移动到用户家目录下的rpmbuild目录里,即$HOME/rpmbuild

首先yum -y install rpm-build rpmdevtools,然后rpmdev-setuptree来创建工作目录,也可以不安装rpmdevtools,直接使用mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}来创建。具体的各目录的含义如下:

默认位置 宏代码 名称 用途
~/rpmbuild/SPECS %_specdir Spec 文件目录 保存 RPM 包配置(.spec)文件
~/rpmbuild/SOURCES %_sourcedir 源代码目录 保存源码包(如 .tar 包)和所有 patch 补丁
~/rpmbuild/BUILD %_builddir 构建目录 源码包被解压至此,并在该目录的子目录完成编译
~/rpmbuild/BUILDROOT %_buildrootdir 最终安装目录 保存 %install 阶段安装的文件
~/rpmbuild/RPMS %_rpmdir 标准 RPM 包目录 生成/保存二进制 RPM 包
~/rpmbuild/SRPMS %_srcrpmdir 源代码 RPM 包目录 生成/保存源码 RPM 包(SRPM)

使用rpm -ivh来安装srpm包时,会生成很多文件在/root/rpmbuild下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~]# rpm -ivh ntp-4.2.6p5-19.el7.centos.1.src.rpm
Updating / installing...
1:ntp-4.2.6p5-19.el7.centos.1 ################################# [100%]
[root@localhost ~]# tree /root/rpmbuild/
/root/rpmbuild/
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
│ ├── ntp-4.2.4p7-getprecision.patch
│ ├── ntp-4.2.6p1-cmsgalign.patch
。。。
│ └── sntp.sysconfig
├── SPECS
│ └── ntp.spec
└── SRPMS

解压完成之后,srpm本身就提供了spec文件,可以直接通过以下方式直接安装:

1
2
[root@study ~]# rpmbuild -ba ntp.spec   <==编译并同时产生RPM与SRPM档案 
[root@study ~]# rpmbuild -bb ntp.spec <==仅编译成RPM档案

spec打包流程

SPECS下是RPM包的配置文件,是RPM打包的“图纸”,这个文件会告诉rpmbuild命令如何去打包。“宏代码”这一列就可以在SPEC文件中用来代指所对应的目录,类似于编程语言中的宏或全局变量。当然~/rpmbuild这个文件夹也是有宏代码的,叫做%_topdir。

打包的过程有点像是流水线,分好几个工序:

  1. 首先,需要把源代码放到%_sourcedir中;
  2. 然后,进行编译,编译的过程是在%_builddir(~/rpmbuild/BUILD)中完成的,所以需要先把源代码复制到这个目录下边,一般情况下,源代码是压缩包格式,那么就解压过来即可;
  3. 第三步,进行“安装”,这里有点类似于预先组装软件包,把软件包应该包含的内容(比如二进制文件、配置文件、man文档等)复制到%_buildrootdir(~/rpmbuild/BUILDROOT)中,并按照实际安装后的目录结构组装,比如二进制命令可能会放在/usr/bin下,那么就在%_buildrootdir下也按照同样的目录结构放置;
  4. 然后,需要配置一些必要的工作,比如在实际安装前的准备啦,安装后的清理啦,以及在卸载前后要做的工作啦等等,这样也都是通过配置在SPEC文件中来告诉rpmbuild命令;
  5. 还有一步可选操作,那就是检查软件是否正常运行;
  6. 最后,生成的RPM包放置到%_rpmdir,源码包放置到%_srpmdir下。

spec文件内容

使用vim a.spec之后,会自动生成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
Name:								==>软件包的名字,后续可以用%{name}来引用
Version: ==>软件版本
Release: 1%{?dist} ==>发布的序号
Summary: ==>软件包的摘要信息

Group: ==>软件包的安装分类,参见/usr/share/doc/rpm-4.x.x/GROUPS这个文件
License: ==>GPLv2,授权方式
URL: ==>这里本来写源码包的下载路径或者自己的博客地址或者公司网址之类,无实际用处
Source0: ==>%{name}-%{version}.tar.gz源代码包的名称,这里的name和version就是前两行定义的值。如果有其他配置或脚本则依次用Source1、Source2等等往后增加即可。

BuildRequires: ==>在本机编译rpm包时需要的辅助工具,以逗号分隔。假如,要求gcc的版本至少为4.4.2,则可以写成gcc >=4.2.2。
Requires: ==>编译好的rpm软件在其他机器上安装时,需要依赖的其他软件包,也以逗号分隔,有版本需求的可以

%description ==>软件包的详细说明信息,但最多只能有80个英文字符。

%prep ==>读取位于 %_sourcedir 目录的源代码和 patch 。之后,解压源代码至 %_builddir 的子目录并应用所有 patch。
%setup -q ==>使用这个语句实现%prep功能,仅能解压Source 或 patch 定义好的文件

%build ==>编译位于 %_builddir 构建目录下的文件。通过执行类似 ./configure && make 的命令实现。
%configure
make %{?_smp_mflags}

%install ==>读取位于 %_builddir 构建目录下的文件并将其安装至 %_buildrootdir 目录。这些文件就是用户安装 RPM 后,最终得到的文件。注意一个奇怪的地方: 最终安装目录 不是 构建目录。通过执行类似 make install 的命令实现。
make install DESTDIR=%{buildroot}

%files ==>软件文件和目录
%doc ==>软件说明文件

%changelog ==>更新说明,星号(*) 后面应该要以时间,修改者, email 与软体版本来作为说明, 减号(-) 后面则是你要作的详细说明啰

除了以上这些,还是以下阶段:

  • %pre 在目标系统上安装软件包之前执行的Scriptlet。注意跟%prep的区别。
  • %post 在目标系统上安装软件包完成之后执行的Scriptlet。
  • %preun 在从目标系统卸载软件包之前执行的Scriptlet。
  • %postun 从目标系统卸载软件包完成之后执行的Scriptlet。

以下介绍一下比较重要的阶段。

%prep阶段

这个阶段里主要完成对源代码包的解压和打补丁(如果有的话),而解压时最常见到的就是一句指令:%setup -q。这句指令可以成功执行的前提是你位于SOURCES目录下的源码包必须是name-version.tar.gz的格式才行,它还会完成后续阶段目录的切换和设置。如果在这个阶段你不用这条指令,那么后面每个阶段都要自己手动去改变相应的目录。

如果在%prep阶段,只使用了%setup这个宏变量,代表了会运行以下操作(假设Source为cdplayer-1.0.tgz):

1
2
3
4
5
6
7
8
9
10
cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

可以看到,默认操作为先进入BUILD目录,然后再删除%{name}-%{version},再解压,最后再修改权限、属主等。参数-q是指不输出运行的过程,还有其他参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
%setup -n newdir 先删除/usr/src/redhat/BUILD/newdir目录,然后将软件包解压在newdir目录;再进行这个目录进行权限设置
%setup -c 先创建目录,进入这个目录后再解压。与不加参数的%setup比较,仅仅是多了mkdir 以及 cd 这2个命令
%setup -D 解压缩时,不会去删除目录。与不加参数的%setup比较,少了rm -rf操作
%setup -T 不进行解压的操作。与不加参数的%setup比较,少了gzip以及if判断语句

%setup -b num 将第num个source文件解压缩。如果是 %setup -b 0,就会解压2次,可以使用 %setup -T -b 0,这样就只会解压一次了。但只效果跟 %setup 不加参数的效果是一样的。
%setup -T -a num 先进入目录再解压第num个source文件


%patch 最简单的补丁方式,自动指定patch level。
%patch 0 使用第0个补丁文件,相当于%patch ?p 0。
%patch -s 不显示打补丁时的信息。
%patch -T 将所有打补丁时产生的输出文件删除。
*************************************/
%setup -q -n %{name}-%{version}

/* 如果原来只有一个“Patch:”,您增加“Patch1:”,则在SPEC文件%setup行后面的
%patch -p1后面新增一行: %patch1 -p1 依此类推.
*/
%patch0 -p1

实例,如果%prep的内容如下:

1
2
3
4
%prep
%setup
%setup -T -D -a 14
%setup -T -D -a 15

其运行的过程如下:

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
[root@localhost SPECS]# rpmbuild -bb nginx.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.N1Tdpo
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd /root/rpmbuild/BUILD
+ rm -rf nginx-1.16.1
+ /usr/bin/tar -xvvf -
+ /usr/bin/gzip -dc /root/rpmbuild/SOURCES/nginx-1.16.1.tar.gz
...
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd nginx-1.16.1
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ cd /root/rpmbuild/BUILD
+ cd nginx-1.16.1
+ /usr/bin/tar -xvvf -
+ /usr/bin/gzip -dc /root/rpmbuild/SOURCES/nginx-http-concat.tar.gz
...
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ cd /root/rpmbuild/BUILD
+ cd nginx-1.16.1
+ /usr/bin/tar -xvvf -
+ /usr/bin/gzip -dc /root/rpmbuild/SOURCES/ngx_cache_purge.tar.gz
...

需要注意的是,%setup -q 是指解压不输出任何提示消息,只会解压source0的文件,即第0个source文件。%setup -T -a num 先进入目录再解压第num个source文件,进入目录的意思是进入%{name}-%{version}这个目录,即spec前面定义好的Name以及Version。

https://book.huihoo.com/maximum-rpm/s1-rpm-inside-macros.html

%build阶段

这个阶段就是执行常见的configure和make操作,如果有些软件需要最先执行bootstrap之类的,可以放在configure之前来做。这个阶段我们最常见只有两条指令:

1
2
%configure
make %{?_smp_mflags}

这里的%configure是个宏常量,会自动将prefix设置成/usr。另外,这个宏还可以接受额外的参数,如果某些软件有某些高级特性需要开启,可以通过给%configure宏传参数来开启。如果不用 %configure这个宏的话,就需要完全手动指定configure时的配置参数了。同样地,我们也可以给make传递额外的参数,例如: make %{?_smp_mflags} CFLAGS="" …

%install阶段

这个阶段就是执行make install操作。这个阶段会在%_buildrootdir目录里建好目录结构,然后将需要打包到rpm软件包里的文件从%_builddir里拷贝到%_buildrootdir里对应的目录里。这个阶段最常见的两条指令是:

1
2
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT

其中$RPM_BUILD_ROOT也可以换成我们前面定义的BuildRoot变量,不过要写成%{buildroot}才可以,必须全部用小写,不然要报错。

如果软件有配置文件或者额外的启动脚本之类,就要手动用copy命令或者install命令你给将它也拷贝到%{buildroot}相应的目录里。用copy命令时如果目录不存在要手动建立,不然也会报错,所以推荐用install命令。

制作rpm软件包的阶段

这个阶段必须引出下面一个叫做%files的阶段。它主要用来说明会将%{buildroot}目录下的哪些文件和目录最终打包到rpm包里。

1
2
3
%files
%defattr(-,root,root,-)
%doc

%defattr 表示的意思是: %defattr(文件权限,用户名,组名,目录权限) ,如果不牵扯到文件、目录权限的改变则一般用%defattr(-,root,root,-)这条指令来为其设置缺省权限。

关于%files阶段有两个特性要牢记:

  1. %{buildroot}里的所有文件都要明确被指定是否要被打包到rpm里。什么意思呢?假如,%{buildroot}目录下有4个目录a、b、c和d,在%files里仅指定a和b要打包到rpm里,如果不把c和d用exclude声明是要报错的;
  2. 如果声明了%{buildroot}里不存在的文件或者目录也会报错。

代表路径的宏列表

如果您看到一个不熟悉的宏,您可以使用rpm --showrc 或者 rpm --eval %{_bindir}以下方法对其进行查看:

1
2
3
4
5
6
$ rpm --eval %{_bindir}
/usr/bin

[root@localhost noarch]# rpm --showrc |grep buildrootdir
-14: _buildrootdir %{_topdir}/BUILDROOT
-14: buildroot %{_buildrootdir}/%{name}-%{version}-%{release}.%{_arch}

也可以直接查看/usr/lib/rpm/macros文件,以下是常见的宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
%{_sysconfdir}        /etc
%{_prefix} /usr
%{_exec_prefix} %{_prefix}
%{_bindir} %{_exec_prefix}/bin
%{_libdir} %{_exec_prefix}/%{_lib}
%{_libexecdir} %{_exec_prefix}/libexec
%{_sbindir} %{_exec_prefix}/sbin
%{_sharedstatedir} /var/lib
%{_datarootdir} %{_prefix}/share
%{_datadir} %{_datarootdir}
%{_includedir} %{_prefix}/include
%{_infodir} /usr/share/info
%{_mandir} /usr/share/man
%{_localstatedir} /var
%{_initddir} %{_sysconfdir}/rc.d/init.d
%{_var} /var
%{_tmppath} %{_var}/tmp
%{_usr} /usr
%{_usrsrc} %{_usr}/src
%{_lib} lib (lib64 on 64bit multilib systems)
%{_docdir} %{_datadir}/doc
%{buildroot} %{_buildrootdir}/%{name}-%{version}-%{release}.%{_arch}
$RPM_BUILD_ROOT %{buildroot}

要注意的是%{buildroot} 跟%{_buildrootdir}不同。%{_buildrootdir}对应的目录是~/rpmbuild/BUILDROOT;而%{buildroot}是%{_buildrootdir}/%{name}-%{version}-%{release}.%{_arch},有关其他内容可以参考 https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros#RPM_directory_macros

实例1:打包shell

可以把一个平时写的一个脚本打包成一个rpm包,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
[root@localhost SPECS]# cat disk.spec
Name: showdiskinfo
Version: 1.0
Release: 1%{?dist}
Summary: Show Raid and Disk info
License: GPL
URL: http://www.wumingx.cn
Source0: showdiskinfo.sh

#BuildRequires:
Requires: bash

%description
showdiskinfo shell script.

%prep
#%setup -q

%build
cp %_sourcedir/showdiskinfo.sh %_builddir/showdiskinfo

%install
mkdir -p %{buildroot}/usr/local/bin
install -m 755 showdiskinfo %{buildroot}/usr/local/bin

%files
/usr/local/bin/showdiskinfo
%doc
%changelog

开始编译:

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
[root@localhost SPECS]# rpmbuild -bb disk.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.agda2b
+ umask 022
+ cd /root/rpmbuild/BUILD
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.spexQx
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cp /root/rpmbuild/SOURCES/showdiskinfo.sh /root/rpmbuild/BUILD/showdiskinfo
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.grcfHT
+ umask 022
+ cd /root/rpmbuild/BUILD
+ '[' /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64 '!=' / ']'
+ rm -rf /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64
++ dirname /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64
+ mkdir -p /root/rpmbuild/BUILDROOT
+ mkdir /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64
+ mkdir -p /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64/usr/local/bin
+ install -m 755 showdiskinfo /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64/usr/local/bin
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-compress
+ /usr/lib/rpm/redhat/brp-strip /usr/bin/strip
+ /usr/lib/rpm/redhat/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump
+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python 1
+ /usr/lib/rpm/redhat/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-java-repack-jars
Processing files: showdiskinfo-1.0-1.el7.x86_64
Provides: showdiskinfo = 1.0-1.el7 showdiskinfo(x86-64) = 1.0-1.el7
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: /bin/bash
Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64
Wrote: /root/rpmbuild/RPMS/x86_64/showdiskinfo-1.0-1.el7.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.4uHq3D
+ umask 022
+ cd /root/rpmbuild/BUILD
+ /usr/bin/rm -rf /root/rpmbuild/BUILDROOT/showdiskinfo-1.0-1.el7.x86_64
+ exit 0

可以看到编译成功了,文件保存在Wrote: /root/rpmbuild/RPMS/x86_64/showdiskinfo-1.0-1.el7.x86_64.rpm下,开始查看:

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
[root@localhost SPECS]# cd ../RPMS/x86_64/
[root@localhost x86_64]# rpm -qpi showdiskinfo-1.0-1.el7.x86_64.rpm
Name : showdiskinfo
Version : 1.0
Release : 1.el7
Architecture: x86_64
Install Date: (not installed)
Group : Unspecified
Size : 38390
License : GPL
Signature : (none)
Source RPM : showdiskinfo-1.0-1.el7.src.rpm
Build Date : Fri 08 Mar 2019 06:41:42 PM EST
Build Host : localhost
Relocations : (not relocatable)
URL : http://www.wumingx.cn
Summary : Show Raid and Disk info
Description :
showdiskinfo shell script.
[root@localhost x86_64]# rpm -qpl showdiskinfo-1.0-1.el7.x86_64.rpm
/usr/local/bin/showdiskinfo
#开始安装
[root@localhost x86_64]# rpm -ivh showdiskinfo-1.0-1.el7.x86_64.rpm
Preparing... ################################# [100%]
Updating / installing...
1:showdiskinfo-1.0-1.el7 ################################# [100%]
[root@localhost x86_64]# ll /usr/local/bin/
total 40
-rwxr-xr-x. 1 root root 38390 Mar 8 18:41 showdiskinfo
[root@localhost x86_64]# showdiskinfo
no raid

此内容参考了 https://rpm-packaging-guide.github.io/#hello-world ,可以阅读原版。

实例2:打包main

此代码来源鸟哥私房菜,先来处理原始码的部份:

1
2
3
4
5
6
[root@study ~]# cd /root/rpmbuild/SOURCES 
[root@study SOURCES]# wget http://linux.vbird.org/linux_basic/0520source/main-0.1.tgz
[root@study SOURCES]# wget http ://linux.vbird.org/linux_basic/0520source/main_0.1_to_0.2.patch
[root@study SOURCES]# ll main*
-rw-r--r--. 1 root root 703 Sep 4 14:47 main-0.1.tgz
-rw-r--r--. 1 root root 1538 Sep 4 14:51 main_0.1_to_0.2.patch

到SPECS目录下创建main.spec文件,建议使用vim创建,会自动生成格式。也可以用rpmdev-newspec -o Name-version.spec命令来生成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
[root@localhost SPECS]# cat main.spec
Name: main
Version: 0.1
Release: 1%{?dist}
Summary: Shows sin and cos value

Group: Scientific Support
License: GPLv2
URL: http://www.fangdm.com/
Source0: main-0.1.tgz
Patch0: main_0.1_to_0.2.patch

#BuildRequires:
#Requires:

%description
This package will let you input your name and calculate sin cos value.

%prep
%setup -q
%patch0 -p1

%build
make clean main

%install
mkdir -p %{buildroot}/usr/local/bin
install -m 755 main %{buildroot}/usr/local/bin

%files
/usr/local/bin/main
%doc

%changelog
* Wed Sep 09 2015 VBird Tsai <vbird@mail.vbird.idv.tw> 0.2
- build the program

然后就直接编译rpmbuild -ba main.spec,再安装rpm -ivh main-0.1-1.el7.x86_64.rpm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@localhost x86_64]# rpm -qi main
Name : main
Version : 0.1
Release : 1.el7
Architecture: x86_64
Install Date: Thu 07 Mar 2019 05:42:50 PM EST
Group : Scientific Support
Size : 7272
License : GPLv2
Signature : (none)
Source RPM : main-0.1-1.el7.src.rpm
Build Date : Thu 07 Mar 2019 05:42:07 PM EST
Build Host : localhost
Relocations : (not relocatable)
URL : http://www.fangdm.com/
Summary : Shows sin and cos value
Description :
This package will let you input your name and calculate sin cos value.

实例3:打包hello

使用区域语言(Locale)文件的程序应遵循 处理 i18n 文件的建议方法:

  • 在 %install 步骤中找出文件名: %find_lang ${name}
  • 添加必要的编译依赖: BuildRequires: gettext
  • 使用找到的文件名: %files -f ${name}.lang

%find_lang此宏将找到属于您的包的所有语言环境文件(按名称),并将此列表放在一个文件中。然后,您可以使用该文件包含所有语言环境。

1
2
3
4
[root@localhost SPECS]# rpm --eval %find_lang
/usr/lib/rpm/find-lang.sh /root/rpmbuild/BUILDROOT/%{name}-%{version}-%{release}.x86_64
[root@localhost SPECS]# rpm --eval %make_install
/usr/bin/make install DESTDIR=/root/rpmbuild/BUILDROOT/%{name}-%{version}-%{release}.x86_64

%find_lang %{name}在这个spec里面等价于/usr/lib/rpm/find-lang.sh /root/rpmbuild/BUILDROOT/hello-2.10-1.el7.x86_64 hello

而引用的方法是:%files -f %{name}.lang即可。

%make_install是等价于make install DESTDIR=%{buildroot}

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
[root@localhost SPECS]# cat hello.spec
Name: hello
Version: 2.10
Release: 1%{?dist}
Summary: The "Hello World" program from GNU

License: GPLv3
URL: http://ftp.gnu.org/gnu/hello/
Source0: hello-2.10.tar.gz

BuildRequires: gettext
#Requires:

%description
The "Hello World" program, done with all bells and whistles of a proper FOSS
project, including configuration, build, internationalization, help files, etc.

%prep
%setup -q


%build
%configure
make %{?_smp_mflags}

%install
%make_install
%find_lang %{name}
rm -f %{buildroot}/%{_infodir}/dir

%post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :

%preun
if [ $1 = 0 ] ; then
/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi

%files -f %{name}.lang
%{_bindir}/%{name}

%doc
%{_infodir}/%{name}.info.*
%{_mandir}/man1/%{name}.1.*

%changelog

然后直接rpmbuild -bb hello.spec进行编译,此实例来源于:https://fedoraproject.org/wiki/How_to_create_a_GNU_Hello_RPM_package/zh-cn

实例4:打包smartmontools

smartmontools 7.0已经支持json格式的输出,对于采集的话,有很大的优势。目前在centos官方上面可以找到centos7的包,如https://cbs.centos.org/koji/buildinfo?buildID=25069,但是还没有centos6的,所以需要自己写一个spec。

访问https://cbs.centos.org/koji/buildinfo?buildID=25069,先下载src.rpm包,然后`rpm -ivh smartmontools-7.0-3.el7.src.rpm`就可以提取到smartmontools.spec,这个是centos7下面环境编译的,不能直接使用。需要再下载 http://yum.aclub.net/pub/linux/centos/6/umask/SRPMS/smartmontools-6.3-1.el6.src.rpm smartmontools 6.x的版本,就可以对比开工改写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
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
[root@7ftbv7mdz7cdpc ~]# cat smartmontools.spec
Summary: Tools for monitoring SMART capable hard disks
Name: smartmontools
Version: 7.0
Release: 3%{?dist}
Epoch: 1
License: GPLv2+
URL: http://smartmontools.sourceforge.net/
#Source0: http://downloads.sourceforge.net/%{name}/%{name}-%{version}.tar.gz
Source0: %{name}-%{version}.tar.gz
Source1: smartd.initd
Source2: smartmontools.sysconf
Source4: smartdnotify
#semi-automatic update of drivedb.h
%global UrlSource5 https://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/drivedb.h?format=raw
Source5: drivedb.h

#fedora/rhel specific
Patch1: smartmontools-5.38-defaultconf.patch

#BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: fileutils chkconfig initscripts
BuildRequires: readline-devel ncurses-devel automake util-linux groff gettext
BuildRequires: libselinux-devel libcap-ng-devel

%description
The smartmontools package contains two utility programs (smartctl
and smartd) to control and monitor storage systems using the Self-
Monitoring, Analysis and Reporting Technology System (SMART) built
into most modern ATA and SCSI hard disks. In many cases, these
utilities will provide advanced warning of disk degradation and
failure.

%prep
%setup -q
%patch1 -p1 -b .defaultconf

# update SOURCE5 on maintainer's machine prior commiting, there's no internet connection on builders
#curl %{UrlSource5} -o %{SOURCE5} ||:
cp %{SOURCE5} .

%build
autoreconf -i
%configure --with-selinux --with-libcap-ng=yes --with-systemdsystemunitdir=no --with-cxx11-option=no
%ifarch sparc64
make CXXFLAGS="$RPM_OPT_FLAGS -fPIE -fno-strict-aliasing" LDFLAGS="-pie -Wl,-z,relro,-z,now"
%else
make CXXFLAGS="$RPM_OPT_FLAGS -fpie -fno-strict-aliasing" LDFLAGS="-pie -Wl,-z,relro,-z,now"
%endif

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install

rm -f examplescripts/Makefile*
chmod a-x -R examplescripts/*
mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/smartd_warning.d
install -D -p -m 755 %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/rc.d/init.d/smartd
install -D -p -m 644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/smartmontools
install -D -p -m 755 %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/smartd_warning.d/smartdnotify
rm -rf $RPM_BUILD_ROOT%{_docdir}/%{name}
mkdir -p $RPM_BUILD_ROOT%{_sharedstatedir}/%{name}

%clean
rm -rf $RPM_BUILD_ROOT

%preun
if [ "$1" = "0" ] ; then
/sbin/service smartd stop >/dev/null 2>&1
/sbin/chkconfig --del smartd
fi

%post
/sbin/chkconfig --add smartd


%files
%defattr(-,root,root,-)
%doc AUTHORS ChangeLog COPYING INSTALL NEWS README
%doc TODO examplescripts smartd.conf
%{_sbindir}/smartd
%{_sbindir}/update-smart-drivedb
%{_sbindir}/smartctl
%{_initddir}/smartd
%{_mandir}/man?/smart*.*
%{_mandir}/man?/update-smart*.*
%{_datadir}/%{name}
%{_sharedstatedir}/%{name}
%dir %{_sysconfdir}/%name
%dir %{_sysconfdir}/%name/smartd_warning.d
%config(noreplace) %{_sysconfdir}/smartd.conf
%config(noreplace) %{_sysconfdir}/smartd_warning.sh
%config(noreplace) %{_sysconfdir}/%{name}/smartd_warning.d/smartdnotify
%config(noreplace) %{_sysconfdir}/sysconfig/smartmontools

%changelog
* Thu Sep 26 2019 fangdm <fangdm@wangsu.com> - 1:7.0-3
- create smartmontools 7.0 base on centos 6

注意,Source这几个文件一定要存在,从上可以得知,加入了启动脚本、配置文件等,准备好了之后就可以使用rpmbuild -bb smartmontools.spec来编译,经过多次失败后,根据报错信息进行调整之后,终于编译成功了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@7ftbv7mdz7cdpc SPECS]# rpm -qp --changelog /root/rpmbuild/RPMS/x86_64/smartmontools-7.0-3.el6.x86_64.rpm
* Thu Sep 26 2019 fangdm <fangdm@wangsu.com> - 1:7.0-3
- create smartmontools 7.0 base on centos 6

[root@7ftbv7mdz7cdpc SPECS]# rpm -qpi /root/rpmbuild/RPMS/x86_64/smartmontools-7.0-3.el6.x86_64.rpm
Name : smartmontools Relocations: (not relocatable)
Version : 7.0 Vendor: (none)
Release : 3.el6 Build Date: Thu 26 Sep 2019 10:53:19 PM CST
Install Date: (not installed) Build Host: 7ftbv7mdz7cdpc
Group : System Environment/Base Source RPM: smartmontools-7.0-3.el6.src.rpm
Size : 2081911 License: GPLv2+
Signature : (none)
URL : http://smartmontools.sourceforge.net/
Summary : Tools for monitoring SMART capable hard disks
Description :
The smartmontools package contains two utility programs (smartctl
and smartd) to control and monitor storage systems using the Self-
Monitoring, Analysis and Reporting Technology System (SMART) built
into most modern ATA and SCSI hard disks. In many cases, these
utilities will provide advanced warning of disk degradation and
failure.

另外,在%preun阶段有使用了if [ "$1" = "0" ],表示的意思是uninstall,如下图:

实例5:打包nginx

用tar包打包成rpm的最简单的方法就是在网上找到相对应src.rpm的版本,这里面有包括了spec文件,可以少走很多弯路。nginx官方在释放tar包之后,也会相应提供的rpm以及srpm包,这次我们先在 http://nginx.org/packages/centos/7/SRPMS/ 下载好nginx-1.16.1-1.el7.ngx.src.rpm,使用rpm -ivh nginx-1.16.1-1.el7.ngx.src.rpm进行安装。有生成了如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@localhost rpmbuild]# tree
.
|-- BUILD
|-- BUILDROOT
|-- RPMS
|-- SOURCES
| |-- COPYRIGHT
| |-- logrotate
| |-- nginx-1.16.1.tar.gz
| |-- nginx-debug.service
| |-- nginx-debug.sysconf
| |-- nginx.check-reload.sh
| |-- nginx.conf
| |-- nginx.init.in
| |-- nginx.service
| |-- nginx.suse.logrotate
| |-- nginx.sysconf
| |-- nginx.upgrade.sh
| `-- nginx.vh.default.conf
|-- SPECS
| `-- nginx.spec
`-- SRPMS

主要是SPECS以及SOURCES这些文件。查看nginx.spec之后,大部分都有写好了,并且都兼容了centos 6/7。添加了gperftools GeoIP-devel ngx_cache_purge nginx-http-concat这四个模块,修改如下:

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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
[root@localhost SPECS]# cat nginx.spec
#
%define nginx_home %{_localstatedir}/cache/nginx
%define nginx_user nginx
%define nginx_group nginx
%define nginx_loggroup adm

# distribution specific definitions
%define use_systemd (0%{?rhel} >= 7 || 0%{?fedora} >= 19 || 0%{?suse_version} >= 1315 || 0%{?amzn} >= 2)

%if %{use_systemd}
BuildRequires: systemd
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
%endif

%if 0%{?rhel}
%define _group System Environment/Daemons
%endif

%if 0%{?rhel} == 6
Requires(pre): shadow-utils
Requires: initscripts >= 8.36
Requires(post): chkconfig
Requires: openssl >= 1.0.1
BuildRequires: openssl-devel >= 1.0.1
%endif

%if 0%{?rhel} == 7
%define epoch 1
Epoch: %{epoch}
Requires(pre): shadow-utils gperftools GeoIP-devel
Requires: openssl >= 1.0.2
BuildRequires: openssl-devel >= 1.0.2
BuildRequires: GeoIP-devel gperftools
%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

%if 0%{?suse_version} >= 1315
%define _group Productivity/Networking/Web/Servers
%define nginx_loggroup trusted
Requires(pre): shadow
BuildRequires: libopenssl-devel
%define _debugsource_template %{nil}
%endif

# end of distribution specific definitions

%define main_version 1.16.1
%define main_release 1%{?dist}.ngx

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

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

%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-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module")

Summary: High performance web server
Name: nginx
Version: %{main_version}
Release: %{main_release}
Vendor: Nginx, Inc.
URL: http://nginx.org/
Group: %{_group}

Source0: http://nginx.org/download/%{name}-%{version}.tar.gz
Source1: logrotate
Source2: nginx.init.in
Source3: nginx.sysconf
Source4: nginx.conf
Source5: nginx.vh.default.conf
Source7: nginx-debug.sysconf
Source8: nginx.service
Source9: nginx.upgrade.sh
Source10: nginx.suse.logrotate
Source11: nginx-debug.service
Source12: COPYRIGHT
Source13: nginx.check-reload.sh
Source14: nginx-http-concat.tar.gz
Source15: ngx_cache_purge.tar.gz

License: 2-clause BSD-like license

BuildRoot: %{_tmppath}/%{name}-%{main_version}-%{main_release}-root
BuildRequires: zlib-devel
BuildRequires: pcre-devel

Provides: webserver

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

%if 0%{?suse_version} >= 1315
%debug_package
%endif

%prep
%setup
%setup -T -D -a 14
%setup -T -D -a 15
cp %{SOURCE2} .
sed -e 's|%%DEFAULTSTART%%|2 3 4 5|g' -e 's|%%DEFAULTSTOP%%|0 1 6|g' \
-e 's|%%PROVIDES%%|nginx|g' < %{SOURCE2} > nginx.init
sed -e 's|%%DEFAULTSTART%%||g' -e 's|%%DEFAULTSTOP%%|0 1 2 3 4 5 6|g' \
-e 's|%%PROVIDES%%|nginx-debug|g' < %{SOURCE2} > nginx-debug.init

%build
./configure %{BASE_CONFIGURE_ARGS} \
--with-cc-opt="%{WITH_CC_OPT}" \
--with-ld-opt="%{WITH_LD_OPT}" \
--with-debug \
--with-google_perftools_module \
--add-module=ngx_cache_purge \
--add-module=nginx-http-concat \
--with-http_geoip_module

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

%install
%{__rm} -rf $RPM_BUILD_ROOT
%{__make} DESTDIR=$RPM_BUILD_ROOT INSTALLDIRS=vendor install

%{__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%{_datadir}/doc/%{name}-%{main_version}
%{__install} -m 644 -p %{SOURCE12} \
$RPM_BUILD_ROOT%{_datadir}/doc/%{name}-%{main_version}/

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

%{__mkdir} -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig
%{__install} -m 644 -p %{SOURCE3} \
$RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/nginx
%{__install} -m 644 -p %{SOURCE7} \
$RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/nginx-debug

%{__install} -p -D -m 0644 %{bdir}/objs/nginx.8 \
$RPM_BUILD_ROOT%{_mandir}/man8/nginx.8

%if %{use_systemd}
# install systemd-specific files
%{__mkdir} -p $RPM_BUILD_ROOT%{_unitdir}
%{__install} -m644 %SOURCE8 \
$RPM_BUILD_ROOT%{_unitdir}/nginx.service
%{__install} -m644 %SOURCE11 \
$RPM_BUILD_ROOT%{_unitdir}/nginx-debug.service
%{__mkdir} -p $RPM_BUILD_ROOT%{_libexecdir}/initscripts/legacy-actions/nginx
%{__install} -m755 %SOURCE9 \
$RPM_BUILD_ROOT%{_libexecdir}/initscripts/legacy-actions/nginx/upgrade
%{__install} -m755 %SOURCE13 \
$RPM_BUILD_ROOT%{_libexecdir}/initscripts/legacy-actions/nginx/check-reload
%else
# install SYSV init stuff
%{__mkdir} -p $RPM_BUILD_ROOT%{_initrddir}
%{__install} -m755 nginx.init $RPM_BUILD_ROOT%{_initrddir}/nginx
%{__install} -m755 nginx-debug.init $RPM_BUILD_ROOT%{_initrddir}/nginx-debug
%endif

# install log rotation stuff
%{__mkdir} -p $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d
%if 0%{?suse_version}
%{__install} -m 644 -p %{SOURCE10} \
$RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/nginx
%else
%{__install} -m 644 -p %{SOURCE1} \
$RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/nginx
%endif

%{__install} -m755 %{bdir}/objs/nginx-debug \
$RPM_BUILD_ROOT%{_sbindir}/nginx-debug

%check
%{__rm} -rf $RPM_BUILD_ROOT/usr/src
cd %{bdir}
grep -v 'usr/src' debugfiles.list > debugfiles.list.new && mv debugfiles.list.new debugfiles.list
cat /dev/null > debugsources.list
%if 0%{?suse_version} >= 1500
cat /dev/null > debugsourcefiles.list
%endif

%clean
%{__rm} -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root)

%{_sbindir}/nginx
%{_sbindir}/nginx-debug

%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

%config(noreplace) %{_sysconfdir}/logrotate.d/nginx
%config(noreplace) %{_sysconfdir}/sysconfig/nginx
%config(noreplace) %{_sysconfdir}/sysconfig/nginx-debug
%if %{use_systemd}
%{_unitdir}/nginx.service
%{_unitdir}/nginx-debug.service
%dir %{_libexecdir}/initscripts/legacy-actions/nginx
%{_libexecdir}/initscripts/legacy-actions/nginx/*
%else
%{_initrddir}/nginx
%{_initrddir}/nginx-debug
%endif

%attr(0755,root,root) %dir %{_libdir}/nginx
%attr(0755,root,root) %dir %{_libdir}/nginx/modules
%dir %{_datadir}/nginx
%dir %{_datadir}/nginx/html
%{_datadir}/nginx/html/*

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

%dir %{_datadir}/doc/%{name}-%{main_version}
%doc %{_datadir}/doc/%{name}-%{main_version}/COPYRIGHT
%{_mandir}/man8/nginx.8*

%pre
# 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

%post
# Register the nginx service
if [ $1 -eq 1 ]; then
%if %{use_systemd}
/usr/bin/systemctl preset nginx.service >/dev/null 2>&1 ||:
/usr/bin/systemctl preset nginx-debug.service >/dev/null 2>&1 ||:
%else
/sbin/chkconfig --add nginx
/sbin/chkconfig --add nginx-debug
%endif
# print site info
cat <<BANNER
----------------------------------------------------------------------

Thanks for using nginx!

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

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

Commercial subscriptions for nginx are available on:
* http://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

%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 Sep 30 2019 fangdm <fangdm@wangsu.com>
- Add modules nginx-http-concat and ngx_cache_purge

* Tue Aug 13 2019 Andrei Belov <defan@nginx.com>
- 1.16.1

编译完成之后,查看changelog,是成功了。

1
2
3
4
5
6
[root@localhost SPECS]# rpm --changelog -qp /root/rpmbuild/RPMS/x86_64/nginx-1.16.1-1.el7.ngx.x86_64.rpm
* Mon Sep 30 2019 fangdm <fangdm@wangsu.com>
- Add modules nginx-http-concat and ngx_cache_purge

* Tue Aug 13 2019 Andrei Belov <defan@nginx.com>
- 1.16.1

fpm打包方法

使用fpm这个方式也是可以将tar打包为rpm包,其打包流程大致是这样的:

  1. 编译安装被打包软件,指定安装的临时目录,如指定--prefix=/fpmbulid
  2. 创建安装脚本、卸载脚本
  3. 安装FPM工具
  4. 使用fpm打包

具体的细节,请参考:FPM,更快捷方便的制作rpm包

参考资料

官方文档:https://rpm-packaging-guide.github.io/

RPM打包原理、示例、详解及备查 https://blog.csdn.net/get_set/article/details/53453320

RPM 包制作(详细):https://jin-yang.github.io/post/linux-create-rpm-package.html

spec文件查找:https://git.centos.org/

一堂课玩转rpm包的制作

rpm Scriptlets变量说明: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/

spec中的 Install命令:https://www.cnblogs.com/wangziyi0513/p/10252458.html

  • 本文作者: wumingx
  • 本文链接: https://www.wumingx.com/linux/tar_to_rpm.html
  • 本文主题: 手把手教你将tar转rpm包
  • 版权声明: 本博客所有文章除特别声明外,转载请注明出处!如有侵权,请联系我删除。
0%