构建RPM包

本章介绍通过本地或OBS构建RPM软件包的方法。详见打包规则

打包说明

原理介绍

RPM打包的时候需要编译源码,需要把编译好的配置文件、二进制命令文件等放到合适的位置,还要根据需要对RPM的包进行测试,这些都需要先有一个“工作空间”。rpmbuild命令使用一套标准化的“工作空间”:

  1. $ rpmdev-setuptree

rpmdev-setuptree这个命令就是安装 rpmdevtools 带来的。可以看到运行了这个命令之后,在“/root“目录(非root用户为“/home/用户名“目录)下多了一个 rpmbuild 的文件夹,目录结构如下:

  1. $ tree rpmbuild
  2. rpmbuild
  3. ├── BUILD
  4. ├── RPMS
  5. ├── SOURCES
  6. ├── SPECS
  7. └── SRPMS

内容相关的说明如下:

目录

宏代码

名称

功能

~/rpmbuild/BUILD

%_builddir

构建目录

源码包被解压至此,并在该目录的子目录完成编译

~/rpmbuild/RPMS

%_rpmdir

标准 RPM 包目录

生成/保存二进制 RPM 包

~/rpmbuild/SOURCES

%_sourcedir

源代码目录

保存源码包(如 .tar 包)和所有 patch 补丁

~/rpmbuild/SPECS

%_specdir

Spec 文件目录

保存 RPM 包配置(.spec)文件

~/rpmbuild/SRPMS

%_srcrpmdir

源代码 RPM 包目录

生成/保存源码 RPM 包(SRPM)

SPECS 下是RPM包的配置文件,是RPM打包的“图纸”,这个文件会告诉rpmbuild命令如何去打包。“宏代码”这一列就可以在SPEC文件中用来代指所对应的目录,类似于编程语言中的宏或全局变量。

打包流程

打包的过程主要分为如下步骤:

  1. 把源代码放到%_sourcedir中。
  2. 进行编译,编译的过程是在%_builddir中完成的,一般情况下,源代码是压缩包格式,需要先进行解压。
  3. 进行“安装”,类似于预先组装软件包,把软件包应该包含的内容(比如二进制文件、配置文件、man文档等)复制到%_buildrootdir中,并按照实际安装后的目录结构组装,比如二进制命令可能会放在/usr/bin下,那么就在%_buildrootdir下也按照同样的目录结构放置。
  4. 做一些必要的配置,比如在实际安装前的准备,安装后的清理等等。这些都是通过配置在SPEC文件中来告诉rpmbuild命令。
  5. 检查软件是否正常运行。
  6. 生成的RPM包放置到%_rpmdir,源码包放置到%_srpmdir下。

在SPEC文件中的,各个阶段说明如下:

阶段

读取的目录

写入的目录

具体动作

%prep

%_sourcedir

%_builddir

读取位于 %_sourcedir 目录的源代码和 patch 。之后,解压源代码至 %_builddir 的子目录并应用所有 patch

%build

%_builddir

%_builddir

编译位于 %_builddir 构建目录下的文件。通过执行类似 ./configure && make 的命令实现。

%install

%_builddir

%_buildrootdir

读取位于 %_builddir 构建目录下的文件并将其安装至 %_buildrootdir 目录。这些文件就是用户安装 RPM 后,最终得到的文件。

%check

%_builddir

%_builddir

检查软件是否正常运行。通过执行类似 make test 的命令实现。

bin

%_buildrootdir

%_rpmdir

读取位于 %_buildrootdir 最终安装目录下的文件,以便最终在 %_rpmdir 目录下创建 RPM 包。在该目录下,不同架构的 RPM 包会分别保存至不同子目录, noarch 目录保存适用于所有架构的 RPM 包。这些 RPM 文件就是用户最终安装的 RPM 包。

src

%_sourcedir

%_srcrpmdir

创建源码 RPM 包(简称 SRPM,以.src.rpm 作为后缀名),并保存至 %_srcrpmdir 目录。SRPM 包通常用于审核和升级软件包。

