RPM 打包进阶

>| 继上篇rpm打包(rpm-max翻译) 这里将我三年前packaging时候的一些整理先贴出来,免得遗忘.
Chapter 15. Making a Relocatable Package
第十五章 -- 制作可重定位的软件包
RPM也给用户安装软件包的自主权,用户可以决定软件包安装在他们系统的位置。然而,软件包构建者必须首先在软件包的设计中给用户这种自由。
有这样的功能感觉固然是好的,那么我们想追问:软件包的重定位有那么重要吗?下面我们来解释:


Why relocatable packages?  为什么软件包要重定位?
如果你是一位系统管理者,可能市场困扰你的一个麻烦的事情是磁盘空间。有时候没有充足的磁盘空间,即便有充足的磁盘空间,有时候可能不在正确的位置。
我们假设一个例子:
<1>比如有一些新的软件面世并且社区用户都非常想要使用他。
<2>在安装软件包之前系统管理员仔细的阅读了软件的安装文档。【1】她注意到这个软件共需要150MB的空间,就将其安装到了/opt 目录下。
<3>管理员在后面的操作后眉头紧锁,当系统管理者火速敲下 df 命令之后:
----------------------------------------------------------------------
# df
Filesystem         1024-blocks  Used Available Capacity Mounted on
/dev/sda0             100118   28434    66514     30%   /
/dev/sda6             991995  365527   575218     39%   /usr

----------------------------------------------------------------------
结果是:在文件系统根目录中没有150MB这么大的空间来安装软件包。【2】
<4>管理员深深叹了一口气,管理员仔细考虑下一步该怎么处理呢!如果有一种方式可以将软件安装在文件系统的/usr目录下呢....


并不一定都是这样的。RPM提供一种前缀的方式来控制软件包安装到用户指定的位置。如果使得软件包可重定位,软件包的构建者再一次让系统管理者活得更潇洒一些。(这让我想到,与人方便于己方便嘛!!)
回过头来仔细想想,到怎样才是一个真正意义上的可重定位的软件包呢??
---本质上讲,一个可重定位的软件包就是一个各个方面都符合标准的软件包。区别在于 prefix 标签。当这个标签被添加进spec文件中后,构建过程中RPM会‘尝试’构建一个可重定位的软件包。


注意“尝试”这个词。要构建成功一个可重定位的软件包必须满足一些条件,本章后面部分将主要介绍这些。
首先,我们来看RPM是如何重定位一个包的。并且来看看可重定位的软件包的一个核心组件 prefix 标签。


【1】 注意,我说的是假设!!
【2】 当然,也可以使用符号链接(软连接)到/usr 下来解决空间不足的问题。然而,由于本章的重点是探索RPM的可重定位特性,我们不会在这里赘述这种方法。




================
=============================
The prefix tag: Relocation Central  -----前缀prefix标签:重定位的核心。
解释 Prefix 标签如何使用最好的方式就是来看一个实实在在地例子。如下:
-------------------------
Prefix: /opt
--------------------------
在上面的这个例子中我们定义路径前缀为 /opt 。这意味着,默认情况下软件将会安装在 /opt 目录 。 我们来假定在spec文件的文件列表%files中存在下面一行:
---------------------------------
/opt/bin/baz
---------------------------------
如果这个软件包没有重定位,文件会被安装在/opt/bin 下面。这完全就是一个非重定位包的安装。然而,如果软件包被重定位安装,文件列表中的每一个路径将会按照假面的步骤被修改:
<1>文件路径中和 Prefix 标签行指定的路径对应相等的会被移除。
<2>用户指定的重定位的前缀会被添加到文件路径。
使用我们的 /opt/bin/baz 文件做一个示范,我们假设有一个用户安装软件包时希望复写默认的前缀Prefix (/opt)为一个新的前缀(/usr/local/opt)。按照上面的步骤,我们首先从文件路径中移除原始的前缀Prefix 路径:
-----------------------------------
/opt/bin/baz
------------------------------------
变为:
------------------------------------
/bin/baz
------------------------------------
接下来,我们添加用户指定的前缀到剩下来的文件名前。
-------------------------------------------------------
/usr/local/opt + /bin/baz = /usr/local/opt/bin/baz
--------------------------------------------------------
现在新的文件路径已经创建,RPM 安装文件就变的so easy 了!!这一部分看起来似乎足够简单。正如上面我们看到的,在做这件事情之前,这里有几个问题是包的构建者需要考虑的。


===========
============================
Relocatable Wrinkles: Things to Consider  重定位的问题:要考虑周全
虽然确定在spec文件的 Prefix 标签行添加前缀没有什么问题,但有必要考虑其他一些问题:
<1>文件列表中的每个路径必须以 Prefix 标签行指定的路径开始。
<2>软件必须写权限,保证在重定位之后可以正常运行。绝对符号链接是一个典型的例子。
<3>其他软件不能依赖重定位软件包被安装在特定的路径。
我们来从文件列表%files开始介绍每一个问题。


-----%files List Restrictions  文件列表限制条件
如上所述,文件列表中的每个文件路径的必须以重定位前缀开头。如果不这么做,构建显而易见会失败。
--------------------------------------------------------------------------
# rpmbuild -ba cdplayer-1.0.spec
* Package: cdplayer
+ umask 022
+ echo Executing: %prep

Binary Packaging: cdplayer-1.0-1
Package Prefix = usr/local
File doesn't match prefix (usr/local): /usr/doc/cdplayer-1.0-1
File not found: /usr/doc/cdplayer-1.0-1
Build failed.

