本文系原创,转载或其它任何使用方法均需要得到作者授权!

一、RPM简介
Linux系统上的软件包管理器用于实现对系统上的安装的程序进行跟踪和管理,其中比较著名的有rpm,dpkg等。RPM的全称为RPM Package Manager(早期也叫做Red Hat Package Manager),它最早是Red Hat开发用于实现在Red Hat Linux上软件管理的工具;而目前得益于其强大的管理功能,许多发行版都采用了RPM作为其进行软件包管理的工具,这也使得RPM成为了事实上的工业标准。当然,RPM这个软件本身也由Red Hat基于开源协定发行。最近,RPM文件格式也被LSB(Linux Standards Base)采用为官方标准。
RPM最早主要由Red Hat的Marc Ewing和Erik Troan开发,其在管理软件包方面表现出了以下特性(即其设计目标):
(1)易用性
(2)软件包导向(即以package为中心)
(3)便于升级
(4)跟踪软件包之间的依赖关系
(5)易于查询
(6)支持多种系统架构
(7)使用纯净的源代码(pristine source)
制作好的RPM格式的软件包包含一组带标签的数据(如NAME:nginx)和软件包主体,其通常是一个归档压缩文件,里面包含一个或多个文件,以及用于指明各文件安装位置、属主/属组、权限等信息的安装指令。标签数据用于描述软件包必要可选特征;而软件包主体则主要是指要安装在系统上的文件。进一步来说,RPM包通常包含四个组成部分:用于标示文件格式为RPM格式的前导标示区、数字签名、标签数据(也叫首部)和主体。
为了便于软件包的管理,所有的rpm包的命令格式均遵循统一的规范,其共有四个部分组成:name-version-release.architecture.rpm。
RPM分为两种类型:二进制格式和源码。二进制格式的RPM包已经编译好且可安装于特定平台,里面通常包含编译好的程序、库文件、配置或帮助文件等。当然,也有一些二进制格式的RPM包是平台无关的,即其名称中平台部分为noarch的RPM包。而源码格式的RPM包通常是打包起来的可用于制作二进制格式RPM包的命令、脚本、程序源码和程序补丁等,其可用于在特定平台上进行快速制作生成二进制格式的RPM包。
在系统上,RPM数据库用于存储安装至当前系统的所有RPM包的相关信息,其通常是位于/var/lib/rpm/目录中的众多文件。这些文件可用于已安装RPM包的查询、校验等功能,其通常为:
[[email protected] ~]# ls -lh /var/lib/rpm/
total 35M
-rw-r--r-- 1 root root 4.8M Jan 15 14:09 Basenames
-rw-r--r-- 1 root root  12K Jan 15 14:08 Conflictname
-rw-r--r-- 1 root root    0 Jan 15 14:02 __db.000
-rw-r--r-- 1 root root  24K Jan 15 14:00 __db.001
-rw-r--r-- 1 root root 1.3M Jan 15 14:00 __db.002
-rw-r--r-- 1 root root 440K Jan 15 14:00 __db.003
-rw-r--r-- 1 root root 1.1M Jan 15 14:09 Dirnames
-rw-r--r-- 1 root root 5.1M Jan 15 14:09 Filemd5s
-rw-r--r-- 1 root root  12K Jan 15 14:09 Group
-rw-r--r-- 1 root root  16K Jan 15 14:09 Installtid
-rw-r--r-- 1 root root  40K Jan 15 14:09 Name
-rw-r--r-- 1 root root  24M Jan 15 14:09 Packages
-rw-r--r-- 1 root root 168K Jan 15 14:09 Providename
-rw-r--r-- 1 root root  76K Jan 15 14:09 Provideversion
-rw-r--r-- 1 root root  12K Feb  2  2012 Pubkeys
-rw-r--r-- 1 root root 232K Jan 15 14:09 Requirename
-rw-r--r-- 1 root root 156K Jan 15 14:09 Requireversion
-rw-r--r-- 1 root root  84K Jan 15 14:09 Sha1header
-rw-r--r-- 1 root root  44K Jan 15 14:09 Sigmd5
-rw-r--r-- 1 root root  12K Jan 15 14:02 Triggername
其中,__db.001类的文件是RPM用于软件包管理时的锁文件,余下的其它文件通常为Berkeley DB格式的文件,其中最重要的是Packages,其包含了当前系统已经安装的所有RPM包的首部标签信息。其它文件如Name,Providename和Group等通常是为提高访问效率的,一旦损坏,它们都可以使用rpm命令的--rebuild进行重建。
二、RPM的相关命令简介
rpm命令:执行RPM包的安装、升级、卸载、校验、查询等相关管理工作的基本命令。
rpmbuild命令:制作RPM包的命令。
rpm2cpio命令:将RPM包展开为cpio格式的命令,其展开结果通常通过“管道”传送给cpio命令做进一步处理。如:
# rpm2cpio  ngnix-1.0.14-1.el5.i386.rpm | cpio -t
# rpm2cpio  ngnix-1.0.14-1.el5.i386.rpm | cpio -id
三、RPM包的依赖关系
程序通常不是完全独立运行的,其总会建构在其它功能的基础上,如nginx就需要用到系统上的网络相关的库文件、SSL库文件、I/O相关的库文件等。而这些表现在提供各不同功能的RPM包上,就是包之间的依赖关系。
以RPM特有的方式描述,每个包都提供一种特定的能力(capabilities)。在大多数情况下,能力用文件的名字或软件包的名字表示,但也可以为某RPM明确定义其“能力”。一个特定RPM包的依赖关系通常定义为其依赖于某种“能力”,安装一个RPM包时,其“能力”就会保存至RPM数据库。而卸载RPM时,如果其“能力”被其它的RPM所依赖,卸载命令就会生成一个错误信息。
在定义RPM包的依赖关系时,通常还能够根据实际定义其依赖的RPM包的版本信息。此外,当多个软件包也有可能提供相同“能力”,比如httpd和thttpd。在系统上存在某“能力”时,系统再安装相同能力的软件包,就会引发RPM包冲突(conflicts)。同时,当一个新版本的RPM包提供比其老版本的更新的“能力”时,就会导致原来“能力”的废弃(obsoletes)。
rpm命令的--requires选项和-q选项一起使用,可以用来查询某RPM包所依赖的“能力”,如:
[[email protected] ~]# rpm -q --requires httpd
/bin/bash
/bin/mktemp
/bin/mv
/bin/rm
/bin/sh
/bin/sh
/bin/sh
/bin/sh
/bin/sh
/etc/mime.types
/sbin/chkconfig
/usr/bin/find
/usr/sbin/useradd
/usr/share/magic.mime
config(httpd) = 2.2.3-31.el5
gawk
initscripts >= 8.36
... ...
... ...
rpm命令的--provides选项和-q选项一起使用,可以用来查询某RPM包所提供的“能力”,如:
[[email protected] ~]# rpm -q --provides httpd
config(httpd) = 2.2.3-31.el5
httpd-mmn = 20051115
mod_actions.so
mod_alias.so
mod_asis.so
mod_auth_basic.so
mod_auth_digest.so
... ...
... ...
rpm命令的--conflicts选项和-q选项一起使用,可以用来查询跟某RPM包所冲突的“能力”,如:
[[email protected] ~]# rpm -q --conflicts httpd
pcre < 4.0
rpm命令的--whatrequires选项和-q选项一起使用,可以用来查询依赖于某RPM包所提供“能力”的其它“能力”,如:
[[email protected] ~]# rpm -q --whatrequires initscripts
portmap-4.0-65.2.2.1
foomatic-3.0.2-38.3.el5
kbd-1.12-21.el5
dhclient-3.0.5-21.el5
... ...
... ...
四、RPM中的事务
事务即为被看作一个整体的一组操作,所有的操作可以由“回滚”撤销,并且“回滚”后,系统可以重回原来的状态。如果事务执行成功,系统将从旧状态转至新状态,而一旦事务执行过程中的任何一个操作出现故障,系统将会处于不一致状态,此时就需要事务“回滚”来保证系统状态的一致性。
在RPM系统中,在安装或删除软件包时就会引发一个事务,并给予被操作的所有rpm包一个事务ID。而后,基于相同的事务ID可以实现对事务的回滚等各种操作。但目前来说,RPM系统仅支持对软件升级的操作进行回滚。事务给予RPM系统的一个最大好处就是在一个rpm命令中,如果某软件包安装出现错误,整个命令就不会进行,而不会出现安装了部分软件包的情况。
查看某软件包安装时的事务ID的方法如下:
[[email protected] ~]# rpm -q --qf "%-20{NAME} %-20{INSTALLTID}\n" httpd
httpd                1332925671          
如果想查看某个ID的事务中安装了哪些软件包,可使用如下方法:
[[email protected] ~]# rpm -q --tid 1332925671
httpd-2.2.3-31.el5

