免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: duanjigang
打印 上一主题 下一主题

Linux平台软件管理系统设计与规划-进阶篇(2)-rpm生成:rpmbuild 和 spec文件剖析 [复制链接]

论坛徽章:
1
白银圣斗士
日期:2015-11-23 08:33:04
11 [报告]
发表于 2012-12-17 14:30 |只看该作者
还有人奉献!加油!

论坛徽章:
0
12 [报告]
发表于 2012-12-17 23:51 |只看该作者
回复 6# duanjigang
相关的部分为:
%build_src_dir/scripts/config \
        --set-str CONFIG_LOCALVERSION %release_num-%build_flavor \
        --enable  CONFIG_SUSE_KERNEL \
%if 0%{?__debug_package:1}
        --enable  CONFIG_DEBUG_INFO \
        --disable  CONFIG_DEBUG_INFO_REDUCED
%else
        --disable CONFIG_DEBUG_INFO
%endif

该spec是内核编译的spec,具体请见附件
kernel-defaule.spec.tar.gz (661.89 KB, 下载次数: 50)

   

论坛徽章:
0
13 [报告]
发表于 2012-12-18 09:26 |只看该作者
本帖最后由 duanjigang 于 2012-12-18 09:37 编辑
lofeng410 发表于 2012-12-17 23:51
回复 6# duanjigang
相关的部分为:
%build_src_dir/scripts/config \


这个表达式的写法我没看懂,但是意思应该很明确,就是判断 rpmbuild 时 是否打开了 __debug_package 宏
如果打开的话,在config 时就支持debug子包,否则就不支持。
这个配置在:
/usr/lib/rpm/macros

中有

  1. #       Template for debug information sub-package.
  2. %debug_package \
  3. %ifnarch noarch\
  4. %global __debug_package 1\
  5. %package debug\
  6. Summary: Debug information for package %{name}\
  7. Group: Development/Debug\
  8. AutoReqProv: 0\
  9. %description debug\
  10. This package provides debug information for package %{name}.\
  11. Debug information is useful when developing applications that use this\
  12. package or when debugging this package.\
  13. %files debug -f debugfiles.list\
  14. %defattr(-,root,root)\
  15. %endif\
  16. %{nil}
复制代码
如果是非 noarch 平台,则定义 __debug_package 为1,并且在产生子包 xxx-debug.

论坛徽章:
0
14 [报告]
发表于 2012-12-18 09:45 |只看该作者
本帖最后由 duanjigang 于 2012-12-18 09:59 编辑

平台架构和OS相关的TAGS:


excludearch :
说明该RPM不能在该平台上 build,因为该软件还未移植到该平台或者在该平台运行不稳定。一般写法是

  1. ExcludeArch: sparc alpha
复制代码
可以exclude 一个或者多个 arch.

  1. # rpm -ba cdplayer-1.0.spec
  2. Arch mismatch!
  3. cdplayer-1.0.spec doesn't build on this architecture
  4. #
复制代码
exclusivearch
  与上面的 excludearch 相反,指明该包只能在某个平台上build,支持一到多个arch.
比如:

  1. ExclusiveArch: sparc alpha
复制代码
ExcludeOS 和 exclusiveos 也是类似的,只不过针对的是OS
例子如下:

  1. ExcludeOS: linux irix
  2. ExclusiveOS: linux
复制代码
目录相关的TAGS:

prefix:
主要为了构建 “relocatable ”包而用,也就是你在rpm 安装时可以通过 --prefix=xx 来改变默认安装位置。
比如你的 spec 中 prefix 指定为: prefix: /opt
而你的 %files 中文件路径为 “/opt/blather/foonly”这样的,那么,如果你在安装时指定prefix 为

  1. --prefix=/tmp/123
复制代码
则对应的包blather 会安装到 /tmp/123/blather 目录下。


buildroot 主要是包在编译生成RPM时用到,可以参考开始的文章内容:

  1. BuildRoot: /tmp/cdplayer