--------------------------------------------------------------------------
在我们的例子中,构建过程运行到创建二进制包的时候失败了,因为RPM在这一点探测到的问题。这个错误信息说:spec文件中的前缀 Prefix行的 (/usr/local) 没有在文件的路径的开始(/usr/doc/cdplayer-1.0-1) 。这阻止了构建。


实际上每一个可重定位的软件包必须安装在前缀Prefix行指定的目录下,这就引发了一些问题。例如,一个程序读取的一个配置文件通常保存在 /etc目录下吗??
这个问题可以引领我们进入下一个部分。


--------Relocatable Packages Must Contain Relocatable Software  
--------重定位包必须包含重定位软件
虽然这一节的标题似乎很明显,但它并不总是容易告诉我们如果一个特定的软件可以被重定位。让我们看看在前一节中提出的问题。如果一个程序已经写入从 /etc 读取它的配置文件。有三种可能的方法来使此软件可重定位:
<1>设置前缀 Prefix 为 /etc 并打包 /etc 下的所有东西。
<2>打包/etc 以外的所有目录并完全保留为配置文件。
<3>修改程序。
第一种方法从纯技术的角度来看可行,但是没有多少人希望安装软件到 /etc 下面。所以这种方法并不可行。
第二种方法看起来更合理一点,但它迫使用户完成安装之后还要自己创建配置文件。如果RPM的目标是使软件更容易安装和删除,这也不是一个可行的方法,!
最后的方法可能是最好的。一旦安装程序,当重写软件第一次运行时,它可以不存在配置文件在 /etc 并创建一个。


然而,尽管这样可以工作,卸载软件包时配置文件会被保留。RPM并没有安装它,所以RPM并不会把他“甩掉”。还有这样一个事实:这种方法可能比大多数包需要更多的体力劳动对于构建者来说。
这些方法都非常吸引人,是吗?有些软件包不能很好的重定位。一般来说,有下列情况的预警信号,重定位将会是一个问题:
<1>该软件包含了一个或多个文件且必须安装在特定目录。
<2>软件使用相对路径引用系统文件(所谓相对路径是指:说明软件必须安装在一个特定的目录的另一种方式)
如果出现这类问题,让软件重定位将是困难的。这里还有一个问题需要考虑。


---------The Relocatable Software Is Referenced By Other Software
---------重定位软件被其它软件引用。
即使假设编写软件,这样就可以放进一个重定位软件包,仍然可能是一个问题。这个问题的核心不是重定位软件包本身而是因为他引用的重定位软件包。


例如,有些时候一个包需要执行其他程序。这可能包括后台软件需要发送邮件,或通信程序需要压缩文件。如果这些底层软件包是可重定位的,而不是安装在其它软件包所期望的位置,他们将毫无用处。


当然,这并不是一个常见的问题,但它可能发生。当然,如果包builder对构建可重定位包感兴趣,这个问题任需要探索。不幸的是,这种类型的问题是比较难找的。


然而,如果一个软件产品被发现是可重定位的,实际构建一个可重定位的包的机制很简单。我们来小试牛刀!!!


=======================
===========================================
Building a Relocatable Package   构建一个可重定位的包。
对于这个示例,我们将使用我们的经过检验而可靠的cdplayer应用程序。我们首先回顾一下可能出现的问题在spec文件:
---------------------------------------------------------------------------
#
# Example spec file for cdplayer app...
#
Summary:A CD player app that rocks!
Name: cdplayer

%files
%doc README
/usr/local/bin/cdp
/usr/local/bin/cdplay
%doc /usr/local/man/man1/cdp.1
%config /etc/cdp-config
------------------------------------------------------------------------
看起来一切都好,除了文件列表 %files。这里文件在/usr/local/bin,帮助页在/usr/local/man/man1/以及一个配置文件在/etc 下。一个前缀的/usr/local会工作得很好,除了cdp-config文件。


为了第一次构建,我们将声明配置文件不是必需的并且从未见列表中移除它。我们还添加了一个前缀 Prefix 标签行并设置前缀为 /usr/local 。做完这些改动,我们运行构建:
---------------------------------------------------------------------------
# rpmbuild -ba cdplayer-1.0.spec
* Package: cdplayer
+ umask 022
+ echo Executing: %prep
Executing: %prep
+ cd /usr/src/redhat/BUILD
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz

Binary Packaging: cdplayer-1.0-1
Package Prefix = usr/local
File doesn't match prefix (usr/local): /usr/doc/cdplayer-1.0-1
File not found: /usr/doc/cdplayer-1.0-1
Build failed.

---------------------------------------------------------------------------
构建过程正常的执行直到创建二进制包时停止。构建信息中“Package Prefix = usr/local”行确定了RPM活得前缀Prefix标签行。但是构建终止---因为在一个叫做“/usr/doc/cdplayer-1.0-1” 。
但是此文件根本就没有在文件列表%files 中出现。发生了什么事??


仔细分析文件列表。看到这一行读 %doc README? 了吗??在第十三章的相关章节In the Section called The %doc Directive in Chapter 13中,我们讨论了%doc指示如何在/usr/doc 创建目录。
就是这个文件终结了构建过程----这个目录是有指示%doc创建的。


我们临时从文件列表%fiels中移除这一行然后再试一次:
---------------------------------------------------------------------------
# rpmbuild -ba cdplayer-1.0.spec
* Package: cdplayer
+ umask 022
+ echo Executing: %prep
Executing: %prep
+ cd /usr/src/redhat/BUILD
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz

Binary Packaging: cdplayer-1.0-1
Package Prefix = usr/local
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0
bin/cdp
bin/cdplay
man/man1/cdp.1
90 blocks
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm
+ umask 022
+ echo Executing: %clean
Executing: %clean
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ exit 0
Source Packaging: cdplayer-1.0-1
cdplayer-1.0.spec
cdplayer-1.0.tgz
82 blocks
Generating signature: 0
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm
#
---------------------------------------------------------------------------
整个构建过程顺利完成。注意看文件是如何放进二进制包中的列表,减去前缀  /usr/local 。
你们中的一些人可能想知道为什么 cdp.1 文件没有造成问题。毕竟,cdp.1 前面也有一个%doc指示。
答案就在于此文件是被指定的!!因为文件使用绝对路径指定并且路径以前缀Prefix (/usr/local)开始。
关于%doc指示更详细的讨论可以阅读第十三章的相关章节。


---------Tying Up the Loose Ends   这里有--收尾 的意思
这个包在构建的过程中,我们遇到了两个问题:
<1>配置文件cdp-config无法安装在 /etc 下面。
<2>README文件使用%doc指示无法打包。
这些问题都是由于这一事实造成---文件路径不首先以默认前缀路径/usr/local 开始.这是否意味着这个包不能重定位吗?
可能,但需要考虑以下两个选项。第一个选项是检查前缀。在我们的例子中,如果我们选择了一个前缀/usr 而不是/usr/local,
此时README文件可以使用%doc指示,因为默认的文档目录是/usr/doc. 另一个方法是使用%ocdir指示来定义另一个documentation-holding目录为前缀路径。【1】


这种方法不会让/etc/cdp-config顺利被打包。要打包此文件,我们需要采取更极端的措施。基本上,这个方法需要包装在一个可接受的文件目录(在/usr/local)和使用%post安装后脚本将文件移动到/etc。
另一种方式是采用符号链接(软连接)配置文件。


当然,这种方法有一些问题。首先,您需要编写一个%postun脚本撤销%post脚本执行的动作。%verifyscript 脚本确保文件完好。
第二,因为文件或符号链接并不是由RPM安装,没有进入的RPM数据库。这减弱了RPM 使用-c 和 -d选项的实用性。
最后,如果你实际上使用%post脚本移动文件,这些文件将不会验证正确,包被移除的时候您的用户将得到一些令人不安的消息,比如RPM找不到要移除的文件等。
如果你要使用这些技巧来解决问题,最好忘记试图让软件包重定位。


----------Test-Driving a Relocatable Package  测试一个重定位软件包
看起来像cdplayer重定位是一个不靠谱的候选方案。然而,由于我们的一个残缺的版本构建成功,我们可以用它来展示如何测试一个可重定位的包。
---------------------------------------------------------------------------
# rpm -qp --queryformat '%{DEFAULTPREFIX}\n' cdplayer-1.0-1.i386.rpm
/usr/local
#
---------------------------------------------------------------------------
DEFAULTPREFIX标记指导RPM来显示在构建所使用的前缀。正如我们所看到的/usr/local,如我们所想。
在第五章中讨论了--queryformat选项。


看来我们有一个可重定位的包。让我们尝试一些安装,看看我们是否真的可以安装在不同的位置。
首先,让我们来试试没有前缀指定的常规安装:
--------------------------------------------------------------
# rpm -Uvh cdplayer-1.0-1.i386.rpm
cdplayer    ##################################################
#
--------------------------------------------------------------
似乎很好地完成了安装。看看这些文件是否在我们所想的位置:
-----------------------------------------------------------------------------
# ls -al /usr/local/bin
total 558
-rwxr-xr-x   1 root     root        40739 Oct  7 13:23 cdp*
lrwxrwxrwx   1 root     root           18 Oct  7 13:40 cdplay -> /usr/local/bin/cdp*

# ls -al /usr/local/man/man1
total 9
-rwxr-xr-x   1 root     root         4550 Oct  7 13:23 cdp.1*

#
-----------------------------------------------------------------------------
看起来不错。让我们卸载包并重新安装使用一个不同的前缀:
-------------------------------------------------------------------------
# rpm -e cdplayer
# rpm -Uvh --prefix /usr/foonly/blather cdplayer-1.0-1.i386.rpm
cdplayer    ##################################################
#
-------------------------------------------------------------------------
我们应该注意到foonly目录和 blather目录在安装cdplayer之前不存在。


RPM还有其他的标签可以使用 --queryformat 选项来查询。它叫INSTALLPREFIX,使用它显示安装包前缀。我们试一试:
--------------------------------------------------------------------------
# rpm -q --queryformat '%{INSTALLPREFIX}\n' cdplayer
/usr/foonly/blather
#
--------------------------------------------------------------------------
正如我们所看到的,它显示我们在命令行输入的前缀。让我们来看看我们制定的安装:
--------------------------------------------------------------------------
# cd /usr/foonly/blather/
# ls -al
total 2
drwxr-xr-x   2 root     root         1024 Oct  7 13:45 bin/
drwxr-xr-x   3 root     root         1024 Oct  7 13:45 man/
#
--------------------------------------------------------------------------
目前为止,有适当的目录。首先让我们看看手册页:
--------------------------------------------------------------------------
# cd /usr/foonly/blather/man/man1/
# ls -al
total 5
-rwxr-xr-x   1 root     root         4550 Oct  7 13:23 cdp.1*
#
--------------------------------------------------------------------------
看起来ok!!现在来看/bin下的文件:
--------------------------------------------------------------------------
# cd /usr/foonly/blather/bin
# ls -al
total 41
-rwxr-xr-x   1 root     root        40739 Oct  7 13:23 cdp*
lrwxrwxrwx   1 root     root           18 Oct  7 13:45 cdplay -> /usr/local/bin/cdp
#
--------------------------------------------------------------------------
哦。这cdplay符号链接并不是正确的。发生了什么事?如果我们看看cdplayer的makefile,我们就能发现答案:
--------------------------------------------------------------------------
install: cdp cdp.1.Z

