RPM包制作

基础知识

rpmbuild工作流程

  • 读取并解析 filename.spec 文件
  • 运行 %prep 部分来将源代码解包到一个临时目录,并应用所有的补丁程序。
  • 运行 %build 部分来编译代码。
  • 运行 %install 部分将代码安装到构建机器的目录中。
  • 读取 %files 部分的文件列表,收集文件并创建二进制和源 RPM 文件。
  • 运行 %clean 部分来除去临时构建目录。

spec文件内容解释

第一部分 多种信息的定义

  • Summary
    关于该软件包的描述。

  • Name
    软件的基名

  • Version
    软件的版本号

  • Release
    RPM本身的版本号,如果修复了 spec 文件中的一个错误并发布了该软件同一版本的新 RPM,就应该增加发行版号。

  • License
    应该给出一些许可术语(如:“GPL”、“Commercial”、“Shareware”)。

  • Group
    标识软件类型,可以在 /usr/share/doc/rpm/GROUPS 文件看到一个 Red Hat 使用的组列表。但是您还可以使用那些组名以外的名称。

  • Source0 、 Source1
    等等给这些源文件命名(通常为 tar.gz 文件).

  • BuildRoot
    安装或者编译时的虚拟目录,%{name} 和 %{version} 是 RPM 宏,它们扩展成为头中定义的 rpm 名称和版本。

  • URL
    软件的主页

  • Vendor
    发行商或打包组织的信息,例如RedFlag Co,Ltd

  • Disstribution
    发行版标识

  • Patch
    补丁源码,可使用Patch1、Patch2等标识多个补丁,使用%patch0或%{patch0}引用

  • Prefix
    %{_prefix} 这个主要是为了解决今后安装rpm包时,并不一定把软件安装到rpm中打包的目录的情况。这样,必须在这里定义该标识,并在编写%install脚本的时候引用,才能实现rpm安装时重新指定位置的功能

  • Prefix
    %{_sysconfdir} 这个原因和上面的一样,但由于%{_prefix}指/usr,而对于其他的文件,例如/etc下的配置文件,则需要用%{_sysconfdir}标识

  • Build Arch
    指编译的目标处理器架构,noarch标识不指定,但通常都是以/usr/lib/rpm/marcros中的内容为默认值

  • Requires
    该rpm包所依赖的软件包名称,可以用>=或<=表示大于或小于某一特定版本,例如:
    libpng-devel >= 1.0.20 zlib
    >=号两边需用空格隔开,而不同软件名称也用空格分开;
    还有例如PreReq、Requires(pre)、Requires(post)、Requires(preun)、Requires(postun)、BuildRequires等都是针对不同阶段的依赖指定;

  • Provides
    指明本软件一些特定的功能,以便其他rpm识别

  • Packager
    打包者的信息

  • %description
    软件的详细说明。