打包选项

通过rpmbuild命令构建软件包。rpmbuild构建软件包一般可以通过构建SPEC文件、tar文件、source文件实现。

rpmbuild命令格式为:rpmbuild [option…]

常用的rpmbuild打包选项如表1所示。

表 1 rpmbuild打包选项

option取值

说明

-bp specfile

specfile文件的%prep段开始构建(解开源码包并打补丁)。

-bc specfile

specfile文件的%build段开始构建。

-bi specfile

specfile文件的%install段开始构建。

-bl specfile

specfile文件的%file段开始检查。

-ba specfile

通过specfile文件构建源码包和二进制包。

-bb specfile

通过specfile文件构建二进制包

-bs specfile

通过specfile文件构建源码包。

-rp sourcefile

sourcefile文件的%prep段开始构建(解开源码包并打补丁)。

-rc sourcefile

sourcefile文件的%build段开始构建。

-ri sourcefile

sourcefile文件的%install段开始构建。

-rl sourcefile

sourcefile文件的%file段开始检查。

-ra sourcefile

通过sourcefile文件构建源码包和二进制包。

-rb sourcefile

通过sourcefile文件构建二进制包

-rs sourcefile

通过sourcefile文件构建源码包。

-tp tarfile

tarfile文件的%prep段开始构建(解开源码包并打补丁)。

-tc tarfile

tarfile文件的%build段开始构建。

-ti tarfile

tarfile文件的%install段开始构建。

-ta tarfile

通过tarfile文件构建源码包和二进制包。

-tb tarfile

通过tarfile文件构建二进制包

-ts tarfile

通过tarfile文件构建源码包。

--buildroot=DIRECTORY

在构建时,使用DIRECTORY目录覆盖默认的/root目录。

--clean

完成打包后清除BUILD目录下的文件。

--nobuild

不执行任何实际的构建步骤。可用于测试spec文件。

--noclean

不执行spec文件的”%clean”阶段(即使它确实存在)。

--nocheck

不执行spec文件的”%check”阶段(即使它确实存在)。

--dbpath DIRECTORY

使用DIRECTORY中的数据库,而不是默认的 /var/lib/rpm。

--root DIRECTORY

使DIRECTORY为最高级别的路径,默认为“/”为最高路径。

--rebuild sourcefile

将安装指定的源代码包sourcefile,然后进行准备、编译、安装。

--recompile sourcefile

在--recompile 的基础上额外构建一个新的二进制包。在构建结束时,构建目录、源代码和 spec 文件都将被删除。

将被删除(就好像用了 --clean),

-?,--help

打印详细的帮助信息。

--version

打印详细的版本信息。

本地构建

本章通过一个简单的示例介绍如何在本地构建RPM软件包的方法。

搭建开发环境

前提条件

需要root权限,已设置openEuler的repo软件源。

操作步骤

用户可以直接使用DNF工具安装 rpmdevtools,其中包含 rpm-build 等命令以及相关依赖(例如make、gdb)。使用如下命令:

  1. # dnf install rpmdevtools*

创建Hello World RPM包

这里以GNU “Hello World” 项目的打包过程作为示例,包含了典型的FOSS(Free and Open Source Software) 软件项目相关的最常用的外围组件,其中包括配置/编译/安装环境、文档、国际化等等。

下载源码

我们直接下载官方例子的源码,使用如下命令:

  1. $ cd ~/rpmbuild/SOURCES
  2. $ wget http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz

编辑SPEC文件

~/rpmbuild/SPECS目录下新建spec文件,参考命令如下:

  1. $ cd ~/rpmbuild/SPECS
  2. $ vi hello.spec

