1 说明
因centos停止支持,需要将一些应用软件迁移到OpenEuler上。原本在centos上只需要直接安装官网提供的预编译rpm包即可,现在没有了现成的安装包,只能从源代码自行编译。如果只是少数机器,逐台编译尚可忍耐,对于大量机器需要安装的情况,显然还是制作成rpm更为合适。
本文内容整理自官网文档:https://rpm-packaging-guide.github.io/,将其中的一个实例进行了解释,供学习之用。
2 准备源代码
2.1 C语言程序
创建目录cello-1.0,在其中创建一个输出Hello World的C语言程序文件cello.c:
mkdir cello-1.0
cd cello-1.0
touch cello.c
cello.c内容如下:
#include <stdio.h>int main(void) {printf("Hello World\n");return 0;
}
用gcc将该程序编译链接,即可直接运行:
yun install -y gcc
gcc -g -o cello cello.c
此时目录下会出现一个cello的可执行文件,直接使用./cello 即可输出Hello World。
如果将该可执行文件放在PATH路径下,那么在任何目录都可以直接使用cello命令执行。
2.2 补丁文件
在cello-1.0目录下创建一个普通文件cello.conf,假设为配置文件,安装完成后准备放在/etc/cello/目录下,内容为“before” 。
echo "before" > cello.conf
这个文件内容计划在安装完成后被修改,因此需要用到补丁。创建内容为“after”的cello2.conf文件,并用diff命令生成补丁。生成后将cello2.conf文件删除,将补丁文件cello-patch.patch复制到上层目录下,后面另有用处,只保留原文件cello.conf。
echo "after" > cello2.conf
diff -Naur cello.conf cello2.conf > cello-patch.patch
rm -f cello2.conf
mv cello-patch.patch ..
可以用patch < cello-patch.patch进行补丁功能的测试。
2.3 说明文件
在cello-1.0目录下再创建一个README.md,假设为说明文件,安装完成后准备放在/usr/share/doc/cello-1.0/目录下,文件内容随意。
2.4 Makefile文件
除了用gcc直接编译、链接,使用make工具可以更方便、更规范的完成这个流程。首先确保已经安装了make工具:
yum install -y make
编写Makefile内容如下:
cello:gcc -g -o cello cello.c
install:mkdir -p $(DESTDIR)/usr/bininstall -m 0755 cello $(DESTDIR)/usr/bin/cello
clean:rm -f cellorm -f $(DESTDIR)/usr/bin/cello
- 运行
make或者make cello命令,则会在当前目录下生成可执行文件cello。 - 运行
make install命令,则会将cello文件复制到$(DESTDIR)/usr/bin/cello目录下,并赋予755的权限。这里的$(DESTDIR)是一个宏,可以人工指定(后面spec文件的%make_install部分会涉及),如果不指定则为根目录/。 - 运行
make clean,则会将当前目录下的cello删除,同时将$(DESTDIR)/usr/bin/cello目录下的cello删除。 - 运行
make distclean(本例中未涉及configure操作),除了同make clean外,还会将Makefile、config.log等文件一起删除,用于重新configure
2.5 总结
至此,源代码文件就准备好了。此时整个cello-1.0目录下,一共有4个文件:
└── cello-1.0├── cello.c├── cello.conf├── Makefile└── README.md
进入上层目录,将cello-1.0目录打包成tar.gz文件,以便进行下一步工作
cd ..
tar -zcvf cello-1.0.tar.gz cello-1.0/
3 制作rpm包
3.1 安装工具
yum install -y rpm-build
yum install -y rpmdevtools
3.2 构建工作空间
使用rpmdev-setuptree命令构建打包工作空间,该工作空间会创建在当前用户的家目录下,例如/root/rpmbuild,该目录结构如下:
└── rpmbuild├── BUILD├── RPMS├── SOURCES├── SPECS└── SRPMS
各目录解释如下:
| 目录 | 内容 |
|---|---|
| BUILD | 主要是解压后的源代码 |
| RPMS | 如果制作的是RPM包,则存放在此 |
| SOURCES | 存放源代码包、补丁等 |
| SPECS | 制作时使用的spec文件 |
| SRPMS | 如果制作的是SRPM包,则存放在此 |
| BUILDROOT | 制作rpm包时才会生成,源代码会安装在此目录而非系统目录,以及放置文档、license等 |
3.3 编辑spec文件
将准备好的源代码文件cello-1.0.tar.gz和补丁文件cello-patch.patch放入SOURCES目录。
进入SPECS目录,创建新spec文件
cd SPECES
rpmdev-newspec
会生成一个新的spec文件newpackage.spec,本例中修改为cello.spec,内容如下:
Name: cello
Version: 1.0
Release: 1%{?dist}
Summary: test
License: GPLv3+
Source0: %{name}-%{version}.tar.gz
Patch0: cello-patch.patch
BuildRequires: gcc, make%description
test-1%prep
%setup -q%patch0%build
make %{?_smp_mflags}%install
%make_install
mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}
install -m 0644 %{name}.conf $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/%{name}.conf%files
%doc README.md
%attr(0755, vmuser, vmuser) %{_bindir}/%{name}
%attr(0644, vmuser, vmuser) %{_sysconfdir}/%{name}/%{name}.conf%postun
if [ "$1" = "0" ]; thenrmdir %{_sysconfdir}/%{name}
fi
spec文件主要构成有:
- Preamble Items
包含了包的元数据,这些项可以作为宏在spec文件中使用,以提高文件的可移植性。
| 关键字 | 说明 |
|---|---|
| Name | 包的名称 |
| Version | 包的版本 |
| Release | release版本 |
| Summary | 简要说明 |
| License | 遵循的License |
| Source0 | 使用的源代码包,如果有多个可以有Source1、 Source2……,由于使用了宏,替换后这里的源代码包即为cello-1.0.tar.gz |
| Patch0 | 使用的补丁,可以有多个:Patch1、Patch2…… |
| BuildRequires | build时需要的包 |
| Requires | 运行时需要的包,本例中无 |
- Body Items
用于编译过程中的阶段控制。为提高文件的可移植性,文件中使用了一些宏,可参考下一小节。
| 关键字 | 说明 |
|---|---|
| %description | 说明描述 |
| %prep | 准备阶段,%setup -q意为将源代码压缩包(即cello-1.0.tar.gz)解压,并限制输出 |
| %build | 构建阶段,make %{?_smp_mflags}意为调用多处理器并行构建 |
| %install | 安装阶段,%make_install意为调用make install命令,此时$(DESTDIR)不为系统跟目录,而是该工作空间的$RPM_BUILD_ROOT目录,实际为/BUILDROOT/cello-1.0-1.el7.x86_64/。再应用install命令将conf文件复制到%{_sysconfdir}/%{name}/%下,即/etc/cello/下 |
| %files | 文件声明,将$RPM_BUILD_ROOT下的文件复制到系统的根目录/下。本例中是将README复制到/usr/shar/doc/cello-1.0/下(默认);cello可执行文件复制为%{_bindir}/%{name},实际为/usr/bin/cello,权限755;cello.conf复制到%{_sysconfdir}/%{name}/%{name}.conf,实际为/etc/cello/cello.conf,权限644 |
| %postun | 卸载后操作,本例中会保留一个空目录%{_sysconfdir}/%{name},即/etc/cello,因此需要删除 |
- 宏
SPEC文件中使用的宏,除了如%{name}、%{version}等关键字外,还有rpm定义的宏,例如%{_sysconfdir}和%{_bindir},这些宏具体说明可以在 /usr/lib/rpm/macros 文件中查看。
常用的rpm宏例如:
| 宏 | 说明 |
|---|---|
| % | bin目录,默认为/usr/bin |
| % | sbin目录,默认为/usr/sbin |
| % | 配置目录,默认为/etc |
| % | 数据目录,默认为/usr/share |
此类宏可以用rpm --eval %{_bindir}命令输出具体值。
3.4 创建rpm包
至此,rpmbuild目录结构如下:
└── rpmbuild├── BUILD├── BUILDROOT├── RPMS├── SOURCES│ └── cello-1.0.tar.gz│ └── cello-patch.patch├── SPECS│ └── cello.spec└── SRPMS
应用命令rpmbuild -bb SPECS/cello.spec即可进行rpm包的创建,其中bb为选项之一:
| 选项 | 说明 |
|---|---|
| -bp | build prep,只执行spec的%pre段 |
| -bc | build compile,只执行spec的%pre和%build段 |
| -bi | build install,只执行spec的%pre、%build和%install段 |
| -bl | 检查spec中的%file段 |
| -ba | 同时做成rpm和src.rpm文件 |
| -bb | build binary,只做成rpm文件 |
| -bs | build source,只做成srpm文件 |
创建rpm成功后,会默认清空BUILDROOT目录,如果再加上--noclean选项,则会保留,便于进行调试。
本例的完整结构如下,可以对照查看:
├── BUILD
│ └── cello-1.0
│ ├── cello
│ ├── cello.c
│ ├── cello.conf
│ ├── debugfiles.list
│ ├── debuglinks.list
│ ├── debugsources.list
│ ├── elfbins.list
│ ├── Makefile
│ └── README.md
├── BUILDROOT
│ └── cello-1.0-1.el7.x86_64
│ ├── etc
│ │ └── cello
│ │ └── cello.conf
│ └── usr
│ ├── bin
│ │ └── cello
│ ├── lib
│ │ └── debug
│ │ └── usr
│ │ └── bin
│ │ └── cello.debug
│ ├── share
│ │ └── doc
│ │ └── cello-1.0
│ │ └── README.md
│ └── src
│ └── debug
│ └── cello-1.0
│ └── cello.c
├── RPMS
│ └── x86_64
│ ├── cello-1.0-1.el7.x86_64.rpm
│ └── cello-debuginfo-1.0-1.el7.x86_64.rpm
├── SOURCES
│ ├── cello-1.0.tar.gz
│ └── cello-patch.patch
├── SPECS
│ └── cello.spec
└── SRPMS
3.5 安装rpm包
构建完成后,可以用:
rpm -ivh xxx.rpm:安装rpm包rpm -qa:查看已安装的rpm包rpm -e xxx.rpm:卸载rpm包
卸载rpm包时,如果%preun或者%postun写的不正确,会出现无法卸载的情况,此时可以加上--noscripts跳过两个阶段强制卸载,相应的残留文件需要手工删除
3.6 使用src.rpm包
src.rpm包也可以使用rpm -ivh命令安装,但是并不会实际安装软件,而是解压出源代码压缩包、补丁、spec文件以及其他普通文件。网络资料显示源代码会接到到 /usr/src/redhat/ 或 /usr/src/packages/ 目录下,但是实际文件出现在了~/rpmbuild/目录下。
也可以使用rpm2cpio package.src.rpm | cpio -idmv命令将源代码接到到当前目录下。
笔记
常用宏
| 宏 | 含义 | 示例 |
|---|---|---|
| % | 无条件宏展开:如果宏已定义,则替换为对应值;如果未定义,则报错。多为用户自定义宏或非路径类系统宏 | % |
| % | 无条件展开。以下划线开头,通常为系统预定义的路径宏,用于表示标准目录或系统环境变量 | %{_bindir} → 展开为/usr/bin |
| % | 当宏已定义且非空时展开为x,否则替换为空字符串 | %{?alphatag:%{?alphatag}.},如果alphatag已定义,则展开 |
| % | 当宏未定义时执行冒号后的操作 | %{!?build_agent: %global build_agent 1},如果build_agent未定义,则全局置为1 |
常用Body Items
| Body Items | 含义 |
|---|---|
| %package | 定义子包,。当需要从一个 spec 文件生成多个 RPM 包时,可通过%package声明子包并设置其独立属性 |