五、创建RPM包快速实现步骤

5.1、制作过程概述
我们首先通过展开一个制作完成的rpm包中的文件列表来查看其内部文件的构成:
[[email protected] ~]# rpm2cpio nginx-1.0.14-1.i386.rpm | cpio -t
./etc/logrotate.d/nginx
./etc/nginx
./etc/nginx/fastcgi.conf
./etc/nginx/fastcgi.conf.default
./etc/nginx/fastcgi_params
./etc/nginx/fastcgi_params.default
./etc/nginx/koi-utf
./etc/nginx/koi-win
./etc/nginx/mime.types
./etc/nginx/mime.types.default
./etc/nginx/nginx.conf
./etc/nginx/nginx.conf.default
./etc/nginx/scgi_params
./etc/nginx/scgi_params.default
./etc/nginx/uwsgi_params
./etc/nginx/uwsgi_params.default
./etc/nginx/win-utf
./etc/rc.d/init.d/nginx
./usr/html/50x.html
./usr/html/index.html
./usr/sbin/nginx
./usr/share/doc/nginx-1.0.14
./usr/share/doc/nginx-1.0.14/CHANGES
./usr/share/doc/nginx-1.0.14/LICENSE
./usr/share/doc/nginx-1.0.14/README
由此可见,制作完成的二进制rpm中包含的其实是要安装到Linux系统上的文件的集合,其在上面列表中路径即为其安装在系统上之后所在的位置。因此,制作RPM包的第一步就是要确定包含在其中的文件以,同时还要确定这些文件的来源,比如,对一个应用程序来说,是事先编译好的各文件及其它文件,还是在制作RPM包的过程中编译生成等。当这一切确定好之后,还需要创建一个制作此RPM包的specs文件,用于指明制作过程如何进行。
而后就可以测试制作以及生成最后的rpm包了。上述过程大致说来,可以分为如下几个步骤:
(1)确定要生成的RPM包,包括打包的所有内容和制作完成后要生成RPM包的类型;
(2)收集打包用到的相关源程序、相关文件及补丁文件;
(3)如要打包的是一个应用程序,还需要事先阅读其README、INSTALL等相关文件,并测试出其符合制作需要并可正常完成编译安装过程;
(4)如果此RPM包可能用于升级老版本程序,还需要规划其升级相关事项,比如,如何处理老版本程序的配置文件,是不是要执行一些处理脚本等;
(5)确定此RPM包的依赖关系,包括制作时依赖的“能力”及安装时依赖的“能力”等;
(6)制作生成RPM包;
而具体制作生成RPM包的过程,简单来说,就是为制作过程提供一个“工作车间”,即一个目录,里面需要包含以下几个子目录:
BUILD   —— 编译相关源码包时的工作目录;
RPMS    —— 生成的二进制格式的RPM包存放的位置;
SOURCES —— 程序的源码文件及其它辅助文件的存放位置;
SPCES   —— spec文件的存放位置;
SRPMS   —— 生成的src格式的RPM包存放的位置;
当“工作车间”准备停当之后,将用到的源码及辅助文件放置于相应的SOURCES目录中,并在SPECS目录中创建一个spec文件,就可以使用rpmbuild命令开始制作过程了。由于root用户的权限没有任何限制,RPM制作过程的不当命令有可能会给系统带去破坏,所以,一定不能使用root用户执行制作过程。切记!
5.2、rpm宏及其定义方法
rpm、rpmbuild等命令在工作时依赖于事先定义好的工作环境,这些环境相关的信息是通过rpm宏来定义的,可以使用命令的--showrc选项来获得。比如,通常情况下,在Red Hat Linux系统上,制作RPM包的“工作车间”默认为/usr/src/redhat目录,而事实上这就是通过_topdir这个rpm宏来定义的。我们就可以通过rpmbuild的--showrc选项来中的宏_topdir来获取“工作车间”位置的定义,在一切均为默认设置的rpm工作环境中,由于_topdir也引用了其它的宏,因此,我们可以使用如下命令一并获取其相关信息:
[[email protected] ~]$ rpmbuild --showrc |grep  _usr
-14: _defaultdocdir %{_usr}/share/doc
-14: _topdir %{_usrsrc}/redhat
-14: _usr /usr
-14: _usrsrc %{_usr}/src
如果要专门获取宏_topdir自身的相关信息,可以使用如下命令:
[[email protected] ~]$ rpmbuild --showrc |grep _topdir
-14: _builddir %{_topdir}/BUILD
-14: _rpmdir %{_topdir}/RPMS
-14: _sourcedir %{_topdir}/SOURCES
-14: _specdir %{_topdir}/SPECS
-14: _srcrpmdir %{_topdir}/SRPMS
-14: _topdir %{_usrsrc}/redhat
事实上,也可以使用rpm命令直接获取某个rpm宏的定义:
[[email protected] ~]$ rpm --eval  '%_topdir'
/usr/src/redhat
上文中,_topdir用于定义制作RPM包时的“工作车间”目录,它被定义为了一个“宏”,其作用类似于脚本编程中的变量。定义或引用某宏的值的时候使用%{macro_name}或%macro_name的方式进行。上面的命令执行结果可以看出,_topdir宏也是通过引用_usr和_usrsrc这两个宏来确定自己的位置的,即为%{_usrsrc}/redhat这个路径。rpmbuild命令在制作RPM时,会以_topdir所指定的目录作为工作的顶级目录,并在其中按需要查找BUILD,RPMS,SOURCES,SPRMS,SPECS等目录。因此,只要改变了其值就可以实现改变“工作车间”的位置。
默认情况下,这些宏是通过rpm宏的相关配置文件来来定义的,其配置文件通常有多个,可以使用如下命令得到当前系统的相关文件的路径信息:
[[email protected] ~]$ rpmbuild --showrc | grep macrofiles
macrofiles         : /usr/lib/rpm/macros:/usr/lib/rpm/athlon-linux/macros:/usr/lib/rpm/redhat/macros:/etc/rpm/macros.*:/etc/rpm/macros:/etc/rpm/athlon-linux/macros:~/.rpmmacros
macrofiles标签后的多个路径就是rpm、rpmbuild等命令运行时所用到的宏定义相关配置文件,在上面命令输出中的列表中的次序也是rpm、rpbuild命令搜索宏定义时依次使用文件的次序,因此,如果在多个文件中定义了相同的宏,最后找到的将最终生效。由于排在最后的是~/.rpmmacros,因此,每个用户都可以在自己的家目录中通过修改此文件来改变系统上某宏的默认定义,比如_topdir。
本文后续的过程期望基于mage用户家目录中的rpmworkshop作为制作RPM包的“工作车间”,只需要以mage用户的身份在自己的家目录中建立.rpmmacros文件,并在其中重新定义宏_topdir即可实现,文件内容如下:
%_topdir  /home/mage/rpmworkshop
而后使用rpm或rpmbuild命令进行验正,如下:
[[email protected] ~]$ rpmbuild --showrc | grep "_topdir"
-14: _builddir %{_topdir}/BUILD
-14: _rpmdir %{_topdir}/RPMS
-14: _sourcedir %{_topdir}/SOURCES
-14: _specdir %{_topdir}/SPECS
-14: _srcrpmdir %{_topdir}/SRPMS
-14: _topdir /home/mage/rpmworkshop
或者:
[[email protected] ~]$ rpm --eval '%_topdir'
/home/mage/rpmworkshop
制作RPM包的“工作车间”位置修改完成后,就需要建立此“工作车间”及其内部结构了。即如前文中所述,此目录中要包含BUILD、RPMS、SOURCES、SPECS、SRPMS这5个子目录。这可以使用下面的命令创建:
[[email protected] ~]$ mkdir -pv rpmworkshop/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
5.3 准备制作RPM包
5.3.1 准备制作RPM包概览
简单来讲,准备制作RPM包的过程只需要将所有打算打包的源材料放置于%{_topdir}/SOURCES目录中,并在%{_topdir}/SPECS目录中提供一个指示制作过程的specs文件即可。通常,specs文件与制作的RPM包同名,且以.specs结尾以方便识别。
再次说明,用于打包的放置于%{_topdir}/SOURCES目录中的文件既可能是某程序归档格式的源码包,如nginx-1.0.14.tar.gz,也可以是纯粹的一些文件。只不过,如果包含源码,为了制作生成二进制格式的RPM包,打包的过程需要将此源程序先解压编译(在BUILD目录中完成编译)后进行打包,否则,就省去了编译这道程序而可以直接进行打包。
以将nginx-1.0.14.tar.gz制作为RPM包的最简单方法为例,我们只需要将nginx-1.0.14.tar.gz放置于%{_topdir}/SOURCES目录中,并为其在%{_topdir}/SPECS目录中创建一个名为nginx.spec的文件,并在此文件中设定好制作nginx这个RPM包的各相关指令即可。事实上,制作spec文件的过程是相当复杂的,但其也是制作RPM包过程中最关键的步骤。
下面是一个框架性的spec文件,几乎所有的spec文件都可以基于此框架扩展而来。
Name:
Version:
Release: 1%{?dist}
Summary:
Group:
License:
URL:
pagkager: magedu.com 
Vendor: http://www.magedu.com/
Source0:
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX
BuildRequires:
Requires:
%description
%prep
%setup -q
%build
%configure
make %{?_smp_mflags}
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
%clean
rm -rf $RPM_BUILD_ROOT
#%pre
#%post
#%preun
#%postun
%files
%defattr(-,root,root,-)
%doc
%changelog
上面的这个框架大致可以分为八个部分(更详细的介绍请参见本文的第6节)。
(1) %description前面的所有内容都是以“tag: value”的格式定义,包括%description这些均为rpm包相关信息的描述段。其中许多信息即为使用“rpm -qi”对其查询时所显示的内容。许多标签可以做到见其名知其义,如Name用于定义RPM包的名字、License用于说明其许可证信息、Summary用于为此rpm包定义一个简单描述等。而%description则用于定义一个此rpm包的比Summary标签中更详细些描述信息。
(2) %prep部分为制作rpm过程的准备阶段(prepare section)。如果是对某归档格式的源程序制作rpm包,此段中经常要使用%setup来实现将源码解压至%{_topdir}/BUILD目录中,并使用cd命令将制作过程的工作目录切换至此解压后的源码目录。当然,%setup也有一些选项实现更进一步的设置。
(3) %build部分为编译阶段(build section)。如果制作rpm包的源材料不是源程序,此段可以不用设置;如果是C/C++等类的程序,此处对应于其编译安装过程中的“configure”和“make”两步;如果是perl类的源程序,此处通常对应于其编译安装过程中的“perl Makefile.PL”和“make”两步。然而,为了编译过程中的编译环境设定不出现问题,此处如果要用到configure脚本时通常要使用宏%configure来替换。可以使用“rpm --eval '%configure'”命令来获取当前系统上%configure宏的相关定义。
(4)%install部分为安装阶段。制作rpm时,所有要打包进rpm包的文件都需要事先按照其计划最终安装在系统上的路径先安装至一个临时目录中,此临时目录做为模拟的系统根目录,其通常称为BUILDROOT目录。此目录可以在spec文件中使用“BuildRoot:”标签进行定义。 因此,可以在后文中使用%{BuildRoot}引用,事实上其也可以使用$RPM_BUILD_ROOT引用。
(5)%clean部分为清理阶段。此段通常用于清理其它阶段的命令所创建的文件,比如“rm -rf $RPM_BUILD_ROOT”命令即为删除前次制作rpm包时安装生的BUILDROOT目录。
(6)%pre、%post、%preun和%postun为脚本段。%pre为制作完成后的rpm在其安装之前要执行的脚本,%post为安装完成后执行的脚本,%preun为卸载开始之前执行的脚本,%postun为卸载完成后要执行的脚本。
(7)%files部分为文件列表段。此段中需要列出打包进rpm包的所有文件,同时还需要为这些文件定义安装到系统上之后的权限。
(8)%changlog部分为改变日志段。此段中用于定义当前spec文件每一次改进的作者及改进的相关说明信息。
5.3.2 案例
仍以前文中提到的以简单制作nginx-1.0.14.tar.gz为RPM为例。其过程为,首先将nginx-1.0.14.tar.gz源码包放入%{_topdir}/SOURCES目录,这里为/home/mage/rpmworkshop/SOURCES目录。此外,通常情况下安装至Red Hat Linux系列Linux系统上的服务通常都有SysV风格的控制脚本,为了使此rpm包有基本的完整性,我们还需要事先写好一个nginx的服务脚本,将其一并打包起来,并于安装时安装在/etc/rc.d/init.d/目录中。因此,我们还需要在%{_topdir}/SOURCES创建好这个脚本文件,这里将其命名为nginx.init,其内容如下:
#!/bin/sh
#
# nginx - this script starts and stops the nginx daemon
#
# chkconfig:   - 85 15
# description:  Nginx is an HTTP(S) server, HTTP(S) reverse \
#               proxy and IMAP/POP3 proxy server
# processname: nginx
# config:      /etc/nginx/nginx.conf
# config:      /etc/sysconfig/nginx
# pidfile:     /var/run/nginx.pid
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
nginx="/usr/sbin/nginx"
prog=$(basename $nginx)
NGINX_CONF_FILE="/etc/nginx/nginx.conf"
[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
lockfile=/var/lock/subsys/nginx
make_dirs() {
   # make required directories
   user=`nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`
   options=`$nginx -V 2>&1 | grep 'configure arguments:'`
   for opt in $options; do
       if [ `echo $opt | grep '.*-temp-path'` ]; then
           value=`echo $opt | cut -d "=" -f 2`
           if [ ! -d "$value" ]; then
               # echo "creating" $value
               mkdir -p $value && chown -R $user $value
           fi
       fi
   done
}
start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
    make_dirs
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}
stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}
restart() {
    configtest || return $?
    stop
    sleep 1
    start
}
reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP
    RETVAL=$?
    echo
}
force_reload() {
    restart
}
configtest() {
  $nginx -t -c $NGINX_CONF_FILE
}
rh_status() {
    status $prog
}
rh_status_q() {
    rh_status >/dev/null 2>&1
}
case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart|configtest)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
            ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
        exit 2
esac
#END Of nginx.init
接下来在%{_topdir}/SPECS目录中创建nginx.spec文件,其内容如下:
Name:           nginx
Version:        1.0.14
Release:        1%{?dist}
Summary:        A free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server.
Group:          System Environment/Daemons 
Vendor:  http://www.magedu.com
Packager:  mage 
License:        BSD
URL:            http://www.nginx.org/
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires:      pcre-devel,zlib-devel,openssl-devel
BuildRequires:      libxslt-devel,gd-devel
Requires:           pcre,openssl,gd
# for /usr/sbin/useradd
Requires(pre):      shadow-utils
Requires(post):     chkconfig
# for /sbin/service
Requires(preun):    chkconfig, initscripts
Requires(postun):   initscripts
Provides:           webserver
Source0:    http://sysoev.ru/nginx/nginx-%{version}.tar.gz
Source1:    nginx.init
%description
Nginx is a free, open-source, high-performance HTTP server and reverse proxy,
as well as an IMAP/POP3 proxy server. Igor Sysoev started development of Nginx
in 2002, with the first public release in 2004. Nginx now hosts nearly 12.18%
(22.2M) of active sites across all domains. Nginx is known for its high
performance, stability, rich feature set, simple configuration, and low
resource consumption.
%prep
%setup -q
%build
export DESTDIR=%{buildroot}
./configure \
  --sbin-path=/usr/sbin/nginx \
  --conf-path=/etc/nginx/nginx.conf \
  --error-log-path=/var/log/nginx/error.log \
  --http-log-path=/var/log/nginx/access.log \
  --pid-path=/var/run/nginx/nginx.pid  \
  --lock-path=/var/lock/nginx.lock \
  --user=nginx \
  --group=nginx \
  --with-http_ssl_module \
  --with-http_flv_module \
  --with-http_stub_status_module \
  --with-http_gzip_static_module \
  --http-client-body-temp-path=/var/tmp/nginx/client/ \
  --http-proxy-temp-path=/var/tmp/nginx/proxy/ \
  --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \
  --with-pcre