ln -s /usr/local/bin/cdp /usr/local/bin/cdplay
--------------------------------------------------------------------------
额,我们知道在软件构建的install过程中是Makefile文件设置符号链接。倒回去看文件列表%files ,我么看到cdplay的列表。RPM盲目的打包符号链接,完成其非重定位字符。
这就是为什么我们提到绝对路径作为非重定位软件的符号链接是一个很好的例子。


幸运的是,这个问题并不难解决。所有我们需要做的是改变makefile中创建符号链接的行:
--------------------------------------------------------------------------
ln -s /usr/local/bin/cdp /usr/local/bin/cdplay
--------------------------------------------------------------------------

--------------------------------------------------------------------------
ln -s ./cdp /usr/local/bin/cdplay
--------------------------------------------------------------------------
现在cdplay总是指向cdp,不管它是在哪里安装。在构建重定位包时,相对符号链接才是你的密友!


重新构建完成,让我们看看修改收到预期的效果。首先,正常安装使用默认前缀:
--------------------------------------------------------------------------
# rpm -Uvh cdplayer-1.0-1.i386.rpm
cdplayer    ##################################################
# cd /usr/local/bin/
# ls -al cdplay
lrwxrwxrwx   1 root     root           18 Oct  8 22:32 cdplay -> ./cdp*

--------------------------------------------------------------------------
接下来,我们将尝试第二次安装使用 --prefix 选项(我们删除第一次原包装后):
--------------------------------------------------------------------------
# rpm -e cdplayer
# rpm -Uvh --prefix /a/dumb/prefix cdplayer-1.0-1.i386.rpm
cdplayer    ##################################################
# cd /a/dumb/prefix/bin/
# ls -al cdplay
lrwxrwxrwx   1 root     root           30 Oct  8 22:34 cdplay -> ./cdp*
#
--------------------------------------------------------------------------


正如你所看到的,关于建立重定位包最棘手的部分是确保软件包是你的任务。一旦完成工作的一部分,实际的修改非常简单。


在下一章,我们将介绍如何包可以建在非标准目录,以及非root用户如何构建软件包。




鉴于RPM的发展已经很好的解决掉了非root用户构建包的困难,所以第十六章我不在这里讨论了!
关于pgp验证的内容我觉得非常必要,但在我先跳过这一部分,在最后来阐述这一部分的内容!!


我们下面来看第十八章:


Chapter 18. Creating Subpackages  
第十八章----创建子包(一些列软件包为实现有关联的功能)
在本章我们将探索RPM的一个非常有趣且有用的功能:那就是创建子包。
什么是子包呢???
简单的讲,子包就是有一个spec文件构建出的一系列多个软件包。RPM为我们提供了创建一个主包和一个或者多个子包的功能。
也可以之创建子包而没有主包。当然这一切都取决于软件包构建者(的心情,哈哈)!!


==================
==============================
Why Are They Needed?  为什么需要这些规则呢??
如果这世上所有的软件都按照“一个源文件对应一个二进制文件”这样的结构,那也就没有子包什么事儿了!毕竟,RPM处理程序的构建和打包成一个单一的包文件。


但是软件并总是以这样简单的结构存在。这种支持两个或者多个操作模式的软件并非稀少。比如,一个客户/服务程序,一个是客户端,一个是服务器。
还有比这更复杂的。有时软件完全依赖于另一个程序,所以,这两个包不能单独的构建。结果往往就是几个包同时被构建。


虽然我们也可以通过比较复杂的手段来强制这些软件最后生成一个包,但更有意义的做法是让RPM管理子包的创建。
为什么呢?从包构建者的角度来看,使用子包的主要原因是消除任何重复性的工作。


通过使用子包,你就不必为每个包维护单独的spec文件和忍受当新版本的软件发布时带来的头痛(如果不这样,你需要修改不同的spec文件)。
所这些都在一个spec文件中来控制,所以新版本的软件集成会很迅速,而且每一个相关的子包的构建只需要一个命令就解决了!!


好了,我们的开场白就说这些,下面我们来看如何着手创建我们的子包。


====================
================================
Our Example Spec File: Subpackages Galore!  
我们的示例spec文件中:子包好多啊!!!
--本章中我们将构造一个包含大量子包的spec文件。我们首先列出spec问价要求:
<1>这里的主包名称为foo。
<2>版本号为2.7 。
<3>这里有三个子包:我们暂且称之为:
\--服务端程序包
\--客户端程序包
\--baz开发需要的库文件子包,我们命名为bazlib
<4>bazlib的版本为5.6 
<5>每个子包都有自己的summary 和 description 标签。
每个spec都以spec为序言开始,当然这个也不例外。在本例中,序言包含下面的标签:
------------------------------------------------------------------------------------
Name: foo
Version: 2.7
Release: 1
Source: foo-2.7.tgz
License: probably not
Summary: The foo app, and the baz library needed to build it
Group: bogus/junque
%description
This is the long description of the foo app, and the baz library needed to
build it...
------------------------------------------------------------------------------------
我们看到,这里还没有什么特别的:到此为止这还是一个非常普通的spec文件。让我们来深入的研究多一点,看看我们还需要增加
那些内容来创建我们需要的子包。