第二部分 脚本主体

  • %prep
    负责对软件包解包。在最常见情况下,您只要用 %setup 宏即可,它会做适当的事情,在构建目录(BUILD)下解包源 tar 文件。加上 -q项只是为了减少输出。

  • %setup
    把源码包解压并放好

    1
    2
    3
    4
    5
    6
    7
    
    %setup    不加任何选项,仅将软件包打开。 
    %setup -n newdir 将软件包解压在newdir目录。 
    %setup -c 解压缩之前先产生目录。 
    %setup -b num 将第num个source文件解压缩。 
    %setup -T 不使用default的解压缩操作。 
    %setup -T -b 0 将第0个源代码文件解压缩。 
    %setup -c -n newdir 指定目录名称newdir,并在此目录产生rpm套件。
    
  • %patch
    打补丁,通常补丁都会一起在源码tar.gz包中,或放到SOURCES目录下。一般参数为:

    1
    2
    3
    4
    5
    6
    
    %patch -p1 使用前面定义的Patch补丁进行,-p1是忽略patch的第一层目录 
    %patch     最简单的补丁方式,自动指定patch level。 
    %patch 0   使用第0个补丁文件,相当于%patch ?p 0。 
    %patch -s  不显示打补丁时的信息。 
    %patch -T  将所有打补丁时产生的输出文件删除。
    %Patch2 -p1 -b xxx.patch 打上指定的补丁,-b是指生成备份文件
    
  • %build
    开始编译软件包

  • %configure
    这个不是关键字,而是rpm定义的标准宏命令。意思是执行源代码的configure配置;
    在/usr/src/asianux/BUILD/%{name}-%{version}目录中进行,使用标准写法,会引用/usr/lib/rpm/marcros中定义的参数。
    另一种不标准的写法是,可参考源码中的参数自定义,例如:
    CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{_prefix}

  • %install
    在构建系统上安装软件包。这似乎和 make install 一样简单,但通常要复杂些。

  • %makeinstall
    这不是关键字,而是rpm定义的标准宏命令。也可以使用非标准写法:
    make DESTDIR=$RPM_BUILD_ROOT install

  • %clean
    通常内容为:

    1
    2
    
    [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT"
    rm -rf $RPM_BUILD_DIR/%{name}-%{version}
    
  • %pre
    rpm安装前执行的脚本
    第一个参数为1说明是初次安装,为2说明是版本升级;

  • %post
    rpm安装后执行的脚本
    第一个参数为1说明是初次安装,为2说明是版本升级;

  • %preun
    rpm卸载前执行的脚本
    第一个参数为1,表示升级;
    第一个参数为0,表示卸载;

  • %postun
    rpm卸载后执行的脚本
    第一个参数为1,表示升级;
    第一个参数为0,表示卸载;

  • %files
    列出应该捆绑到 RPM 中的文件,并能够可选地设置许可权和其它信息可以在 %files 中用一行包括多个文件。此处要特别注意,这里要用对与buildroot的相对路径,一定不能用绝对路径,因为对file部分的解析会自动添加路径的前面一部分;
    可以通过在行中添加 %doc 或 %config 来标记文件;

  • %defattr (-,root,root)
    指定包装文件的属性,分别是(mode,owner,group),-表示默认值,对文本文件是0644,可执行文件是0755

  • %attr(permissions,user,group)
    覆盖个别文件的所有者和许可权。

  • %doc
    告诉 RPM 这是一个文档文件,因此如果用户安装软件包时使用 —excludedocs ,将不安装该文件。您也可以在 %doc 下不带路径列出文件名,RPM 会在构建目录下查找这些文件并在 RPM 文件中包括它们,并把它们安装到 /usr/share/doc/%{name}-%{version} 。以 %doc 的形式包括 README 和 ChangeLog 这样的文件是个好主意。

spec文件中的变量定义和宏定义

可以通过命令rpm --showrc查看实现代码。另外直接通过rpm --eval "%{macro}"来查看具体对应路径。

比如我们要查看%{_bindir}的路径,就可以使用命令rpm --eval "%{ _bindir}"来查看。

另外,所有的宏都可以在/usr/lib/rpm/macros里找到。

  • _sourcedir
    RPM 在哪里查找源文件(tar 文件,等)

  • _srcrpmdir
    RPM 在哪里放入新的源 RPM 文件

  • _rpmdir
    RPM 将把新的二进制 RPM 文件放在哪里(在特定于体系结构的子目录中)

其中一些根据其它变量定义;例如,当您看到 %{_topdir} ,查找 _topdir 的定义,等等。

一些变量和宏的定义可以在以下文件中找到:

rpmrc Configuration

  • /usr/lib/rpm/rpmrc
  • /usr/lib/rpm/redhat/rpmrc
  • /etc/rpmrc
  • ~/.rpmrc

Macro Configuration

  • /usr/lib/rpm/macros
  • /usr/lib/rpm/redhat/macros
  • /etc/rpm/macros
  • ~/.rpmmacros

下面是宏对应路径一览表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
宏                  描述 
%{\_sysconfdir}     /etc
%{\_prefix}         /usr
%{\_exec\_prefix}   %{_prefix}
%{\_bindir}         %{_exec_prefix}/bin
%{\_lib}            lib (lib64 on 64bit systems)
%{\_libdir}         %{_exec_prefix}/%{_lib}
%{\_libexecdir}     %{_exec_prefix}/libexec
%{\_sbindir}        %{_exec_prefix}/sbin
%{_sharedstatedir}  /var/lib
%{\_datadir}        %{_prefix}/share
%{\_includedir}     %{_prefix}/include
%{\_oldincludedir}  /usr/include
%{\_infodir}        /usr/share/info
%{\_mandir}         /usr/share/man
%{\_localstatedir}  /var
%{\_initddir}       %{_sysconfdir}/rc.d/init.d

#On releases older than Fedora 10 (and EPEL), %{_initddir} does not exist. Instead, you should use the deprecated %{_initrddir} macro.

RPM directory macros

1
2
3
4
5
6
7
8
9
10
宏                描述 
%{_topdir}        %{getenv:HOME}/rpmbuild
%{_builddir}      %{_topdir}/BUILD
%{_rpmdir}        %{_topdir}/RPMS
%{_sourcedir}     %{_topdir}/SOURCES
%{_specdir}       %{_topdir}/SPECS
%{_srcrpmdir}     %{_topdir}/SRPMS
%{_buildrootdir}  %{_topdir}/BUILDROOT

#Note: On releases older than Fedora 10 (and EPEL), %{_buildrootdir} does not exist.

Build flags macros

1
2
3
宏                 描述 
%{_global_cflags}  -O2 -g -pipe
%{_optflags}       %{__global_cflags} -m32 -march=i386 -mtune=pentium4 # if redhat-rpm-config is installed

Other macros

1
2
3
4
5
6
宏           描述 
%{_var}      /var
%{_tmppath}  %{_var}/tmp
%{_usr}      /usr
%{_usrsrc}   %{_usr}/src
%{_docdir}   %{_datadir}/doc

一些重要文件

  • rpm软件包系统的标准分组:/usr/share/doc/rpm-4.3.3/GROUPS
  • 各种宏定义: /usr/lib/rpm/macros
  • 已经安装的rpm包数据库: /var/lib/rpm

准备工作

安装生成rpm文件所需的各工具

1
yum -y install rpmdevtools

几个重要的命令

  • rpmdev-setuptree
    命令生成工作目录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    rpmdev-setuptree
    ll ~
    rpmbuild
    tree rpmbuild
    rpmbuild/
    |--BUILD #SOURCES目录中的tar.gz源码包解压后所存放位置
    |--RPMS #打包好的rpm文件
    |--SOURCES #源码包.tar.gz
    |--SPECS #spec配置文件
    |--SRPMS #打包好的src.rpm文件
    
    • build
      打包过程中的工作目录;
    • RPMS
      存放生成的二进制包,根据硬件平台不同分类,分成i386和x86_64两个目录;
    • SOURCES
      放置打包资源,包括源码打包文件和补丁文件等;
    • SPECS
      spec配置文件
    • SRPMS
      生成的源码包
  • rpmdev-newspec
    在当前目录下生成spec模板文件

    1
    2
    3
    
    rpmdev-newspec
    lsnewpackage.spec
    
  • rpmbuild

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    rpmbuild -ba spec # 同时产生rpm和srpm包
    rpmbuild -bb spec # 只产生rpm包
    rpmbuild -bp      # 执行到%prep段
    rpmbuild -bc      # 执行到%build段
    rpmbuild -bi      # 执行到%install段
    rpmbuild -bl      # 通过%file进行列表检查
    rpmbuild -bs      # 构建源码包
    rpmbuild –sign    # 给软件包签名
    rpmbuild –showrc  # 查看配置文件内容
    rpmbuild --rebuild src.rpm # 用srpm包生成rpm包
    
  • rpm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    rpm -root       # 切换根目录
    rpm -i          # 安装
    rpm -U          # 升级
    rpm -F          # 存在才升级
    rpm --force     # 相当与执行 --replacepkgs, --replacefiles, and --oldpackage
    rpm --test      # 测试
    rpm -e          # 卸载
    rpm -a          # 所有包
    rpm -f          # 包含文件的包
    rpm -p          # 查询一个未安装的包
    rpm --changelog # 查询changelog
    rpm -c          # 查询配置文件
    rpm -i          # 包信息
    rpm -l          # 包含的文件
    rpm --provides  # 包兼容性
    rpm -R          # 包依赖
    rpm --scripts   # 包脚本
    rpm --s         # 包状态
    rpm -V          # 验证包
    rpm --rebuilddb # 重建rpm信息库
    rpm -showrc     # 查看配置信息
    

制作tengine-1.5.2的rpm包

  1. 准备制作环境

    1
    2
    3
    
    mkdir -pv rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
    echo "%{_topdir} " > ~/.rpmacro
    echo "%{_topdir}/home/kuangl/rpmbuild" >~/.rpmmacros
    
  2. 拷贝源码包至SOURCES目录

    1
    
    cp tengine-1.5.2.tar.gz /rpmbuild/rpmbuild/SOURCES
    
  3. 编写spec文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    vim /rpmbuild/SPEC/tengine-1.5.2.spec
    
    Name:		tengine
    Version:	1.5.2
    Release:	1%{?dist}
    Summary:	Tengine  
    
    Group:		System Enviroment/Daemons
    License:	GPL
    URL:		http://tengine.taobao.org/
    Source0:	tengine-1.5.2.tar.gz
    BuildRoot:	%(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)	
    
    %description
    Taobao Tengine package
    
    %prep
    %setup -q
    
    %build
    ./configure --user=www --group=www --prefix=/usr/local/tengine --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module
    make
    
    %install
    make install DESTDIR=%{buildroot}
    
    %clean
    rm -rf %{buildroot}
    
    %files
    %defattr(-,root,root,-)
    /usr/local/tengine
    
  4. 生成rpm包

    1
    
    rpmbuild -ba /rpmbuild/SPEC/tengine-1.5.2.spec
    

其他

  • 关于报错 File not found: /root/rpmbuild/BUILDROOT/
    修改/usr/lib/rpm/macros文件:
    1
    2
    3
    
    vim /usr/lib/rpm/macros
    %_topdir	%{getenv:HOME}/rpmbuild
    %_topdir	%{_usrsrc}/redhat
    

在spec文件中的make install后面加上DESTDIR=%{buildroot}

1
make install  DESTDIR=%{buildroot}
  • 关于rpm中的执行脚本
    如果正在制作的rpm包是准备作为放到系统安装光盘中的话,则需要考虑rpm中定义的脚本是否有问题。由于系统在安装的时候只是依赖于一个小环境进行,而该环境与实际安装完的环境有很大的区别,所以,大部分的脚本在该安装环境中都是无法生效,甚至会带来麻烦的。

    所以,对于这样的,需要放到安装光盘中的套件,不加入执行脚本是较佳的方法。

    另外,为提供操作中可参考的信息,rpm还提供了一种信号机制:不同的操作会返回不同的信息,并放到默认变量$1中。

    0代表卸载、1代表安装、2代表升级

    可这样使用:

    1
    2
    3
    4
    
    %postun
    if [ "$1" = "0" ];then
      /sbin/ldconfig
    fi
    
  • 避免生成debuginfo包

    1
    
    echo '%debug_package %{nil}' >> ~/.rpmmacros
    
  • 生成与架构无关的包
    在spec文件中添加:

    1
    2
    
    %define _binaries_in_noarch_packages_terminate_build   0
    BuildArch: noarch
    
  • centos5与centos6中的一些不同

    1
    2
    3
    4
    5
    
    CentOS 5                    CentOS 6
    %{initrddir}                %{initddir}
    %_topdir %{_usrsrc}/redhat  %_topdir	%{_usrsrc} %{getenv:HOME}/rpmbuild
    make install                make install DESTDIR=%{buildroot}
    no %{?dist}                 %{%dist}
    
  • 关于check-rpaths报错
    注释~/.rpmmacros中的%__arch_install_post /usr/lib/rpm/check-rpaths /usr/lib/rpm/check-buildroot

  • 关于Found xxxxx in installed files aborting报错
    修改 make install DESTDIR=%{buildroot}make install INSTALL_ROOT=%{buildroot}
    DESTDIR是Makefile文件中定义的一个安装路径的变量,根据实际情况修改,比如mysql和nginx的是DESTDIR,而php的是INSTALL_ROOT。

  • 关于error: Symlink points to BuildRoot报错
    ln -sf /usr/local/bin/phar.phar ${RPM_BUILD_ROOT}%{_bindir}/phar

参考:
http://blog.csdn.net/trochiluses/article/details/11179901
http://hlee.iteye.com/blog/343499
http://socol.iteye.com/blog/518846
http://www.xdays.info/rpm%E5%8C%85%E5%88%B6%E4%BD%9C.html
http://www.shencan.net/index.php/2013/05/05/rpm%E6%89%93%E5%8C%85%E8%B7%9Fyum%E4%BB%93%E5%BA%93/

yum