make %{?_smp_mflags}
%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}
%{__install} -p -D -m 0755 %{SOURCE1} %{buildroot}%{_initrddir}/%{name}
%{__install} -p -d -m 0755 %{buildroot}/var/run/nginx
%{__install} -p -d -m 0755 %{buildroot}/log/run/nginx
%clean
rm -rf %{buildroot}
%pre
if [ $1 == 1 ]; then
    /usr/sbin/useradd  -s /bin/false -r  nginx 2>/dev/null || :
fi
%post
if [ $1 == 1 ]; then
    /sbin/chkconfig --add %{name}
fi
%preun
if [ $1 = 0 ]; then
    /sbin/service %{name} stop >/dev/null 2>&1
    /sbin/chkconfig --del %{name}
fi
%files
%defattr(-,root,root,-)
%doc LICENSE CHANGES README
%{_sbindir}/%{name}
%dir /var/run/nginx
%dir /var/log/nginx
%dir /etc/nginx
%config(noreplace) /etc/nginx/win-utf
%config(noreplace) /etc/nginx/mime.types.default
%config(noreplace) /etc/nginx/fastcgi.conf
%config(noreplace) /etc/nginx/fastcgi.conf.default
%config(noreplace) /etc/nginx/fastcgi_params
%config(noreplace) /etc/nginx/fastcgi_params.default
%config(noreplace) /etc/nginx/%{name}.conf
%config(noreplace) /etc/nginx/mime.types
%config(noreplace) /etc/nginx/scgi_params
%config(noreplace) /etc/nginx/scgi_params.default
%config(noreplace) /etc/nginx/uwsgi_params
%config(noreplace) /etc/nginx/uwsgi_params.default
%config(noreplace) /etc/nginx/koi-win
%config(noreplace) /etc/nginx/koi-utf
%config(noreplace) /etc/nginx/%{name}.conf.default
/usr/local/nginx/html/50x.html
/usr/local/nginx/html/index.html
%attr(0755, root, root) %{_initrddir}/%{name}
%changelog
* Wed Apr 11 2012 magedu.com  - 1.0.14-1
- Initial version
# End Of nginx.spec
为了保证接下来的制作过程能够顺利进行,此时可以使用rpmbuild命令的--nobuild选项测试spec文件所依赖的环境是否已经完全准备好。例如:
[[email protected] SPECS]$ rpmbuild --nobuild nginx.spec
error: Failed build dependencies:
 pcre-devel is needed by nginx-1.0.14-1.i386
 gd-devel is needed by nginx-1.0.14-1.i386
