使用已有的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