Jusene's Blog

RPM包 制作

字数统计: 3.1k阅读时长: 13 min
2017/08/21 Share

RPM包

安装程序的时候大多是编译安装,但是确实是费时费力,而且无法定制化安装后的内容,需要重复的配置文件及调优化参数,所以不得不学习下rpm包的制作来满足下偷懒的想法。

整个rpm包的制作过程中,spec文件是整个制作过程总纲型文件,它的作用相当于编译程序时的Makefile文件,spec文件包含建立一个rpm包必需的信息,包含哪些文件是包的一部分以及它们安装在那些目录下。

而整个制作过程通过rpmbuild来实现,而整个制作工厂包含以下目录:

  • BUILD rpmbuild制作rpm的目录,在这个目录下编译,make等操作。
  • RPMS rpmbuild放置制作好的二进制rpm包的目录
  • SOURCES 放置源材料的目录
  • SPECS 放置spec文件的目录
  • SRPMS rpmbuild放置制作好的源码rpm包的目录
  • BUILDROOT rpmbuild在安装(make install)过程中的虚根,默认会自动生成,也可以自定义buidroot

整个rpm包的制作过程中,我们会遇到许多宏变量,这些宏我们可以定义为变量,其实这些宏可以很好的规避掉不同OS下,命令不同或路径不同而造成的制作错误,也定语许多了环境变量等。我们可以使用如下命令来查看:

1
~]# yum install -y rpm-build rpmdevtools
2
~]# rpmbuild --showrc

其中我们需要注意下的宏有:

1
~]# rpmbuild --showrc | grep Macro
2
Macro path: /usr/lib/rpm/macros:/usr/lib/rpm/macros.d/macros.*:/usr/lib/rpm/platform/%{_target}/macros:/usr/lib/rpm/fileattrs/*.attr:/usr/lib/rpm/redhat/macros:/etc/rpm/macros.*:/etc/rpm/macros:/etc/rpm/%{_target}/macros:~/.rpmmacros

这是宏定义读取的路径,后面定义的宏会覆盖前面定义的宏,所以很简单,我们需要定义宏的时候,只要在自己的家目录下创建下~/.rpmmacros即可

除了这个我们还需要查看下rpmbuid的工作目录的宏:

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

从上面的宏中我们可以很清楚的看出我们的制作工厂应该在那了,每个用户家目录下的rpmbuild目录,当然这个_topdir只要我们在~/.rpmmacros修改掉宏就会改变。

让我们来创建下rpmbuild工厂,建议不要在root下制作rpm,因为spec文件写错,在root权限下可能是灾难性的:

1
~]# useradd rpm
2
~]# su - rpm
3
~]$ mkdir rpmbuild
4
~]$ cd rpmbuild
5
~]$ rpmdev-setuptree
6
~]$ ls
7
BUILD  RPMS  SOURCES  SPECS  SRPMS
8
~]# cd SPECS
9
~]# rpmdev-newspec -o nginx-1.12.0.spec           #创建个模板
10
nginx-1.12.0.spec created; type minimal, rpm version >= 4.11.
11
~]# vim nginx-1.12.0.spec
12
Name:           nginx           #把版本去掉,spec文件却不允许有‘-’号
13
Version:
14
Release:        1%{?dist}
15
Summary:
16
17
License:
18
URL:
19
Source0:
20
21
BuildRequires:
22
Requires:
23
24
%description
25
26
27
%prep
28
%setup -q
29
30
31
%build
32
%configure
33
make %{?_smp_mflags}
34
35
36
%install
37
rm -rf $RPM_BUILD_ROOT
38
%make_install
39
40
41
%files
42
%doc
43
44
45
46
%changelog

spec文件编写

Preamle(序言)

序言主要包含了包的功能描述、包的软件版本、版权信息、所属包组、依赖关系、资源等。

  • Name:软件包的基名
  • Version:软件包的版本
  • Release:rpm包的发行版本,如果修改了rpm的内容,就需要增加
  • Summary:是一行关于该软件的描述
  • License:软件包遵循的开源协议
  • URL:该软件包可以从哪里获取
  • Group:标识软件类型,可以在/usr/share/doc/rpm-4.11.3/GROUPS查看rpm支持的组
  • Source0:标识SOURCES目录下的资源(通常为tar.gz),也可以是我们修改好的配置文件等
  • BuildRequires:编译时需要的依赖,可以出现多次
  • Requires:安装时需要的依赖,可以出现多次
  • Vendor:提供该软件包的组织
  • Packager:制作者的名字
  • Patch1:补丁
  • BuildRoot:修改BUILDROOT的工作目录
  • Provides:该rpm包提供的功能
  • %description:该rpm包的详细描述

软件包所属类别,具体类别有:
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硬件支持)


%prep

prep进行实际打包的准备过程,一般来说,这个过程主要过程是检查标签语法是否正确,删除旧的软件源程序,对包含对源程序的tar文件进行解码。如果包含补丁(patch)文件,将补丁文件应用到解开的源码中。一般为%setup和%patch两个命令,%setup将软件源码包解开,执行%patch将补丁文件加入解开的源程序。

%setup

  • -n newdir 将压缩的软件源程序在newdir目录下解开
  • -c newdir 在解开源程序之前先创建目录
  • -b num 将第num个source文件解压缩
  • -T 不使用default的解压缩操作
  • -q 静默模式

%patch
%patch 0 使用第0个补丁信息,相当于%patch ?p 0。
%patch -s 不显示打补丁的信息
%patch -T 将所以打补丁时产生的输出文件删除
%patch -b name 在加入补丁文件之前,将源文件名加上name。若指定此参数,则缺省源文件加入.orig

%build

开始构建包,也就是开始编译的过程,在%{_topdir}/BUILD/%{name}-%{version}目录中进行make的工作,常见的写法:
make %{?_smp_mflags}

%install

开始将软件安装在虚拟根目录下 %{_topdir}/BUILDROOT ,当然如果我们在 BuildRoot中重新定义虚根的位置,多数为%{_tmppath}/%{name}-%{version}-%{release}-root,个人不明白为什么要这么定义,我使用默认的。

在install之前都需要清理下虚根目录,我们在看下%make_install的定义:

1
~]# rpmbuild --showrc | grep make_install
2
-14: make_install       %{__make} install DESTDIR=%{?buildroot}

这个宏定义已经满足我们的要求了。

在install过程我们可以把我们需要修改的文件source,优化文件等,install进虚根的目录,这里的虚根下我们加的内容在等下的%file过程中都是需要包含的。

1
%{__install}  -d  $RPM_BUILD_ROOT/unit   创建一个目录
2
%{__install} -p -D -m 644 %{SOURCE1} $RPM_BUILD_ROOT/conf/nginx.conf  替换掉一个优化过的配置文件

%clean

这里是清理工作,一般清理$RPM_BUILD_ROOT下的内容,但是我们需要注意的是如果有人恶意修改了宏定义为‘/’,将会发生灾难性的事情,所以不要在root下制作rpm包。
rm -rf $RPM_BUILD_ROOT
或者
[ “$RPM_BUILD_ROOT” != ‘/‘ ] && rm -rf $RPM_BUILD_ROOT 加层判断

脚本段

%pre rpm安装前执行的脚本
%post rpm安装后执行的脚本
%preun rpm卸载前执行的脚本
%postun rpm卸载后执行的脚本

这里还有一层定义$1:
$1 == 0 卸载
$1 == 1 安装
$1 == 2 升级

%file

定义将那些文件或目录加入rpm包中,这里是最容易出错的地方,在虚根中存在的文件未加入rpm包会报错,这里定义了,虚根下没有内容也会报错。这里的一切都是相对于虚根下进行的,千万别写确定路径。

可以使用%defattr 来定义缺省的许可权、所有者和组;如%defattr(-,root,root)。
可以用 %attr(permissions,user,group) 覆盖个别文件的所有者和许可权。
%doc 告诉 RPM 这是一个文档文件,可以在 %doc 下不带路径列出文件名,RPM会在构建目录下查找这些文件并在 RPM 文件中包括它们,并把它们安装到 /usr/share/doc/%{name}-%{version}。
%docdir 告诉RPM 这目录下全部都是文档文件
%config 告诉RPM 这是一个配置文件,配置文件有简单的属性定义(noreplace,missingok),从字面的定义上来说,noreplace表示该配置文件再升级rpm包的时候不能被替换,missingok表示不存在也可以。

%changelog

changelog就是记录下这次rpm的重新发行,我们都做那些调整。

补充的宏

1
1. RPM_BUILD_DIR:    /usr/src/redhat/BUILD
2
2. RPM_BUILD_ROOT:   /usr/src/redhat/BUILDROOT
3
3. %{_sysconfdir}:   /etc
4
4. %{_sbindir}:     /usr/sbin
5
5. %{_bindir}:       /usr/bin
6
6. %{_datadir}:      /usr/share
7
7. %{_mandir}:       /usr/share/man
8
8. %{_libdir}:       /usr/lib64
9
9. %{_prefix}:       /usr
10
10. %{_localstatedir}:   /usr/var
11
11. %{_exec_prefix}  %{_prefix}
12
12. %{_lib}          lib (lib64 on 64bit system)

gpg

rpm存在被篡改的风险,所以这里我们可以使用gpg来进行认证rpm包是否被篡改过。

1
gpg --gen-key   生成私钥
2
gpg --list-keys  查看私钥
3
gpg --export -a ‘Jusene’ > rpm-gpg-key-Jusene  提取公钥
4
rpm --addsign nginx.rpm  签名rpm包
5
6
rpm --import rpm-gpg-key-Jusene  载入公钥
7
rpm -checksig nginx.rpm  检查签名

我们还可以在spec文件生成好自动签名rpm包:

1
%_signature gpg
2
%_gpg_name Jusene

拆包

%package libs 我们需要才开libs包
Summary
Group
Requires: %{name} = %{version}
%description libs

只要在相同的操作步骤下指定这是针对那个包执行的操作即可。

1
%define  nginx_user  www
2
%define  nginx_group %{nginx_user}
3
4
Name:           nginx
5
Version:        1.10.3 
6
Release:        4
7
Summary:        nginx server rpm
8
9
License:        BSD
10
URL:            http://nginx.org
11
Source0:        %{name}-%{version}.tar.gz
12
Source1:        nginx.conf
13
Source2:        nginx.unit
14
Source3:        enable-php.conf
15
BuildRequires:  openssl-devel,pcre-devel,gcc
16
Requires:       openssl,pcre
17
Requires(pre):  shadow-utils
18
Requires(post): systemd
19
%description
20
nginx server
21
22
23
%prep
24
%setup -q
25
26
27
%build
28
./configure --prefix=/usr/local/nginx \
29
        --with-http_ssl_module \
30
        --with-http_stub_status_module \
31
        --with-http_realip_module \
32
        --user=%{nginx_user} \
33
        --group=%{nginx_group}
34
make %{?_smp_mflags}
35
36
37
%install
38
rm -rf $RPM_BUILD_ROOT
39
%make_install
40
%{__install} -p -D -m 644 %{SOURCE1} $RPM_BUILD_ROOT/usr/local/nginx/conf/nginx.conf
41
%{__install} -p -D -m 644 %{SOURCE3} $RPM_BUILD_ROOT/usr/local/nginx/conf/enable-php.conf
42
%{__install} -p -D -m 644 %{SOURCE2} $RPM_BUILD_ROOT/usr/local/nginx/unit/nginx.service
43
%{__install} -p -d $RPM_BUILD_ROOT/usr/local/nginx/conf/vhosts
44
45
46
%files
47
%defattr(-,root,root,-)
48
%config(noreplace) /usr/local/nginx/conf/*.conf
49
%config(noreplace) /usr/local/nginx/conf/*.default
50
%config(noreplace) /usr/local/nginx/conf/*params
51
%config(noreplace) /usr/local/nginx/conf/koi-utf
52
%config(noreplace) /usr/local/nginx/conf/koi-win
53
%config(noreplace) /usr/local/nginx/conf/win-utf
54
%config(noreplace) /usr/local/nginx/conf/mime.types
55
%dir /usr/local/nginx/conf/vhosts
56
%dir /usr/local/nginx/logs
57
/usr/local/nginx/unit/nginx.service
58
/usr/local/nginx/html/index.html
59
/usr/local/nginx/html/50x.html
60
/usr/local/nginx/sbin/nginx
61
62
%clean
63
rm -rf $RPM_BUILD_ROOT
64
65
66
%pre
67
if [ $1 == 1 ];then
68
        if ! id -u www &> /dev/null;then
69
                /usr/sbin/useradd -s /bin/false -r www
70
        fi
71
fi
72
73
%post
74
if [ $1 == 1 ];then
75
        %{__install} -p -m 644 $RPM_BUILD_ROOT/usr/local/nginx/unit/nginx.service /usr/lib/systemd/system/nginx.service
76
        /usr/bin/systemctl enable nginx.service  &> /dev/null
77
fi
78
79
%preun
80
if [ $1 == 0 ];then
81
        /usr/bin/systemctl disable nginx.service &> /dev/null
82
        /usr/bin/systemctl stop nginx.service
83
        if [ -f /usr/local/nginx/logs/nginx.pid ];then
84
                /usr/bin/killall nginx
85
        fi 
86
fi
87
88
%postun
89
if [ $1 == 0 ];then
90
        %{__rm} -f /usr/lib/systemd/system/nginx.service
91
        %{__rm} -rf /usr/local/nginx
92
fi
93
94
%changelog
95
* Wed Aug 23 2017 Jusene
96
- initial nginx server

rpmbuild

rpmbuild
-bp 执行到%prep段
-bi 执行到%install段
-bc 执行到%build段
-bb 制作成二进制的包
-bs 制作成源码包
-ba 制作成二进制包和源码包

–buildroot=DIRECTORY #确定以root目录建立包
–clean #完成打包后清除BUILD下的文件目录
–nobuild #不进行%build的阶段
–nodeps #不检查建立包时的关联文件
–nodirtokens
–rmsource #完成打包后清除SOURCES
–rmspec #完成打包后清除SPEC
–short-cricuit
–target=CPU-VENDOR-OS #确定包的最终使用平台

CATALOG
  1. 1. RPM包
  2. 2. spec文件编写
    1. 2.1. Preamle(序言)
    2. 2.2. %prep
    3. 2.3. %build
    4. 2.4. %install
    5. 2.5. %clean
    6. 2.6. 脚本段
    7. 2.7. %file
    8. 2.8. %changelog
    9. 2.9. 补充的宏
    10. 2.10. gpg
    11. 2.11. 拆包
    12. 2.12. rpmbuild