rpm包制作

最近学习rpm打包,参考ibm文档库里rpm打包的文章,结合自己的实践,总结如下,一来备忘,二来和大家交流。 和deb打包不同,rpm打包需要特定的目录及结构。查看rpm打包目录,以下为在CentOS5.5下的输出结果:
$ rpm --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 --showrc|grep _usrsrc
-14: _topdir    %{_usrsrc}/redhat
-14: _usrsrc    %{_usr}/src
$ rpm --showrc|grep _usr
-14: _defaultdocdir     %{_usr}/share/doc
-14: _topdir    %{_usrsrc}/redhat
-14: _usr       /usr
-14: _usrsrc    %{_usr}/src
经过层层寻找,最终发现打包目录在/usr/src/redhat目录下,看看目录结构:
$ tree /usr/src/redhat
/usr/src/redhat
|-- BUILD
|-- RPMS
|   |-- athlon
|   |-- geode
|   |-- i386
|   |-- i486
|   |-- i586
|   |-- i686
|   `-- noarch
|-- SOURCES
|-- SPECS
`-- SRPMS
其中BUILD存放编译生成的临时文件,RPMS存放根据各种构架生成的rpm包,SOURCES存放源码包,SPECS存放spec文件,SRPMS存放生成的SRPM包。

最简单例子

下面以hello world为例,构建一个最小化打包过程。 首先需要写一个SPEC文件hello.spce:
Summary:    hello world rpm package
Name:       hello
Version:    0.1
Release:    1
Source:     hello-0.1.tar.gz
License:    GPL
Packager:   amoblin
Group:      Application
URL:        http://www.ossxp.com

%description
This is a software for making your life more beautiful!

%prep
%setup -q

%build
gcc -o hello hello.c

%install
install -m 755 hello /usr/local/bin/hello

%files
/usr/local/bin/hello
放到上述SPECS目录下。 然后一个源程序hello.c:
#include <stdio.h>
int main()
{
    printf("Hello, World!\n");
    return 0;
}
存放在hello-0.1目录,然后打包放到SOURCES目录:
$ tar zcvf hello-0.1.tar.gz hello-0.1
hello-0.1/
hello-0.1/hello.c
$ sudo mv hello-0.1.tar.gz /usr/src/redhat/SOURCES
在SPECS目录下使用rpmbuild进行打包:
$ cd /usr/src/redhat
$ sudo rpmbuild -ba hello.spec
...
Wrote: /usr/src/redhat/SRPMS/hello-0.1-1.src.rpm
Wrote: /usr/src/redhat/RPMS/i386/hello-0.1-1.i386.rpm
这时会逐个运行hello.spec文件的内容,最终生成两个文件,一个包含源码的rpm包和一个二进制rpm包。 使用 -bs 选项只生成源码rpm包;使用 -bb 选项只生成rpm包。 查看rpm包信息和包内容:
$ rpm -qpi ../RPMS/i386/hello-0.1-1.i386.rpm
$ rpm -qpl ../RPMS/i386/hello-0.1-1.i386.rpm
第一个命令的输出是spec文件的序言部分的内容,第二个命令的输出是%files部分的文件列表。 现在有个问题,打包目录在/usr/src/redhat下,需要root权限才能操作,太不方便了,能不能在用户自定义目录下打包呢?

自定义打包目录

我们可以通过修改topdir宏的值来自定义打包路径:
$ echo %_topdir $HOME/rpmbuild > ~/.rpmmacros
这样再查看topdir的值会发现已变为用户主目录下rpm子目录了。这时修改文件就方便多了。 但在执行rpmbuild时仍会出问题:
$ rpmbuild -ba hello.spec
...
install: 无法删除 “/usr/local/bin/hello”: 权限不够
error: Bad exit status from /var/tmp/rpm-tmp.65773 (%install)
...
这是因为rpmbuild在构建rpm包时会将程序安装一遍,然后再提取安装文件。由于需要复制二进制文件hello到系统目录/usr/local/bin/下,所以普通用户执行就报错了。 那么怎么办呢?这里需要使用虚拟根了。 修改spec文件,在Description段落前,URL字段之后增加一行:
BuildRoot:  %{_builddir}/%{name}-root
修改install段落,将绝对安装路径改为使用构建根的方式::
%install
mkdir -p $RPM_BUILD_ROOT/usr/local/bin
install -m 755 hello $RPM_BUILD_ROOT/usr/local/bin/hello
通过BuildRoot的值告诉rpmbuild,我们的构建根是builddir下的hello-root目录。其中以%{}括起来的是RPM宏,_builddir代表~/rpmbuild/BUILD目录;name代表spec文件开头的Name字段值。 以下划线开头的builddir是系统RPM宏,我们可以通过rpm --showrc看到,可以在.rpmmacros中自定义。 RPM_BUILD_ROOT和前面的宏不同,这里没有{}括起来,是为了在以后安装生成的rpm时不至于也去寻找传说中的构建根。 如果喜欢的话,可以修改Source字段如下::
Source: %{name}-%{version}.tar.gz
好,继续回到构建根。现在执行rpmbuild,会在BUILD下创建hello-root目录作为虚拟根,hello安装在其中的usr/local/bin目录下。

