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 #确定包的最终使用平台