复制代码

论坛徽章:
0
15 [报告]
发表于 2012-12-18 10:11 |只看该作者
本帖最后由 duanjigang 于 2012-12-18 10:12 编辑

其它的TAG 再大致列举下:

源码和patche相关的:

  1. source
  2. nosource
  3. patch
  4. nopatch
复制代码
这几个Tag,具体的意思可以参考spec文件的语法和例子spec文件。
比如openssl.spec文件中的例子:

  1. Source: openssl-fips-%{version}-usa.tar.bz2
  2. Source1: hobble-openssl
  3. Source2: Makefile.certificate
  4. Source3: ca-bundle.crt
  5. Source4: https://rhn.redhat.com/help/RHNS-CA-CERT
  6. Source5: https://rhn.redhat.com/help/RHNS-CA-CERT.asc
  7. Source6: make-dummy-cert
  8. Source8: openssl-thread-test.c
  9. Source9: opensslconf-new.h
  10. Source10: opensslconf-new-warning.h
  11. Source11: README.FIPS

  12. Patch0: openssl-fips-0.9.8e-redhat.patch
  13. Patch1: openssl-0.9.8a-defaults.patch
  14. Patch2: openssl-0.9.8a-link-krb5.patch
  15. Patch3: openssl-0.9.8b-soversion.patch
  16. Patch4: openssl-0.9.8a-enginesdir.patch
  17. Patch5: openssl-0.9.8a-no-rpath.patch
  18. %{SOURCE1} > /dev/null
  19. %patch0 -p1 -b .redhat
  20. %patch1 -p1 -b .defaults
  21. # Fix link line for libssl (bug #111154).
  22. %patch2 -p1 -b .krb5
  23. %patch3 -p1 -b .soversion
  24. %patch4 -p1 -b .enginesdir
  25. %patch5 -p1 -b .no-rpath


  26. %patch32 -p1 -b .ia64
  27. #patch33 is applied after make test
  28. %patch34 -p1 -b .x509
  29. %patch35 -p1 -b .version-add-engines
  30. %patch38 -p1 -b .cipher-change
  31. %patch39 -p1 -b .ipv6-apps
复制代码

论坛徽章:
0
16 [报告]
发表于 2012-12-18 10:14 |只看该作者
本帖最后由 duanjigang 于 2012-12-18 20:05 编辑

tag的说明就到此结束,下来看看spec中的
scripts 脚本

spec 中的脚本,也称为 hook 脚本,因为他们就想 netfilter 中的 hook 函数,就像 yum 的 plugin,在一个流程的不同点去执行,控制流程的行为,丰富流程的功能。

spec 中的脚本可以分为三大类,根据执行的场景划分:

(1): build 时脚本:在 RPM 的编译过程中执行
(2): 安装/卸载时脚本:在RPM的安装/升级/卸载时执行
(3): 校验时脚本: 在 RPM 命令校验已安装 RPM 时执行。
(4): 另外,还会再补充下 触发器(Trigger)脚本

build 时脚本:
    作为开发人员可能都知道。源代码编译成二进制包,基本上分为以下不可缺少的四步:

  1.     1): 解压缩源码
  2.     2): 编译源码
  3.     3): 安装二进制程序/软件
  4.     4):清除编译环境
复制代码
对应于这四个不同的点,SPEC 提供了四个脚本hook点,每个hook点的声明:

  1. %hook_name #比如 %pre, %build, %install, %clean
  2. hook点的脚本内容
复制代码
每个hook点的脚本都会最终被存储到一个脚本文件中去解释,因此,在这些hook点,你就当是在写你的脚本文件。

在 %pre 这个hook点一般做的事情都是编译前环境准备,大致有:

  1.    (1):创建工作的顶级目录
  2.    (2): 把源代码解压缩到 build 目录。
  3.    (3): 如果有patch定义的话,patch所有代码。
  4.    (4): 其它事情,为代码build做好准备。