================
================================
Spec File Changes For Subpackages  为构建子包修改spec文件
子包的创建严格按照spec文件来控制。这并不意味着你想必须学习一个全新的标签,条件是,创建子包的顺序。实际上你只需要学习一个个东西。
spec文件的主要变化是结构和开始为每个子包定义序言。


--------The Subpackage's "Preamble"  子包的“序言”
我们在第十章介绍RPM包的构建时,我们说过每个spec文件都包含一个序言。序言中包含各种各样的标签定义和各种各样的关于包的信息。
在只有一个包的情况下,序言一般存在于spec文件的开始。我们正在创建的spec文件也将有一个这样的序言。


当创建一个要构建子包的spec文件时,每个子包本身也需要一个序言。这种“子序言”中只需要定义一些和主序言中不同的描述子包的信息。
比如说,如果我们想要为子包定义一个安装前缀,我们就要添加一个合适的前缀 Prefix 标签在子包的序言中。这样子包才能重定位。


在只有一个包的spec文件中,没有明确标示序言,除了他的位置在文件的顶部。然而对于子包来说,我们需要更加明确的标示。
所以我们使用%package指示来识别每个子包的序言。


-----\The %package Directive
-----\ %packages 指示
这里 %packages 指示有两个功能,如上所述,他用以表明子包序言的开始。他还有一个作用就是形成子包的名称。例子中主序言包含
下面的 name 标签:
---------------------
name: foo
---------------------
在spec文件的后面有一个 %package 指示如下:
--------------------
%package bar
---------------------
这样最后的子包名称为foo-bar ;


这样来看,我们很容易发现子包和主包之间的关系。当然,此种命名约定可能不适用于每一种情况。所以你可以适用 %package 指示的选项来处理这种情况。


--------\Adding -n To the %package directive
--------\为 %package 指示添加 -n 选项
这里的 -n 选项可用来改变最终子包的名称为指定的名称(<mainpackage>-<subpackage> 为 <subpackage>)。我们来修改
上面的 %package 指示为:
------------------------------
%package -n bar
------------------------------
最后的结果是子包的名称会是bar而不是foo-bar 。


---------\Updating Our Spec File
---------\更新spec文件
我们来使用我们新发现的知识来重写我们的spec文件。下面列出了我们要创建的子包:
\--服务端程序包名称为foo-server
\--客户端程序包foo-client
\--baz开发需要的库文件子包,我们命名为bazlib


因为我们的报名为foo,又因为 %package 指示创建子包的名称会附加在包名后面,所以 %package指示对应的子包foo-server 和 foo-client应该
写为:
------------------------------------------
%package server
%package client
------------------------------------------
又因为baz 库的包名称没有以foo开头,我们需要使用 %package 指示的-n 选项来实现:
---------------------------------
%package -n bazlib
---------------------------------
我们的要求更进一步foo-server和foo-client与主包有相同的版本。


一个节省时间的方式就是在每个子包的序言中复用的信息在主包的序言中已经定义。因而在主包的序言中 version 标签已经被定义为 2.7 ,所以
在子包的序言中可以省略 version 标签的定义而直接继承主序言中的version 标签信息。


由于bazlib子包的序言包含一个 version 标签,他必须使用自己独特的版本。


此外每一个子包必须要有自己的summary 标签。


所以基于以上需求,我们的spec文件如下所示:
--------------------------------------------------------------------------------------------------
Name: foo
Version: 2.7
Release: 1
Source: foo-2.7.tgz
License: probably not
Summary: The foo app, and the baz library needed to build it
Group: bogus/junque
%description
This is the long description of the foo app, and the baz library needed to
build it...


%package server
Summary: The foo server


%package client
Summary: The foo client


%package -n bazlib
Version: 5.6
Summary: The baz library
--------------------------------------------------------------------------------------------------
我们现在可以比较清楚的看到子包的结构。


--------\Required Tags In Subpackages 
-----\Required 标签在子包构建中的使用
还有几个标签我们应该添加到我们示例中的子包的spec文件中。事实上,如果没有这些标记,RPM将产生令人印象深刻的警告:
----------------------------------------------------------------------
# rpmbuild -ba foo-2.7.spec
* Package: foo
* Package: foo-server
Field must be present    : Description
Field must be present    : Group
* Package: foo-client
Field must be present    : Description
Field must be present    : Group
* Package: bazlib
Field must be present    : Description
Field must be present    : Group


Spec file check failed!!
Tell rpm-list@redhat.com if this is incorrect.
#
---------------------------------------------------------------------
我们的规范文件是不完整的。底线是,每个子包必须有这三个标签:
1.The %description tag.
2.The group tag.
3.The summary tag.
很容易看到,前两个标记是必需的,但是 summary 标签呢?嗯,我们很幸运,一:我们已经包括 summary 为每个子包在我们的示例中的spec文件中。


我们首先来看一下 %description 标签。


----------\The %description Tag
正如你可能已经注意到的, %description 标签和其他标签有所不同。首先它以百分号开始。其次他标示的数据可以放在多行。再者,他必须包含对子包名称的描述。
这是通过附加子包名称到 %description 标签本身来完成的。下面我们给出 %package 指示:
----------------------------
%package server
%package client
%package -n bazlib
----------------------------
我们的 %description 标签要以下面的方式开始:
----------------------------
%description server
%description client
%description -n bazlib
----------------------------
你也注意到了我们在 %description 标签后面为bazlib也使用了 -n 选项。这是有意的,因为它使这个名字更加明确。


