To master the spec, just read this article, the benefits of CentOS, RedHat, and SUSE fans are here

What is spec (configuration specification file)? The core of the RPM compilation process is processing .spec files. It explains how the package is configured, which patches are patched, which files are installed, where to be installed, and which system-level activities need to be run before or after the package is installed. It must be handwritten, but the simpler way is to take someone else's writing and modify it on this basis. RPM itself does not have too many restrictions on what you can do in the spec file, so you can make it very complicated.


Chapter preview:


1. Encoding of
spec files 2. Authorization of spec files
3. Writing spec files
3.1 Generating an installation package with an empty file
3.2 Generating an installation package containing files
3.3 Using rpm2cpio to decompress the source package and installation package
3.4 Generating a complete source package
3.5 The role of different folders in rpmbuild
3.6 Write a spec file containing the source code
3.7 Make and use patch
4. FAQ


Chapter content:


1. Encoding of spec file

If you don't need to use characters outside the ASCII character set, you don't need to care about the encoding of the spec file. If you use characters outside the ASCII character set, please save the spec file in UTF-8 encoding.


2. Authorization of spec file

Due to some legal reasons, the spec file must have an authorization statement header. Please note that if you don't write, the Open Build Service will add it to you by default. If this is not what you want, you can refer to the following template to write your own. ——Quoted from the openSUSE wiki
  Of course, if we write a non-open spec file, there is no need to increase the authorization description header. The template example is as follows:

 #
 # 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. Write the spec file

First analyze a complete spec file:

Name:			rpm包名
Version:		版本
Release:		发布修正号(在指定版本的第一次发布假设为1,之后每次修改这个版本发布时增加1)
Summary:		介绍摘要
Group:			包所属组的类型
License:		发布许可类型
URL:			网址
AutoReqProv: 	是否产生RPM依赖关系 yes/no
PAckager:		打包者的信息
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 Generate an installation package with an empty file

Next, write a file named test.spec in the "/root/rpmbuild/SPECS" folder to test the packaging:

Name:       test
Version:    1.2
Release:    1
Summary:    测试包
License:    GPL
URL:        123456
Packager:   小明
AutoReqProv:no

%description

%prep

%pre

%post

%preun

%postun

%files

This spec file only has the basic description information of the package, and does not specify the specific execution content. Next, execute this file, and enter the command line: rpmbuild -ba test.spec, refer to Figure 1:

Insert picture description here

Figure 1 An installation package that generates an empty file

The test-1.2-1.src.rpm source package was generated in the "/root/rpmbuild/SRPMS/" folder, and test-1.2-1.x86_64.rpm was generated in the "/root/rpmbuild/RPMS" folder Installation package.


3.2 Generate an installation package containing files

We continue to use the test.spec file and add the following content under "%files":

%defattr(-, root, root)
/usr/local/test.txt

Full content:

Name:       test
Version:    1.2
Release:    1
Summary:    测试包
License:    GPL
URL:        123456
Packager:   小明
AutoReqProv:no

%description

%prep

%pre

%post

%preun

%postun

%files
%defattr(-, root, root)
/usr/local/test.txt

Enter again on the command line: rpmbuild -ba test.spec, refer to Figure 2:

![(https://img-blog.csdnimg.cn/20210605215705331.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV=0L2Ey2,OAFF_FF_size_color_color=0L2Ey16,OATU2)

Figure 2 Generate an installation package containing files

Next, according to the error message, create the test-1.2-1.x86_64 folder under the "/root/rpmbuild/BUILDROOT" path, then enter the test-1.2-1.x86_64 folder to create the "usr/local" path, and then enter usr/local and create a test.txt file, refer to Figure 3:

Insert picture description here

Figure 3 Create test.txt file

Enter again on the command line: rpmbuild -ba test.spec, refer to Figure 4:

Insert picture description here

Figure 4 Re-generate the installation package containing the files

This time successfully generated test-1.2-1.src.rpm source package and test-1.2-1.x86_64.rpm installation package.


3.3 Use rpm2cpio to decompress the source package and installation package

Next, we understand how to decompress the source package and the installation package, and how to use the source package to compile the installation package.

Unzip the source package:

First enter the "/root/rpmbuild/SRPMS" folder (SRPMS is used to store the generated source package), command line input: rpm2cpio test-1.2-1.src.rpm | cpio -div, refer to Figure 5:

Insert picture description here

Figure 5 Unzip the source package

We unzip and get the test.spec file, which is the spec file that we compiled above to include the file installation package. We can generate the test-1.2-1.x86_64.rpm installation package again by executing this spec file (copy test.spec to the "/root/rpmbuild/SPECS" path or decompress the source package under the ← path).

Unzip the installation package:

Now we enter the "/root/rpmbuild/RPMS/x86_64" folder (RPMS is used to store the generated installation package, where x86_64 belongs to my cpu architecture type), command line input: rpm2cpio test-1.2-1.x86_64.rpm | cpio -div, refer to Figure 6:

Insert picture description here

Figure 6 Unzip the source package

We unzip it to get the "usr/local" folder and test.txt file, which are the same as the content we created in the test-1.2-1.x86_64 folder.
    It needs to be explained here that when we install test-1.2-1.x86_64.rpm, the folders and files in the package correspond to the folders and files in the system, that is to say, "usr/local/test." in the installation package. "txt" will be installed to "usr/local/test.txt" in the system.


3.4 Generate a complete source package

The source package generated by the above example only contains the test.spec file. If you try to compile in a new system, you need to extract the test-1.2-1.src.rpm source package and the test-1.2-1.x86_64.rpm installation package to the corresponding It can be compiled and passed in the folder, which is not allowed in the actual project, and it is more troublesome. Next, we began to come to the road of formal production of the source package.

Source0 is called "source package description" in the above introduction. Of course, this is correct, but it is not comprehensive. If our test.txt file does not belong to the source code, it can also be generated into the source package in this way.

First add the following content to the test.spec file:

Below "AutoReqProv:no":

Source0:    test-1.2.tar.gz
由 Name-Version 组成的压缩包名称

Below "%install":

tar xvf %{SOURCE0} --strip-components 1 -C $RPM_BUILD_ROOT
这条指令表示解压"SOURCE0"对应的压缩包到"/root/rpmbuild/BUILDROOT"文件夹内

Below "/usr/local/test.txt":

%changelog
* Sat Jun  5 2021 小明 <[email protected]> 1.2-2
- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包

上述为增加更新内容日志,用于描述本次更新做的修改,也方便别人了解这个源包做的变更
这次更新属于版本不变,只修改"发布修正号",对Release值加1即可

Full content:

Name:       test
Version:    1.2
Release:    2
Summary:    测试包
License:    GPL
URL:        123456
Packager:   小明
AutoReqProv:no
Source0:    test-1.2.tar.gz

%description

%prep

%install
tar 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 小明 <[email protected]> 1.2-2
- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包

Now that the modification of test.spec is complete, we add the test-1.2.tar.gz compressed package to the "/root/rpmbuild/SOURCES" folder. The operation is as follows, refer to Figure 7:

mkdir test-1.2
mkdir -p test-1.2/usr/local
echo '123456' > test-1.2/usr/local/test.txt
tar cvf test-1.2.tar.gz test-1.2/
Insert picture description here

Figure 7 Making test-1.2.tar.gz compressed package

Next we execute test.spec, command line input: rpmbuild -ba test.spec, refer to Figure 8 and Figure 9:

Insert picture description here

Figure 8 Generate installation package and complete source package containing files (top)

Insert picture description here

Figure 9 Generate installation package and complete source package containing files (below)

The test-1.2-2.src.rpm source package and the test-1.2-2.x86_64.rpm installation package were successfully generated. For the decompression check method, please refer to 3.3. Use rpm2cpio to decompress the source package and the installation package . We will continue with the following content and execute the source package.

Next, we copy the source package to the SOURCES folder (verify whether the source package can generate an installation package and a new source package), unzip the source package and copy test.spec to the "/root/rpmbuild/SPECS" folder, Refer to Figure 10, command line input:

cp /root/rpmbuild/SRPMS/test-1.2-2.src.rpm /root/rpmbuild/SOURCES/
rpm2cpio test-1.2-2.src.rpm | cpio -div
cp test.spec /root/rpmbuild/SPECS/
Insert picture description here

Figure 10 Unzip the source package and place the spec file

Execute test.spec again, enter the command line: rpmbuild -ba test.spec, refer to Figure 11:

Insert picture description here

Figure 11 Decompress the complete source package to generate an installation package and a new source package

After testing, the complete source package can generate the installation package and the new source package again, and there is no need for us to manually create a folder and copy files in "/root/rpmbuild/BUILDROOT".


3.5 The role of different folders in rpmbuild

In the "/root/rpmbuild" folder, we can see many folders through the ls command, such as BUILD, BUILDROOT, RPMS, SOURCES, SPECS, SRPMS, among them:

BUILD generally generates complete source code (introduced in patch later), compiled source code, etc. when spec is executed, corresponding to "%prep" and "%build" in the spec file, etc.

BUILDROOT is generally used for temporary installation, testing, packaging, etc. when the spec is executed, corresponding to "%install" and "%clean" in the spec file, etc.

RPMS is used to store the generated installation package, which is generally stored according to the cpu architecture, such as the x86_64 installation package in the "RPMS/x86_64" folder

SPECS is often used to store spec files, generally compile spec in this folder

SRPMS is used to store source packages


3.6 Write a spec file containing source code

The above content belongs to the basic article. From this part to enter the product spec, first we download < zip-3.0-23.el8.src.rpm >, and transplant the source code compressed package, patch and spec information in this source package to our In the example.

First unzip zip-3.0-23.el8.src.rpm in the "/root/rpmbuild/SOURCES" folder, and analyze the spec file:

...
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.patch
Patch4: man.patch
Patch5: zip-3.0-format-security.patch
Patch6: zipnote.patch
Patch7: zip-3.0-configure.patch
Patch8: zip-3.0-covscan1.patch
BuildRequires: bzip2-devel
Requires: 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

...

%build
make -f unix/Makefile generic_gcc refix=%{_prefix} LFLAGS2="$RPM_LD_FLAGS" CFLAGS_NOOPT="-I. -DUNIX $RPM_OPT_FLAGS" %{?_smp_mflags}

%install
tar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT%{_bindir}
mkdir -p $RPM_BULD_ROOT%{_mandir}/man1

make -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*


The above content needs to be written into our test.spec file, and our test.spec file needs to be slightly modified:

Source1:    test-1.2.tar.gz

...

%install
tar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOT

Since "SOURCE" is defined in zip.spec to mean zip source compressed package, here the original "SOURCE0" in test.spec is changed to "SOURCE1", the complete test.spec is as follows:

Summary: test
Name: test
Version: 1.2
Release: 3
License: GPL
Source: http://downloads.sourceforge.net/infozip/zip30.tar.gz
Source1:    test-1.2.tar.gz
Patch1: zip-3.0-exec-shield.patch
# Not upstreamed.
Patch2: zip-3.0-currdir.patch
# Not upstreamed.
Patch3: zip-3.0-time.patch
Patch4: man.patch
Patch5: zip-3.0-format-security.patch
Patch6: zipnote.patch
Patch7: zip-3.0-configure.patch
Patch8: zip-3.0-covscan1.patch
BuildRequires: bzip2-devel
Requires: unzip




%description
test

%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


%build
make -f unix/Makefile generic_gcc refix=%{_prefix} LFLAGS2="$RPM_LD_FLAGS" CFLAGS_NOOPT="-I. -DUNIX $RPM_OPT_FLAGS" %{?_smp_mflags}

%install
tar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT%{_bindir}
mkdir -p $RPM_BULD_ROOT%{_mandir}/man1

make -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 小明 <[email protected]> 1.2-3
- 增加zip源码压缩包及相关信息

* Sat Jun  5 2021 小明 <[email protected]> 1.2-2
- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包

In the new test.spec, we also added the compilation dependency package bzip2-devel, command line input: yum install bzip2-devel, and then execute spec, command line input: rpmbuild -ba test.spec, refer to Figure 12:

Insert picture description here

Figure 12 Adding zip source code compilation and installation in spec

Successfully generated test-1.2-3.src.rpm source package and test-1.2-3.x86_64.rpm, test-debugsource-1.2-3.x86_64.rpm, test-debuginfo-1.2-3.x86_64.rpm installation package.


3.7 Make and use patch

When we transplanted the zip source code compression package, we saw the patch file and the way it was written in the spec. Now we first make the patch file manually, and then describe how to use the patch file.

Command line input: rpmbuild -bp test.spec, only the complete source code is generated (decompress the source code to the "/root/rpmbuild/BUILD" folder and mark it with patch).

Now you can see the zip30 folder in the "/root/rpmbuild/BUILD" folder, copy it to the SOURCES folder and then copy the source code folder to be modified, refer to the following:

cp  -rf /root/rpmbuild/BUILD/zip30/ /root/rpmbuild/SOURCES/
cp  -rf /root/rpmbuild/BUILD/zip30/ /root/rpmbuild/SOURCES/zip30.new/

Next, we randomly modify a file in the zip30.new file, and then generate a patch, refer to Figure 13:

cd /root/rpmbuild/SOURCES/
gedit zip30.new/zip.c

增加内容:
/*
 * 增加test示例内容
 * 生成patch
 */
Insert picture description here

Figure 13 Modify the source file

Command line input: enter diff -Naur zipw30 zip30.new> test_zip_c.patch, then enter cat test_zip_c.patch to check the content of the patch file generated, refer to Figure 14:

Insert picture description here

Figure 14 Generate patch file

In the test.spec file, we see "Patch2: zip-3.0-currdir.patch", "%patch2 -p1 -b .currdir" and other content, where "Patch2: zip-3.0-currdir.patch" means Patch2 corresponds to zip -3.0-currdir.patch, "%patch2 -p1 -b .currdir" means to patch the first-level directory and generate a backup file. Next, we add test_zip_c.patch to the test.spec file:

Patch9: test_zip_c.patch

...

%patch9 -p1

In the above content, we add Patch9 to indicate test_zip_c.patch, and also apply patches to the first-level directory. Here -p1 means the first-level directory, the same also has -p0 means the root directory, -p2 means the second-level directory, etc. If the path is not found for a patch when executing the spec, you can also analyze whether the directory where the patch is located is correct.

Now the complete content of test.spec is:

Summary: test
Name: test
Version: 1.2
Release: 3
License: GPL
Source: http://downloads.sourceforge.net/infozip/zip30.tar.gz
Source1:    test-1.2.tar.gz
Patch1: zip-3.0-exec-shield.patch
# Not upstreamed.
Patch2: zip-3.0-currdir.patch
# Not upstreamed.
Patch3: zip-3.0-time.patch
Patch4: man.patch
Patch5: zip-3.0-format-security.patch
Patch6: zipnote.patch
Patch7: zip-3.0-configure.patch
Patch8: zip-3.0-covscan1.patch
Patch9: test_zip_c.patch
BuildRequires: bzip2-devel
Requires: unzip


%description
test

%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 


%build
make -f unix/Makefile generic_gcc refix=%{_prefix} LFLAGS2="$RPM_LD_FLAGS" CFLAGS_NOOPT="-I. -DUNIX $RPM_OPT_FLAGS" %{?_smp_mflags}

%install
tar xvf %{SOURCE1} --strip-components 1 -C $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT%{_bindir}
mkdir -p $RPM_BULD_ROOT%{_mandir}/man1

make -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 小明 <[email protected]> 1.2-3
- 增加zip源码压缩包及相关信息

* Sat Jun  5 2021 小明 <[email protected]> 1.2-2
- 把/usr/local/test.txt制作到压缩包中,用于生成完整的源包

Command line input: rpmbuild -ba test.spec, successfully generated test-1.2-3.src.rpm source package and test-1.2-3.x86_64.rpm, test-debugsource-1.2-3.x86_64.rpm, test-debuginfo -1.2-3.x86_64.rpm installation package.

The source code is marked with patch:

If we want to modify some content, but don't want to bring the patch content just now, we can use the source code to patch and generate a new patch file.

Enter the "/root/rpmbuild/SOURCES" folder, and enter the command line: patch -p0 <test_zip_c.patch, refer to Figure 15:

Insert picture description here

Figure 15 The source code is patched

Now we generate a new patch, enter the command line: diff -Naur zip30 zip30.new> test_zip.patch, and then cat test_zip.patch is empty, indicating that the modified content of test_zip_c.patch has been patched to the zip30 folder.

If you want to uninstall patch, enter the command line: patch -p0 -R <test_zip_c.patch, refer to Figure 16:

Insert picture description here

Figure 16 Uninstall patch from source code

Now we generate a new patch again, enter the command line: diff -Naur zip30 zip30.new> test_zip.patch, and then cat test_zip.patch, you can see the last modified content.


4. Frequently Asked Questions

Some "strange" problems will be encountered when executing the spec file, such as source code compilation and installation, and an error is reported during test or check. Here is a brief introduction to the solution.

1) When porting the source package under the suse system to the centos series (including python files), if the python type is not clear, you should specify python2 or python3 :

Add %{!?install_mod_dir: %global install_mod_dir updates} before "%description"

2) When the Centos series is executed to "%install", first clear the installation package folder in the "/root/rpmbuild/BUILDROOT" folder (such as test-1.2.x86_64), but the suse system is not cleared :

Add %define __spec_install_pre /bin/true before "%description", but this is not a better way. You can back up the installation package folder at the end of "%build" and copy it to "/root" in "%install" /rpmbuild/BUILDROOT" folder

3) The Centos series source code is compiled and installed, and an error is reported during test or check :

Add %global __spec_install_post %{nil} before "%description"

4) The source code is compiled and installed, and an error is reported when the debug is generated :

Add --nodebuginfo when executing spec file

5) What to do if it is difficult to find some compiling dependent packages :

Add --nodeps when executing the spec file, ignore the dependency package compilation, if there is an error message, you may be able to find a replaceable dependency package based on the error message

6) The installation package in the suse system cannot find the dependent package when installing the centos series :

Analyze the content of the dependent package in the suse system, and search for the name under centos (such as openssl) based on the main name of the dependent package