使用Makefile

一般源程序都使用Makefile的,因此我们再进一步,添加一个Makefile文件,在spec里使用make来编译。 简单的Makefile文件如下:
SRC = hello.c

hello: $(SRC)
    gcc $^ -o $@

clean:
    rm -f hello

install:
    -mkdir -p $(RPM_INSTALL_ROOT)/usr/local/bin/
    install -m 755 hello $(RPM_INSTALL_ROOT)/usr/local/bin/hello
由于使用了Makefile,我们最好升级一下版本号,将原先的hello-0.1目录复制为hello-0.2目录,放入Makefile文件。 修改spec文件,更新版本号,同时将build和install部分用make替换::
Version:    0.2

...

%build
make

%install
RPM_INSTALL_ROOT=$RPM_BUILD_ROOT make install

...
这里将构建根参数传递给Makefile,从而将程序安装在指定的根目录下。 如果喜欢做事不留痕的话,可以在install段落后面增加clean段,清理生成的虚拟根:
%clean
rm -rf $RPM_BUILD_ROOT

使用补丁文件

有很多源码包里都有补丁文件,在打包时要先打补丁,这也要在spec文件里告诉rpmbuild一声才行。 复制hello-0.2一份,随便修改一个hello.c,和原目录做比较,生成补丁文件:
$ cp -r hello-0.2 hello-0.2.my
$ vi hello-0.2.my/hello.c
$ diff -uNr hello-0.1 hello-0.2 > hello-0.1.patch
然后修改spec文件,增加Patch字段,以及在prep段落增加打补丁动作::
...
Patch0:  %{name}-%{version}.patch
...
%prep
%setup -q
%patch -p1
...
Patch0告诉rpmbuild这是一个补丁,如果补丁不止一个的话,可以通过Patch1,Patch2增加。 再次执行rpmbuild,生成rpm包。 查看SRPM包内容:
$ rpm -qpl ../SRPMS/hello-0.2-1.src.rpm
hello-0.2.patch
hello-0.2.tar.gz
hello.spec
补丁果然被打包进来了!

在安装过程中使用脚本

可以使用%pre,%post,%preun,%postun段落来定义安装前后,卸载前后的脚本动作::
%pre
echo This is pre for %{version}-%{release}: arg=$1
%post
echo This is post for %{version}-%{release}: arg=$1
%preun
echo This is preun for %{version}-%{release}: arg=$1
%postun
echo This is postun for %{version}-%{release}: arg=$1
具体的脚本内容可自行替换。 打包rpm的过程就这么简单! 但上面这些方法一般都是针对打包者的,打包者不管以什么手段从任何地方得到了源代码包,然后保持一颗虔诚的心,恭恭敬敬地将之安放在rpmbuild/SOURCES目录下,然后天马行空,随意发挥或挥发,写下一篇洋洋洒洒的spec文件,对号入座到SPECS目录下,然后rpmbuild隆重出场,进行一系列生产加工,最终产生出来了传说中的rpm包和src.rpm包。 但,有时我们不总是打包者,更多时候我们是开发者,使用某种版本控制系统,比如Git,进行开发,最后的产品希望打包发布,还用上面的方式吗?不行,太麻烦了~ 那怎么办呢?欲知结果,敬请期待,清明之后见分晓~
 
 

在源代码中打包

仍拿前面的helloworld举例,把spec文件放到开发目录下,修改去掉补丁文件,然后打包:
$ ls hello-0.2
hello.c  hello.spec  Makefile
$ tar zcvf hello-0.2.tar.gz hello-0.2
hello-0.2/
hello-0.2/Makefile
hello-0.2/hello.spec
hello-0.2/hello.c
接下来就要用到rpmbuild另一个很有用的参数了:-t,可以看一个rpmbuild的man:
$ man rpmbuild
NAME
       rpmbuild - Build RPM Package(s)

