通常,供Linux使用的源代码包还提供一个名为 { 软件名称 }.spec 的文件,即spec文件。如果提供spec文件,则该源代码可以直接编译成RPM包。
spec文件只是一个具有特殊语法的文本文件。
spec文件中包含了软件包的诸多信息,如软件包的名字、版本、类别、说明摘要、创建时要执行什么指令、安装时要执行什么操作,以及软件包所要包含的文件列表等。
一、文件头
语法:TagName: value
,比如Version: 2.1.0
,tag名大小写不敏感。
spec支持定义宏,要定义宏,使用:%define testMacro 2
,这里定义了一个宏,名称为testMacro,值为2,要使用这个宏,使用%{testMacro}
或者%testMacro
。
1.1 Name
软件包的名字,最终RPM软件包是用该名字与版本号,释出号及体系号来命名软件包的。后面可使用%{name}
的方式引用
1.2 Version
软件版本号。仅当软件包比以前有较大改变时才增加版本号。
1.3 Release
软件包发行号。一般我们对该软件包做了一些小的补丁的时候就应该把释出号加1。
1.4 Packager
打包的人(一般喜欢写个人邮箱),Packager: changhungtao@126.com
1.5 License
软件授权方式,通常是GPL(自由软件)或GPLv2、BSD
1.6 Summary
用一句话概括该软件包尽量多的信息。
1.7 Group
软件包所属类别,具体类别有: Amusements/Games (娱乐/游戏) Amusements/Graphics(娱乐/图形) Applications/Archiving (应用/文档) Applications/Communications(应用/通讯) Applications/Databases (应用/数据库) Applications/Editors (应用/编辑器) Applications/Emulators (应用/仿真器) Applications/Engineering (应用/工程) Applications/File (应用/文件) Applications/Internet (应用/因特网) Applications/Multimedia(应用/多媒体) Applications/Productivity (应用/产品) Applications/Publishing(应用/印刷) Applications/System(应用/系统) Applications/Text (应用/文本) Development/Debuggers (开发/调试器) Development/Languages (开发/语言) Development/Libraries (开发/函数库) Development/System (开发/系统) Development/Tools (开发/工具) Documentation (文档) System Environment/Base(系统环境/基础) System Environment/Daemons (系统环境/守护) System Environment/Kernel (系统环境/内核) System Environment/Libraries (系统环境/函数库) System Environment/Shells (系统环境/接口) User Interface/Desktops(用户界面/桌面) User Interface/X (用户界面/X窗口) User Interface/X Hardware Support (用户界面/X硬件支持)
1.8 Source0
源代码包的名字,多个源码文件或配置文件时可以增加Source1,Source2……,后面可使用%{SOURCE0}
%{SOURCE1}
%{SOURCE2}
的方式引用
Source0: %{name}-%{version}.tar.gz
Source1: %{name}.conf
1.9 BuildRoot
编译的路径。是安装或编译时使用的“虚拟目录”,考虑到多用户的环境,一般定义为:~/rpmbuild/BUILDROOT
该参数非常重要,因为在生成rpm的过程中,执行make install时就会把软件安装到上述的路径中,在打包的时候(即%files段),同样依赖“虚拟目录”为“根目录”进行操作。
后面可使用$RPM_BUILD_ROOT
方式引用。
1.10 URL
软件的主页。URL: <web-site>
1.11 Vendor
发行商或打包组织的信息,例如RedFlagCo,Ltd。Vendor: <RedFlag Co,Ltd>
1.12 Provides
指明本软件一些特定的功能,以便其他rpm识别。Provides: <features>
二、依赖关系
依赖关系定义了一个包正常工作需要依赖的其他包,RPM在升级、安装和删除的时候会确保依赖关系得到满足。rpm支持4种依赖:
- Requirements, 包依赖其他包所提供的功能
- Provides, 这个包能提供的功能
- Conflicts, 一个包和其他包冲突的功能
- Obsoletes, 其他包提供的功能已经不推荐使用了,这通常是其他包的功能修改了,老版本不推荐使用了,可以在以后的版本中会被废弃。
1.1 定义依赖关系
定义依赖关系的语法是:
Requires: capability
Provides: capability
Obsoletes: capability
Conflicts: capability
大部分时候,capability应该是所依赖的包的名称。一行中也可以定义多个依赖,比如:Requires: tbsys tbnet
1.2 指定依赖的版本号
在指定依赖关系的时候还可以指定版本号,比如:Requires: tbsys >= 2.0
rpm支持的比较如下:
比较符 | 含义 |
---|---|
package < version | 小于version的包 |
package > version | 大于version的包 |
package >= version | 大于等于version的包 |
package <= version | 小于等于version的包 |
package = version | 等于version的包 |
package | 任意版本 |
1.3 先决依赖
先决(prerequisites)依赖和依赖类似,但是只有在该先决依赖安装的前提下,依赖它的包才会被安装。比如Tair依赖tbsys:prereq: tbsys >= 2.0
1.4 构建依赖
有些依赖在构建的时候就需要满足,比如构建tbnet依赖tbsys,这可以使用buildRequires来指定,比如:buildRequires: tbsys >= 2.0
和requires一样,还可以指定:buildConflicts
、buildPreReq
三、说明 %description
描述允许更详细的介绍,描述支持少量的格式化,空行用于分割段落,以空白开头(比如空格或者Tab)的行通常会以等宽字体显示。
四、预处理 %prep
预处理通常用来执行一些解开源程序包的命令,为下一步的编译安装作准备。%prep和下面的%build,%install段一样,除了可以执行RPM所定义的宏命令(以%开头)以外,还可以执行SHELL命令。功能上类似于./configure。
用来准备要编译的软件。通常,这一段落将归档中的源代码解压,并应用补丁。这些可以用标准的 shell 命令完成,但是更多地使用预定义的宏。
检查标签语法是否正确,删除旧的软件源程序,对包含源程序的tar文件进行解码。如果包含补丁(patch)文件,将补丁文件应用到解开的源码中。它一般包含%setup与%patch两个命令。%setup用于将软件包打开,执行%patch可将补丁文件加入解开的源程序中。
4.1 宏 %setup
这个宏解压源代码,将当前目录改为源代码解压之后产生的目录。这个宏还有一些选项可以用。例如,在解压后,%setup 宏假设产生的目录是%{name}-%{version}
如果 tar 打包中的目录不是这样命名的,可以用 -n 选项来指定要切换到的目录。例如:
%setup -n %{name}-April2003Rel
-
%setup -q #将 tar 命令的繁复输出关闭。
-
%setup -n newdir #将压缩的软件源程序在newdir目录下解开。
-
%setup -c #在解开源程序之前先创建目录。
-
%setup -b num #在包含多个源程序时,将第num个源程序解压缩。
-
%setup -T #不使用缺省的解压缩操作。
例如:
%setup -T -b 0 #解开第一个源程序文件。
%setup -c -n newdir #创建目录newdir,并在此目录之下解开源程序。
4.2 宏 %patch
这个宏将头部定义的补丁应用于源代码。如果定义了多个补丁,它可以用一个数字的参数来指示应用哪个补丁文件。它也接受 -b extension 参数,指示 RPM 在打补丁之前,将文件备份为扩展名是 extension 的文件。
%patchN———-这里N是数字,表示使用第N个补丁文件,等价于%patch-P N
-p0————–指定使用第一个补丁文件,-p1指定使用第二个补丁文件。
-s—————在使用补丁时,不显示任何信息。
-bname———-在加入补丁文件之前,将源文件名上加入name。若为指定此参数,则缺省源文件加入.orig。
-T—————将所有打补丁时产生的输出文件删除
五、编译 %build
定义编译软件包所要执行的命令, 这一节一般由多个make命令组成。
作用:
在这个段落中,包含用来配置和编译已配置的软件的命令。与 Prep 段落一样,这些命令可以是 shell 命令,也可以是宏。
如果要编译的宏使用了 autoconf,那么应当用 %configure 宏来配置软件。这个宏自动为 autoconf 指定了安装软件的正确选项,编译优化的软件。
如果软件不是用 autoconf 配置的,那么使用合适的 shell 命令来配置它。
软件配置之后,必须编译它。由于各个应用程序的编译方法都各自不同,没有用来编译的宏。只要写出要用来编译的 shell 命令就可以了。
环境变量 $RPM_OPT_FLAGS 在编译软件时很常用。这个 shell 变量包含针对 gcc 编译器套件的正确的优化选项,使用这样的语法:
makeCC=”gcc $RPM_OPT_FLAGS”
或者
makeCFLAGS=”$RPM_OPT_FLAGS”
就可以保证总是使用合适的优化选项。也可以使用其他编译器标志和选项。默认的 $RPM_OPT_FLAGS 是:
-O2 -g-march=i386 -mcpu=i686
六、安装 %install
定义在安装软件包时将执行命令,类似于make install命令。有些spec文件还有%post-install段,用于定义在软件安装完成后的所需执行的配置工作。
作用:
这个段落用于将已编译的软件安装到虚拟的目录结构中,从而可以打包成一个 RPM。
在 Header 段落,可以定义 BuildRoot,它定义了虚拟目录树的位置,软件将安装到那里。通常,它是这样的:BuildRoot:%{_tmppath}/%{name}-buildroot
使用 RPM 内建的宏来指定 /var/tmp 目录中一个私用的目录。
在 spec 文件的其余部分可以用 shell 变量 $RPM_BUILD_ROOT 获取 Buildroot 的值。
mkdir -p $RPM_BUILD_ROOT/usr/share/icons/
cp %{SOURCE3} $RPM_BUILD_ROOT/usr/share/icons/
Install 段落通常列出要将已编译的软件安装到 BuildRoot 中的命令
宏 %makeinstall 可以用于安装支持 autoconf 的软件。这个软件自动地将软件安装到 $RPM_BUILD_ROOT 下的正确的子目录中。
有时,软件包必须被构建多次,由于打包错误或其他原因。每次构建时,Install 段落将复制文件到 Buildroot 中。要防止由于 Buildroot 中的旧文件而导致错误的打包,必须在安装新文件之前将 BuildRoot 中任何现有的文件删除。为此,可以使用一个 clean 脚本。这个脚本通常以 %clean 标记表示,通常仅仅包含这样一句:
rm -rf $RPM_BUILD_ROOT
如果有的话,在制作了在 Install 段落中安装的文件的打包之后,将运行 %clean,保证下次构建之前 Buildroot 被清空。
七、清理 %clean
%clean 是用来清理 build 后的临时文件,主要是怕这些旧的文件影响以后编译。主要是要删除 $RPM_BUILD_ROOT 和运行 make clean 。例如:
%clean
rm -rf $RPM_BUILD_ROOT
make clean
八、Scriptlets
这些选项可以让你动态的使用 shell 脚本来控制安装和删除,%pre
、%post
是用脚本运行在包安装后,注意没法交互%preun
、%postun
用脚本运行在包删除时
%pre #安装前的准备工作
%post #安装后的工作,如设置服务的开机启动,定时任务的设定,启动服务等等
%preun #卸载前的工作,例如停止服务,关闭服务的开机启动,删除定时任务等
rpm -q –scripts packagename
# 可以看到脚本的信息
%pre
groupadd -g 201 foo
useradd -g foo -s /bin/false -d /var/foo -M foo
%post
/sbin/ldconfig
chkconfig --add food
%preun
if [ $1 = 0 ]
then
service food stop > /dev/null 2>&1
chkconfig --del food
fi
%postun
if [ $1 = 0 ]
then
userdel foo
groupdel foo
else
/sbin/ldconfig
service food condrestart > /dev/null 2>&1
fi
注意,在这个当中的 $1 的数字,如果是 1 是指是第一次安装,如果是 2 是指也许是升级。如果是 0 在 %postun , 是指被完全的删除
八、文件 %files
任何打包的文件,都需要在这个包的详细的文件列表中,如果是目录,包的所有者的全部目录都在中间,%dir 来指定空目录,可以用 %files -f /tmp/dyanmic_filelist 来指定一个文件列表。默认 %config 会替换掉配置,给原来的配置修改名字为 .rpmorig ,如果不想修改的话,就用%config(noreplace) 就会给新的配置文件名字命名为 .rpmnew.
%defattr(mode,user,group) %attr(mode,user,group) filename %config 配置文件 %doc 文档
对于spec文件中在%files段的某一个文件,我们要讨论三种情况:
- 没有带%config指令。例如:%{_sbindir}/redis-server
- 带了%congfig指令。例如:%config %{_sysconfdir}/redis/redis.conf
- 带了%config(noreplace)指令。例如:%config(noreplace) %{_sysconfdir}/redis/redis.conf
具体一点,包含如下场景:
1. 一个文件没有被%config或%config(noreplace)指令配置
此时,不管该文件在安装完成后,有没有在本地被修改过,当升级该rpm包时,该文件会被这个新的rpm包的里的同名文件替换,(旧文件被删除)。
2. 一个文件被%config指令配置
此时包含如下情况:
-
该文件在新的rpm包里相对之前的rpm有变化,且在本地没有被修改过。此时执行
rpm -Uvh xxxx
时,新rpm包里的该文件会替换旧的文件。(旧文件被删除) -
该文件在新的rpm包里相对之前的rpm有变化,且在本地被修改过。此时执行
rpm -Uvh xxxx
时,新rpm包里的该文件会替换掉掉旧的文件,旧的文件会被保存为xx.rpmsave,如/etc/redis/redis.conf.rpmsave -
该文件在新的rpm包里相对之前的rpm没有变化,且在本地没有被修改过。此时执行
rpm -Uvh xxxx
时,新rpm包里的该文件会替换掉旧的文件。(旧文件被删除) -
该文件在新的rpm包里相对之前的rpm没有变化,且在本地被修改过。此时执行
rpm -Uvh xxxx
时,新rpm包里的该文件不会覆盖旧的文件,旧文件保持不变。
3. 一个文件被%config(noreplace)指令配置
此时包含如下情况:
-
该文件在新的rpm包里相对之前的rpm有变化,且在本地没有被修改过。此时执行
rpm -Uvh xxxx
时,新rpm包里的该文件会替换旧的文件。(旧文件被删除) -
该文件在新的rpm包里相对之前的rpm有变化,且在本地被修改过。此时执行
rpm -Uvh xxxx
时,旧文件保持不变,新rpm包里的该文件并重命名为xx.rpmnew,例如/etc/redis/redis.conf.rpmnew -
该文件在新的rpm包里相对之前的rpm没有变化,且在本地没有被修改过。此时执行
rpm -Uvh xxxx
时,新rpm包里的该文件会替换旧的文件。(旧文件被删除) -
该文件在新的rpm包里相对之前的rpm没有变化,且在本地被修改过。此时执行
rpm -Uvh xxxx
时,新rpm包里的该文件不会覆盖旧的文件,旧文件保持不变。
总结如下表所示:
文件标记 | 在update包中是否更新 | 本地磁盘没有被修改 | 本地磁盘被修改过 |
---|---|---|---|
无 | 没更新 | 替换(旧文件删除) | 替换(旧文件删除) |
无 | 更新 | 替换(旧文件删除) | 替换(旧文件删除) |
%config | 没更新 | 替换(旧文件删除) | 保留修改过的文件,不替换 |
%config | 更新 | 替换(旧文件删除) | 旧文件被保存为xxx.rpmsave,新文件替换旧文件 |
%config(noreplace) | 没更新 | 替换(旧文件删除) | 保留修改过的文件,不替换 |
%config(noreplace) | 更新 | 替换(旧文件删除) | 旧文件保持不变,新文件重命名为xxx.rpmnew |
当rpm包中该文件的标记方法发生改变的两个场景:
文件标记 | 在update包中是否更新 | 本地磁盘被修改过 |
---|---|---|
之前是%config(noreplace),现在是%config | 更新 | 文件被替换,旧文件被保存为xxx.rpmsave |
之前是%config,现在是%config(noreplace) | 更新 | 旧文件保持不变,update包中的新文件重命名为xxx.rpmnew |
九、更新日志 %changelog
每次软件的更新内容可以记录在此到这里,保存到发布的软件包中,以便查询之用。
十、常用变量
macro | definition | comment |
---|---|---|
%{_sysconfdir} |
/etc |
|
%{_prefix} |
/usr |
can be defined to /app for flatpak builds |
%{_exec_prefix} |
%{_prefix} |
default: /usr |
%{_includedir} |
%{_prefix}/include |
default: /usr/include |
%{_bindir} |
%{_exec_prefix}/bin |
default: /usr/bin |
%{_libdir} |
%{_exec_prefix}/%{_lib} |
default: /usr/%{_lib} |
%{_libexecdir} |
%{_exec_prefix}/libexec |
default: /usr/libexec |
%{_sbindir} |
%{_exec_prefix}/sbin |
default: /usr/sbin |
%{_datadir} |
%{_datarootdir} |
default: /usr/share |
%{_infodir} |
%{_datarootdir}/info |
default: /usr/share/info |
%{_mandir} |
%{_datarootdir}/man |
default: /usr/share/man |
%{_docdir} |
%{_datadir}/doc |
default: /usr/share/doc |
%{_rundir} |
/run |
|
%{_localstatedir} |
/var |
|
%{_sharedstatedir} |
/var/lib |
|
%{_lib} |
lib64 |
lib on 32bit platforms |
可以使用rpm命令行查看变量的值:
$ rpm --eval "some text printed on %{_arch}"
some text printed on x86_64
十一、参考文档
https://pureage.info/2014/04/30/noreplace-in-rpm-spec-file.html
http://people.ds.cam.ac.uk/jw35/docs/rpm_config.html
https://docs.fedoraproject.org/en-US/packaging-guidelines/RPMMacros/#RPM_directory_macros