摘要:
RPM(Red Hat Package Manager)是用于 Linux 分发版(distribution)的最常见的软件包管理器。因为它允许分发已编译的软件,所以用户只用一个命令就可以安装软件。RPM 是 Linux“标准基本库”版本 1.0.0 指定的安装工具。在 Linux 分发版前 10 名中,有 8 个是基于 RPM(请参阅“Comparison of Linux Distributions”)。即使某些通常不使用 RPM 的分发版,如 Debian,也有可用工具将 RPM 转换成它们自己的格式。在 Linux 上,对于除开发人员以外的任何人,RPM 也是用来打包软件的最佳选择。本文通过一个简单的hello word程序,介绍rpm软件包的制作原理。
本文来源:http://blog.csdn.net/trochiluses/article/details/11179901
1.简单实例:
在这篇博文中,我们使用一个简单的“hello world”来说明问题,其中我们的源文件包括hello.c,configure,makefile,内容大致如下:
hello.c
1 #include<stdio.h>
2 int main()
3 {
4 printf("hellow\n");
5 }
configure
1 #!/bin/bash
2 echo configure now ...
makefile
1 all:hello
2 hello.o:hello.c
3 gcc -c hello.c
4 hello:hello.o
5 gcc hello.o -o hello
6 fresh:
7 rm -f Makefile
8 clean:
9 rm -f hello hello.o
10 install:
11 mkdir -p ${DESTDIR}/home/huangyukun/usr/local/bin
12 cp hello ${DESTDIR}/home/huangyukun/usr/local/bin/hello
13 uninstall:
14 rm -r ${DESTDIR}/usr/local/bin/hello
如果需要安装这个hello程序的话,我们只需要运行常规的三部曲即可。现在我们需要将这个程序制作成rpm包,需要rpm包的制作工具
rpm-build。
2.制作基本的rpm软件包
2.1.rpm安装以后的相关目录
#yum install rpmbuild
~/rpmbuild
~/rpmbuild/SOURCES
~/rpmbuild/SPECS
~/rpmbuild/BUILD
~/rpmbuild/RPMS
~/rpmbuild/RPMS/i386
~/rpmbuild/SRPMS
如果你的用户目录主目录下没有类似目录结构,你可以通过一个工具软件来自动配置和生成,如下。
#yum install rpmdevtools
下了运行自动配置命令自动生成如上目录,并配置一些必要操作。
#rpmdev-setuptree
rpmdev-setuptree命令默认将再当前用户主目录下创建一个RPM构建根目录结构,
如果需要改变次默认位置,可以修改配置文件:~/.rpmmacros中变量_topdir对应
的值即可。
2.2制作过程概览
为了构建 RPM 软件包,您需要写一个名为 spec 文件的 RPM 输入文件,该文件告诉 RPM 如何构建和打包您的软件。编写 spec 文件您需要:
- 创建文件 hello-1.spec,如下所示。您可以任意地给它命名并把它放到任何地方;RPM 对这些没有要求。
- 以 root 用户登录。
- 将hello.tar.gz 文件复制到 SOURCES。
- 运行
rpm -ba hello-1.spec
,将 hello.spec 改为您使用的名字。
Name: hello
Version: 1.0
Release: 2
Summary: this is a test hello world
Group: test
License: GPL
#URL: no
Source0: hello-1.0.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
#BuildRequires:
#Requires:
%description
this is my first rpm
%prep
#rm -rf %{_builddir}/hello-1.0
#mkdir -p %{buildroot}
#echo buildroot: %{buildroot}
#tar -xvf %{_sourcedir}/hello-1.0.tar.gz
%setup -q
%build
#cd %{_builddir}/hello-1.0
#./configure --prefix=/home/huangyukun/usr/local/hello
make
#%configure
#make %{?_smp_mflags}
%install
#cd %{_builddir}/hello-1.0
make install
#make install DESTDIR=%{buildroot}
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
/home/huangyukun/usr/local/bin/hello
%doc
%changelog
您应该看到 RPM 解包这个 tar 文件,编译并安装它。 centos6.3 上,工作目录将是 /home/user/rpmbuild/BUILD。
最后,RPM 将创建两个 RPM 文件。将在 SRPMS/hello-1.0.src.rpm 中创建一个源 RPM 文件,而在 RPMS/i386/helo-1.0.i386.rpm 中创建一个二进制 RPM 文件。
源 RPM 文件简单地捆绑了 spec 文件和构建软件包用到的所有源文件和补丁文件。如果您选择分发它,则其他人可以很容易地用它重建您的软件。二进制 RPM 文件仅包含已编译的软件和如何安装的信息。
下面总结了在您运行 rpm -ba filename.spec 时,RPM 都做些什么%开头的是宏定义,而不是注释:
- 读取并解析 filename.spec 文件
- 运行 %prep 部分来将源代码解包到一个临时目录,并应用所有的补丁程序。
- 运行 %build 部分来编译代码。
- 运行 %install 部分将代码安装到构建机器的目录中。
- 读取 %files 部分的文件列表,收集文件并创建二进制和源 RPM 文件。
- 运行 %clean 部分来除去临时构建目录。
spec 文件有几个部分。第一部分是未标记的;其它部分以 %prep 和 %build 这样的行开始。
头
第一部分(未标记)定义了多种信息,其格式类似电子邮件消息头。
Summary 是一行关于该软件包的描述。
Name 是该软件包的基名, Version 是该软件的版本号。 Release 是 RPM 本身的版本号 ― 如果修复了 spec 文件中的一个错误并发布了该软件同一版本的新 RPM,就应该增加发行版号。
License 应该给出一些许可术语(如:“GPL”、“Commercial”、“Shareware”)。
Group 标识软件类型;那些试图帮助人们管理 RPM 的程序通常按照组列出 RPM。您可以在 /usr/share/doc/rpm-4.0.2/GROUPS 文件看到一个 Red Hat 使用的组列表(假设您安装的 RPM 版本是 4.0.2)。但是您还可以使用那些组名以外的名称。
Source0 、 Source1 等等给这些源文件命名(通常为 tar.gz 文件)。 %{name} 和 %{version} 是 RPM 宏,它们扩展成为头中定义的 rpm 名称和版本。因此,在这个实例中, Source0 被设置为 indent-2.2.6.tar.gz 。
不要在 Source 语句中包含任何路径。缺省情况下,RPM 会在 SOURCES 中寻找文件。请将您的源文件复制或链接到那里。(要使 spec 文件尽量可移植的话,应当尽量避免嵌入自己开发机器上的假想路径。其他开发人员就可以指示 RPM 在别的目录下查找源文件,而不用修改您的 spec 文件。)
描述
接下来的部分从 %description 行开始。您应该在这里提供该软件更多的描述,这样任何人使用 rpm -qi 查询您的软件包时都可以看到它。您可以解释这个软件包做什么,描述任何警告或附加的配置指令,等等。
Shell 脚本
下面几部分是嵌入 spec 文件中的 shell 脚本。
%prep 负责对软件包解包。在最常见情况下,您只要用 %setup 宏即可,它会做适当的事情,在构建目录(BUILD)下解包源 tar 文件。加上 -q项只是为了减少输出。
%build 应该编译软件包。该 shell 脚本从软件包的子目录下运行,在我们这个例子里是 hello-1.0目录,因而这常常与运行 make一样简单。
%install 在构建系统上安装软件包。这似乎和 make install 一样简单,但通常要复杂些。我将在下面解释这点。
文件列表
%files 列出应该捆绑到 RPM 中的文件,并能够可选地设置许可权和其它信息。
在 %files 中,您可以使用 一次 %defattr 来定义缺省的许可权、所有者和组;在这个示例中, %defattr(-,root,root) 会安装 root 用户拥有的所有文件,使用当 RPM 从构建系统捆绑它们时它们所具有的任何许可权。
可以用 %attr(permissions,user,group) 覆盖个别文件的所有者和许可权。
可以在 %files 中用一行包括多个文件。此处要特别注意,这里要用对与buildroot的相对路径,一定不能用绝对路径,以为对file部分的解析会自动添加路径的前面一部分
可以通过在行中添加 %doc 或 %config 来标记文件。 %doc 告诉 RPM 这是一个文档文件,因此如果用户安装软件包时使用 --excludedocs ,将不安装该文件。您也可以在 %doc 下不带路径列出文件名,RPM 会在构建目录下查找这些文件并在 RPM 文件中包括它们,并把它们安装到 /usr/share/doc/%{name}-%{version} 。以 %doc 的形式包括 README 和 ChangeLog 这样的文件是个好主意。
%config 告诉 RPM 这是一个配置文件。在升级时,RPM 将会试图避免用 RPM 打包的缺省配置文件覆盖用户仔细修改过的配置。
警告:如果在 %files 下列出一个目录名,RPM 会包括该目录下的所有文件。通常这不是您想要的,特别对于 /bin 这样的目录。
这个最基本的 spec 文件有几个问题。最大的问题之一就是您最后在构建系统上实际安装了该产品。而这可能只是一个软件测试版本,您也许并不想在构建系统中安装它。
RPM 用一个名为 构建根(build root)的特性来处理这个问题。它的想法是设置您的 spec 文件,以将所有安装的文件复制到一个虚拟目录树(从构建根开始);然后 RPM 从那里得到文件。
但是,这需要一些软件包的支持。在包括 indent 在内的很多 GNU 软件包中,在 make install 的时候定义 DESTDIR 将会在所有安装路径之前添加 DESTDIR 值。
请注意 不要使用 ./configure --prefix=$RPM_BUILD_ROOT 。这会在假设整个软件包文件的最终位置是构建根的情况下安装整个软件包。这对于 indent 可能没有关系,但任何需要在运行时找到其安装文件的程序都将失败,因为当 RPM 最终安装到用户系统后,这些文件就不再位于构建根之下 ― 那只是您构建系统上的一个临时目录。
修改后的spec文件内容如下:
%prep
#rm -rf %{_builddir}/hello-1.0
mkdir -p %{buildroot}
echo buildroot: %{buildroot}
#tar -xvf %{_sourcedir}/hello-1.0.tar.gz
%setup -q
%build
#cd %{_builddir}/hello-1.0
#./configure --prefix=/home/huangyukun/usr/local/hello
make
#%configure
#make %{?_smp_mflags}
%install
#cd %{_builddir}/hello-1.0
make install DESTDIR=%{buildroot}
注意:最主要的变化是将install的路径变化了,包制作完成后会自动删除这个目录之下的文件。
4.关于spec文件中的变量定义和宏定义
如果您在使用不同的 Linux 分发版,RPM 可能会有不同的内置路径。要确定希望的 RPM 安装路径,请运行 rpm --showrc 并查看下列部分如何被定义:
- _sourcedir
- RPM 在哪里查找源文件(tar 文件,等)
- _srcrpmdir
- RPM 在哪里放入新的源 RPM 文件
- _rpmdir
- RPM 将把新的二进制 RPM 文件放在哪里(在特定于体系结构的子目录中)
其中一些根据其它变量定义;例如,当您看到 %{_topdir} ,查找 _topdir 的定义,等等。
/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】http://www.ibm.com/developerworks/cn/linux/management/package/rpm/part1/index.html
【2】http://blog.chinaunix.net/uid-21123336-id-1830527.html
【3】http://bbs.voip88.com/home.php?mod=space&uid=1&do=blog&id=727