SYNOPSIS
   BUILDING PACKAGES:
       rpmbuild {-ba|-bb|-bp|-bc|-bi|-bl|-bs} [rpmbuild-options] SPECFILE ...

       rpmbuild {-ta|-tb|-tp|-tc|-ti|-tl|-ts} [rpmbuild-options] TARBALL ...

       rpmbuild {--rebuild|--recompile} SOURCEPKG ...

   MISCELLANEOUS:
       rpmbuild --showrc

...
rpmbuild通过-t参数来构建打包过的源程序。-ta就是构建rpm包和srpm包,-tb就是只构建二进制rpm包,-tp是只构建打过补丁的包,-ts是只构建srpm包。 好了,现在可以生成rpm包了:
$ rpmbuild -ta hello-0.2.tar.gz
如果使用git的话,就更方便了,因为Git提供了对任意分支打包的命令:git-archive。下面我们先使用git初始化我们的开发目录,然后使用git-archive来生成压缩包。
$ cd hello-0.2
$ git init
Initialized empty Git repository in /home/admin/work/hello-0.2/.git/
$ git add -A
$ git ci -m init
 3 files changed, 73 insertions(+), 0 deletions(-)
 create mode 100644 Makefile
 create mode 100644 hello.c
 create mode 100644 hello.spec
$ git archive --prefix=hello-0.2/ master|gzip > hello-0.2.tar.gz
$ ls
hello-0.2.tar.gz  hello.c  hello.spec  Makefile
通过 --prefix参数使当前master分支下文件打包到hello-0.2目录下,注意在文件夹名后面一定要有一个斜扛,否则就变成一般的文件名前缀了。 生成压缩包以后,就可以像之前一样使用rpmbuild来生成rpm包了。 使用这种方式编译rpm包,无疑给开发者带来了很多方便,尤其是使用git做版本控制的开发者。而且也比debian打包简单,debian打包需要一个debian目录,里面一大堆文件,而rpm打包,一切尽在一个spec文件中,经典! 接下来说说python程序包的制作,这个比较特殊。

python应用程序的RPM包制作