上述命令的执行结果即显示制作过程的依赖的两个rpm包尚未安装,其结果就是制作过程根本不能启动。因此,这里需要事先解决依赖关系。当测试没有任何信息提示后就可以开始下一节的步骤了。
5.4 制作rpm包
5.4.1 rpmbuild命令使用说明
制作rpm包使用rpmbuild命令进行,其语法格式为:
 rpmbuild  -bBuildStage  spec_file

其中,-b选项用于告诉rpmbuild命令开始制作一个RPM包,而BuildStage选项通常也为一个字符,其用于控制rpmbuild命令在基于指定的spec文件制作rpm包时进行到哪个步骤。其常用方式有:
-ba  同时制作成生二进制格式的rpm包和源码格式的rpm包;
-bb  仅制作二进制格式的rpm包;
-bc  仅执行到spec文件中的%build阶段即停止进行;
-bp  仅执行至spec文件中的%prep阶段即停止进行;
-bi  仅运行至spec文件中的%install阶段即停止运行;
-bl  检查spec文件中%file段所列出的文件是不是与BUILDROOT目录中存在的文件完全匹配;
-bs  仅制作生成源码格式的rpm包;

5.4.2 案例
5.4.2.1 分步执行制作过程,以检验执行过程中存在的问题
仍以上文中的制作过程为例,我们分阶段执行制作过程,以检查步相关步骤的存在的问题。再次提醒,一定要使用普通作为制作rpm包的用户。
第一步,使用-bp选项测试执行至%prep阶段的效果;
[[email protected] SPECS]$ rpmbuild -bp nginx.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.72469
+ umask 022
+ cd /home/mage/rpmworkshop/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY
+ cd /home/mage/rpmworkshop/BUILD
+ rm -rf nginx-1.0.14
+ /usr/bin/gzip -dc /home/mage/rpmworkshop/SOURCES/nginx-1.0.14.tar.gz
+ tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd nginx-1.0.14
++ /usr/bin/id -u
+ '[' 502 = 0 ']'
++ /usr/bin/id -u
+ '[' 502 = 0 ']'
+ /bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0
命令输出内容表示出此步骤执行正常,nginx-1.0.14.tar.gz被解压至/home/mage/rpmworkshop/BUILD/目录中,而且解压之前先尝试了删除此前解压生成的文件等。
第二步,使用-bc选项让制作过程重新开始,并执行至%build段;
[[email protected] SPECS]$ rpmbuild -bc nginx.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.46906
... ...
... ...
# (此处省略了如同第一步中输出的内容若干行)
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.46906
+ umask 022
+ cd /home/mage/rpmworkshop/BUILD
+ cd nginx-1.0.14
+ LANG=C
+ export LANG
+ unset DISPLAY
+ export DESTDIR=/var/tmp/nginx-1.0.14-1-root
+ DESTDIR=/var/tmp/nginx-1.0.14-1-root
+ ./configure --sbin-path=/usr/sbin/nginx '--conf-path=%{nginx_confdir}/nginx.conf' --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_flv_module --with-http_stub_status_module --with-http_gzip_static_module --http-client-body-temp-path=/var/tmp/nginx/client/ --http-proxy-temp-path=/var/tmp/nginx/proxy/ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ --with-pcre
checking for OS
... ...
... ...
# (省略输出内容若干行)
make[1]: Leaving directory `/home/mage/rpmworkshop/BUILD/nginx-1.0.14'
+ exit 0
由上面的输出的信息可以看出,%build阶段执行了configure脚本和make命令,并成功结束。
第三步、使用-bi选项让制作过程重新开始,并执行至%install段;
[[email protected] SPECS]$ rpmbuild -bc nginx.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.46906
... ...
... ...
# (此处省略了如同第一步中输出的内容若干行)
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.46906
... ...
... ...
# (此处省略了如同第二步中输出的内容若干行)
rocessing files: nginx-debuginfo-1.0.14-1
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Checking for unpackaged file(s): /usr/lib/rpm/check-files /var/tmp/nginx-1.0.14-1-root
由上述信息的最后一行可以看出,编译好的程序等已经成功安装至以/var/tmp/nginx-1.0.14-1-root为根目录的路径中(此目录即为BUILDROOT),并且spec文件中指定要打包的文件和此目录下安装生成的文件相匹配。
此步骤中,如果考虑不周全极易产生错误。错误通常有两类,一是安装至BUILDROOT目录中的文件未能被spec指定打包;另一个是spec文件中指定要打包了,但BUILDROOT中并不存在此文件。此两类错误容易解决,只需要认真对照BUILDROOT和spec文件中%files部分所列出的文件即可。当然,我们也可以在spec文件中安装过程完成之后删除不想打包的文件,进而也就不需要在%files段中包含此类文件了。
5.4.2.2 制作nginx的rpm包
如果5.4.1.1节中的分阶段执行过程没有任何问题,我们接着就可以开始真正的rpm包制作了。一般说来,制作的rpm可以仅是二进制格式的包,也可以仅是源码格式的rpm包,也可以两者同时都制作。只是,它们需要使用rpmbuild命令的不同选项来实现。具体就参照5.4.1节中的说明。
我们这里来同时制作生成二进制格式的包和源码格式的包,这可以使用如下命令实现:
[[email protected] SPECS]$ rpmbuild -ba nginx.spec
此命令执行输出的最后几行通常如下所示:
Wrote: /home/mage/rpmworkshop/SRPMS/nginx-1.0.14-1.src.rpm
Wrote: /home/mage/rpmworkshop/RPMS/i386/nginx-1.0.14-1.i386.rpm
Wrote: /home/mage/rpmworkshop/RPMS/i386/nginx-debuginfo-1.0.14-1.i386.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.33201
+ umask 022
+ cd /home/mage/rpmworkshop/BUILD
+ cd nginx-1.0.14
+ rm -rf /var/tmp/nginx-1.0.14-1-root
+ exit 0
其输出表明,此命令最后共生成了三个rpm包,其中源码格式的nginx-1.0.14-1.src.rpm位于SRPMS目录中,二进制格式的nginx-1.0.14-1.i386.rpm位于RPMS/i386目录中(i386目录主要用于标示制作生成的rpm所属的平台),而nginx-debuginfo-1.0.14-1.i386.rpm则是调试运行nginx时用到的程序或文件所在的rpm包。
5.4.2.3 查看完成的rpm包摘要信息
对于制作完成的rpm包,其摘要信息是由spec文件中标签段定义生成的。对于未安装的rpm包,可以使用rpm命令的-qpi选项进行。
[[email protected] ~]$ rpm -qpi ~/rpmworkshop/RPMS/i386/nginx-1.0.14-1.i386.rpm
Name        : nginx                        Relocations: (not relocatable)
Version     : 1.0.14                            Vendor: http://www.magedu.com
Release     : 1                             Build Date: Wed 11 Apr 2012 05:01:14 PM CST
Install Date: (not installed)               Build Host: mail.magedu.com
Group       : System Environment/Daemons    Source RPM: nginx-1.0.14-1.src.rpm
Size        : 705987                           License: BSD
Signature   : (none)
Packager    : mage 
URL         : http://www.nginx.org/
Summary     : A free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server.
Description :
Nginx is a free, open-source, high-performance HTTP server and reverse proxy,
as well as an IMAP/POP3 proxy server. Igor Sysoev started development of Nginx
in 2002, with the first public release in 2004. Nginx now hosts nearly 12.18%
(22.2M) of active sites across all domains. Nginx is known for its high
performance, stability, rich feature set, simple configuration, and low
resource consumption.
至此,nginx-1.0.14的rpm包已经制作完成了,其可以分发至相应的平台进行安装。但rpm包尚且未进行签名,而且在定制属于某种特定环境使用的rpm包方面仍有许多工作要做。这些,我们将在后面的章节中进行阐述。
六、SPECS文件详解
七、使用rpmbuld命令控制制作过程
八、RPM包签名的实现
九、案例
十、问题总结