----------\Our Spec File So Far…
----------\到目前为止我们spec文件…
好吧,让我们看一下我们为每个子包添加了适当的%descriptionspec 标签连同组group标记后的spec文件:
---------------------------------------------------------------------
Name: foo
Version: 2.7
Release: 1
Source: foo-2.7.tgz
License: probably not
Summary: The foo app, and the baz library needed to build it
Group: bogus/junque
%description
This is the long description of the foo app, and the baz library needed to
build it...


%package server
Summary: The foo server
Group: bogus/junque
%description server
This is the long description for the foo server...


%package client
Summary: The foo client
Group: bogus/junque
%description client
This is the long description for the foo client...


%package -n bazlib
Version: 5.6
Summary: The baz library
Group: bogus/junque
%description -n bazlib
This is the long description for the bazlib...
---------------------------------------------------------------------
我们再来看看我们做了什么。我们按照通常的做法先创建了主序言,然后我们创建了三个额外的序言,每个序言以 %package 开始。
最后我们为子序言添加了一些标签。


但是关于 version 标签呢?难道 server 和 client 子包忘记添加版本标签了吗?
答案是:不,记住如果子包少一个给定的标签,它将会自动继承主序言中的对应标签的值。我们想要一个完整的spec文件,
但是至此我们还没有完成这个愿望。


让我们一起来继续欣赏spec文件的下面部分的变动关于子的包编译。


----------The %files List  文件列表
在一个普通单包的spec文件中, 文件列表 %files 用来决定那个文件实际上会被打包。但是在构建子包时这里稍有不同。
不同的是,每个子包必须有一个文件列表 %files 。


因为每一个文件列表 %files 必须与一个指定的 %package 指示关联,我们简单的使用子包名称来标记每个文件列表 %files ,由每个 %package 指示 。
回顾我们的例子,我们的 %package 行为:
-----------------------
%package server
%package client
%package -n bazlib
-----------------------
因此,我们的%files文件列表应该以下面的方式开始:
-------------------------------
%files server
%files client
%files -n bazlib
-------------------------------
此外,我们需要的主要包的%files文件列表,目前仍未有名称:
-------------------
%files
-------------------
每个文件列表 %files的内容完全由软件的要求来决定。例如,如果某个文件需要打包在多个包,是很好的包括不止一个文件名列表。


--------\Controlling Packages With the %files List
--------\使用文件列表 %files 控制包
文件列表 %files 对子包控制有非常大的权利。他甚至可以阻止一个包的创建。但是为什么你会想去创建麻烦的子包,为何不只保持一个被创建出来?


实际上,有。例如,如果客户机/服务器软件打包。当然,创建两个子包是有意义的:一个客户端和一个服务器。但是那个是主包呢?这里还需要什么吗?
通常不需要一个主导方案。在这种情况下,完全删除主文件列表%files就不会产生主包了。


------------A Point Worth Noting 值得注意的一点
请记住,一个空文件列表%files(即文件列表%files中不包含文件)不等于没有文件列表files。正如我们上面所提到的,完全删除在RPM文件列表%files结果不创建包。
然而,如果RPM遇到%files文件列表没有文件,它将愉快地创建一个空包文件。


他的这个功能(也适用于子包%files文件列表)与条件配合使用时往往能派上用场。如果一个文件列表被一个封闭的条件语句包含,这个包将按照条件的直来创建。


--------Our Spec File So Far…  至此,我们的spec文件
好吧,让我们更新spec文件示例。这就是它看起来像在添加每个子包的%files文件列表:
---------------------------------------------------------------------
Name: foo
Version: 2.7
Release: 1
Source: foo-2.7.tgz
License: probably not
Summary: The foo app, and the baz library needed to build it
Group: bogus/junque
%description
This is the long description of the foo app, and the baz library needed to
build it...


%package server
Summary: The foo server
Group: bogus/junque


%package client
Summary: The foo client
Group: bogus/junque


%package -n bazlib
Version: 5.6
Summary: The baz library
Group: bogus/junque




%files
/usr/local/foo-file


%files server
/usr/local/server-file


%files client
/usr/local/client-file


%files -n bazlib
/usr/local/bazlib-file
            
---------------------------------------------------------------------
正如你所看到的我们添加了%files文件列表:
The main foo package.
The foo-server subpackage.
The foo-client subpackage.
The bazlib subpackage.
每个包包含一个文件。如果没有需要一个主包,我们可以简单地删除未命名的%files文件列表。请记住,即使你不创建一个主包,
主包的序言中定义的标签会出现某个地方——特别是在源包文件。


让我们最后看看子包指定的spec文件的一部分:安装和卸载时脚本。


------\Install- and Erase-time Scripts
-------\安装和卸载时脚本
安装和卸载时脚本,%pre, %preun, %post, 和 %postun,都可以用完全相同的方法命名以用于其他子包部分的spec文件。
脚本中使用包验证,%verifyscript 标签可以指定与包相关。从我们的案例中的使用子包结构的spec文件,我们最后可以
将脚本定义为:
--------------------------------------
%pre server
%postun client
%preun -n bazlib
%verifyscript client
--------------------------------------
除了命名的变化,为子包创建脚本的过程中有一件事需要注意。重要的是你要考虑多个子包脚本之间交互的可能性。
当然这这是一个简单的脚本实践,即使包之间并不相关。


