如何编译自定义的rpm包

556 阅读8分钟

使用已有的spec文件编译rpm包

如果已经有一个test.spec文件,则可以直接通过该文件进行编译,执行以下命令

rpmbuild -ba test.spec

则会生成一个rpm文件,该文件位于~/rpmbuild/RPMS/x86_64/

spec文件的语法

注释

我们可以用#来进行注释,例如以下语句

# This is a comment

宏定义

我们可以用%define来定义宏,使用:

%define myMacro 5

这里定义了一个宏,名称为myMacro,值为5,如果要使用该宏,可以用语句%myMacro或者%{myMacro}

rpm默认使用的宏定义,可以查看/usr/lib/rpm/macros

条件判断

架构判断

我们可以用%ifarch来判断架构

%ifarch s390 s390x 
BuildRequires: s390utils-devel 
%endif

变量判断

我们可以用%if来判断变量的值

这里是判断是否满足条件:定义了变量with_foo且没有定义变量with_bar

%if %{defined with_foo} && %{undefined with_bar}

如果想判断optimize_flags是否为空,可以用以下语句

%if "%{optimize_flags}" != "none"

如果想数学逻辑判断,可以用以下语句

%if 0%{?fedora} > 10 || 0%{?rhel} > 7

这里是判断fedora是否大于10或者rhel是否大于7 除此之外,&&,||,!=等运算符,包括三目运算符?:也是支持的

基本信息

Name

Name字段即为包的名称,可以包含常用字符(字母、数字等),可以包含短横杠-,但是不能包含空格,也不能包含任何数字运算符('<','>','=')

Version

Version字段即为包的版本号,版本字符串通常由字母数字字符组成,可以使用. _ + ~ ^等分隔符。其中~ 可强制分选低于基数(1.1~201601 < 1.1),^可用于强制排序高于基数(1.1^201601 > 1.1)

Release

Release字段用于区分同一软件版本的不同构建

License

License字段用于指定包的证书信息(少于70个字符),例如

License: GPLv3

Group

可选,在Group字段中,我们可以给出简短的组信息(少于70个字符),即包的分组信息,例如

Gourp: Development/Libraries

Summary

在Summary字段中,我们可以给出简短的总结信息,来说明包的用途,例如

Summary: Utility for converting mumbles into giggles

Source

声明用于构建包的源。所有的源将被打包到源rpm中,源可以是本地的文件(一般需要放在~/rpmbuild/SOURCES目录),例如

Source0: mysoft-1.0.tar.gz
Source1: mysoft-data-1.0.zip

源也可以远程的软件包,执行构建命令时,会自动从远程下载到本地的~/rpmbuild/SOURCES目录

Source0: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz

源的类型也可以是源代码文件

Source1: test.cpp
Source2: test.sh

但不可以是目录,指定目录的话会在执行rpmbuild命令时报错

Patch

声明用于给源代码打补丁的代码文件。例如

Patch1: first-patch.patch

声明补丁文件后,打补丁操作,一般在%prep阶段中,可以这样打patch

%patch1 -p1

Packager

Packager字段用于说明打包人员/维护人员的信息,可选

URL

URL字段提供有关包的进一步信息的URL,通常是上游网站

BugURL

提供Bug提交的URL

构建相关

BuildRequires

BuildRequires是用于指定构建包所需的功能。构建依赖项是在构建之前需要解决的依赖项,如果不满足条件,则会构建失败。如果构建环境需要gcc,则可以写如下的语句

BuildRequires: gcc

如果要求构建依赖项满足特定版本,可以写如下的语句

BuildRequires: e2fsprofs-devel = 1.17-1

BuildRequires支持同一行写多个依赖,也可以写多行依赖,例如

BuildRequires: gcc g++
BuildRequires: python3

BuildConflicts

BuildConflicts用于指定兼容性冲突,被指定的包不能在构建期间存在,如果在构建期间安装了某个包会导致编译失败,则可以这样指定

BuildConflicts: somelib-devel

ExcludeArch

ExcludeArch用于排除某些特定的架构,该字段指定的架构将会在构建阶段被排除,假如不希望该软件包在arm64架构上运行,可以这样写

ExcludeArch: arm64

ExclusiveArch

ExclusiveArch用于指定某些特定的架构,该软件包只能在指定的架构中进行构建,假如希望该软件包在amd64和arm64架构上运行,可以这样写

ExclusiveArch: amd64 arm64

ExcludeOS

ExcludeOS字段用于排除某些特定的操作系统,该字段指定的操作系统 将会在构建阶段被排除

ExclusiveOS

ExclusiveOS用于指定某些特定的操作系统,该软件包只能在指定的操作系统中进行构建

BuildArch(BuildArchitectures)

BuildArch用于指定生成的二进制包所运行的架构 如果是典型的无关包,例如html、perl、python、java和ps包,则可以在子包级别上使用BuildArch:noarch来进行指定

构建前的步骤

%prep

使用%prep定义构建前要做的准备事项,通常是将源代码包解压,或者是按照指定目录结构进行创建

如果只是简单的将源代码包解压,可以使用%autosetup进行自动解压,一般会解压到~/rpmbuild/BUILD目录

%prep
%autosetup

如果是比较复杂的源代码结构,则可以使用%setup指定解压,使用%patch指定打补丁

%setup

%setup的主要功能是为软件包设置构建目录,通常是解压源代码,但也可以只创建目录,%setup支持以下一些选项参数

-a N 在切换到构建目录后解压缩源N
-b N 在切换到构建目录之前解压缩源N
-c 在解包之前创建构建目录(并切换到该目录)
-D 在解包之前不删除构建目录(当要使用' -a '或' -b '解压缩多个源时)
-n DIR设置构建目录的名称(默认为%{name}-%{version})
-T 跳过第一个源的默认解包(使用' -a '或' -b ')
-q quiet模式,不打印日志

