CentOS下 rpm 包制作
RPM(Red Hat Package Manager)是用于 Linux 分发版(distribution)的最常见的软件包管理器。因为它允许分发已编译的软件,所以用户只用一个命令就可以安装软件。RPM 是 Linux“标准基本库”版本 1.0.0 指定的安装工具。在 Linux 分发版前 10 名中,有 8 个是基于 RPM(请参阅“Comparison of Linux Distributions”)。即使某些通常不使用 RPM 的分发版,如 Debian,也有可用工具将 RPM 转换成它们自己的格式。在 Linux 上,对于除开发人员以外的任何人,RPM 也是用来打包软件的最佳选择。本文通过一个webhook,介绍rpm软件包的制作
使用centos7
准备环境
1 | git clone https://github.com/XiaoMi/soar.git |
从spec文档建立有以下选项:
1 | -bp #只执行spec的%pre 段(解开源码包并打补丁,即只做准备) |
2 | -bc #执行spec的%pre和%build 段(准备并编译) |
3 | -bi #执行spec中%pre,%build与%install(准备,编译并安装) |
4 | -bl #检查spec中的%file段(查看文件是否齐全) |
5 | -ba #建立源码与二进制包(常用) |
6 | -bb #只建立二进制包(常用) |
7 | -bs #只建立源码包 |
1. 配置工作目录
安装构建工具
1 | yum install rpmdevtools wget tree |
使用命令生成工作目录
1 | rpmdev-setuptree |
rpmdev-setuptree
命令默认将再当前用户主目录下创建一个RPM构建根目录结构,
如果需要改变次默认位置,可以修改配置文件:~/.rpmmacros中变量 _topdir 对应
的值即可。
生成的目录如下
1 | rpmbuild/ |
2 | ├── BUILD # BUILD 打包过程中的工作目录 |
3 | ├── RPMS # 存放生成的二进制包 |
4 | ├── SOURCES # 放置打包资源,包括源码打包文件和补丁文件 |
5 | ├── SPECS # SPECS 目录放置 SPEC 文档 |
6 | └── SRPMS # 目录存放生成的源码包 |
BUILDROOT
是制作过程中临时安装程序的地方
2. 制作过程概览
为了构建 RPM 软件包,您需要写一个名为 spec 文件的 RPM 输入文件,该文件告诉 RPM 如何构建和打包您的软件。编写 spec 文件您需要:
如果您首次创建 .spec 文件,vim 或 emacs 会自动生成模板:
1 | Name: |
2 | Version: |
3 | Release: 1%{?dist} |
4 | Summary: |
5 | Group: |
6 | License: |
7 | URL: |
8 | Source0: |
9 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) |
10 | |
11 | BuildRequires: |
12 | Requires: |
13 | |
14 | %description |
15 | |
16 | %prep |
17 | %setup -q |
18 | |
19 | %build |
20 | %configure |
21 | make %{?_smp_mflags} |
22 | |
23 | %install |
24 | rm -rf %{buildroot} |
25 | make install DESTDIR=%{buildroot} |
26 | |
27 | %clean |
28 | rm -rf %{buildroot} |
29 | |
30 | %files |
31 | %defattr(-,root,root,-) |
32 | %doc |
33 | |
34 | %changelog |
您可以使用 $RPM_BUILD_ROOT
代替 %{buildroot}
,两者都可以使用。
spec文档的内容
1 | Name: 软件包的名称,后面可使用%{name}的方式引用,具体命令需跟源包一致 |
2 | Summary: 软件包的内容概要 |
3 | Version: 软件的实际版本号,具体命令需跟源包一致 |
4 | Release: 发布序列号,具体命令需跟源包一致 |
5 | 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硬件支持)
License: 软件授权方式,通常就是GPL
1 | Source: 源代码包,可以带多个用Source1、Source2等源,后面也可以用%{source1}、%{source2}引用 |
2 | BuildRoot: 这个是安装或编译时使用的“虚拟目录”,考虑到多用户的环境,一般定义为: |
3 | %{_tmppath}/%{name}-%{version}-%{release}-root 或 %{_tmppath}/%{name}-%{version}-%{release}-buildroot-%(%{__id_u} -n} |
4 | 该参数非常重要,因为在生成[rpm](https://www.fooher.com/tag/rpm)的过程中,执行make install时就会把软件安装到上述的路径中,在打包的时候,同样依赖“虚拟目录”为“根目录”进行操作。 |
5 | 后面可使用$RPM_BUILD_ROOT 方式引用。 |
URL: 软件的主页
Vendor: 发行商或打包组织的信息,例如RedFlag Co,Ltd
Disstribution: 发行版标识
Patch: 补丁源码,可使用Patch1、Patch2等标识多个补丁,使用%patch0或%{patch0}引用
Prefix: %{_prefix} 这个主要是为了解决今后安装rpm包时,并不一定把软件安装到rpm中打包的目录的情况。这样,必须在这里定义该标识,并在编写%install脚本的时候引用,才能实现rpm安装时重新指定位置的功能
Prefix: %{_sysconfdir} 这个原因和上面的一样,但由于%{_prefix}指/usr,而对于其他的文件,例如/etc下的配置文件,则需要用%{_sysconfdir}标识
Build Arch: 指编译的目标处理器架构,noarch标识不指定,但通常都是以/usr/lib/rpm/marcros中的内容为默认值
Requires: 该rpm包所依赖的软件包名称,可以用>=或<=表示大于或小于某一特定版本,
spec脚本主体
spec脚本的主体中也包括了很多关键字和描述,下面会一一列举。我会把一些特别需要留意的地方标注出来。
%prep 预处理脚本
%setup -n %{name}-%{version} 把源码包解压并放好
注:可根据你的源码的名字格式,来确认解压后名字的格式,否则可能导致install的时候找不到对应的目录
通常是从/usr/src/redhat/SOURCES里的包解压到/usr/src/redhat/BUILD/%{name}-%{version}中。
一般用%setup -c就可以了,但有两种情况:一就是同时编译多个源码包,二就是源码的tar包的名称与解压出来的目录不一致,此时,就需要使用-n参数指定一下了。
%patch 打补丁
通常补丁都会一起在源码tar.gz包中,或放到SOURCES目录下。一般参数为:
%patch -p1 使用前面定义的Patch补丁进行,-p1是忽略patch的第一层目录
%Patch2 -p1 -b xxx.patch 打上指定的补丁,-b是指生成备份文件
◎补充一下
%setup 不加任何选项,仅将软件包打开。
%setup -n newdir 将软件包解压在newdir目录。
%setup -c 解压缩之前先产生目录。
%setup -b num 将第num个source文件解压缩。
%setup -T 不使用default的解压缩操作。
%setup -T -b 0 将第0个源代码文件解压缩。
%setup -c -n newdir 指定目录名称newdir,并在此目录产生rpm套件。
%patch 最简单的补丁方式,自动指定patch level。
%patch 0 使用第0个补丁文件,相当于%patch ?p 0。
%patch -s 不显示打补丁时的信息。
%patch -T 将所有打补丁时产生的输出文件删除。
预处理 %prep:
预处理通常用来执行一些解开源程序包的命令,为下一步的编译安装做准备。
%prep 和下面的 %build,%install 段一样,除了可以执行 RPM 所定义的
宏命令(以 % 开头)以外,还可以执行 SHELL 命令,功能类似 ./configure
作用:
用来准备要编译的软件。通常,这一段落将归档中的源代码解压,并应用补丁。
这些可以用标准的 Shell 命令完成,但是更多地使用预定义的宏.
检查标签语法是否正确,删除旧的软件源程序,对包含源程序的 tar 文件进行解码。
如果包含补丁(patch)文件,将补丁文件应用到解开的源码中。它一般包含 %setup
与 %patch 两个命令。%setup 用于将软件包打开,执行 %patch 可将补丁文件加入
解开的源程序中.
%prep
宏 %setup
该宏解开源代码,将当前目录改为源代码解压之后产生的目录。这个宏还有一些选项
可以使用,如,在解压后, %setup 宏假设产生的目录是 %{name}-%{version}
如果 tar 打包中的目录不是这样命名的,可以用 -n 选项来指定要切换到的目录。
如:%setup -n%{name}-April2003Rel
还有如下其他参数:
%setup -q — 将 tar 命令的繁复输出关闭
%setup -nnewdir — 将压缩的软件源程序在 newdir 目录下解开
%setup -c — 在解开源程序之前先创建目录
%setup -bnum — 在包含多个源程序时,将第 num 个源程序解压缩
%setup -T — 不使用缺省的解压缩操作
%files 基础
%defattr
用于设置默认文件权限,通常可以在 %files
的开头看到它。注意,如果不需要修改权限,则不需要使用它。其格式为:
1 | %defattr(<文件权限>, <用户>, <用户组>, <目录权限>) |
第 4 个参数通常会省略。常规用法为 %defattr(-,root,root,-)
,其中 “-
“ 表示默认权限。
您应该列出该软件包拥有的所有文件和目录。尽量使用宏代替目录名,查看宏列表 Packaging:RPMMacros(例如:使用 %{_bindir}/mycommand
代替 /usr/bin/mycommand
)。如果路径以 “/
“ 开头(或从宏扩展),则从 %{buildroot}
目录取用。否则,假设文件在当前目录中(例如:在 %{_builddir}
中,包含需要的文档)。如果您的包仅安装一个文件,如 /usr/sbin/mycommand
,则 %files
部分如下所示:
1 | %files |
2 | %{_sbindir}/mycommand |
若要使软件包不受上游改动的影响,可使用通配符匹配所有文件:
1 | %{_bindir}/* |
包含一个目录:
1 | %{_datadir}/%{name}/ |
注意,%{_bindir}/*
不会声明此软件包拥有 /usr/bin
目录,而只包含其中的文件。如果您列出一个目录,则该软件包拥有这个目录,及该目录内的所有文件和子目录。因此,不要列出 %{_bindir}
,并且要小心的处理那些可能和其他软件包共享的目录。
如果存在以下情况,可能引发错误:
- 通配符未匹配到任何文件或目录
- 文件或目录被多次列出
- 未列出
%{buildroot}
下的某个文件或目录 - 您也可以使用
%exclude
来排除文件。这对于使用通配符来列出全部文件时会很有用,注意如果未匹配到任何文件也会造成失败。
您可能需要在 %files
部分添加一个或多个前缀;请用空格分隔。详情请查看 Max RPM section on %files directives。
通常,”%doc
“ 用于列出 %{_builddir}
内,但未复制到 %{buildroot}
中的文档。通常包括 README
和 INSTALL
。它们会保存至 /usr/share/doc
下适当的目录中,不需要声明 /usr/share/doc
的所有权。
注意: 如果指定 %doc
条目,rpmbuild < 4.9.1 在安装前会将 %doc 目录删除。这表明已保存至其中的文档,例如,在 %install
中安装的文档会被删除,因此最终不会出现在软件包中。如果您想要在 %install
中安装一些文档,请将它们临时安装到 build 目录(不是 build root 目录)中,例如 _docs_staging
,接着在 %files
中列出,如 %doc _docs_staging/*
这样。
配置文件保存在 /etc
中,一般会这样指定(确保用户的修改不会在更新时被覆盖):
1 | %config(noreplace) %{_sysconfdir}/foo.conf |
如果更新的配置文件无法与之前的配置兼容,则应这样指定:
1 | %config %{_sysconfdir}/foo.conf |
“%attr(mode, user, group)
“ 用于对文件进行更精细的权限控制,”-
“ 表示使用默认值:
1 | %attr(0644, root, root) FOO.BAR |
“%caps(capabilities)
“ 用于为文件分配 POSIX capabilities。例如:
1 | %caps(cap_net_admin=pe) FOO.BAR |
如果包含特定语言编写的文件,请使用 %lang
来标注:
1 | %lang(de) %{_datadir}/locale/de/LC_MESSAGES/tcsh* |
使用区域语言(Locale)文件的程序应遵循 处理 i18n 文件的建议方法:
- 在
%install
步骤中找出文件名:%find_lang ${name}
- 添加必要的编译依赖:
BuildRequires: gettext
- 使用找到的文件名:
%files -f ${name}.lang
以下前缀在 Fedora 中无效:%license
和 %readme
。
常用的宏
1 | RPM_BUILD_DIR: /usr/src/redhat/BUILD |
2 | RPM_BUILD_ROOT: /usr/src/redhat/BUILDROOT |
3 | %{_sysconfdir}: /etc |
4 | %{_sbindir}: /usr/sbin |
5 | %{_bindir}: /usr/bin |
6 | %{_datadir}: /usr/share |
7 | %{_mandir}: /usr/share/man |
8 | %{_libdir}: /usr/lib64 |
9 | %{_prefix}: /usr |
10 | %{_localstatedir}: /usr/var |
11 | %{_unitdir} /usr/lib/systemd/system/ |
%{_bindir} |
/usr/bin |
二进制目录:保存可执行文件 |
---|---|---|
%{_builddir} |
~/rpmbuild/BUILD |
构建目录:软件在 build 的子目录被编译。参考 %buildsubdir |
%{buildroot} |
~/rpmbuild/BUILDROOT |
Build root:%install 阶段中,将 %{_builddir} 子目录下的文件复制到 %{buildroot} 的子目录(之前,%{buildroot} 使用的位置为 “/var/tmp/“) |
%{buildsubdir} |
%{_builddir}/%{name} |
构建子目录:%build 阶段中,文件会在 %{_builddir} 的子目录中编译。此宏在 %autosetup 之后设置 |
%{_datadir} |
/usr/share |
共享数据目录 |
%{_defaultdocdir} |
/usr/share/doc |
默认文档目录 |
%{dist} |
.fc*NUMBER* |
发行版名称+版本号(例如 “.fc30 “) |
%{fedora} |
*NUMBER* |
Fedora 发行版本号(例如 “30 “) |
%{_includedir} |
/usr/include |
程序头文件目录 |
%{_infodir} |
/usr/share/info |
info 手册目录 |
%{_initrddir} |
/etc/rc.d/init.d |
init 脚本目录 |
%{_libdir} |
/usr/lib |
共享库目录 |
%{_libexecdir} |
/usr/libexec |
仅由系统调用执行该目录中的命令 |
%{_localstatedir} |
/var |
保存缓存/日志/lock等信息的目录 |
%{_mandir} |
/usr/share/man |
man 手册目录 |
%{name} |
软件包名称,通过 Name: tag 设置 | |
%{_sbindir} |
/usr/sbin |
保存管理员可执行命令 |
%{_sharedstatedir} |
/var/lib |
保存程序运行所处理的文件 |
%{_sysconfdir} |
/etc |
配置文件目录 |
%{version} |
软件包版本,通过 Version: tag 设置 |
您可以查看 /etc/rpm/*
和 /usr/lib/rpm
,以及 /usr/lib/rpm/macros
以进一步了解宏。或使用 rpm --showrc
显示当前 RPM 所使用的宏变量和值(根据 rpmrc
和宏)
1 | %define debug_package %{nil} |
2 | Name: webhooks |
3 | Version: 0.1.1 |
4 | Release: 1%{?dist} |
5 | Summary: a git sync tools |
6 | |
7 | Group: Development/Tools |
8 | License: GPL |
9 | URL: http://www.github.com/becivells/go-webhook |
10 | Source0: %{name}-%{version}.tar.gz |
11 | Source1: webhooks.service |
12 | |
13 | BuildRoot: %{_tmppath}/%{name}-%{main_version}-%{main_release}-root |
14 | %description |
15 | a git tool for repo to webroot |
16 | |
17 | %prep |
18 | %setup -q |
19 | |
20 | %build |
21 | go build -o webhooks |
22 | |
23 | %install |
24 | %{__rm} -rf $RPM_BUILD_ROOT |
25 | %{__mkdir} -p $RPM_BUILD_ROOT/usr/lib/systemd/system/ |
26 | %{__mkdir} -p $RPM_BUILD_ROOT/opt/webhooks |
27 | %{__install} -m755 webhooks $RPM_BUILD_ROOT/opt/webhooks/webhooks |
28 | %{__install} webhooks.yaml $RPM_BUILD_ROOT/opt/webhooks/webhooks.yaml |
29 | %{__install} -m644 %SOURCE1 $RPM_BUILD_ROOT/%{_unitdir}/webhooks.service |
30 | |
31 | %files |
32 | %defattr(-,root,root,-) |
33 | /opt/webhooks/ |
34 | /usr/lib/systemd/system/ |
35 | %clean |
36 | %{__rm} -rf $RPM_BUILD_ROOT |
https://github.com/Becivells/linux-package
https://www.cnblogs.com/jimodetiantang/p/9573098.html
https://www.fooher.com/20180112_155.html
https://fedoraproject.org/wiki/How_to_create_an_RPM_package/zh-cn