正如前面所见的,rpmbuild内置了很多宏,使得在写rpm包时方便很多。所有这些宏都在/usr/lib/rpm/macros中定义。 查看python相关的宏:
$ cat /usr/lib/rpm/macros | grep python
%__python               /usr/bin/python
%__python_provides      /usr/lib/rpm/pythondeps.sh --provides
%__python_requires      /usr/lib/rpm/pythondeps.sh --requires
#       at the python prompt for example, after "import rpm".
如上,在spec文件里可以使用%{__python}来调用系统python。 这里没有定义sitelib,我们一般在spec文件开头自定义:
%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
可以看出,%()里面是shell命令,通过python的get_python_lib函数得到python的site lib路径。 此外,还有安装要求:
BuildRequires:  python-devel
%if 0%{?fedora} >= 8
BuildRequires: python-setuptools-devel
%else
BuildRequires: python-setuptools
%endif
在安装部分,可以使用%{__python}宏来进行安装:
%install
rm -rf $RPM_BUILD_ROOT
%{__python} setup.py install --root $RPM_BUILD_ROOT
--root $RPM_BUILD_ROOT的作用就是告诉setup.py安装到虚拟根上,类似前面的 RPM_INSTALL_ROOT=$RPM_BUILD_ROOT。 此外,这里还有一个INSTALLED_FILES标志可以使用,用来标记安装到系统中的文件,但来自fedoraproject的建议是,不提倡使用 INSTALLED_FILES,因为它只会标记文件,而不会标记目录,所以有可能遗漏。 打包文件列表部分写法如下:
%files
%defattr(-,root,root,-)
%attr(755,root,root) %{_bindir}/gistore
%{python_sitelib}/*
%doc /usr/share/doc/gistore/README
%doc /usr/share/doc/gistore/COPYING
%doc /usr/share/doc/gistore/CHANGELOG
%files部分列出要打包的文件和目录,可以使用前面的标记安装文件,但最好的方法就是把文件或目录手动列出来。 %defattr(-,root,root,-)代表使用默认的文件和目录权限 %{_bindir}是系统bin目录,具体为/usr/bin/,这个目录下的文件一般是可执行的。 %doc 代表文档,可以这样写::
%doc README COPYING CHANGELOG
这样的话,rpmbuild会从源码根目录下复制上述文件到/usr/share/doc/%{name}-%{version}/目录下,而按照前面的那种三行的写法的话,是自定义doc文件夹的位置,可以看出,自定义的doc文件夹名称没有版本号。 写完了以后就可以打包了,打包过程和前面无异。 下次有时间的话,会讲如何进行多个子包的打包~
 
我们自己制作的软件包,为了防止被篡改,一般要使用私钥进行签名,然后将公钥广而告之,下载使用软件包的话,可以通过公钥进行验证签名,从而确保文件的原始性。

生成密钥对

首先我们要使用gpp来生成公私钥对。目前最先进的加密算法是RSA,所以我们使用RSA来生成密钥对:
$ gpg --gen-key
gpg (GnuPG) 1.4.5; Copyright (C) 2006 Free Software Foundation, Inc.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. See the file COPYING for details.

gpg: 已创建目录‘/home/admin/.gnupg’
gpg: 新的配置文件‘/home/admin/.gnupg/gpg.conf’已建立
gpg: 警告:在‘/home/admin/.gnupg/gpg.conf’里的选项于此次运行期间未被使用
gpg: 钥匙环‘/home/admin/.gnupg/secring.gpg’已建立
gpg: 钥匙环‘/home/admin/.gnupg/pubring.gpg’已建立
请选择您要使用的密钥种类:
   (1) DSA 和 ElGamal (默认)
   (2) DSA (仅用于签名)
   (5) RSA (仅用于签名)
您的选择? 5
...
生成密钥对以后,查看公钥:
$ gpg --list-keys
/home/admin/.gnupg/pubring.gpg
------------------------------
pub   2048R/C0EB63C7 2011-04-11
uid                  Repository Signer (OSSXP) <amoblin@ossxp.com>

rpm软件包签名

修改rpm宏,使用我们的密钥对:
$ echo %_signature gpg >> ~/.rpmmacros
$ echo "%_gpg_name Repository Signer (OSSXP)" >> ~/.rpmmacros
对已有rpm软件包进行签名:
$ rpm --addsign package_name.rpm
Enter pass phrase:
Pass phrase is good.
...
如果签名时出现下面的错误:
$ rpm --addsign package_name.rpm
Enter pass phrase:
Pass phrase check failed
那么应该仔细检查密钥名称和写入rpm宏里面的是否一致,注意括号前有一个空格。 在rpmbuild打包时签名:
$ rpmbuild --sign --ta hello-0.1-1.tar.gz
这样生成的RPM包就包含签名啦~

验证软件包

验证签名需要使用公钥,这里我们先将gpg产生的公钥导出到一个文件,然后将这个公钥文件导入到RPM数据库里,然后使用rpm命令进行检验。 导出公钥到一个文本文档:
$ gpg --export -a "Repository Signer (OSSXP)" > RPM-GPG-KEY-OSSXP
查看rpm数据库中已有的公钥:
$ rpm -q gpg-pubkey-*
gpg-pubkey-e8562897-459f07a4
gpg-pubkey-cf4c4ff9-4d5efb53
gpg-pubkey-217521f6-45e8a532
将公钥导入到RPM数据库。
$ sudo rpm --import RPM-GPG-KEY-OSSXP
再次查看:
$ rpm -q gpg-pubkey-*
gpg-pubkey-e8562897-459f07a4
gpg-pubkey-cf4c4ff9-4d5efb53
gpg-pubkey-217521f6-45e8a532
gpg-pubkey-c0eb63c7-4da2c3e0
最后一个就是我们导入进来的公钥。 在rpm命令中使用 -K 参数进行签名验证:
$ rpm -K noarch/ossxp-release-0.1-2.noarch.rpm
noarch/ossxp-release-0.1-2.noarch.rpm: rsa sha1 (md5) pgp md5 OK
如果结果最后不是OK而是类似下面的:
$ rpm -K noarch/ossxp-release-0.1-2.noarch.rpm
ossxp-release-0.1-2.noarch.rpm: RSA sha1 (MD5) (PGP) (MD5) (PGP) md5 NOT OK (MISSING KEYS: PGP#c0eb63c7 PGP#c0eb63c7)
那么可能是导入了不匹配的公钥或导入未遂,请重新导入正确的公钥。 有人说,这么麻烦,我不导入公钥,不验证,直接安装不行吗?当然可以,只不过在安装时会出现一个小小的警告罢了:
$ rpm -Uvh ossxp-release-0.1-2.noarch.rpm
warning: ossxp-release-0.1-2.noarch.rpm: Header V3 RSA/SHA1 signature: NOKEY, key ID c0eb63c7
Preparing...                ########################################### [100%]
        package ossxp-release-0.1-2.noarch is already installed

在YUM中验证签名

将公钥复制到系统RPM公钥目录:
$ sudo cp RPM-GPG-KEY-OSSXP /etc/pki/rpm-gpg/
在源配置文件中通过如下两行来指定gpg key检验:
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-OSSXP
这样在通过YUM安装软件包时,当下载完毕以后会首先使用公钥进行签名验证。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值