---------Back At the Spec File…   我们在回到spec文件
下面我们添加这些脚本到我们的spec文件。因为我们在这里尽量保持示例的简要,所以我们这里只为每个包添加安装前脚本:
-----------------------------------------------------------------------------
Name: foo
Version: 2.7
Release: 1
Source: foo-2.7.tgz
License: probably not
Summary: The foo app, and the baz library needed to build it
Group: bogus/junque
%description
This is the long description of the foo app, and the baz library needed to
build it...


%package server
Summary: The foo server
Group: bogus/junque
%description server
This is the long description for the foo server...


%package client
Summary: The foo client
Group: bogus/junque
%description client
This is the long description for the foo client...


%package -n bazlib
Version: 5.6
Summary: The baz library
Group: bogus/junque
%description -n bazlib
This is the long description for the bazlib...


********************************************************************************#星号之间为我们新添加的
%pre
echo "This is the foo package preinstall script"


%pre server
echo "This is the foo-server subpackage preinstall script"


%pre client
echo "This is the foo-client subpackage preinstall script"


%pre -n bazlib
echo "This is the bazlib subpackage preinstall script"
********************************************************************************
%files
/usr/local/foo-file


%files server
/usr/local/server-file


%files client
/usr/local/client-file


%files -n bazlib
/usr/local/bazlib-file
----------------------------------------------------------------------------
这些安装前脚本虽说不会执行实际性的动作,但我们可以了解到一个子包指定的脚本如何来定义。


到此为止,我们的spec文件还缺少一些东西。我们在下面部分继续“战斗”!!


======
=================
Build-Time Scripts: Unchanged For Subpackages  
构建时脚本:子包构建保持不变的脚本部分
虽然子包构建改变了一般的spec文件的结构,但是其中任然有一部分是保持不变的:构建时脚本。这就表示在任何一个spec
文件中仅仅只有一组%prep, %build, 和 %install 脚本。


无疑,即使RPM不需要这部分脚本有任何改变,但是我们好人就需要做一些与子包构建相关的改动。一般情况下这些改变主要涉及
对源文件的解包、编译和安装内容。从我们的例子来看,如果我们此时要打包client/server软件,这两部分源码首先必须被解包以及
被编译成二进制并且安装(拷贝到指定目录下...)。


----再来看我们的spec文件:
我们着手添加几行构建是时脚本,再来看spec文件:
----------------------------------------------------------------------------
Name: foo
Version: 2.7
Release: 1
Source: foo-2.7.tgz
License: probably not
Summary: The foo app, and the baz library needed to build it
Group: bogus/junque
%description
This is the long description of the foo app, and the baz library needed to
build it...


%package server
Summary: The foo server
Group: bogus/junque
%description server
This is the long description for the foo server...


%package client
Summary: The foo client
Group: bogus/junque
%description client
This is the long description for the foo client...


%package -n bazlib
Version: 5.6
Summary: The baz library
Group: bogus/junque
%description -n bazlib
This is the long description for the bazlib...


%prep
%setup


%build
make


%install
make install


%pre
echo "This is the foo package preinstall script"


%pre server
echo "This is the foo-server subpackage preinstall script"


#%pre client
#echo "This is the foo-client subpackage preinstall script"


%pre -n bazlib
echo "This is the bazlib subpackage preinstall script"


%files
/usr/local/foo-file


%files server
/usr/local/server-file


%files client
/usr/local/client-file


%files -n bazlib
/usr/local/bazlib-file
----------------------------------------------------------------------------
如我们所看到的,构建时脚本就是这么简约!!(这得益于make为我们所做的工作)


【1】如果你对觉得这些示例都比较low,可以各个镜像站点wget下来几个包的源码(xxx.src.rpm),来分析其中的技术细节。




=======
======================
Building Subpackages   下面我们着手构建一个子包。
是时候拉出来练练了,我们瞅瞅spec文件发现也没有太多的不同:
----------------------------------------------------------------------------
# rpmbuild -ba foo-2.7.spec
* Package: foo
* Package: foo-server
* Package: foo-client
* Package: bazlib

Executing: %prep

Executing: %build

Executing: %install

Executing: special doc
+ cd /usr/src/redhat/BUILD
+ cd foo-2.7
+ DOCDIR=//usr/doc/foo-2.7-1
+ DOCDIR=//usr/doc/foo-server-2.7-1
+ DOCDIR=//usr/doc/foo-client-2.7-1
+ DOCDIR=//usr/doc/bazlib-5.6-1
+ exit 0
Binary Packaging: foo-2.7-1
Finding dependencies...
usr/local/foo-file
1 block
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/foo-2.7-1.i386.rpm
Binary Packaging: foo-server-2.7-1
Finding dependencies...
usr/local/server-file
1 block
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/foo-server-2.7-1.i386.rpm
Binary Packaging: foo-client-2.7-1
Finding dependencies...
usr/local/client-file
1 block
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/foo-client-2.7-1.i386.rpm
Binary Packaging: bazlib-5.6-1
Finding dependencies...
usr/local/bazlib-file
1 block
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/bazlib-5.6-1.i386.rpm

Source Packaging: foo-2.7-1
foo-2.7.spec
foo-2.7.tgz
4 blocks
Generating signature: 0
Wrote: /usr/src/redhat/SRPMS/foo-2.7-1.src.rpm
#
----------------------------------------------------------------------------
我们看到我们开始执行一个非常熟悉的命令。紧接着这个命令,RPM表示这里将会有四个包被创建。然后%prep, %build, 和 %install三个脚本悉数执行。


接下来,RPM执行指定的doc内部脚本,即使我们没有声明任何文件的文档。然而,值得注意的是,DOCDIR环境变量显示,如果spec文件已经声明一些文件的文档,
那么RPM会为每个包创建适当的文档目录。