在文件中写入对应内容后保存文件。文件内容示例如下,请根据实际情况修改相应字段。

  1. Name: hello
  2. Version: 2.10
  3. Release: 1%{?dist}
  4. Summary: The "Hello World" program from GNU
  5. Summary(zh_CN): GNU "Hello World" 程序
  6. License: GPLv3+
  7. URL: http://ftp.gnu.org/gnu/hello
  8. Source0: http://ftp.gnu.org/gnu/hello/%{name}-%{version}.tar.gz
  9. BuildRequires: gettext
  10. Requires(post): info
  11. Requires(preun): info
  12. %description
  13. The "Hello World" program, done with all bells and whistles of a proper FOSS
  14. project, including configuration, build, internationalization, help files, etc.
  15. %description -l zh_CN
  16. "Hello World" 程序, 包含 FOSS 项目所需的所有部分, 包括配置, 构建, 国际化, 帮助文件等.
  17. %prep
  18. %setup -q
  19. %build
  20. %configure
  21. make %{?_smp_mflags}
  22. %install
  23. make install DESTDIR=%{buildroot}
  24. %find_lang %{name}
  25. rm -f %{buildroot}/%{_infodir}/dir
  26. %post
  27. /sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :
  28. %preun
  29. if [ $1 = 0 ] ; then
  30. /sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
  31. fi
  32. %files -f %{name}.lang
  33. %doc AUTHORS ChangeLog NEWS README THANKS TODO
  34. %license COPYING
  35. %{_mandir}/man1/hello.1.*
  36. %{_infodir}/hello.info.*
  37. %{_bindir}/hello
  38. %changelog
  39. * Thu Dec 26 2019 Your Name <youremail@xxx.xxx> - 2.10-1
  40. - Update to 2.10
  41. * Sat Dec 3 2016 Your Name <youremail@xxx.xxx> - 2.9-1
  42. - Update to 2.9
  • Name 标签是软件名,Version 标签是版本号,而 Release 标签是发布编号。
  • Summary 标签是简要说明,英文的话第一个字母应大写,以避免 rpmlint 工具(打包检查工具)警告。
  • License 标签说明软件包的协议版本,审查软件的 License 状态是打包者的职责,这可以通过检查源码或 LICENSE 文件,或与作者沟通来完成。
  • Group 标签过去用于按照 /usr/share/doc/rpm-/GROUPS 分类软件包。目前该标记已丢弃,vim的模板还有这一条,删掉即可,不过添加该标记也不会有任何影响。%changelog 标签应包含每个 Release 所做的更改日志,尤其应包含上游的安全/漏洞补丁的说明。%changelog 条目应包含版本字符串,以避免 rpmlint 工具警告。
  • 多行的部分,如 %changelog 或 %description 由指令下一行开始,空行结束。
  • 一些不需要的行 (如 BuildRequires 和 Requires) 可在行首使用 ‘#’ 注释。
  • %prep、%build、%install、%file暂时用默认的,未做任何修改。

构建RPM包

构建源码、二进制和包含调试信息的软件包,在spec文件所在目录执行如下命令:

  1. $ rpmbuild -ba hello.spec

