背景:
常见的Linux发行版主要可以分为两类,类ReadHat系列和类Debian系列,这里我们是以其软件包的格式来划分的,这两类系统分别提供了自己的软件包管理系统和相应的工具。类RedHat系统中软件包的后缀是rpm;类Debian系统中软件包的后缀是deb。另一方面,类RedHat系统提供了同名的rpm命令来安装、卸载、升级rpm软件包;类Debian系统同样提供了dpkg命令来对后缀是deb的软件包进行安装、卸载和升级等操作。
rpm的全称是Redhat Package Manager,常见的使用rpm软件包的系统主要有Fedora、CentOS、openSUSE、SUSE企业版、PCLinuxOS以及Mandriva Linux、Mageia等。使用deb软件包后缀的类Debian系统最常见的有Debian、Ubuntu、Finnix等。
无论是rpm命令还是dpkg命令在安装软件包时都存在一个让人非常头疼的问题,那就是软件包的依赖关系。这一点很多人应该深有体会,这也使初学者在接触Linux系统时觉得很不方便的地方。庆幸的是,很多发行版都考虑到了这问题,于是Fedora和CentOS提供了yum来自动解决软件包的安装依赖,同样的openSUSE提供了zypper,类Debian系统提供了apt-*命令。也就是说这些工具本质上最终还是调用了rpm(或者dpkg)是不过安装前自动帮用户解决了软件包的安装依赖。如下表所示:
简单点了说,如果你会在Fedora或者CentOS上用yum来自动安装软件包,那么在Debian或者Ubuntu上你就会用apt-get自动安装软件,同理,在openSUSE上你就会用zypper自动安装软件包。
前期准备:
1.现在所有的打包都是基于源码的基础做打包,所有的源码包都保存在192.168.0.101:/root目录下。
2.使用yum安装rpm有关的包
yum install rpm* rpm-build rpmdev*
3.查看rpm的版本
rpm --version
推荐使用4.5以上的版本(我们用的是4.11.3)
START(通过软件包的源代码构建rpm软件安装包)
1.从101上拿到**.src.rpm源码并安装
rpm -ivh ****.src.rpm
2.安装源码包后会在/root目录下生成rpmbuild目录
其中rpmbuild结构及各个文件夹的作用
目录名 说明 macros中的宏名
BUILD 编译rpm包的临时目录 %_builddir
BUILDROOT 编译后生成的软件临时安装目录 %_buildrootdir
RPMS 最终生成的可安装rpm包的所在目录 %_rpmdir
SOURCES 所有源代码和补丁文件的存放目录 %_sourcedir
SPECS 存放SPEC文件的目录(重要) %_specdir
SRPMS 软件最终的rpm源码格式存放路径 %_srcrpmdir
.
SPECS文件下存放了.spec文件(下面会重点讲)
SOURCE文件下存放了刚刚安装的rpm包源码(patch、tar.gz文件)
3.我们可以看到其中有很多0001-0028的补丁文件,此时我们需要进入horizon * 文件夹,将一个一个补丁打上去
patch -p1 <../0001-disable-debug-move-web-root.patch
patch -p1 <../000*
4. 完成之后我们需要删除horizon*目录中的AUTHORS Changelog文件,不然打包时会出现错误
之后删除SOURCE目录下所有的.patch文件
并且要将源码文件下对应的文件清理干净。 两类文件:
a. py文件编译文件pyc 命令: find ./ -name *.pyc -exec rm -rf {} \; b. lock文件,key文件, 命令: find ./ -name *.lock -exec rm -rf {} \; find ./ -name *.key -exec rm -rf {} \;
并对horzion*文件夹进行tar打包 tar -zcvf horizon 2015.1.0.tar.gz /root/rpmbuild/SOURCES/horizon 2015.1.0
* rpm只支持tar.gz格式,并且tar.gz文件必须在SOURCES中才可以进行打包
5.进入刚刚的/root/rpmbuild/SPECS文件夹,执行rpmbuild命令进行rpm打包
rpmbuild -ba *.spec 生成的.rpm文件在/root/rpmbuild/RPMS 中
------------------------------------------------------以上是rpm打包操作流程-------------------------------------------------------------------
重点:
1.除了 rpmbuild/SOURCES文件中的tar.gz外,更重要的还是rpmbuild/RPMS中的.spec文件
#.spec脚本文件告诉RPM如何去构建和打包软件,命名格式一般是“软件名-版本.spec”
2.可以使用 rpmdev-newspec
-o Name-version.spec 命令来生成spec文件模板
*3.spec文件详解
a.SPEC文件的核心是它定义了一些“阶段”(%prep、%build、%install和%clean),当rpmbuild执行时它首先会去解析SPEC文件,然后依次执行每个“阶段”里的指令。
我们来简单了解一下SPEC文件的头部。假如,我们的源码包名字是ecloud-0.1.0.tar.gz,那么myapp-0.1.0.spec的头部一般如下的样子:
b.
spec关键字的具体形式及简略解释
Name: ecloud <===软件包的名字(后面会用到)
Version: 0.1.0 <===软件包的版本(后面会用到)
Release: 1%{?dist} <===发布序号
Summary: my first rpm <===软件包的摘要信息
Group: <===软件包的安装分类,参见/usr/share/doc/rpm-4.x.x/GROUPS这个文件
License: GPL <===软件的授权方式
URL: www.yunrongtech.com <===这里本来写源码包的下载路径或者自己的博客地址或者公司网址之类
Source0: %{name}-%{version}.tar.gz <===源代码包的名称(默认时rpmbuid回到SOURCES目录中去找),这里的name和version就是前两行定义的值。如果有其他配置或脚本则依次用Source1、Source2等等往后增加即可。
BuildRoot: %{_topdir}/BUILDROOT <=== 这是make install时使用的“虚拟”根目录,最终制作rpm安装包的文件就来自这里。
BuildRequires: <=== 在本机编译rpm包时需要的辅助工具,以逗号分隔。假如,要求编译myapp时,gcc的版本至少为4.4.2,则可以写成gcc >=4.2.2。还有其他依赖的话则以逗号分别继续写道后面。
Requires: <=== 编译好的rpm软件在其他机器上安装时,需要依赖的其他软件包,也以逗号分隔,有版本需求的可以
%description <=== 软件包的详细说明信息,但最多只能有80个英文字符。
关键字详解
Name: 软件包的名称,后面可使用%{name}的方式引用
Summary: 软件包的内容概要
Version: 软件的实际版本号,例如:1.0.1等,后面可使用%{version}引用
Release: 发布序列号,例如:1linuxing等,标明第几次打包,后面可使用%{release}引用
Group: 软件分组,建议使用标准分组
License: 软件授权方式,通常就是GPL
Source: 源代码包,可以带多个用Source1、Source2等源,后面也可以用%{source1}、%{source2}引用
BuildRoot: 这个是安装或编译时使用的“虚拟目录”,考虑到多用户的环境,一般定义为:
%{_tmppath}/%{name}-%{version}-%{release}-root
或
%{_tmppath}/%{name}-%{version}-%{release}-buildroot-%(%{__id_u} -n}
该参数非常重要,因为在生成rpm的过程中,执行make install时就会把软件安装到上述的路径中,在打包的时候,同样依赖“虚拟目录”为“根目录”进行操作。
后面可使用$RPM_BUILD_ROOT 方式引用。
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 zlibpre)、Requires(post)、Requires(preun)、Requires(postun)、BuildRequires等都是针对不同阶段的依赖指定
Provides: 指明本软件一些特定的功能,以便其他rpm识别
Packager: 打包者的信息
%description 软件的详细说明
c.制作rpm包的几个关键阶段,以及所发生的事情:
阶段 动作
%prep 将%_sourcedir目录下的源代码解压到%_builddir目录下。如果有补丁的需要在这个阶段进行打补丁的操作
%build 在%_builddir目录下执行源码包的编译。一般是执行./configure和make指令
%install 将需要打包到rpm软件包里的文件从%_builddir下拷贝%_buildrootdir目录下。当用户最终用
rpm -ivh name-version.rpm安装软件包时,这些文件会安装到用户系统中相应的目录里
制作rpm包 这个阶段是自动完成的,所以在SPEC文件里面是看不到的,这个阶段会将%_buildroot目录的相关文件制作成rpm软件包最终放到%_rpmdir目录里
%clean 编译后的清理工作,这里可以执行make clean以及清空%_buildroot目录等
d.spec脚本主体详解
%prep阶段
这个阶段里通常情况,主要完成对源代码包的解压和打补丁(如果有的话),而解压时最常见到的就是一句指令:
%setup -q
当然,这句指令可以成功执行的前提是你位于SOURCES目录下的源码包必须是name-version.tar.gz的格式才行,它还会完成后续阶段目录的切换和设置。如果在这个阶段你不用这条指令,那么后面每个阶段都要自己手动去改变相应的目录。解压完成之后如果有补丁文件,也在这里做。
%build阶段
这个阶段就是执行常见的configure和make操作,如果有些软件需要最先执行bootstrap之类的,可以放在configure之前来做。这个阶段我们最常见只有两条指令:
%configure
make %{?_smp_mflags}
#它就自动将软件安装时的路径自动设置成如下约定:
可执行程序/usr/bin
依赖的动态库/usr/lib或者/usr/lib64视操作系统版本而定。
二次开发的头文件/usr/include
文档及手册/usr/share/man
注意,这里的%configure是个宏常量,会自动将prefix设置成/usr。另外,这个宏还可以接受额外的参数,如果某些软件有某些高级特性需要开启,可以通过给%configure宏传参数来开启。如果不用 %configure这个宏的话,就需要完全手动指定configure时的配置参数了。同样地,我们也可以给make传递额外的参数,例如:
make %{?_smp_mflags} CFLAGS="" …
%install阶段
这个阶段就是执行make install操作。这个阶段会在%_buildrootdir目录里建好目录结构,然后将需要打包到rpm软件包里的文件从%_builddir里拷贝到%_buildrootdir里对应的目录里。这个阶段最常见的两条指令是:
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
其中$RPM_BUILD_ROOT也可以换成我们前面定义的BuildRoot变量,不过要写成%{buildroot}才可以,必须全部用小写,不然要报错。
如果软件有配置文件或者额外的启动脚本之类,就要手动用install命令你给将它也拷贝到%{buildroot}相应的目录里。
%clean阶段
编译完成后一些清理工作,主要包括对%{buildroot}目录的清空,通常执行诸如make clean之类的命令。
制作rpm软件包的阶段
这个阶段必须引出下面一个叫做%files的阶段。它主要用来说明会将%{buildroot}目录下的哪些文件和目录最终打包到rpm包里。
%files
%defattr(-,root,root,-)
%doc
在%files阶段的第一条命令的语法是:
%defattr(文件权限,用户名,组名,目录权限)
如果不牵扯到文件、目录权限的改变则一般用%defattr(-,root,root,-)这条指令来为其设置缺省权限。所有需要打包到rpm包的文件和目录都在这个地方列出,例如:
%files
%{_bindir}/*
%{_libdir}/*
%config(noreplace) %{_sysconfdir}/*.conf
在安装rpm时,会将可执行的二进制文件放在/usr/bin目录下,动态库放在/usr/lib或者/usr/lib64目录下,配置文件放在/etc目录下,并且多次安装时新的配置文件不会覆盖以前已经存在的同名配置文件。
这里在写要打包的文件列表时,既可以以宏常量开头,也可以为“/”开头,没任何本质的区别,都表示从%{buildroot}中拷贝文件到最终的rpm包里;如果是相对路径,则表示要拷贝的文件位于%{_builddir}目录,这主要适用于那些在%install阶段没有被拷贝到%{buildroot}目录里的文件,最常见的就是诸如README、LICENSE之类的文件。如果不想将%{buildroot}里的某些文件或目录打包到rpm里,则用:
%exclude dic_name或者file_name
但是关于%files阶段有两个特性要牢记:
%{buildroot}里的所有文件都要明确被指定是否要被打包到rpm里。意思就是说,假如 %{buildroot}目录下有4个目录a、b、c和d,在%files里仅指定a和b要打包到rpm里,如果不把c和d用exclude声明是要报错的;
如果声明了%{buildroot}里不存在的文件或者目录也会报错。
关于%doc宏,所有跟在这个宏后面的文件都来自%{_builddir}目录,当用户安装rpm时,由这个宏所指定的文件都会安装到/usr/share/doc/name-version/目录里。
%changelog阶段
这是最后一个阶段,主要记录的每次打包时的修改变更日志。标准格式是:
点击(此处)折叠或打开
* date +"%a %b %d %Y" 修改人 邮箱 本次版本x.y.z-p
- 本次变更修改了那些内容
e.spec文件总览
可以用rpmdev-newspec -o Name-version.spec命令来生成SPEC文件的模板,然后在上面修改就可
Name: ecloud
Version:
Requires:
%description
…
#==================================SPEC头部====================================
%prep
%setup -q
%patch <==== 在这里打包
%build
%configure
make %{?_smp_mflags}
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
要打包到rpm包里的文件清单
%doc
%changelog
#==================================SPEC主体====================================
%pre
安装或者升级软件前要做的事情,比如停止服务、备份相关文件等都在这里做。
%post
安装或者升级完成后要做的事情,比如执行ldconfig重构动态库缓存、启动服务等。
%preun
卸载软件前要做的事情,比如停止相关服务、关闭进程等。
%postun
卸载软件之后要做的事情,比如删除备份、配置文件等。
f.example
以进行打包的虚拟机192.168.0.124:/root/rpm/ecloud/rpmbuild/SPECS/中的ecloud.spec文件
###################################################################
Summary: System init. (Enterprise quality)
Name: ecloud #软件包的名称
Version: 1.0 #软件的实际版本号
Release: 1.139 #发布序列号,后面的139是与系统包区分开
License: GPL #软件授权方式,通常就是GPL
Group: Applications/System
Source: ecloud-%{version}.tar.gz #源代码包
BuildRoot: %{_tmppath}/%{name}-%{version}-root #这个是安装或编译时使用的“虚拟目录”
BuildRequires: libevent,libevent-devel #该rpm包所依赖的软件包名称
Url: http://www.yunrongtech.com
%description
Ecloud system init
Memcached is an in-memory key-value store for smallchunks of arbitrary data (strings, objects) from results of database calls, APIcalls, or page rendering.
# -i, pre >> post, $1 = 1
# -U, pre >> post >> preun >> postun, pre and post are performed against new package, now $1 = 2,preun and postun are perform against old package, now $1 = 1
# -e, preun >> postun, $1 = 0
#为之后的更新删除添加做准备
%prep #预处理脚本
%setup –q #提取源码到BUILD 目录; -q 指不显示输出(quietly)
%install
rm -rf ${RPM_BUILD_ROOT}
mkdir -p ${RPM_BUILD_ROOT}/usr/bin
install -m 755 ecloud_i.sh ${RPM_BUILD_ROOT}%{_bindir}
install -m 755 ecloud_e.sh ${RPM_BUILD_ROOT}%{_bindir}
#将软件安装到虚拟目录中
%pre
# recover before Update
if [ $1 == 2 ]; then
ecloud_e.sh >/dev/null
fi
%post
#directly install
ecloud_i.sh >/dev/null
%preun
#directoy uninstall
if [ $1 == 0 ]; then
ecloud_e.sh >/dev/null
fi
%clean
rm -rf ${RPM_BUILD_ROOT} #清楚buildroot目录下文件
%files #定义哪些些文件或目录会放入rpm中
%defattr(-,root,root) #指定包装文件的属性
%attr(755,root,root) %{_bindir}/ecloud_i.sh #文件权限,用户名,组名,目录权限
%attr(755,root,root) %{_bindir}/ecloud_e.sh
#ecloud_i.sh ecloud_e.sh为编写好的shell辅助脚本
############################################################################
总结
A.系统打包的动作过程
1)读取并解析 filename.spec 文件
2)运行 %prep 部分来将源代码解包到一个临时目录,并应用所有的补丁程序。
3)运行 %build 部分来编译代码。
4)运行 %install 部分将代码安装到构建机器的目录中。
5)读取 %files 部分的文件列表,收集文件并创建二进制和源 RPM 文件。
6)运行 %clean 部分来除去临时构建目录。
B.打rpm包的基本思路
1) 取回软件的源代码和相关的补丁
2) 测试所需的补丁,以便于顺利的构建整个程序
3) 把源码、配置文件等文件放到正确的位置 (SOUCES文件夹)
4) 编辑spec文件
5) 使用rpmbuild构建rpm包 (rpmbuild -ba *.spec)
#补充
rpmbuild命令:rpmbuild命令会根据spec文件来生成rpm包,主要用到以下几个参数:
rpmbuild --help
-bp 执行到%prep阶段结束
-bc 执行到%build阶段结束
-bi 执行到%install阶段结束
-bl 检测%files文件是否有丢失
-ba 创建src.rpm和二进制包
-bs 只创建src.rpm源码包
-bb 只创建二进制rpm包