复制代码
%build hook点的脚本一般就是configure和build源码,比如 wget 的SPEC中的 %build 段是这么写的:

  1. %build
  2. ./configure  --with-ssl=openssl
  3. make
复制代码
%install 是包build过程中安装到buid_root时执行的脚本。
最常见的执行脚本就是:

  1. %install
  2. make install prefix=$RPM_BUILD_ROOT/usr

  3. mkdir -p $RPM_BUILD_ROOT/usr/xxoo
  4. install binname $RPM_BUILD_ROOT/usr/xxoo/bin
复制代码
等。

%clean 这个hook点脚本是rpm build完成后执行的脚本,大多数内容是直接删除build 目录:

  1. %clean
  2. [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
复制代码
RPM 的 安装/升级/卸载 hook 脚本

在一个机器上第一次安装一个RPM,那是很容易而且简单的事情,但是,一旦目标机器已经安装了某个包,软件升级和卸载就不像

初次安装那么容易了。

我们很容易能想到的事情有:

第一:软件自带的默认配置文件,你可能在使用一段时间后,已经做了很多修改,当然不希望升级时默认的配置将其覆盖或者

软件卸载时将其删除。

第二:软件产生的数据和日志文件,有时我们希望都保留下来或者删除日志,保留数据等等。

要完成这些任务,仅仅靠RPM的文件安装功能是不能做到的,必须得在一些执行点执行某些命令来配合完成。

因此,RPM提供了四个安装/卸载时执行的hook函数可以潜入在RPM中,两个给安装用,两个给卸载用。

这种在一些列操作流程中注入hook操作点的方法,在软件中已经很常见了,比如 yum 的 branch插件,netfilter的hook模块等,都是

典型的范例。

%pre 段的命令会在 rpm 安装前执行

%post 段的命令会在 rpm 安装后执行。

%preun 段的命令会在 rpm 卸载前执行。

%postun 段的命令会在 rpm 卸载后执行。


然后看下 rpm 升级时 rpm 命令的流程。

RPM升级流程

(1): 执行 新版rpm 的 pre 段命令

(2): 安装新版RPM的文件列表

(3): 执行新版RPM的post 段命令

(4): 执行老版本rpm 的 preun 段命令

(5): 删除老版本RPM中没有被新版本RPM覆盖的文件(也就是说不再被新版本RPM使用的文件),这个过程是自动的。

(6): 执行老版本RPM的 postun 段命令。




关于以上这个顺序,一定要牢记和理解,因为,如果理解不对的话,在 rpm 升级时很可能就造成很大的差错。

举一个最常见的例子,好多人不知道老版本的rpm的文件在升级时会被自动删除掉,就认为需要人为来删除这些文件。

于是他会在rpm的 postun 中写入脚本去 rm -fr /安装目录。

这样会出现什么问题呢?

我们可以看到,卸载的话,是没问题的,先 preun, 再postun.

但是如果是升级呢?

最后一步执行的老版本的postun,这样的话,新安装的文件都会被干掉,导致不仅升级失败,而且原来的文件也丢失了。

为了保证 rpm 的制作者或者开发人员能够灵活的使用这四个hook,rpm 为 四个hook点的执行命令提供了一个参数,来区分是安装,还是升级

还是卸载操作,如下:

(1): %pre 如果参数是1,说明是初次安装,如果是2,说明是版本升级

(2): %post 参数1表示首次安装,2表示升级

(3): %preun 和 %postun 的参数如果是1,表示是升级

(4): %preun 和 %postun 的参数是0,表示是卸载。


hook点的命令默认是用shell解释期来解释执行的,当然你也可以自己定义解释器,比如:

  1. %pre -p /usr/bin/perl
  2. if ( $ARGV[0] == 1 ) {
  3.   print "Preparing for initial install...\n"
  4. }
  5. elsif ( $ARGV[0] == 2 ) {
  6.   print "Preparing to upgrade software...\n"
  7. }
复制代码

论坛徽章:
0
17 [报告]
发表于 2012-12-18 20:17 |只看该作者
本帖最后由 duanjigang 于 2012-12-18 20:17 编辑

RPM的校验时脚本-%verifyscript

这个脚本一般会在rpm包校验时执行,脚本行为完全根据包制作人的想法去走,因为rpm本身会校验rpm的文件列表,因此,你可以在这里做任何蛋疼的事情。
而且,默认情况下 %verifyscript 的打印内容不会打印出来,除非你用 verbose 模式进行校验,比如,在 test-baby 的 spec 文件中写:

  1. %verifyscript
  2. echo "verify by duanjigang now"
复制代码
然后编译生成包后进行 verify,如下:

  1. rpm -qpVv RPMS/x86_64/test-baby-1.1-1.x86_64.rpm  
  2. Unsatisfied dependencies for test-baby-1.1-1.x86_64: baby-is-provided
  3. missing   d /usr/bin/test_baby
  4. verify by duanjigang now
复制代码

论坛徽章:
0
18 [报告]
发表于 2012-12-18 20:20 |只看该作者
本帖最后由 duanjigang 于 2012-12-18 20:24 编辑

RPM 安装/卸载时的 trigger 脚本

trigger 脚本不同于 hook脚本之处在于,hook 脚本会在这些点无条件执行,而trigger只有满足定义的条件时才执行指定脚本。

trigger这个机制,为被安装包提供了更灵活的与其它rpm包交互或者通讯的方法。

这样描述:

安装A包时安装了几个触发器trigger,并且安装的trigger定义了对其它包B的检测,一般的对B的检测就是安装,卸载等状态,

如果触发器被触发,则执行该trigger对应的脚本。

A %triggerin script runs when the named package is installed.
%triggerun runs when the named package is uninstalled.
A %triggerpostun script runs after the named package is uninstalled.

(1): B %triggerin 当A包被安装时,如果B包已经安装,该触发器对应的脚本会执行。
(2): B %triggerun 当A包被安装时,如果B包已经被卸载,该触发器对应的脚本执行。
(3): B %triggerpostun: A包安装后,如果B包被卸载,卸载完成后该触发器对应的脚本执行。


通过 --来指定被监控的包,比如:
%triggerin -p /usr/bin/perl -- ruby
# print "ruby alreay installed"
当你的包安装时,如果ruby这个包已经存在,就会打印信息。
trigger 和 hook在软件升级时的执行顺序
印信息。
假设以前安装的老版本RPM是n,新版本的包是N。
1) 执行 N 的pre 脚本
2): 拷贝N的文件列表
3): 执行N的 post 脚本
4): 检查所有其它RPM的triggerin 触发器,如果有被N的安装触发的,就会执行对应的脚本。
5): 执行N的所有 triggerin 触发器。
6): 执行n的所有triggerun触发器
7): 检查所有其它RPM的triggerin 触发器,如果有被N的卸载触发的,就会执行对应的脚本。
: 执行 n 的 preun 脚本。
9): 删除 n 的所有未被 N 覆盖的文件。
10):执行 n 的postun 脚本。

还有一点要记住,切记不要在rpm的hook或者trigger中编写和用户交互的脚本。rpm安装本来就是为了批量自动安装的。
交互式脚本会让rpm的安装 hang 住。


#########################################################################
到此未知,RPM的SPEC中的另一大元素,SCRIPTS(脚本就算介绍完了)。

论坛徽章:
0
19 [报告]
发表于 2012-12-21 13:39 |只看该作者
回复 13# duanjigang


   %debug_package这是个系统内置宏吧?如何使用这个宏呢?我在.rpmmacros中直接添加%debug_package,rpmbuild报错,has empty body。

论坛徽章:
0
20 [报告]
发表于 2012-12-21 13:52 |只看该作者
为啥系统内置宏直接%就可以了?而我们定义时,要使用%define呢
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP