9.6 RPM软件包

RPM软件包管理程序(RPM Package Manager)或简称为RPM(我想你肯定喜欢这样一种递归简写的方式)一开始只是Red Hat Linux的软件包格式,它最初的名字为Red Hat软件包管理程序(Red Hat Package Manager)。从那以后,RPM逐渐成为许多其他Linux发行版(包括SUSE Linux)所接受的一种软件包格式,Linux标准化规范(Linux Standards Base,简称为LSB)将RPM作为其官方软件包格式,LSB的网址为www.linuxbase.org。

下面是RPM的主要优点。

❑ 使用广泛。许多Linux发行版至少都可以安装RPM软件包,或者将RPM作为它的标准软件包格式。RPM还被移植到许多其他的操作系统中。

❑ 它能够只用一条命令来安装软件包。你还可以自动安装软件包,因为RPM就是专为方便无人管理设计的。同样,删除或升级软件包也只需使用一条命令。

❑ 只需要处理一个文件。一个RPM软件包就保存在一个单独的文件中,这使得在不同系统之间传输软件包变得非常容易。

❑ RPM自动处理软件包之间的依赖关系检查。RPM系统包含一个数据库,该数据库中记录了已安装的所有软件包的信息,包括每个软件包所提供的内容以及安装每个软件包的要求。

❑ RPM软件包被设计为由“最干净”的源代码而来,从而可以对它重新编译。RPM支持如patch这样的Linux工具,可以在编译过程中为软件的源代码打上补丁。

9.6.1 使用RPM软件包文件

每个RPM软件包都存储在一个以.rpm为后缀名的文件中。软件包文件通常遵循着一种命名规范,它的结构如下所示:

9.6 RPM软件包 - 图1

在这个结构中,name指定该软件包的通用名称,例如对于MySQL数据库来说,它就是mysql,对于make编译工具来说,它就是make。version指定该软件的版本号,例如MySQL的版本5.0.41。release包含一个数字,它指定软件包的RPM版本号。这个版本号非常重要,因为RPM软件包是通过一组指令建立的(我们将在9.6.3节中介绍这方面的内容),你可以通过release来跟踪编译指令的改动情况。

architecture指定程序的架构,例如对于基于Intel的系统,它为i386。对于已编译好的程序来说,它非常重要,例如,针对SPARC处理器创建的可执行程序是不能在Intel处理器上运行的。architecture的值可以是通用的,例如针对SPARC处理器的sparc,也可以是特定的,例如针对v9 SPARC处理器的sparcv9,或针对AMD Athlon芯片的athlon。除非你强制忽略它,否则RPM系统将阻止安装来自不同架构的软件包。

如果architecture设为一个特殊的值noarch,就表示该软件包并不针对某个特定的架构,例如文档、Java程序或Perl模块。如果architecture设为src,就表示该软件包为RPM源代码软件包,在该软件包中包含的是源文件和用于将它编译为二进制RPM软件包的指令。大多数你可以在网络上找到的RPM软件包都是针对某个特定架构预编译的软件包,这主要是为了方便用户的安装。你可以在网络上找到数千种预编译好的RPM软件包,它们将省去你编译软件的麻烦。

此外,一些软件包的使用非常依赖于某个特定的Linux版本,针对这种情况,直接下载一个预编译好的软件包要比手工测试所有的软件包组件要容易得多。例如,曾经有一个802.11b无线网络软件包,它是针对某个特定的Linux发行版的某个特定内核补丁级别预编译的软件包,比如说它的文件名为kernel-wlan-ng-modules-rh9.18-0.2.0-7-athlon.rpm,它包含的是一个内核模块,针对的用户主机为AMD Athlon处理器的系统、Linux发行版为RedHat 9.0、内核版本为2.4.20-18。

9.6.2 安装RPM软件包

你用rpm命令来安装RPM软件包,该命令的语法格式很简单,如下所示:

9.6 RPM软件包 - 图2

例如:

9.6 RPM软件包 - 图3

这个命令将安装(或是升级)MySQL数据库服务器软件包,该软件包针对的是Intel x86架构的系统。