再接下来,RPM创建二进制文件包,如你所愿每个包都是在文件列表中定义的。


最后源码包被创建,源码包中包含了spec文件和其他的一些原始源文件,一些补丁之类的。


总的来看,一个spec文件。一系列原始文件(源码)。一个命令。四个包。我想来想去这一句该怎么翻译呢!!---All in all, a pretty good deal, isn't it?


----我们来瞄一眼我们辛苦得来的各个子包。
下面我们先来看看每个包的信息:
----------------------------------------------------------------------------
# rpm -qip foo-2.7-1.i386.rpm
Name        : foo                   Distribution: (none)
Version     : 2.7                         Vendor: (none)
Release     : 1                       Build Date: Wed Nov 06 13:33:37 1996
Install date: (none)                  Build Host: foonly.rpm.org
Group       : bogus/junque            Source RPM: foo-2.7-1.src.rpm
Size        : 35
Summary     : The foo app, and the baz library needed to build it
Description :
This is the long description of the foo app, and the baz library needed to
build it...
#
# rpm -qip foo-server-2.7-1.i386.rpm
Name        : foo-server            Distribution: (none)
Version     : 2.7                         Vendor: (none)
Release     : 1                       Build Date: Wed Nov 06 13:33:37 1996
Install date: (none)                  Build Host: foonly.rpm.org
Group       : bogus/junque            Source RPM: foo-2.7-1.src.rpm
Size        : 42
Summary     : The foo server
Description :
This is the long description for the foo server...
#
# rpm -qip foo-client-2.7-1.i386.rpm
Name        : foo-client            Distribution: (none)
Version     : 2.7                         Vendor: (none)
Release     : 1                       Build Date: Wed Nov 06 13:33:37 1996
Install date: (none)                  Build Host: foonly.rpm.org
Group       : bogus/junque            Source RPM: foo-2.7-1.src.rpm
Size        : 42
Summary     : The foo client
Description :
This is the long description for the foo client...
#
# rpm -qip bazlib-5.6-1.i386.rpm
Name        : bazlib                Distribution: (none)
Version     : 5.6                         Vendor: (none)
Release     : 1                       Build Date: Wed Nov 06 13:33:37 1996
Install date: (none)                  Build Host: foonly.rpm.org
Group       : bogus/junque            Source RPM: foo-2.7-1.src.rpm
Size        : 38
Summary     : The baz library
Description :
This is the long description for the bazlib...
#
----------------------------------------------------------------------------
上面我们使用RPM的查询功能显示了每个包的一些列汇总信息。其中有几点是值得注意的。
第一,每个包的信息中都列出了其源码包(foo-2.7-1.src.rpm),这是判断两个包文件的创建来源于一个源码包。尝试使用每个包的名称
作为指示是一种徒劳的做法,比如说你从bazlib包能判断出什么呢!!!


接下来要注意的是,每个包的总结和描述特定于这个包中。因为这些标签被放置根据的位置是根据每个包来命名,这应该不足为奇。


最后我们发现每个包的版本信息都是从主包的序言(preamble)中集成过来的,当然除了bazlib 包的版本我们使用version标签进行了处理。


如果我们来看一下源码包的信息,我们发现他的信息完全来自主包的一些标签信息:
----------------------------------------------------------------------------
# rpm -qip foo-2.7-1.src.rpm
Name        : foo                   Distribution: (none)
Version     : 2.7                         Vendor: (none)
Release     : 1                       Build Date: Wed Nov 06 13:33:37 1996
Install date: (none)                  Build Host: foonly.rpm.org
Group       : bogus/junque            Source RPM: (none)
Size        : 1415
Summary     : The foo app, and the baz library needed to build it
Description :
This is the long description of the foo app, and the baz library needed to
build it...

----------------------------------------------------------------------------
我们很容易发现,如果这里没有主文件列表,也就是说没有主包,主序言的总的标签任然会源码包使用。
这就是为什么RPM强制要求主序言需要包含版权信息(copyright), 描述标签(%description), 和组(group)标签.
所以这里有一点忠告,不要在主序言中为了满足RPM的要求做一些愚蠢的描述(%description),因为你此时的‘回眸一笑’将会永驻人们的视野,也就是说在
以后的发布版本中这些描述将会被继续继承,所以这里的描述需要谨慎为之。【2】


-----检查子包指定的安装和卸载脚本
---------------------------------------------------------------------------
# rpm -Uvh foo-2.7-1.i386.rpm
foo                    This is the foo package preinstall script
##################################################
#
# rpm -Uvh foo-server-2.7-1.i386.rpm
foo-server             This is the foo-server subpackage preinstall script
##################################################
#
# rpm -Uvh foo-client-2.7-1.i386.rpm
foo-client             This is the foo-client subpackage preinstall script
##################################################
#
# rpm -Uvh bazlib-5.6-1.i386.rpm
bazlib                 This is the bazlib subpackage preinstall script
##################################################

---------------------------------------------------------------------------
正如所料,独有的%pre脚本为每个包自己包括。当然,如果我们没有想实际安装包,我们可以使用RPM的--scripts选项显示指定包安装时会执行的脚本:
---------------------------------------------------------------------------
# rpm -qp --scripts foo-2.7-1.i386.rpm
preinstall script:
echo "This is the foo package preinstall script"


postinstall script:
(none)
preuninstall script:
(none)
postuninstall script:
(none)
verify script:
(none)
#
---------------------------------------------------------------------------
这是一种安全的做法,特别是,在安装新包之后会破坏你的系统环境时这一方法比较实用。



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值