%patch

%patch用于在刚刚解包的原始源代码上应用补丁。要应用补丁号1,可以使用以下语句

%patch 1(自RPM >= 4.18)
%patch -P1(所有rpm版本)

%patch支持以下一些参数

-b SUF 备份后缀为SUF的补丁文件
-d DIR 在执行其他操作之前将目录切换为DIR
-E 删除因补丁而清空的文件
-F N 最大模糊因子(上下文补丁)
-p N 从路径中去掉N个前导斜杠
-R 假设反向补丁
-o FILE 发送输出到FILE,而不是原地打补丁
-z SUF 和-b一样
-Z 使用UTC从上下文diff头中设置mtime和time
-P N 应用补丁号N,与传递N作为位置参数相同

构建的步骤

%build

在%build流程中,解压(和配置)的源代码会被编译为二进制文件。这个过程会对解压到~/rpmbuild/BUILD目录(一般是%name-%version的格式,例如hello-world-1.0)的源代码进行编译。我们可以指定编译的指令

%build
make -j6
%build
g++ hello.cpp -o hello

安装前的步骤

%pre

%pre流程中定义的行为,将会在安装前被执行

安装的步骤

%install

在%install流程中,会在~/rpmbuild/BUILDROOT目录创建初始为空的软件包的buildroot目录(一般是%name-%version-%release.%arch的格式,例如hello-world-1.0-1.x86_64)。

因此,安装的过程将会把编译好的二进制文件以及相关的脚本文件,拷贝到~/rpmbuild/BUILDROOT/%name-%version-%release.%arch目录

注意,这个步骤,我们需要明确的指定buildroot的路径,可以用$RPM_BUILD_ROOT或者%{buildroot}进行指定,其路径等同于~/rpmbuild/BUILDROOT/%name-%version-%release.%arch,最后会自动将buildroot中的指定的目录打包成rpm包,然后删除buildroot中的目录,下面是一个例子

%install
install -m 755 helloworld %{buildroot}/usr/libexec/hello-world/

可以使用%make_install的宏,其相当于

/usr/bin/make install DESTDIR=/root/rpmbuild/BUILDROOT/%{NAME}-%{VERSION}-%{RELEASE}.x86_64 INSTALL="/usr/bin/install -p %{?_smp_mflags}"

如果%make_install的宏与编译脚本冲突,也可以直接使用make命令

make install DESTDIR=%{buildroot} %{?_smp_mflags}

安装之后的步骤

%check

如果打包的软件有附带的测试,那么就应该在这里执行测试。例如

%check
make test

%files

这里是指定需要被打包的文件和目录,分为三类:说明文档(doc),配置文件(config)及执行程序,还可定义文件存取权限,拥有者及组别。例如

%attr(700,me,me) %dir %{?_localstatedir}/lib/my_application

%changelog

%changelog用于说明变更日志,记录版本变更信息

%post

%post定义的行为,将会在安装后被执行

卸载前的步骤

%preun

%preun定义的行为,将在卸载前被执行

卸载后的步骤

%postun

%postun定义的行为,将在卸载后被执行

生成的rpm包

一般生成的rpm包位于~/rpmbuild/RPMS/%arch目录,例如x86_64架构,则会在~/rpmbuild/RPMS/x86_64/目录下面,文件名为%name-%version-%release.%arch.rpm

spec文件示例

Name:     hello
Version:  2.10
Release:  1%{?dist}
Summary:  The "Hello World" program from GNU
Summary(zh_CN): GNU Hello World program
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
The Hello World program contains all parts required by the FOSS project, including configuration, build, i18n, and help files.

%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
* Thu Dec 26 2019 Your Name <youremail@xxx.xxx> - 2.10-1
- Update to 2.10
* Sat Dec 3 2016 Your Name <youremail@xxx.xxx> - 2.9-1
- Update to 2.9

常见问题

error: Empty %files file /xxxx/rpmbuild/BUILD/xxxx/debugsourcefiles.list

这个问题是由于编译过程中,没有产生debug信息

如果是用gcc 、g++编译,那么可以直接在命令中加入-g选项

或者也可以定义宏,这样可以跳过debug流程,从而避免报错

%global debug_package %{nil}

也可以在执行rpmbuild命令时,增加--nodebuginfo的选项,产生debug信息

执行shell变量赋值时报错

GCC_PATH=/data/mygcc
GCC_TARGET_PATH=$(GCC_PATH)/build

注意这样写解析时可能出现问题,取shell变量时不要加()或者{},应该改成这样

GCC_PATH=/data/mygcc
GCC_TARGET_PATH=$GCC_PATH/build

Problem: conflicting requests - nothing provides libxxx.so()(64bit) needed by

情况一:对应的so库文件没有打包到rpm包中

修改编译脚本,将对应的so库文件打包到lib中,使得rpm能够找到该库文件

情况二:so库文件没有被rpm依赖收集器找到

如果是自定义安装目录,没有安装到常见的/usr/lib、/usr/lib64等目录,rpm依赖收集器是找不到库文件的。这种情况需要尝试设置二进制文件的rpath,如果库文件没有SONAME,还需要用patchelf命令补全其SONAME信息。

常看宏的方法

使用rpm --eval xxx 命令来查看宏的内容

sh-5.1# rpm --eval %{make_build}  
/usr/bin/make -O -j16 V=1 VERBOSE=1

参考资料

rpm-software-management.github.io/rpm/manual/…

docs.openeuler.org/en/docs/21.…