rpm命令还提供用户与RPM系统交互的能力。你可以用如下命令查询某个软件包是否已安装:

9.6 RPM软件包 - 图4

9.6.3 创建RPM软件包

你可以用命令rpmbuild来创建一个RPM软件包。创建的过程相对而言比较简单,如下所示。

❑ 收集你需要打包的软件。

❑ 创建spec文件,该文件描述了如何建立软件包。

❑ 用rpmbuild命令建立软件包。

由于RPM软件包的创建可能会非常复杂,为了便于说明,我们在本章中将用一个简单的例子来介绍,该例子已足以说明如何以源代码或二进制程序的方式来发布一般应用软件。我们将把更深奥的选项和通过打补丁的方式提供软件包支持留给有兴趣的读者来研究。要想了解更多与rpm程序相关的信息,请参考rpm程序的手册页或RPM HOWTO文档(通常可以在/usr/share/doc目录下找到),你还可以参考由Eric Foster-Johnson编写的书《Red Hat RPM指南》(Red Hat Press/Wiley出版),该书的在线版本位于http://docs.fedoraproject.org/drafts/rpm-guide-en/。

在下面的几小节中,我们将按照上述的3个步骤来创建小应用程序myapp的RPM软件包。

1.收集软件

创建RPM软件包的第一步是收集你需要打包的软件。在大多数情况中,软件包括应用程序的源代码、一个构建文件(如makefile文件),可能还会有一个在线手册页。

将软件所涉及的文件收集到一起的最简单的方法是将所有相关文件打包到一个tarball文件中,并在该文件的名字中包含应用程序名和版本号,例如myapp-1.0.tar.gz。

你可以修改先前的makefile文件Makefile6,在其中增加一个新的目标,将所有文件打包到一个tarball文件中。修改后的makefile文件就命名为Makefile,如下所示:

9.6 RPM软件包 - 图5

9.6 RPM软件包 - 图6

makefile文件中的目标myapp-1.0.tar.gz将为我们的小应用程序的源代码创建一个tarball文件。为了使用简便,上面的代码还在makefile文件中增加了一个调用相同命令的dist目标。你可以运行如下的命令来创建tarball文件:

9.6 RPM软件包 - 图7

接下来,你需要将文件myapp-1.0.tar.gz复制到RPM的SOURCES目录中,对于Red Hat Linux系统来说,该目录为/usr/src/redhat/SOURCES;对于SUSE Linux来说,该目录为/usr/src/packages/SOURCES。执行的命令如下所示:

9.6 RPM软件包 - 图8

RPM系统希望软件的源文件以tarball文件的形式放置在SOURCES目录中(当然还有其他一些选项,但这种情况是最简单的)。SOURCES目录只是RPM系统所需要查找的几个目录之一。

RPM系统需要5个目录的支持,它们列在表9-4中。

表 9-4

9.6 RPM软件包 - 图9

在RPMS目录下通常会有一组针对不同主机架构的子目录,例如在一个Intel x86架构的系统中,RPMS目录中的内容如下所示:

9.6 RPM软件包 - 图10

默认情况下,Red Hat Linux系统期望在/usr/src/redhat目录中创建RPM软件包。

这个目录是特定于Red Hat Linux的。其他Linux发行版会使用其他的目录,例如/usr/src/packages。

一旦收集好RPM软件包所需的所有源文件,下一步就是创建spec文件,该文件告诉rpmbuild命令如何正确地建立软件包。

2.创建RPM Spec文件

创建spec文件可能会非常让人畏惧,因为RPM系统支持的选项数以千计。幸运的是,RPM系统为大多数选项提供了合理的默认值。在本小节中所介绍的这个简单例子中的内容,应该能满足你将要创建的大多数软件包的需要了。此外,你还可以从其他的spec文件中复制你所需要的命令。

关于spec文件的更丰富的资源可以从其他的RPM软件包中找到。你可以安装以.src.rpm为后缀名的RPM源代码软件包,并查看其中的spec文件,你会发现比你所需要的还要复杂许多的例子,例如在软件包anonftp、telnet、vnc和sendmail中的spec文件都是一些比较有趣的例子。

