在 Ubuntu 下直接将二进制文件制作成 rpm 包

时间:2021-12-18 14:43:23

大多数 rpm 包的制作都是用源码包来编译构建,而我的需求是直接将可运行的二进制文件制作成 rpm 包,而且是在 Ubuntu 系统上。网上的大部分资料都是源码来制作 rpm 包,且比较零乱、不完整。rpm 制作的重要一步就是编写 spec 文件,在该文件中定义了如何编译源码,然后又如何打包的过程。通过大量的资料查阅和分析,最后发现在 spec 文件中把 源码编译的部分删掉便可以直接将二进制文件制作成 rpm 包。

安装 rpm 工具

制作 rpm 包需要用到 rpmbuild 工具。在 ubuntu 上,该工具包含在 rpm 包中,可以直接从源里安装:

sudo apt-get install rpm

建立构建目录结构

在类 redhat 系统中,可以用 rpmdev-setuptree 命令直接在常见所需的目录结构,而在 Ubuntu 系统貌似没有该工具,那么手动创建即可:

mkdir -pv /home/konghy/workdir/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}

目录说明:

BUILD    编译之前,如解压包后存放的路径
BUILDROOT 编译后存放的路径
RPMS 打包完成后rpm包存放的路径
SOURCES 源包所放置的路径
SPECS spec文档放置的路径
SPRMS 源码rpm包放置的路径

通过源码编译制作软件包时, 一般都把源码打包成 tar.gz 格式然后存放于 SOURCES 路径下,而在 SPECS 路径下编写 spec 文档,编译生成的二进制文件会临时的存放在 BUILDROOT 目录下等待打包,打包完成后会被删除,通过命令打包后,默认会把打包后的 rpm 包放在 RPMS 下,而源码包会被放置在SRPMS下。

编写 spec 文件

在我看来, spec 文件核心的部分就只有两个部分:一个是软件包的基本信息描述部分,其中NameVersion、 Release 三个字段是必须的,最终生成的软件包的名字会依赖于此;另一个是 %files 段,它定义需要打包的文件。剩下的大致也可以分为两个部分:一个是定义源码的编译过程,这里不做详述;另一个是定义安装和卸载前后要做的工作,主要在四个段中定义:

%pre
在安装包之前运行
%post
在安装包之后运行
%preun
在卸载包之前运行
%postun
在卸载包之后运行

对于 %files 阶段有两个比较重要的特性:

  • %{buildroot}里的所有文件都要明确被指定是否要被打包到rpm里。例如,%{buildroot}目录下有4个目录a、b、c和d,在%files里仅指定a和b要打包到rpm里,如果不把c和d用exclude声明是要报错的;

  • 如果声明了%{buildroot}里不存在的文件或者目录也会报错。

下面是一个 spec 文件模板:

# 这个区域定义的Name、Version这些字段对应的值可以在后面
# 通过%{name},%{version}这样的方式来引用,类似于C语言中的宏

# Name制定了软件的名称
Name: nginx
# 软件版本
Version: 1.5.2
# 释出号,也就是第几次制作rpm
Release: 1%{?dist}
# 软件的介绍,必须设置,最好不要超过50个字符
Summary: Nginx from WangYing

# 软件的分组,可以通过/usr/share/doc/rpm-4.8.0/GROUPS文件中选择,也可以
# 在相应的分类下,自己创建一个新的类型,例如这里的Server
Group: Application/Server
# 许可证类型
License: GPL
# 软件的源站
URL: http://nginx.org
# 制作rpm包的人员信息
Packager: WangYing
# 源码包的名称,在%_topdir/SOURCE下,如果有多个源码包的话,可以通过
# Source1、Source2这样的字段来指定其他的源码包
Source0: %{name}-%{version}.tar.gz
# BuildRoot指定了make install的测试安装目录,通过这个目录我们可以观察
# 生成了哪些文件,方便些files区域。如果在files区域中写的一些文件报
# 不存在的错误,可以查看%_topdir/BUILDROOT目录来检查有哪些文件。
BuildRoot: %_topdir/BUILDROOT
# 指定安装的路径
Prefix: /usr/local/nginx-1.5.2

# 制作过程需要的工具或软件包
BuildRequires: gcc,make
# 安装时依赖的软件包
Requires: pcre,pcre-devel,openssl

