RPM打包浅析
0:需要储备的知识及其目的:
需要要储备的知识
-
对makefile有一定的了解。
-
C代码的编译及基本的C语法。
注:只需要能基本看懂第二节做好准备打包一个简单程序的内容即可。
教学目的
- 能将自己编写的程序打包成RPM包,发布到网上供大家下载并安装使用。
一:RPM 基础知识
若要构建一个标准的 RPM 包,您需要创建 .spec
文件,其中包含软件打包的全部信息。然后,对此文件执行 rpmbuild
命令,经过这一步,系统会按照步骤生成最终的 RPM 包。
一般情况,您应该把源代码包,比如由开发者发布的以 .tar.gz
结尾的文件,放入 ~/rpmbuild/SOURCES
目录。将.spec
文件放入 ~/rpmbuild/SPECS
目录,并命名为 "软件包名.spec" 。当然, 软件包名 就是最终 RPM 包的名字。为了创建二进制(Binary RPM)和源码软件包(SRPM),您需要将目录切换至 ~/rpmbuild/SPECS
并执行:
$ rpmbuild -ba NAME.spec
当执行此命令时,rpmbuild
会自动读取 .spec
文件并按照下表列出的步骤完成构建。下表中,以 %
开头的语句为预定义宏,每个宏的作用如下:
阶段 | 读取的目录 | 写入的目录 | 具体动作 |
---|---|---|---|
%prep |
%_sourcedir |
%_builddir |
读取位于 %_sourcedir 目录的源代码和 patch 。之后,解压源代码至 %_builddir 的子目录并应用所有 patch。 |
%build |
%_builddir |
%_builddir |
编译位于 %_builddir 构建目录下的文件。通过执行类似 "./configure && make " 的命令实现。 |
%install |
%_builddir |
%_buildrootdir |
读取位于 %_builddir 构建目录下的文件并将其安装至 %_buildrootdir 目录。这些文件就是用户安装 RPM 后,最终得到的文件。注意一个奇怪的地方: 最终安装目录 不是 构建目录。通过执行类似 "make install " 的命令实现。 |
%check |
%_builddir |
%_builddir |
检查软件是否正常运行。通过执行类似 "make test " 的命令实现。很多软件包都不需要此步。 |
bin |
%_buildrootdir |
%_rpmdir |
读取位于 %_buildrootdir 最终安装目录下的文件,以便最终在 %_rpmdir 目录下创建 RPM 包。在该目录下,不同架构的 RPM 包会分别保存至不同子目录, "noarch " 目录保存适用于所有架构的 RPM 包。这些 RPM 文件就是用户最终安装的 RPM 包。 |
src |
%_sourcedir |
%_srcrpmdir |
创建源码 RPM 包(简称 SRPM,以.src.rpm 作为后缀名),并保存至 %_srcrpmdir 目录。SRPM 包通常用于审核和升级软件包。 |
在 rpmbuild
中,对上表中的每个宏代码都有对应的目录:
宏代码 | 名称 | 默认位置 | 用途 |
---|---|---|---|
%_specdir |
Spec 文件目录 | ~/rpmbuild/SPECS |
保存 RPM 包配置(.spec )文件 |
%_sourcedir |
源代码目录 | ~/rpmbuild/SOURCES |
保存源码包(如 .tar 包)和所有 patch 补丁 |
%_builddir |
构建目录 | ~/rpmbuild/BUILD |
源码包被解压至此,并在该目录的子目录完成编译 |
%_buildrootdir |
最终安装目录 | ~/rpmbuild/BUILDROOT |
保存 %install 阶段安装的文件 |
%_rpmdir |
标准 RPM 包目录 | ~/rpmbuild/RPMS |
生成/保存二进制 RPM 包 |
%_srcrpmdir |
源代码 RPM 包目录 | ~/rpmbuild/SRPMS |
生成/保存源码 RPM 包(SRPM) |
如果某一阶段失败,请查看输出信息以了解失败原因,并根据需要修改 .spec
文件。
注:rpm中的宏值可通过下面的命令查询得到
$ rpm -E "{填入宏名}"
####例如
$ rpm -E "%{_rpmdir}"
二:做好准备打包一个简单程序
- 创建简单的hello程序
$mkdir ~/rpmtest-01
$cd rpmtest-01
$vi hello.c
#include <stdio.h>
int main()
{
printf("%s\n","hello world");
return 0;
}
- 编写makefile
$vi makefile
hello:hello.c
gcc -o hello hello.c
clean:
rm -f hello
install:
install -m 755 hello $(INSTALL_DES)
- 创建补丁文件
$ cd ~/rpmtest-01
$ cp hello.c hello_src.c
$ vim hello.c //修改里面的内容
$ diff -u hello_src.c hello.c > hello.patch
- 创建tar包
$ cd
$ tar -czvf rpmtest-01.tar.gz rpmtest-01
三:新建一个 .spec 文件
准备工作
首先请准备一个Linux环境,比如CentOS。
RPM打包使用的是rpmbuild命令,这个命令来自rpm-build包,这个是必装的。
$ sudo yum install rpm-build
当然也可以直接安装rpmdevtools,这个工具还包含一些其他的工具,同时它依赖rpm-build,所以直接安装的话会同时把rpm-build装上。
$ sudo yum install rpmdevtools
模板和实例
模板
-
如果您首次创建 .spec 文件,vim 或 emacs 会自动生成模板:
$ cd $ rpmdev-setuptree $ cp ~/rpmtest-01.tar.gz ~/rpmbuild/SOURCES $ cp ~/rpmtest-01/hello.patch ~/rpmbuild/SOURCES $ cd ~/rpmbuild/SPECS $ vim hello.spec
-
示例:
Name: Version: Release: 1%{?dist} Summary: Group: License: URL: Source0: BuildRequires: Requires: %description %prep %setup -q %build %configure make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} %files %doc %changelog
实例
这是将第二章的简单程序制作成一个RPM包
Name: rpmtest
Version: 01
Release: 1
Summary: rpm_test
License: GPL
URL: www.baidu.com
Source0: rpmtest-01.tar.gz
Patch1: hello.patch
%description
this is a test for rpmbuild
%prep
%setup -q
%patch1 -p0 -b .hello
%build
make %{?_smp_mflags}
%install
make install INSTALL_DES=/home/lhx/bin
mkdir -p /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64/home/lhx/
cp /home/lhx/hello /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64/home/lhx/
%files
/home/lhx/hello
%changelog
* Sun Dec 4 2016 Your Name <youremail@xxx.xxx> - 2.10-1
- build the program
优化实例
#%define bin_dest %(echo $HOME)/bin
%define bin_dest %{_sbindir}
Name: rpmtest
Version: 01
Release: 1
Summary: rpm_test
License: GPL
URL: www.baidu.com
Source0: rpmtest-01.tar.gz
Patch1: hello.patch
%description
this is a test for rpmbuild
%prep
%setup -q
%patch1 -p0 -b .hello
%build
make %{?_smp_mflags}
%install
mkdir -p %{bin_dest}
sudo make install INSTALL_DES=%{bin_dest}
mkdir -p %{buildroot}%{bin_dest}
cp %{bin_dest}/hello %{buildroot}%{bin_dest}
%files
%{bin_dest}/hello
%changelog
* Sun Dec 4 2016 Your Name <youremail@xxx.xxx> - 2.10-1
- build the program
四:SPEC文件综述
参数 | 参数意义 |
---|---|
Summary | 一行简短的软件包介绍。请使用美式英语。请勿在结尾添加标点! |
Name | 本软件的软件名称 (最终会是 RPM 档案的档名构成之一) |
Version | 本软件的版本 (也会是 RPM 档名的构成之一) |
Release | 这个是该版本打包的次数说明 (也会是 RPM 档名的构成之一)。 |
License | 这个软件的授权模式,我们是使用 GPL 啦! |
Url | 这个原始码的主要官方网站 |
Source | 源文件的文件名,文件名用于查找 SOURCES 目录 |
Patch | 就是作为补丁的 patch file 啰! |
BuildRoot | 设定作为编译时,该使用哪个目录来暂存中间档案 (如编译过程的目标档案/链接档案等档)。 |
BuildRequires | 编译软件包所需的依赖包列表,以逗号分隔。此标签可以多次指定。编译依赖 不会 自动判断,所以需要列出编译所需的所有依赖包。常见的软件包可省略,例如 gcc
|
Requires | 安装软件包时所需的依赖包列表,以逗号分隔。请注意, BuildRequires 标签是编译所需的依赖,而 Requires 标签是安装/运行程序所需的依赖。 |
%prep | 打包准备阶段执行一些命令(如,解压源码包,打补丁等),以便开始编译。 |
%build | 包含构建阶段执行的命令,构建完成后便开始后续安装。 |
%install | 包含安装阶段执行的命令 |
%files | 需要被打包/安装的文件列表。 |
%changelog | RPM 包变更日志。请使用示例中的格式。 |
五:SPEC文件剖析
%prep部分
"%autosetup
" 命令用于解压源码包。可用选项包括:
-
-n
*name* : 如果源码包解压后的目录名称与 RPM 名称不同,此选项用于指定正确的目录名称。例如,如果 tarball 解压目录为 FOO,则使用 "%autosetup -n FOO
"。 -
-c
*name* : 如果源码包解压后包含多个目录,而不是单个目录时,此选项可以创建名为 name 的目录,并在其中解压。
如果使用 "%setup
" 命令,通常使用 -q
' 抑止不必要的输出。
如果需要解压多个文件,有更多 %spec 选项可用,这对于创建子包很有用。常用选项如下:
-a number |
在切换目录后,只解压指定序号的 Source 文件(例如 "-a 0 " 表示 Source0) |
---|---|
-b number |
在切换目录前, 只解压指定序号的 Source 文件(例如 "-b 0 " 表示 Source0) |
-D |
解压前,不删除目录。 |
-T |
禁止自动解压归档。 |
%prep 部分:%patch 命令
"%patch0
" 命令用于应用 Patch0(%patch1 应用 Patch1,以此类推)。Patches 是修改源码的最佳方式。常用的 "-pNUMBER
" 选项,向 patch
程序传递参数,表示跳过 NUM 个路径前缀。
%files 部分
您应该列出该软件包拥有的所有文件和目录。尽量使用宏代替目录名,查看宏列表 Packaging:RPMMacros(例如:使用 %{_bindir}/mycommand
代替 /usr/bin/mycommand
)。
六:运行结果
-
将tar包移入~/rpmbuild/SOURCE/文件夹下
-
编译rpm包
$cd ~/rpmbuild/SPECS
$rpmbuild -ba hello.spec
执行(%prep): /bin/sh -e /var/tmp/rpm-tmp.WN1NeY
+ umask 022
+ cd /home/lhx/rpmbuild/BUILD
+ cd /home/lhx/rpmbuild/BUILD
+ rm -rf rpmtest-01
+ /usr/bin/tar -xf -
+ /usr/bin/gzip -dc /home/lhx/rpmbuild/SOURCES/rpmtest-01.tar.gz <===>解压源码
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd rpmtest-01 <====> 进入源码文件夹
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ echo 'Patch #1 (hello.patch):'
Patch #1 (hello.patch):
+ /usr/bin/cat /home/lhx/rpmbuild/SOURCES/hello.patch
+ /usr/bin/patch -p0 -b --suffix .hello --fuzz=0 <==打补丁==>对应 %patch1 -p0 -b .hello
patching file hello.c
+ exit 0
执行(%build): /bin/sh -e /var/tmp/rpm-tmp.bitRL1
+ umask 022
+ cd /home/lhx/rpmbuild/BUILD
+ cd rpmtest-01
+ make -j3 <====>对应 make %{?_smp_mflags},
gcc -o hello hello.c <====> 对应makefile中的规则
+ exit 0
执行(%install): /bin/sh -e /var/tmp/rpm-tmp.C3O4R5
+ umask 022
+ cd /home/lhx/rpmbuild/BUILD
+ '[' /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64 '!=' / ']'
+ rm -rf /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64
++ dirname /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64
+ mkdir -p /home/lhx/rpmbuild/BUILDROOT
+ mkdir /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64
+ cd rpmtest-01
+ mkdir -p /usr/sbin <====>对应 mkdir -p %{bin_dest}
+ sudo make install INSTALL_DES=/usr/sbin <===>对应sudo make install INSTALL_DES=%{bin_dest}
[sudo] password for lhx:
install -m 755 hello /usr/sbin <====>对应 sudo make install INSTALL_DES=%{bin_dest}
+ mkdir -p /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64/usr/sbin
+ cp /usr/sbin/hello /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64/usr/sbin
+ /usr/lib/rpm/find-debuginfo.sh --strict-build-id -m --run-dwz --dwz-low-mem-die-limit 10000000 --dwz-max-die-limit 110000000 /home/lhx/rpmbuild/BUILD/rpmtest-01
extracting debug info from /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64/usr/sbin/hello
dwz: Too few files for multifile optimization
/usr/lib/rpm/sepdebugcrcfix: Updated 0 CRC32s, 1 CRC32s did match.
+ '[' '%{buildarch}' = noarch ']'
+ QA_CHECK_RPATHS=1
+ case "${QA_CHECK_RPATHS:-}" in
+ /usr/lib/rpm/check-rpaths
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-compress
+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python 1
+ /usr/lib/rpm/redhat/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-java-repack-jars
处理文件:rpmtest-01-1.x86_64
Provides: rpmtest = 01-1 rpmtest(x86-64) = 01-1
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) rtld(GNU_HASH)
处理文件:rpmtest-debuginfo-01-1.x86_64
Provides: rpmtest-debuginfo = 01-1 rpmtest-debuginfo(x86-64) = 01-1
Requires(rpmlib): rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1
检查未打包文件:/usr/lib/rpm/check-files /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64
写道:/home/lhx/rpmbuild/SRPMS/rpmtest-01-1.src.rpm
写道:/home/lhx/rpmbuild/RPMS/x86_64/rpmtest-01-1.x86_64.rpm
写道:/home/lhx/rpmbuild/RPMS/x86_64/rpmtest-debuginfo-01-1.x86_64.rpm
执行(%clean): /bin/sh -e /var/tmp/rpm-tmp.pERCJS
+ umask 022
+ cd /home/lhx/rpmbuild/BUILD
+ cd rpmtest-01
+ /usr/bin/rm -rf /home/lhx/rpmbuild/BUILDROOT/rpmtest-01-1.x86_64
+ exit 0
七:安装/测试/实际查询 /卸载
[lhx@localhost x86_64]$ sudo rpm -ivh rpmtest-01-1.x86_64.rpm
准备中... ################################# [100%]
正在升级/安装...
1:rpmtest-01-1 ################################# [100%]
[lhx@localhost x86_64]$ rpm -qi rpmtest
Name : rpmtest
Version : 01
Release : 1
Architecture: x86_64
Install Date: 2020年05月07日 星期四 23时20分44秒
Group : Unspecified
Size : 7136
License : GPL
Signature : (none)
Source RPM : rpmtest-01-1.src.rpm
Build Date : 2020年05月07日 星期四 23时09分57秒
Build Host : localhost
Relocations : (not relocatable)
URL : www.baidu.com
Summary : rpm_test
Description :
this is a test for rpmbuild
[lhx@localhost x86_64]$ hello
hello redflag
[lhx@localhost x86_64]$ sudo yum erase rpmtest.x86_64
八:参考资料
- 鸟哥的linux私房菜 第23章(第三版)
- https://fedoraproject.org/wiki/How_to_create_an_RPM_package/zh-cn
- https://docs.fedoraproject.org/en-US/packaging-guidelines/RPMMacros/