此外,RPM系统的设计者很聪明地未在自己的系统中开发另一套工具来取代常用的编译工具,如make或configure,而是在系统中包含了许多短小的功能来利用makefile和configure脚本文件。

在本例中,你将为myapp应用程序创建一个spec文件myapp.spec。该文件的开头是一组名字、版本号以及与软件包有关的其他信息的定义,如下所示:

9.6 RPM软件包 - 图11

RPM spec文件中的这部分内容常被称为导言。在上面的导言中,最重要的定义是Name、Version和Release。它们在本例中分别被定义为myapp、1.0和1,其中RPM软件包的版本(release)为1是因为这是你第一次尝试建立它。

Group定义的作用是帮助图形化安装程序将数千种Linux应用程序分类显示。Distribution定义软件的发行方式,当你只是针对某一个Linux发行版(如Red Hat或SUSE Linux)建立软件包时,它的定义就显得非常重要了。

在spec文件中添加注释是个好主意。如同shell脚本和makefile文件,rpmbuild命令把在该文件中以字符#开头的行看作为注释,例如:

9.6 RPM软件包 - 图12

为了帮助用户判断是否需要安装你的软件包,你可以在spec文件中提供Summary(软件的摘要)和%description(软件的描述),注意上述两个定义在语法上的不一致,在description的前面有一个百分号%。例如,你可以按如下方式描述你的软件包:

9.6 RPM软件包 - 图13

%description的定义可以持续多行(通常也是如此)。

spec文件可以包含软件依赖关系的信息,这包括两方面的内容:软件包提供了什么和软件包依赖什么(你还可以定义源代码软件包依赖什么,例如指定编译软件时所需要的特定头文件)。

Provides定义了软件包所提供的功能,例如:

9.6 RPM软件包 - 图14

上面这条语句声明软件包定义了一个假想的功能goodness。如果没有在spec文件中定义Provides,则RPM系统会自动添加Provides定义,它的值为软件包的name定义,在本例中就是myapp。当有多个软件包提供相同的功能时,Provides定义就非常有用。例如,Apache Web服务器软件包提供的功能是webserver,而其他的软件包如Thy,它可能也提供完全相同的功能。为了帮助处理软件包之间的冲突,RPM还允许指定Conflicts(冲突)和Obsoletes(过时)信息。

可能最重要的依赖关系信息就是Requires定义。你可以通过它定义软件包正常运行所需的所有其他软件包。例如,Web服务器需要网络和安全软件包的支持。在本例中,你定义Requires为MySQL数据库,版本要在3.23及其以上。它的语法如下所示:

9.6 RPM软件包 - 图15

如果只需要MySQL数据库的支持,但版本不限,那么可以使用如下定义:

9.6 RPM软件包 - 图16

如果需要的软件包未安装,RPM将阻止用户安装该软件包。当然,用户也可以强制安装。

RPM系统还将根据情况自动添加一些依赖关系定义,如针对shell脚本的/bin/sh、针对Perl脚本的Perl解释程序和应用程序需要调用的任何共享函数库(后缀名为.so的文件)。RPM系统的每个新版本都会为自动依赖关系检查添加更多的智能。

定义完需求后,你需要定义构成应用程序的源文件。对于大多数应用程序来说,你只需将下面的定义复制到自己的spec文件中即可:

9.6 RPM软件包 - 图17

%{name}语法指向一个RPM宏,在本例中,它指的是软件包的名称。因为你在前面将name定义为myapp,所以rpmbuild命令将把%{name}扩展为myapp,同样,%{version}将被扩展为1.0,这样完整的文件名就是myapp-1.0.tar.gz。rpmbuild命令将在前面提到过的SOURCES目录中查找这个文件。

这个例子设置了一个Buildroot,它定义了一个用于测试安装的目录。你可以将下面的语句复制到自己的spec文件中:

9.6 RPM软件包 - 图18

设置好Buildroot后,你就可以将应用程序安装到Buildroot定义的目录中了。你可以使用变量$RPM_BUILD_ROOT来引用它,该变量可以在spec文件中的所有shell命令中使用。

在定义了所有这些与软件包相关的设置后,下一步就是定义如何建立软件包了。建立过程一共分为4个主要的部分:%prep、%build、%install和%clean。