# 软件的描述,这个可以尽情地写
%description
Nginx is a http server

# %prep指定了在编译软件包之前的准备工作,这里的
# setup宏的作用是静默模式解压并切换到源码目录中,
# 当然你也可以使用tar命令来解压
%prep
%setup -q

# 编译阶段,和直接编译源代码类似,具体的操作或指定的一些参数由configure文件决定。
%build
CFLAGS="-pipe -O2 -g -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror" ./configure --prefix=%{prefix}
# make后面的意思是:如果是多处理器,则并行编译
make %{?_smp_mflags}

# 安装阶段
%install
# 先删除原来的测试安装的,只有在制作失败了%{buildroot}目录才会有内容,
# 如果成功的话,目录下会被清除。
# %{buildroot}指向的目录不是BuildRoot(%_topdir/BUILDROOT)指定的目录,
# 而是该目录下名称与生成的rpm包名称相同的子目录。例如我的是
# /usr/src/redhat/BUILDROOT/nginx-1.5.2-1.el6.x86_64
rm -rf %{buildroot}
# 指定安装目录,注意不是真实的安装目录,是在制作rpm包的时候指定的
# 安装目录,如果不指定的话,默认就会安装到configure命令中指定的prefix路径,
# 所以这里一定要指定DESTDIR
make install DESTDIR=%{buildroot}

# 安装前执行的脚本,语法和shell脚本的语法相同
%pre

# 安装后执行的脚本
%post

# 卸载前执行的脚本,我这里的做的事情是在卸载前将nginx服务器停掉
%preun
MSG=`ps aux | grep nginx | grep -v "grep"`
if [ -z "$MSG" ];then
killall nginx 1>/dev/null 2>/dev/null
fi

# 卸载完成后执行的脚本
%postun
rm -rf %{prefix}

# 清理阶段,在制作完成后删除安装的内容
%clean
rm -rf %{buildroot}

#指定要包含的文件
%files
#设置默认权限,如果没有指定,则继承默认的权限
%defattr (-,root,root,0755)
%{prefix}

spec 文档中常用的几个宏(变量):

RPM_BUILD_DIR:    /usr/src/redhat/BUILD
RPM_BUILD_ROOT: /usr/src/redhat/BUILDROOT
%{_sysconfdir}: /etc
%{_sbindir}: /usr/sbin
%{_bindir}: /usr/bin
%{_datadir}: /usr/share
%{_mandir}: /usr/share/man
%{_libdir}: /usr/lib64
%{_prefix}: /usr
%{_localstatedir}: /usr/var

其他的一些选项
--buildroot=DIRECTORY   #确定以root目录建立包
--clean #完成打包后清除BUILD下的文件目录
--nobuild #不进行%build的阶段
--nodeps #不检查建立包时的关联文件
--nodirtokens
--rmsource #完成打包后清除SOURCES
--rmspec #完成打包后清除SPEC
--short-cricuit
--target=CPU-VENDOR-OS #确定包的最终使用平台

例如我的构建方式为:

rpmbuild -bb --target=i686 SPECS/codeblocks.spec

构建过程输出如下信息:

构建目标平台:i686
为目标i686构建
处理文件:codeblocks-13.12-1.i686
Provides: codeblocks = 13.12-1 codeblocks(x86-32) = 13.12-1
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
检查未打包文件:/usr/lib/rpm/check-files /home/konghy/workdir/rpmbuild/BUILDROOT/codeblocks-13.12-1.i386
写道:/home/konghy/workdir/rpmbuild/RPMS/i686/codeblocks-13.12-1.i686.rpm
执行(%clean): /bin/sh -e /var/tmp/rpm-tmp.uWPBYI
+ umask 022
+ cd /home/konghy/workdir/rpmbuild//BUILD
+ /bin/rm -rf /home/konghy/workdir/rpmbuild/BUILDROOT/codeblocks-13.12-1.i386

参考资料

https://fedoraproject.org/wiki/How_to_create_an_RPM_package/zh-cn

http://blog.csdn.net/justlinux2010/article/details/9905425

http://blog.chinaunix.net/uid-23069658-id-3944462.html

http://www.jinbuguo.com/redhat/rpmbuild.html