执行成功后,查看结果,使用如下命令:

  1. $ tree ~/rpmbuild/*RPMS
  2. /home/testUser/rpmbuild/RPMS
  3. └── aarch64
  4. ├── hello-2.10-1.aarch64.rpm
  5. ├── hello-debuginfo-2.10-1.aarch64.rpm
  6. └── hello-debugsource-2.10-1.aarch64.rpm
  7. /home/testUser/rpmbuild/SRPMS
  8. └── hello-2.10-1.src.rpm

使用OBS构建

本章介绍通过网页和osc构建RPM软件包的方法。主要包括如下两类:

  • 修改已有软件包:修改已有软件包源代码,然后将修改后的源代码构建成一个RPM软件包。
  • 新增软件包:从无到有全新开发一个新的软件源文件,并将新开发的源文件构建成一个RPM软件包。

OBS简介

OBS是基于openSUSE发行版的通用编译框架,用于将源码包构建为RPM软件包或Linux镜像。OBS采用自动化的分布式编译方式,支持多种Linux操作系统发行版(openEuler、SUSE、Debian等)镜像和安装包的编译,且支持在多种架构平台(x86、ARM64等)上编译。

OBS由后端和前端组成,后端实现所有核心功能,前端提供了网页应用和API,用于与后端进行交互。此外,OBS还有一个API命令行客户端osc,osc是在一个单独的存储库中开发的。

OBS使用工程组织软件包。基础的权限控制、相关的存仓库和构建目标(操作系统和架构)都可以在工程中定义。一个工程可以包含多个子工程,各个子工程可以独立配置,共同完成任务。

在线构建软件包

本章介绍通过OBS网页端在线构建RPM软件包的方法。

构建已有软件包

构建RPM包 - 图1 说明:

  • 若为首次使用,请首先在OBS网页注册个人账号。
  • 该方法需要拷贝修改后的代码,因此,请在执行下述操作前完成代码修改并提交到正确的代码路径。代码路径会在_service文件中指定。

使用OBS网页端,修改已有软件的源代码,并将修改后的源文件构建为RPM软件包的操作方法如下:

  1. 登录OBS界面,地址为:https://build.openeuler.org/
  2. 单击“All Projects”进入所有工程页面。
  3. 单击需要修改的对应工程,进入该工程的详情页面,例如单击“openEuler:Mainline”。
  4. 在工程详情页面的搜索框查找需要修改的软件包,然后单击该软件包包名,进入该软件包详请页面。
  5. 单击“Branch package”,在弹出的确认页面单击“Accept”确认创建子工程,如图1所示。

    图 1 Branch Confirmation页面
    构建RPM包 - 图2

  6. 单击“_service”文件进入编辑页面,修改_service内容后点击“Save”保存该文件。_service内容示例如下,其中 userCodeURLuserCommitID 分别为用户代码托管路径、用户代码提交版本号或分支。

    1. <services>
    2. <service name="tar_scm_kernel">
    3. <param name="scm">git</param>
    4. <param name="url">userCodeURL</param>
    5. <param name="revision">userCommitID</param>
    6. </service>
    7. <service name="recompress">
    8. <param name="compression">bz2</param>
    9. <param name="file">*.tar</param>
    10. </service>
    11. </services>

    构建RPM包 - 图3 说明:
    单击“Save”保存_service文件后,OBS服务会根据_service文件描述,从指定的url下载源码到OBS对应工程的软件目录下并替换原有文件,例如上述例子中 “openEuler:Mainline”工程的kernel目录。

  7. 文件拷贝并替换完成后,OBS会自动开始构建RPM软件包。等待构建完成,并查看右侧状态栏的构建状态。

    • succeeded:构建成功。用户可以单击“succeeded”查看构建日志,如图2所示。

      图 2 succeeded的页面
      构建RPM包 - 图4

    • failed:构建失败。请单击“failed”查看错误日志进行问题定位后重新构建。

    • unresolvable:未进行构建,可能由于缺失依赖。
    • disabled:构建被手动关闭或正在排队构建。
    • excluded:构建被禁止,可能由于缺少spec文件或者spec文件中禁止了目标架构的编译。

新增软件包

使用OBS网页端,新增一个全新软件包的操作方法如下:

  1. 登录OBS界面。
  2. 根据新增软件包的依赖情况,选择合适的工程,即单击“All Projects”选择对应工程,例如“openEuler:Mainline”。
  3. 单击工程下任一软件包,进入该软件包的详情页面。
  4. 单击“Branch package”,在弹出的确认页面单击“Accept”确认创建子工程。
  5. 单击“Delete package”,删除新创建子工程中的软件包,如图3所示。

    图 3 删除子工程中软件包
    构建RPM包 - 图5

    构建RPM包 - 图6 说明:
    通过已有软件创建新工程是为了继承环境等依赖,而不需要实际的文件,所以这里需要把这些文件删除。

  6. 单击“Create Package”,在弹出的页面输入软件包名称、标题和软件包描述,然后单击“Create”创建软件包,分别如图4图5所示。

    图 4 Create Package页面
    构建RPM包 - 图7

    图 5 创建软件包信息填写页面
    构建RPM包 - 图8

  7. 在页面中单击“Add file”上传spec文件和需要编译的文件(在spec文件中指定),如图6所示。

    图 6 Add file页面
    构建RPM包 - 图9

  8. 文件上传成功后,OBS会自动开始构建RPM软件包。等待构建完成,并查看右侧状态栏的构建状态。

    • succeed:构建成功。用户可以单击“succeeded”查看构建日志。
    • failed:构建失败。请单击“failed”查看错误日志进行问题定位后重新构建。
    • unresolvable:未进行构建,可能由于缺失依赖。
    • disabled:构建被手动关闭或正在排队构建。
    • excluded:构建被禁止,可能由于缺少spec文件或者spec文件中禁止了目标架构的编译。

获取软件包

RPM软件包构建完成后,通过网页端获取对应RPM软件包的方法如下:

  1. 登录OBS界面。
  2. 单击“All Projects”找到所需软件包的对应工程,例如“openEuler:Mainline”。
  3. 在工程下单击所需软件包的包名,进入该软件包详请页面。例如上述例子中的kernel页面。

  4. 选择Repositories页签进入软件包的软件仓库管理页面,在Publish Flag中通过单击选择“Enable”开启(状态由构建RPM包 - 图10变为构建RPM包 - 图11)对应的RPM软件包下载功能,如图7所示。

    图 7 Repositories页面
    构建RPM包 - 图12

  5. 单击Repository列的构建工程名称,进入RPM软件包下载页面,单击RPM软件包右侧的“Download”即可下载对应RPM软件包,如图8所示。

    图 8 RPM软件包下载页面
    构建RPM包 - 图13

使用osc构建软件包

本章介绍使用OBS的命令行工具osc创建工程并构建RPM软件包的方法。

安装并配置osc

前提条件

需要root权限,已设置openEuler的repo软件源。

操作步骤
  1. 使用root用户安装osc命令行工具及依赖。

    1. # dnf install osc build

    构建RPM包 - 图14 说明:
    编译RPM软件包的过程中会依赖build。

  2. 配置osc。

    1. 打开~/.oscrc,命令如下:

      1. $ vi ~/.oscrc
    2. 在~/.oscrc中添加user和pass字段,如下所示,它们的取值 userNamepassWord 分别是用户在OBS网页(https://build.openeuler.org/)上已经注册的账号和密码。

      1. [general]
      2. apiurl = https://build.openeuler.org/
      3. [https://build.openeuler.org/]
      4. user=userName
      5. pass=passWord

构建已有软件包

创建工程

  1. 通过拷贝已有工程,创建属于用户自己的子工程。例如将openEuler:Mainline工程下的zlib软件包到新分支,参考命令如下:

    1. $ osc branch openEuler:Mainline zlib

    回显如下所示,说明在用户testUser下创建了新的分支工程home:testUser:branches:openEuler:Mainline。

    1. A working copy of the branched package can be checked out with:
    2. osc co home:testUser:branches:openEuler:Mainline/zlib
  2. 将需要修改软件包的相关配置文件(例如_service)下载到本地当前路径。其中 testUser 为~/.oscrc配置文件中配置的账户名称,请根据实际情况修改。

    1. $ osc co home:testUser:branches:openEuler:Mainline/zlib

    回显如下所示:

    1. A home:testUser:branches:openEuler:Mainline
    2. A home:testUser:branches:openEuler:Mainline/zlib
    3. A home:testUser:branches:openEuler:Mainline/zlib/_service
  3. 进入本地子工程目录,并将软件包远程代码同步到本地。

    1. $ cd home:testUser:branches:openEuler:Mainline/zlib
    2. $ osc up -S

    回显如下所示:

    1. A _service:tar_scm_kernel_repo:0001-Neon-Optimized-hash-chain-rebase.patch
    2. A _service:tar_scm_kernel_repo:0002-Porting-optimized-longest_match.patch
    3. A _service:tar_scm_kernel_repo:0003-arm64-specific-build-patch.patch
    4. A _service:tar_scm_kernel_repo:zlib-1.2.11-optimized-s390.patch
    5. A _service:tar_scm_kernel_repo:zlib-1.2.11.tar.xz
    6. A _service:tar_scm_kernel_repo:zlib-1.2.5-minizip-fixuncrypt.patch
    7. A _service:tar_scm_kernel_repo:zlib.spec

构建RPM包

  1. 重命名源文件,然后将重命名后的源文件添加到OBS暂存中。

    1. $ rm -f _service;for file in `ls | grep -v .osc`;do new_file=${file##*:};mv $file $new_file;done
    2. $ osc addremove *
  2. 修改源代码和spec文件,并执行如下命令更新文件。

    1. $ osc up
  3. 将对应软件包的所有修改同步到OBS服务器。参考命令如下,-m参数后的信息为提交记录。

    1. $ osc ci -m "commit log"
  4. 获取当前工程的仓库名称和架构,参考命令如下:

    1. $ osc repos home:testUser:branches:openEuler:Mainline
  5. 修改提交成功后,OBS会自动开始编译软件包。可以通过如下命令,查看对应仓库的编译日志,其中 standard_aarch64aarch64 分别为查询所得仓库名称和架构。

    1. $ osc buildlog standard_aarch64 aarch64

    构建RPM包 - 图15 说明:
    用户也可以通过网页端打开自己创建的对应工程,查看构建日志。

新增软件包

使用OBS的osc工具新增一个全新软件包的操作方法如下:

创建工程

  1. 根据新增软件包的依赖情况,基于合适的工程,创建属于用户自己的个人工程。例如基于 openEuler:Mainline 工程的 zlib 创建工程的参考命令如下,zlib 为工程下的任一软件包。

    1. $ osc branch openEuler:Mainline zlib
  2. 删除创建工程时新增的无用软件包。例如删除zlib软件包的参考命令如下:

    1. $ cd home:testUser:branches:openEuler:Mainline
    2. $ osc rm zlib
    3. $ osc commit -m "commit log"
  3. 在个人工程下创建新增的软件包。例如新增软件包 my-first-obs-package命令如下:

    1. $ mkdir my-first-obs-package
    2. $ cd my-first-obs-package

构建RPM包

  1. 添加准备的源文件和spec文件到软件包目录。
  2. 修改源代码和spec文件,并将对应软件包的所有文件上传到OBS服务器。参考命令如下,-m 参数后的信息为提交记录。

    1. $ cd home:testUser:branches:openEuler:Mainline
    2. $ osc add my-first-obs-package
    3. $ osc ci -m "commit log"
  3. 获取当前工程的仓库名称和架构,参考命令如下:

    1. $ osc repos home:testUser:branches:openEuler:Mainline
  4. 修改提交成功后,OBS会自动开始编译软件包。可以通过如下命令,查看对应仓库的编译日志,其中 standard_aarch64aarch64 分别为查询所得仓库名称和架构。

    1. $ cd home:testUser:branches:openEuler:Mainline/my-first-obs-package
    2. $ osc buildlog standard_aarch64 aarch64

    构建RPM包 - 图16 说明:
    用户也可以通过网页端打开自己创建的对应工程,查看构建日志。

获取软件包

RPM软件包构建完成后,使用osc获取对应RPM软件包的命令如下:

  1. $ osc getbinaries home:testUser:branches:openEuler:Mainline my-first-obs-package standard_aarch64 aarch64

命令中的各参数含义如下,请用户根据实际情况修改:

  • home:testUser:branches:openEuler:Mainline :软件包所在工程名称
  • my-first-obs-package :软件包名称
  • standard_aarch64 :仓库名称
  • aarch64 :仓库架构名称

构建RPM包 - 图17 说明:
使用osc构建的软件包也可以在网页端获取,获取方式请参见获取软件包