顾名思义,%prep部分用于完成准备工作。在大多数情况下,你可以运行宏%setup,使用-q参数可以将其设置为安静模式,如下所示:

9.6 RPM软件包 - 图19

%build部分用于建立应用程序。在大多数情况下,你只需要使用make命令,如下所示:

9.6 RPM软件包 - 图20

这实际上就是RPM系统利用你先前创建makefile文件所做工作的一种方式。

%install部分用于安装应用程序、手册页和其他支持文件。你通常可以使用RPM宏%makeinstall来安装程序,它将调用在makefile文件中定义的install目标。但在本例中,为了显示更多的RPM宏,你将手工安装所有的文件,如下所示:

9.6 RPM软件包 - 图21

这个例子在需要的情况下将创建相应的目录,然后安装可执行程序myapp和手册页myapp.1。环境变量$RPM_BUILD_ROOT包含先前定义的Buildroot的值。宏%{_bindir}和%{_mandir}将分别被扩展为当前二进制程序目录和手册页目录。

如果用configure脚本来创建makefile文件,所有可变的目录名都将被正确地在你的makefile文件中设置。所以,在大多数情况下,你不需要像上面例子那样,手工地在spec文件中指定所有的安装命令。

%clean目标用于清理所有由rpmbuild命令创建的文件,如下所示:

9.6 RPM软件包 - 图22

在定义了如何建立软件包后,你需要定义所有需要安装的文件。RPM对此要求非常严格,为了能够正确地跟踪每个软件包中的每个文件,它也必须如此。%files部分定义了需要包括进软件包的所有文件。在本例中,你只有两个文件需要放在二进制软件包中发布,它们是可执行程序myapp和手册页myapp.1。如下所示:

9.6 RPM软件包 - 图23

RPM系统可以在软件包安装前和安装后运行脚本程序。例如,如果软件包是一个守护进程,你可能需要修改系统的初始化脚本来启动该程序。你可以通过%post脚本来完成这一任务。下面是一个非常简单的%post脚本的例子,它仅用来发送一封电子邮件:

9.6 RPM软件包 - 图24

你可以在服务器RPM的spec文件中看到更多的%post脚本的例子。

下面是这个小应用程序的spec文件的完整内容:

9.6 RPM软件包 - 图25

现在你已准备好建立RPM软件包了。

3.使用rpmbuild命令建立RPM软件包

使用rpmbuild命令来建立软件包的语法如下所示:

9.6 RPM软件包 - 图26

选项-b告诉rpmbuild命令建立一个RPM软件包。附加的选项BuildStage是一个特殊的代码,它的作用是告诉rpmbuild命令在建立时需要做到哪一步。可以使用的选项如表9-5所示。

表 9-5

9.6 RPM软件包 - 图27

如果要同时建立二进制和源代码RPM软件包,就使用选项-ba。源代码RPM软件包允许你重新建立二进制RPM软件包。

将RPM的spec文件复制到正确的SOURCES目录(放置应用程序源代码的目录)中:

9.6 RPM软件包 - 图28

下面显示了在SUSE Linux系统中建立软件包的输出结果(软件包是通过目录/usr/src/packages/SOURCES建立的):

9.6 RPM软件包 - 图29

9.6 RPM软件包 - 图30

9.6 RPM软件包 - 图31

执行完rpmbuild命令后,你将看到两个RPM软件包:在RPMS目录中的二进制RPM软件包,该软件包放置在相应的主机架构子目录中,如子目录RPMS/i586;在SRPMS目录中的源代码RPM软件包。

二进制RPM软件包的文件名将类似下面这样:

9.6 RPM软件包 - 图32

文件名中表示主机架构的部分可能会随着系统的不同而不同。

源代码RPM软件包的文件名将类似下面这样:

9.6 RPM软件包 - 图33

你需要以超级用户的身份来安装软件包。但在建立软件包时,你并不需要root的身份,只要你对RPM目录(通常是/usr/src/redhat)有写权限即可。一般情况下,你不应该以root的身份来创建RPM软件包,因为spec文件中可能会包含对系统造成破坏的命令。