RPM打包原理、示例、详解及备查
原文地址:https://blog.csdn.net/qq_16542775/article/details/80961213
RPM(Redhat Package Manager)是用于Redhat、CentOS、Fedora等Linux 分发版(distribution)的常见的软件包管理器。因为它允许分发已编译的软件,所以用户只用一个命令就可以安装软件。看到这篇文章的朋友想必已经知道RPM是个啥,rpm/yum命令怎么用,废话不多说,直接进入正题,来看看RPM包咋打。
小菜一碟,但 GNU 版本包含了与一个典型的 FOSS 软件项目相关的最常用的外围组件,包括配置/编译/安装环境、文档、国际化等等。GNU 版本包含了一个由源代码和 configure/make 脚本组成的 tar 文件,但并不包含打包信息。因此,这是一个很好的 RPM 包打包示例。
推荐方法 来声明它们:
- 包含程序安装的相关文件
- 查找 %install 中的语言文件:
%find_lang %{name}
- 添加编译依赖:
BuildRequires: gettext
- 声明找到的文件:
%files -f %{name}.lang
- 包含程序安装的相关文件
- 查找 %install 中的语言文件:
%find_lang %{name}
- 添加编译依赖:
BuildRequires: gettext
- 声明找到的文件:
%files -f %{name}.lang
这样下来,%files
部分的内容为:
%files -f %{name}.lang %doc AUTHORS ChangeLog NEWS README THANKS TODO %license COPYING %{_mandir}/man1/hello.1.* %{_infodir}/hello.info.* %{_bindir}/hello
2)info文件的处理
如果程序使用 GNU info 文件,你需要确保安装和卸载软件包,不影响系统中的其他软件,按以下步骤操作:
- 在 %install 中添加删除 ‘dir’ 文件的命令:
rm -f %{buildroot}/%{_infodir}/dir
- 在安装后和卸载前添加依赖
Requires(post): info
和Requires(preun): info
- 添加以下安装脚本(在%install和%files中间即可,分别对应安装后和卸载前的阶段,详见后边内容):
%post /sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || : %preun if [ $1 = 0 ] ; then /sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || : fi
3)看看各个目录里边的东西
* %_sourcedir
下边仍然是源码的压缩包;
* %_builddir
下边是源码解压出来的文件夹hello-2.10及其下边的所有文件;
* %_buildrootdir
下边是一个名为“hello-2.10-1.el7.centos.x86_64”的文件夹(那么生成的RPM包的完整名称也是{Name}-{Version}-{Release}.{Arch}.rpm
),这个文件夹下边有“usr”文件夹,其下还有“bin”、“lib”、“share”、“src”这几个文件夹,可以看到这里的目录结构和安装之后各个文件和文件夹的位置已经是基本一致的了。这里要注意的是,“usr”所在的“根目录”,也就是“hello-2.10-1.el7.centos.x86_64”这个文件夹,用宏表示就是%{buildroot}
,有的地方也用$RPM_BUILD_ROOT
代替 %{buildroot}
,不过跟%{_buildrootdir}
不是一个概念,请注意。
为什么是“趁着失败”呢,因为成功打包之后有些文件夹(比如%_builddir
和%_buildrootdir
)内的内容就会被清理掉了,不过也可以在%build
和%install
阶段的时候把这俩文件夹内的东西tree
一下或者干脆复制到其他地方再看也行。
那么%build
和%install
以及其他几个阶段一般怎么配置呢?
4)本示例最终的完整SPEC
Name: hello Version: 2.10 Release: 1%{?dist} Summary: The "Hello World" program from GNU Summary(zh_CN): GNU "Hello World" 程序 License: GPLv3+ URL: http://ftp.gnu.org/gnu/hello Source0: http://ftp.gnu.org/gnu/hello/%{name}-%{version}.tar.gz BuildRequires: gettext Requires(post): info Requires(preun): info %description The "Hello World" program, done with all bells and whistles of a proper FOSS project, including configuration, build, internationalization, help files, etc. %description -l zh_CN "Hello World" 程序, 包含 FOSS 项目所需的所有部分, 包括配置, 构建, 国际化, 帮助文件等. %prep %setup -q %build %configure make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} %find_lang %{name} rm -f %{buildroot}/%{_infodir}/dir %post /sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || : %preun if [ $1 = 0 ] ; then /sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || : fi %files -f %{name}.lang %doc AUTHORS ChangeLog NEWS README THANKS TODO %license COPYING %{_mandir}/man1/hello.1.* %{_infodir}/hello.info.* %{_bindir}/hello %changelog * Sun Dec 4 2016 Your Name- 2.10-1 - Update to 2.10 * Sat Dec 3 2016 Your Name - 2.9-1 - Update to 2.9
那么就开动起来,在执行一下rpmbuild
命令瞅瞅吧:
rpmbuild -ba hello.spec
OK,执行成功了,看看成果吧:
$ tree ~/rpmbuild/*RPMS /root/rpmbuild/RPMS └── x86_64 ├── hello-2.10-1.el7.centos.x86_64.rpm └── hello-debuginfo-2.10-1.el7.centos.x86_64.rpm /root/rpmbuild/SRPMS └── hello-2.10-1.el7.centos.src.rpm
在RPMS文件夹下生成了RPM包,在x86_64
下,表示所应用的架构,由于没有指定arch为noarch
,所以默认用本机架构。在SRPMS文件夹下生产了源码包,源码包当然木有架构这一说了。
所以有些人喜欢在装软件的时候从源码开始安装,因为更能贴合本机的物理情况,就像用光盘安装windows和GHOST安装windows,相对来说光盘一步一步安装更好一点点,不过我比较懒,还是直接yum install
。
5)运行一下下
既然已经有RPM包了,那就安装上吧:
$ rpm -ivh ~/rpmbuild/RPMS/x86_64/hello-2.10-1.el7.centos.x86_64.rpm
运行一下:
$ hello Hello, world! $ which hello /usr/bin/hello $ rpm -qf `which hello` hello-2.10-1.el7.centos.x86_64
可以看到编译好的二进制文件hello
已经装到/usr/bin
下了,其他位置的文件请自行查看吧^_^。因为这个示例程序五脏俱全,不妨man
一下,看看使用文档~
$ man hello
4 详解
SPEC文件是RPM打包的核心,下面就对SPEC文件中漏掉的而且比较重要的关于各个部分的配置方法进行详细说明:
以此类推)。Patches 是修改源码的最佳方式。常用的 -pNUMBER
选项,向 patch 程序传递参数,表示跳过 NUM 个路径前缀。
补丁文件名通常像这样 telnet-0.17-env.patch
,命名格式为 %{name} - %{version} - REASON.patch
(有时省略 version 版本)。补丁文件通常是 diff -u
命令的输出;如果你在 ~/rpmbuild/BUILD
子目录执行此命令,则之后便不需要指定 -p 选项。
cp foo/bar foo/bar.orig vim foo/bar diff -u foo/bar.orig foo/bar > ~/rpmbuild/SOURCES/PKGNAME.REASON.patch
如果需要修改多个文件,简单方法是复制 BUILD 下的整个子目录,然后在子目录执行 diff。切换至 ~rpmbuild/BUILD/NAME
目录后,执行以下命令:
cp -pr ./ ../PACKAGENAME.orig/ ... 执行修改 ... diff -ur ../PACKAGENAME.orig . > ~/rpmbuild/SOURCES/NAME.REASON.patch
如果你想在一个补丁中编辑多个文件,你可以在编辑之前,使用 .orig
扩展名复制原始文件。然后,使用 gendiff
(在 rpm-build 包中)创建补丁文件。
4.2 %build阶段
%build
阶段顾名思义就是对解压到%_builddir
下的源码进行编译的阶段,整个过程在该目录下完成。
许多程序使用 GNU configure 进行配置。默认情况下,文件会安装到前缀为 “/usr/local” 的路径下,对于手动安装很合理。然而,打包时需要修改前缀为 “/usr”。共享库路径视架构而定,安装至 /usr/lib 或 /usr/lib64 目录。
由于 GNU configure 很常见,可使用 %configure
宏来自动设置正确选项(例如,设置前缀为 /usr
)。一般用法如下:
%configure make %{?_smp_mflags}
若需要覆盖 makefile 变量,请将变量作为参数传递给 make:
make %{?_smp_mflags} CFLAGS="%{optflags}" BINDIR=%{_bindir}
你会发现SPEC中会用到很多预定义好的宏,用来通过一个简单的宏来完成一个或一系列常见的操作,比如:%prep
阶段用于解压的%setup
和%autosetup
,%build
阶段的%configure
等。
4.3 %install阶段
此阶段包含安装阶段需要执行的命令,即从 %{_builddir}
复制相关文件到 %{buildroot}
目录(通常表示从 ~/rpmbuild/BUILD
复制到 ~/rpmbuild/BUILDROOT/XXX
) 目录,并根据需要在 %{buildroot}
中创建必要目录。
容易混淆的术语:
* “build 目录”,也称为 %{_builddir},实际上与 “build root”,又称为 %{buildroot},是不同的目录。在前者中进行编译,并将需要打包的文件从前者复制到后者, %{buildroot}通常为 ~/rpmbuild/BUILD/%{name}-%{version}-%{release}.%{arch}。
* 在 %build 阶段,当前目录为 %{buildsubdir},是 %prep 阶段中在 %{_builddir} 下创建的子目录。这些目录通常名为 ~/rpmbuild/BUILD/%{name}-%{version}。
* %install 阶段的命令不会在用户安装 RPM 包时执行,此阶段仅在打包时执行。
%install rm -rf %{buildroot} # 仅用于 RHEL 5 %makeinstall
- 理想情况下,对于支持的程序,你应该使用
%makeinstall
(这又是一个宏),它等同于DESTDIR=%{buildroot}
,它会将文件安装到%{buildroot}
目录中。
使用 “%makeinstall” 宏。此方法可能有效,但也可能失败。该宏会展开为 make prefix=%{buildroot}%{_prefix} bindir=%{buildroot}%{_bindir} ... install
,可能导致某些程序无法正常工作。请在 %{buildroot}
根据需要创建必要目录。
-
使用
auto-destdir
软件包的话,需要BuildRequires: auto-destdir
,并将make install
修改为make-redir DESTDIR=%{buildroot} install
。这仅适用于使用常用命令安装文件的情况,例如 cp 和 install。 - 手动执行安装。这需要在
%{buildroot}
下创建必要目录,并从%{_builddir}
复制文件至%{buildroot}
目录。要特别注意更新,通常会包含新文件。示例如下:
%install rm -rf %{buildroot} mkdir -p %{buildroot}%{_bindir}/ cp -p mycommand %{buildroot}%{_bindir}/
4.4 %check 阶段
如果需要执行测试,使用 %check
是个好主意。测试代码应写入 %check
部分(紧接在 %install
之后,因为需要测试 %{buildroot}
中的文件),而不是写入 %{build}
部分,这样才能在必要时忽略测试。通常,此部分包含:
make test
有时候也可以用:
make check
请熟悉 Makefile 的用法,并选择适当的方式。
一知半解,这里详细介绍一下。
如果需要在 %files 部分添加一个或多个前缀,用空格分隔。
%doc
用于列出 %{_builddir}
内,但不复制到 %{buildroot}
中的文档。通常包括 README
和 INSTALL
等。它们会保存至 /usr/share/doc
下适当的目录中,不需要声明 /usr/share/doc
的所有权。
注意: 如果指定 %doc
条目,rpmbuild < 4.9.1
在安装前会将 %doc
目录删除。这表明已保存至其中的文档,例如,在 %install
中安装的文档会被删除,因此最终不会出现在软件包中。如果您想要在 %install
中安装一些文档,请将它们临时安装到 build 目录(不是 build root 目录)中,例如 _docs_staging
,接着在 %files
中列出,如 %doc _docs_staging/*
这样。
配置文件保存在 /etc
中,一般会这样指定(确保用户的修改不会在更新时被覆盖):
%config(noreplace) %{_sysconfdir}/foo.conf
如果更新的配置文件无法与之前的配置兼容,则应这样指定:
%config %{_sysconfdir}/foo.conf
“%attr(mode, user, group)” 用于对文件进行更精细的权限控制,”-” 表示使用默认值:
%attr(0644, root, root) FOO.BAR
“%caps(capabilities)” 用于为文件分配 POSIX capabilities。例如:
%caps(cap_net_admin=pe) FOO.BAR
如果包含特定语言编写的文件,请使用 %lang 来标注:
%lang(de) %{_datadir}/locale/de/LC_MESSAGES/tcsh*
使用区域语言(Locale)文件的程序应遵循 处理 i18n 文件的建议方法:
- 在 %install 步骤中找出文件名: %find_lang ${name}
- 添加必要的编译依赖: BuildRequires: gettext
- 使用找到的文件名: %files -f ${name}.lang
4.6 Scriptlets
当用户安装或卸载 RPM 时,您可能想要执行一些命令。这可以通过 scriptlets 完成。
脚本片段可以:
- 在软体包安装之前 (
%pre
) 或之后 (%post
) 执行 - 在软体包卸载之前 (
%preun
) 或之后 (%postun
) 执行 - 在事务开始 (
%pretrans
) 或结束 (%posttrans
) 时执行
例如,每个二进制 RPM 包都会在动态链接器的默认路径中存储共享库文件,并在 %post 和 %postun 中调用 ldconfig 来更新库缓存。如果软件包有多个包含共享库的子包,则每个软体包也需要执行相同动作。
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
如果仅执行一个命令,则 “-p” 选项会直接执行,而不启用 shell。然而,若有许多命令时,不要使用此选项,按正常编写 shell 脚本即可。
如果你在脚本片段中执行任何程序,就必须以 Requires(CONTEXT)
(例: Requires(post)
)的形式列出所有依赖。
%pre
、%post
、%preun
和 %postun
提供 $1
参数,表示动作完成后,系统中保留的此名称的软件包数量。因此可用于检查软件安装情况,不过不要比较此参数值是否等于 2,而是比较是否大于等于 2。对于%pretrans
和 %posttrans
,$1 的值恒为 0。
例如,如果软件包安装了一份 info 手册,那么可以用 info 包提供的 install-info 来更新 info 手册索引。首先,我们不保证系统已安装 info 软件包,除非明确声明需要它;其次,我们不想在 install-info 执行失败时,使软件包安装失败:
Requires(post): info Requires(preun): info ... %post /sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || : %preun if [ $1 = 0 ] ; then /sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || : fi
上边的示例中还有一个安装 info 手册时的小问题需要解释一下。install-info 命令会更新 info 目录,所以我们应该在 %install
阶段删除 %{buildroot}
中无用的空目录:
rm -f %{buildroot}%{_infodir}/dir
5 命令及工具
Fedora Packaging Guidelines
- Spec File Preamble
这篇博文讲解关于RPM打包的过程和原理非常好,转载学习一下。
附上作者原文地址:https://blog.csdn.net/get_set/article/details/53453320