本文共 13342 字,大约阅读时间需要 44 分钟。
什么是 spec(配置规范文件)?RPM 编译过程的核心是处理 .spec 文件。它说明了软件包怎样被配置,补缀哪些补丁,安装哪些文件,被安装到哪里,在安装该包之前或之后需要运行哪些系统级别的活动。它必须手写,但更简单的办法是拿来他人写好的,在此基础上修改。RPM 自身对于你能在 spec 文件中做什么没有太多限制,所以你可以搞的很复杂。
章节预览:
章节内容:
1. spec文件的编码
如果不需要使用 ASCII字符集以外的字符,那就不用关心 spec 文件的编码。如果使用了 ASCII 字符集以外的字符,请把 spec 文件以 UTF-8 编码保存即可。
2. spec文件的授权
由于一些法律上的原因,spec 文件必须有一个授权说明的头部。请注意,如果你不写,开放式构建服务就会把它自己默认的加给你。如果这不是你想要的那样子,你可以参考下面的模板写你自己的。——引自openSUSE维基
当然,如果我们编写非开放式spec文件就不用增加授权说明的头部,模板示例如下:# # spec file for package python-$FOO # # Copyright (c) $CURRENT_YEAR $YOUR_NAME_WITH_MAIL_ADDRESS # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ #
#中文版 # # # 软件包 软件包名写这里 的 spec 文件 # 版权所有 (c) 年份写这里 你的名字 你的电邮 # # 第三方修订者拥有对本文件的任何修正和增补的版权,除非他们宣布放弃。这份文件本身的授权, # 修复和增补的授权,都和软件包的授权相同(除非软件包不是以开源协议发布,例如MIT协议)。 # 开源协议是一种遵守开源行动制定的开源定义的授权许可。 # 请通过 http://bug.opensuse.org 提交错误报告和评论。 #
3. 编写spec文件
首先分析一个完整的spec文件:
Name: rpm包名Version: 版本Release: 发布修正号(在指定版本的第一次发布假设为1,之后每次修改这个版本发布时增加1)Summary: 介绍摘要Group: 包所属组的类型License: 发布许可类型URL: 网址AutoReqProv: 是否产生RPM依赖关系 yes/noPAckager: 打包者的信息Provides: 指定依赖包或可执行文件Source0: 源码包说明,多个源码包可以由Source0、Source1等指定Patch0: 补丁文件说明,与Source0相同,多个补丁文件也可以由Patch0、Patch1等指定PreReq: 前期依赖BuildRequires: 编译依赖Requires: 代码片段的运行依赖%description 描述标签%prep 预备处理%build 编译%install 安装%post rpm包安装时执行的内容%postun rpm包卸载后执行的内容%files 指定文件%defattr(-,root,root) 文件权限%changelog 修订日志
3.1 生成一个空文件的安装包
接下来在"/root/rpmbuild/SPECS"文件夹内写一个名为test.spec的文件 ,测试打包:
Name: testVersion: 1.2Release: 1Summary: 测试包License: GPLURL: 123456Packager: 小明AutoReqProv:no%description%prep%pre%post%preun%postun%files
这个spec文件只有包的基本描述信息,没有指定具体执行内容,接下来执行这个文件,命令行输入:rpmbuild -ba test.spec,参考图1:
在“/root/rpmbuild/SRPMS/”文件夹内生成了test-1.2-1.src.rpm源包,在"/root/rpmbuild/RPMS"文件夹内生成了test-1.2-1.x86_64.rpm安装包。
3.2 生成一个包含文件的安装包
我们继续使用test.spec文件,并在“%files”下增加如下内容:
%defattr(-, root, root)/usr/local/test.txt
完整内容:
Name: testVersion: 1.2Release: 1Summary: 测试包License: GPLURL: 123456Packager: 小明AutoReqProv:no%description%prep%pre%post%preun%postun%files%defattr(-, root, root)/usr/local/test.txt
命令行再次输入:rpmbuild -ba test.spec,参考图2:
接下来我们根据错误提示,在“/root/rpmbuild/BUILDROOT”路径下创建test-1.2-1.x86_64文件夹,然后进入test-1.2-1.x86_64文件夹创建“usr/local”路径,之后进入usr/local并创建test.txt文件即可,参考图3:
命令行再次输入:rpmbuild -ba test.spec,参考图4:
这次顺利生成test-1.2-1.src.rpm源包和test-1.2-1.x86_64.rpm安装包。
3.3 使用rpm2cpio解压源包和安装包
接下来我们了解源包和安装包的解压方式,及如何使用源包编译出安装包。
解压源包:
首先进入"/root/rpmbuild/SRPMS"文件夹(SRPMS用于存放生成的源包),命令行输入:rpm2cpio test-1.2-1.src.rpm | cpio -div,参考图5:
我们解压得到test.spec文件,这个文件是我们上述编译出包含文件安装包的spec文件。我们可以通过执行这个spec文件(把test.spec拷贝到“/root/rpmbuild/SPECS”路径下或者在←路径下解压源包)再次生成test-1.2-1.x86_64.rpm安装包。
解压安装包:
现在我们进入"/root/rpmbuild/RPMS/x86_64"文件夹(RPMS用于存放生成的安装包,其中x86_64属于我的cpu架构类型),命令行输入:rpm2cpio test-1.2-1.x86_64.rpm | cpio -div,参考图6:
我们解压得到“usr/local”文件夹和test.txt文件,这与我们在test-1.2-1.x86_64文件夹内创建的内容相同。
这里需要说明一下,当我们安装test-1.2-1.x86_64.rpm时,包中的文件夹与文件对应这系统中的文件夹与文件,也就是说安装包中的"usr/local/test.txt"将被安装到系统中的"usr/local/test.txt"。3.4 生成一个完整的源包
上述示例生成的源包只包含test.spec文件,如果在新系统中尝试编译,需要把test-1.2-1.src.rpm源包和test-1.2-1.x86_64.rpm安装包解压到对应的文件夹内才能编译通过,这在实际项目中不被允许的,并且也比较麻烦,接下来,我们开始来到正规制作源包的道路。
Source0 在上述介绍中,称为"源码包说明",当然这么说是正确的,但不全面。如我们的test.txt文件不属于源码,也可以通过这种方式生成到源包中。
首先在test.spec文件中增加如下内容:
“AutoReqProv:no” 下方:
Source0: test-1.2.tar.gz由 Name-Version 组成的压缩包名称
“%install” 下方:
tar xvf %{SOURCE0} --strip-components 1 -C $RPM_BUILD_ROOT这条指令表示解压"SOURCE0"对应的压缩包到"/root/rpmbuild/BUILDROOT"文件夹内
“/usr/local/test.txt” 下方:
%changelog* Sat Jun 5 2021 小明1.2-2- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包上述为增加更新内容日志,用于描述本次更新做的修改,也方便别人了解这个源包做的变更这次更新属于版本不变,只修改"发布修正号",对Release值加1即可
完整内容:
Name: testVersion: 1.2Release: 2Summary: 测试包License: GPLURL: 123456Packager: 小明AutoReqProv:noSource0: test-1.2.tar.gz%description%prep%installtar xvf %{SOURCE0} --strip-components 1 -C $RPM_BUILD_ROOT%pre%post%preun%postun%files%defattr(-, root, root)/usr/local/test.txt%changelog* Sat Jun 5 2021 小明1.2-2- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包
现在test.spec修改完成了,我们到"/root/rpmbuild/SOURCES"文件夹中增加test-1.2.tar.gz压缩包,操作如下,参考图7:
mkdir test-1.2mkdir -p test-1.2/usr/localecho '123456' > test-1.2/usr/local/test.txttar cvf test-1.2.tar.gz test-1.2/
接下来我们执行test.spec,命令行输入:rpmbuild -ba test.spec,参考图8、图9:
顺利生成test-1.2-2.src.rpm源包和test-1.2-2.x86_64.rpm安装包,解压检查方式参考,我们继续之后的内容,执行源包。
接下来我们把源包拷贝到SOURCES文件夹内(验证源包是否可以生成安装包与新的源包),解压源包并把test.spec拷贝到"/root/rpmbuild/SPECS"文件夹内,参考图10,命令行输入:
cp /root/rpmbuild/SRPMS/test-1.2-2.src.rpm /root/rpmbuild/SOURCES/rpm2cpio test-1.2-2.src.rpm | cpio -divcp test.spec /root/rpmbuild/SPECS/
再次执行test.spec,命令行输入:rpmbuild -ba test.spec,参考图11:
经过测试,完整源包可以再次生成安装包与新的源包,并且不需要我们手动在"/root/rpmbuild/BUILDROOT"创建文件夹及拷贝文件。
3.5 rpmbuild内不同文件夹的作用
在"/root/rpmbuild"文件夹内,我们通过ls指令可以看到好多文件夹,如BUILD、BUILDROOT、RPMS、SOURCES、SPECS、SRPMS,其中:
BUILD一般在spec执行时,生成完整源码(后续讲述patch时介绍)、编译源码等等,对应spec文件中的"%prep"和"%build"等
BUILDROOT一般在spec执行时,起到临时安装、测试、打包等等,对应spec文件中的"%install"和"%clean"等
RPMS用来存放生成的安装包,一般按照cpu架构存放,如"RPMS/x86_64"文件夹内存放x86_64安装包
SPECS常用来存放spec文件,一般在这个文件夹内编译spec
SRPMS用来存放源包
3.6 编写一个包含源码的spec文件
上述的内容属于基础篇,从这部分进入产品spec,首先我们下载<>,并把这个源包中的源码压缩包、patch和spec信息移植到我们的示例中。
首先在"/root/rpmbuild/SOURCES"文件夹内解压zip-3.0-23.el8.src.rpm,并分析spec文件:
...Source: http://downloads.sourceforge.net/infozip/zip30.tar.gz...Patch1: zip-3.0-exec-shield.patch# Not upstreamed.Patch2: zip-3.0-currdir.patch# Not upstreamed.Patch3: zip-3.0-time.patchPatch4: man.patchPatch5: zip-3.0-format-security.patchPatch6: zipnote.patchPatch7: zip-3.0-configure.patchPatch8: zip-3.0-covscan1.patchBuildRequires: bzip2-develRequires: unzip...%prep%setup -q -n zip30%patch1 -p1 -b .exec-shield%patch2 -p1 -b .currdir%patch3 -p1 -b .time%patch4 -p1 -b .man%patch5 -p1 -b .format-security%patch6 -p1 -b .zipnote%patch7 -p1 -b .zipconfigure%patch8 -p1 -b .covscan1...%buildmake -f unix/Makefile generic_gcc refix=%{_prefix} LFLAGS2="$RPM_LD_FLAGS" CFLAGS_NOOPT="-I. -DUNIX $RPM_OPT_FLAGS" %{?_smp_mflags}%installtar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOTmkdir -p $RPM_BUILD_ROOT%{_bindir}mkdir -p $RPM_BULD_ROOT%{_mandir}/man1make -f unix/Makefile prefix=$RPM_BUILD_ROOT%{_prefix} \ MANDIR=$RPM_BUILD_ROOT%{_mandir}/man1 install...%files%license LICENSE%doc README CHANGES TODO WHATSNEW WHERE README.CR%doc proginfo/algorith.txt%{_bindir}/zipnote%{_bindir}/zipsplit%{_bindir}/zip%{_bindir}/zipcloak%{_mandir}/man1/zip.1*%{_mandir}/man1/zipcloak.1*%{_mandir}/man1/zipnote.1*%{_mandir}/man1/zipsplit.1*
上述内容需要写入我们的test.spec文件中,并且我们的test.spec文件需要做少量修改:
Source1: test-1.2.tar.gz...%installtar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOT
由于zip.spec中定义"SOURCE"表示zip源码压缩包,这里把test.spec中原本的"SOURCE0"修改为"SOURCE1",完整的test.spec如下:
Summary: testName: testVersion: 1.2Release: 3License: GPLSource: http://downloads.sourceforge.net/infozip/zip30.tar.gzSource1: test-1.2.tar.gzPatch1: zip-3.0-exec-shield.patch# Not upstreamed.Patch2: zip-3.0-currdir.patch# Not upstreamed.Patch3: zip-3.0-time.patchPatch4: man.patchPatch5: zip-3.0-format-security.patchPatch6: zipnote.patchPatch7: zip-3.0-configure.patchPatch8: zip-3.0-covscan1.patchBuildRequires: bzip2-develRequires: unzip%descriptiontest%prep%setup -q -n zip30%patch1 -p1 -b .exec-shield%patch2 -p1 -b .currdir%patch3 -p1 -b .time%patch4 -p1 -b .man%patch5 -p1 -b .format-security%patch6 -p1 -b .zipnote%patch7 -p1 -b .zipconfigure%patch8 -p1 -b .covscan1%buildmake -f unix/Makefile generic_gcc refix=%{_prefix} LFLAGS2="$RPM_LD_FLAGS" CFLAGS_NOOPT="-I. -DUNIX $RPM_OPT_FLAGS" %{?_smp_mflags}%installtar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOTmkdir -p $RPM_BUILD_ROOT%{_bindir}mkdir -p $RPM_BULD_ROOT%{_mandir}/man1make -f unix/Makefile prefix=$RPM_BUILD_ROOT%{_prefix} \ MANDIR=$RPM_BUILD_ROOT%{_mandir}/man1 install%pre%post%preun%postun%files%defattr(-, root, root)/usr/local/test.txt%license LICENSE%doc README CHANGES TODO WHATSNEW WHERE README.CR%doc proginfo/algorith.txt%{_bindir}/zipnote%{_bindir}/zipsplit%{_bindir}/zip%{_bindir}/zipcloak%{_mandir}/man1/zip.1*%{_mandir}/man1/zipcloak.1*%{_mandir}/man1/zipnote.1*%{_mandir}/man1/zipsplit.1*%changelog* Sun Jun 6 2021 小明1.2-3- 增加zip源码压缩包及相关信息* Sat Jun 5 2021 小明 1.2-2- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包
在新的test.spec中,我们也增加了编译依赖包 bzip2-devel,命令行输入:yum install bzip2-devel,之后再执行spec,命令行输入:rpmbuild -ba test.spec,参考图12:
顺利生成test-1.2-3.src.rpm源包和test-1.2-3.x86_64.rpm、test-debugsource-1.2-3.x86_64.rpm、test-debuginfo-1.2-3.x86_64.rpm安装包。
3.7 制作并使用patch
在移植zip源码压缩包时我们见到了patch文件及在spec中的写法,现在我们先手动制作patch文件,然后讲述patch文件的使用方式。
命令行输入:rpmbuild -bp test.spec,只生成完整源码(解压源码到"/root/rpmbuild/BUILD"文件夹内并打上patch)。
现在到"/root/rpmbuild/BUILD"文件夹内可以看到zip30文件夹,拷贝到SOURCES文件夹并再拷贝一份待修改的源码文件夹,参考如下:
cp -rf /root/rpmbuild/BUILD/zip30/ /root/rpmbuild/SOURCES/cp -rf /root/rpmbuild/BUILD/zip30/ /root/rpmbuild/SOURCES/zip30.new/
接下来我们随便修改zip30.new文件中的某个文件,然后生成patch,参考图13:
cd /root/rpmbuild/SOURCES/gedit zip30.new/zip.c增加内容:/* * 增加test示例内容 * 生成patch */
命令行输入:输入diff -Naur zipw30 zip30.new > test_zip_c.patch,然后输入cat test_zip_c.patch,检查生成的patch文件内容,参考图14:
在test.spec文件中我们看到"Patch2: zip-3.0-currdir.patch"、"%patch2 -p1 -b .currdir"等内容,其中"Patch2: zip-3.0-currdir.patch"表示Patch2对应zip-3.0-currdir.patch,"%patch2 -p1 -b .currdir"表示在一级目录打上补丁并生成备份文件,接下来,我们把test_zip_c.patch加入test.spec文件中:
Patch9: test_zip_c.patch...%patch9 -p1
上述内容中我们增加Patch9表示test_zip_c.patch,并且也在一级目录打上补丁。这里-p1表示一级目录,同样的也有-p0表示根目录、-p2表示二级目录等,如果执行spec时提示某个patch未找到路径,也可以分析patch所在的目录是否正确。
现在test.spec完整内容为:
Summary: testName: testVersion: 1.2Release: 3License: GPLSource: http://downloads.sourceforge.net/infozip/zip30.tar.gzSource1: test-1.2.tar.gzPatch1: zip-3.0-exec-shield.patch# Not upstreamed.Patch2: zip-3.0-currdir.patch# Not upstreamed.Patch3: zip-3.0-time.patchPatch4: man.patchPatch5: zip-3.0-format-security.patchPatch6: zipnote.patchPatch7: zip-3.0-configure.patchPatch8: zip-3.0-covscan1.patchPatch9: test_zip_c.patchBuildRequires: bzip2-develRequires: unzip%descriptiontest%prep%setup -q -n zip30%patch1 -p1 -b .exec-shield%patch2 -p1 -b .currdir%patch3 -p1 -b .time%patch4 -p1 -b .man%patch5 -p1 -b .format-security%patch6 -p1 -b .zipnote%patch7 -p1 -b .zipconfigure%patch8 -p1 -b .covscan1%patch9 -p1 %buildmake -f unix/Makefile generic_gcc refix=%{_prefix} LFLAGS2="$RPM_LD_FLAGS" CFLAGS_NOOPT="-I. -DUNIX $RPM_OPT_FLAGS" %{?_smp_mflags}%installtar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOTmkdir -p $RPM_BUILD_ROOT%{_bindir}mkdir -p $RPM_BULD_ROOT%{_mandir}/man1make -f unix/Makefile prefix=$RPM_BUILD_ROOT%{_prefix} \ MANDIR=$RPM_BUILD_ROOT%{_mandir}/man1 install%pre%post%preun%postun%files%defattr(-, root, root)/usr/local/test.txt%license LICENSE%doc README CHANGES TODO WHATSNEW WHERE README.CR%doc proginfo/algorith.txt%{_bindir}/zipnote%{_bindir}/zipsplit%{_bindir}/zip%{_bindir}/zipcloak%{_mandir}/man1/zip.1*%{_mandir}/man1/zipcloak.1*%{_mandir}/man1/zipnote.1*%{_mandir}/man1/zipsplit.1*%changelog* Sun Jun 6 2021 小明1.2-3- 增加zip源码压缩包及相关信息* Sat Jun 5 2021 小明 1.2-2- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包
命令行输入:rpmbuild -ba test.spec,顺利生成test-1.2-3.src.rpm源包和test-1.2-3.x86_64.rpm、test-debugsource-1.2-3.x86_64.rpm、test-debuginfo-1.2-3.x86_64.rpm安装包。
源码打上patch:
我们如果想再修改某一处内容,但不想带着刚刚的patch内容,可以通过源码打patch方式,再生成一个新的patch文件。
进入"/root/rpmbuild/SOURCES"文件夹, 命令行输入:patch -p0 < test_zip_c.patch, 参考图15:
现在我们生成一个新的patch, 命令行输入:diff -Naur zip30 zip30.new > test_zip.patch,然后 cat test_zip.patch内容为空,表示test_zip_c.patch修改的内容已经补丁到zip30文件夹中。
如果想卸载patch,命令行输入:patch -p0 -R < test_zip_c.patch, 参考图16:
现在我们再次生成新的patch, 命令行输入:diff -Naur zip30 zip30.new > test_zip.patch,然后 cat test_zip.patch,可以看到上次修改的内容。
4. 常见问题
执行spec文件时会遇到一些"奇怪"的问题,如源码编译、安装通过,在test或check时报错,这里简单说一下解决方案。
1)移植suse系统下的源包到centos系列时(包含python文件),遇到python类型不明确,应指定python2 或 python3:
在"%description"之前增加 %{!?install_mod_dir: %global install_mod_dir updates}
2)Centos系列执行到"%install"时,首先清除"/root/rpmbuild/BUILDROOT"文件夹内的安装包文件夹(比如test-1.2.x86_64),而suse系统未清除:
在"%description"之前增加 %define __spec_install_pre /bin/true ,但这不是较好的办法,可以通过在"%build"末尾处备份安装包文件夹,然后在"%install"中拷贝到"/root/rpmbuild/BUILDROOT"文件夹内
3)Centos系列源码编译、安装通过,在test或check时报错:
在"%description"之前增加 %global __spec_install_post %{nil}
4)源码编译、安装通过,在生成debug时报错:
执行spec文件时增加 --nodebuginfo
5)某些编译依赖包比较难找到怎么办:
执行spec文件时增加 --nodeps,忽略依赖包编译,如果出现报错信息,也许能根据报错信息找到可代替的依赖包
6)suse系统中的安装包在centos系列安装时找不到依赖包:
分析suse系统中的依赖包内容,并根据依赖包的主要名称搜索(如openssl)在centos下的名称
转载地址:https://lkyof.blog.csdn.net/article/details/117594465 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!