From Fedora Project Wiki
No edit summary
No edit summary
Line 2: Line 2:


'''如何创建一个GNU Hello RPM包'''
'''如何创建一个GNU Hello RPM包'''
这是一个简短的实际动手编写RPM文件的教程, 展示了如何快速创建一个简单的源代码包和二进制软件包。这里假设您已对使用预编译的 RPM 软件包以及 FOSS (Free and Open Source Software:自由及开源软件) 的编译过程有所了解。
 
这是一个编写 RPM 文件的简明教程, 展示了如何快速创建一个简单的源代码包和二进制软件包。这里假设您已对使用预编译的 RPM 软件包以及 FOSS (Free and Open Source Software:自由及开源软件) 的编译过程有所了解。


关于如何创建 RPM 文件的完整信息(包括更详细的技巧),请参考[[How to create an RPM package]]。如果你打算为 Fedora 仓库创建一个 RPM 包,请按照[[Join the package collection maintainers|How to join the Fedora Package Collection Maintainers]]中的流程,并遵照各种 Fedora 的指南。
关于如何创建 RPM 文件的完整信息(包括更详细的技巧),请参考[[How to create an RPM package]]。如果你打算为 Fedora 仓库创建一个 RPM 包,请按照[[Join the package collection maintainers|How to join the Fedora Package Collection Maintainers]]中的流程,并遵照各种 Fedora 的指南。


本教程演示了 GNU“Hello World” 项目的打包过程。虽然用 C 语言程序打印 “Hello World” 到标准输出是小菜一碟,但 GNU 版本包含了与一个典型的 FOSS 软件项目相关的最常用的外围组件,包括配置/编译/安装环境、文档、国际化等等。然而 GNU 版本包含了一个传统的由源代码和 configure/make 脚本组成的 tar 文件,但并不包括打包信息。因此,这是一个很好的实践 RPM 包编译的样本。
本教程演示了 GNU“Hello World” 项目的打包过程。虽然用 C 语言程序打印 “Hello World” 到标准输出是小菜一碟,但 GNU 版本包含了与一个典型的 FOSS 软件项目相关的最常用的外围组件,包括配置/编译/安装环境、文档、国际化等等。GNU 版本包含了一个由源代码和 configure/make 脚本组成的 tar 文件,但并不包含打包信息。因此,这是一个很好的 RPM 包打包示例。


== 开发环境 ==
== 开发环境 ==


为了创建 RPM 包,我们需要一套开发工具。这仅需要配置一次即可,包括使用系统管理员(<code>root</code>) 账户运行的那些命令:
为了创建 RPM 包,我们需要一套开发工具。开发环境仅需要配置一次即可,使用系统管理员 (<code>root</code>) 账户运行以下命令:


<pre>
<pre>
# yum install @development-tools
# dnf install @development-tools
# yum install fedora-packager
# dnf install fedora-packager
</pre>
</pre>


如果您想在一个干净的 chroot 中测试编译过程,您必须将您的非特权账户配置为'mock'用户组的成员:
如果您想在一个干净的 chroot 环境中测试编译过程,您需要将您的非特权账户配置为 'mock' 用户组的成员:


<pre>
<pre>
# usermod -a -G mock <your username>
# usermod -a -G mock <用户名>
</pre>
</pre>


以上是仅有的需要 <code>root</code> 权限的命令。 所有剩下的工作应该使用您正常的、非特权账户完成,或甚至使用一个仅为开发工作而创建的隔离账户。现代基于 RPM 的系统中,包括 Fedora,都纯粹在一个非特权账户中搭建以编译和测试 RPM 包。 命令
只有以上命令需要 <code>root</code> 权限。所有其他操作应使用'''非特权'''账户完成,甚至可为开发工作创建专用的隔离账户。现代基于 RPM 的系统中,包括 Fedora,都在一个非特权账户中编译和测试 RPM 包。执行以下命令


<pre>$ rpmdev-setuptree</pre>
<pre>
$ rpmdev-setuptree
</pre>


会在你的 <code>~/rpmbuild</code> 目录下创建一个 RPM 编译区。 这个目录包含若干子目录,用于保存项目源码、RPM 配置文件以及最终的源码和二进制包。
将会创建 <code>~/rpmbuild</code> 目录作为 RPM 编译目录。此目录包含若干子目录,用于保存项目源码、RPM 配置文件以及最终的源码和二进制包。


== 创建 "Hello World" RPM 包 ==
== 创建 "Hello World" RPM 包 ==


我们打包所需要的项目源码通常称为'upstream'源。我们将从项目网站上将它下载到
我们打包所需的项目源码通常称为 'upstream' 源。我们将从项目网站上,将源码下载到
 
  <code>~/rpmbuild/SOURCE</code>
  <code>~/rpmbuild/SOURCE</code>
目录中。 我们获得的是一个压缩的 tarball 文件, 这正是大多数 FOSS 项目首选发布形式。
 
目录中。我们获得了一个 tarball 压缩文件, 这正是大多数 FOSS 项目首选发布形式。


<pre>
<pre>
Line 40: Line 45:
</pre>
</pre>


RPM 包是通过 <code>.spec</code> 文件配置的。这里我们将在合适的目录下创建一个模板文件 <code> hello.spec</code> :
RPM 包通过 <code>.spec</code> 文件进行配置。在 <code>~/rpmbuild/SPECS</code> 目录创建模板文件 <code>hello.spec</code>


<pre>
<pre>
Line 47: Line 52:
</pre>
</pre>


<code>Emacs</code> 和 <code>vi</code> 的最新版本有.spec 文件编辑模式,它也会在创建新文件的时候打开一个类似的模板。所以你可以仅使用以下示例中的命令来自动使用模板。
<code>Emacs</code> 和 <code>vi</code> 的最新版本有 .spec 文件编辑模式,它会在创建新文件时打开一个类似的模板。所以可使用以下命令来自动使用模板文件。


<pre>
<pre>
vi hello.spec
$ vi hello.spec
</pre>
</pre>


=== 深入 <code>.spec</code> 文件 ===
=== 深入 <code>.spec</code> 文件 ===


在我们 <code>.spec</code> 文件中的域需要稍微编辑一下。 详情请参考 [[How_to_create_an_RPM_package#Spec_file_pieces_explained|Fedora rules]]。在本例的情况下,文件可以是如下所示:
在我们 <code>.spec</code> 文件中的字段需要稍作修改。字段说明请参考 [[How_to_create_an_RPM_package#Spec_file_pieces_explained|Fedora rules]]。在本例中,文件如下所示:


<pre>
<pre>
Name:    hello
Name:    hello
Version:  2.8
Version:  2.8
Release:  1
Release:  1%{?dist}
Summary:  The "Hello World" program from GNU
Summary:  The "Hello World" program from GNU
Summary(zh_CN):  GNU "Hello World" 程序
License:  GPLv3+
License:  GPLv3+
URL:      http://ftp.gnu.org/gnu/hello     
URL:      http://ftp.gnu.org/gnu/hello     
Line 69: Line 75:
The "Hello World" program, done with all bells and whistles of a proper FOSS  
The "Hello World" program, done with all bells and whistles of a proper FOSS  
project, including configuration, build, internationalization, help files, etc.
project, including configuration, build, internationalization, help files, etc.
%description -l zh_CN
"Hello World" 程序, 包含 FOSS 项目所需的所有部分, 包括配置, 构建, 国际化, 帮助文件等.


%changelog
%changelog
Line 75: Line 84:
</pre>
</pre>


<code>Version</code> 域应该和'upstream'源的版本号一致,而 <code> Release</code> 则是我们在 Fedora 中使用的编号。
* <code>Version</code> 标签应与 'upstream' 源的版本号一致,而 <code>Release</code> 是在 Fedora 中使用的发布编号。


<code> Summary</code> 域中的第一个字母应该大写,以避免 rpmlint 工具的抱怨。
* <code>Summary</code> 标签的第一个字母应大写,以避免 rpmlint 工具警告。


It is your responsibility to check the <code>License</code> status of the software, by inspecting the source files and/or their LICENSE files, and/or by talking to the authors.
* 审查软件的 <code>License</code> 状态是打包者的职责,这可以通过检查源码或 LICENSE 文件,或与作者沟通来完成。
检查软件的 <code>License</code> 状态是你的职责,这可以通过检查源码文件和/或他们的 LICENSE 文件,和/或通过和其作者沟通。


<code> Group </code> 标签历史上是用于 classify the package in accordance to the list in <code>/usr/share/doc/rpm-<version>/GROUPS</code>. It is being phased out so you will not see it added by default. However, it doesn't hurt to add it anyway.
* <code>Group</code> 标签过去用于按照 <code>/usr/share/doc/rpm-<version>/GROUPS</code> 分类软件包。目前该标记已丢弃,默认不会添加。添加该标记也不会有任何影响。


The <code> %changelog</code> should document the work on preparing the RPM, especially if there are security and bug patches included on top of the base upstream source.  Changelog data can be displayed by <code>rpm --changelog -q <packagename></code>, which is very useful for instance to find out if specific bug and security patches were included in the installed software, thanks to diligent Fedora packagers who include this info with the relevant [http://cve.mitre.org/ CVE] numbers.
* <code>%changelog</code> 标签应包含每个 Release 所做的更改日志,尤其应包含上游的安全/漏洞补丁的说明。Changelog 日志可使用 <code>rpm --changelog -q <packagename></code> 查询,通过查询可得知已安装的软件是否包含指定漏洞和安全补丁,感谢勤劳的 Fedora 打包者添加相关 [http://cve.mitre.org/ CVE] 编号。


The changelog entry should include the version string to avoid rpmlint complaints.
* <code>%changelog</code> 条目应包含版本字符串,以避免 rpmlint 工具警告。


Multi-line sections like <code> %changelog</code> or <code> %description</code> start on a line under the directive, and end with an empty line.
* 多行的部分,如 <code>%changelog</code> <code>%description</code> 由指令下一行开始,空行结束。


Lines which aren't needed (e.g. <code>BuildRequires</code> and <code>Requires</code>) can be commented out with a hash ('#') for now.
* 一些不需要的行 (<code>BuildRequires</code> <code>Requires</code>) 可使用 '#' 注释。


Many lines in the template don't need to be changed at all in many cases, at least for the initial attempt.
* 多数情况下,模板中的许多行不需要修改。


=== 创建 RPM 软件包 ===
=== 构建 RPM 软件包 ===


我们现在可以尝试第一次执行创建命令,以创建源码、二进制和包含调试信息的软件包:
现在,我们可以尝试执行以下命令,以构建源码、二进制和包含调试信息的软件包:


<pre>
<pre>
Line 102: Line 110:
</pre>
</pre>


这次的运行中系统将会抱怨并列出未打包的文件。例如那些需要安装在系统中,但又没有声明其属于这个包的文件。 我们必须在 <code>%files</code> 中声明它们。但不要使用形如 <code>/usr/bin/</code> 的硬编码, 而应该使用类似 <code>%{_bindir}/hello</code> 这样的宏来替代。
命令执行后,提示并列出未打包的文件,例如那些需要安装在系统中,但又未在 <code>.spec</code> 中声明的文件。我们需要在 <code>%files</code> 中声明它们。注意不要使用形如 <code>/usr/bin/</code> 的硬编码, 应使用类似 <code>%{_bindir}/hello</code> 这样的宏来替代。手册页应在 <code>%doc</code> 中声明 : <code>%doc %{_mandir}/man1/hello.1.gz</code>
 
手册页应该在 <code>%doc</code> 中声明 : <code>%doc %{_mandir}/man1/hello.1.gz</code>.


这是一个反复的过程:编辑 <code>.spec</code> 文件后,重新运行 <code>rpmbuild</code>。
这是一个反复的过程:编辑 <code>.spec</code> 文件后,重新运行 <code>rpmbuild</code>。


由于我们的程序使用了翻译和国际化,我们会看到很多未未声明的 i18 文件。
由于我们的程序使用了翻译和国际化,因此会看到很多未声明的 i18 文件。
The [[Packaging:Guidelines#Handling_Locale_Files|recommended method]] to declare them is:
使用 [[Packaging:Guidelines#Handling_Locale_Files|推荐方法]] 来声明它们:


* find the filenames in the <code>%install</code> step: <code> %find_lang %{name}</code>
* 查找 <code>%install</code> 中的语言文件: <code>%find_lang %{name}</code>
* add the required build dependencies: <code>BuildRequires: gettext</code>
* 添加编译依赖: <code>BuildRequires: gettext</code>
* use the found filenames <code>%files -f %{name}.lang</code>
* 声明找到的文件: <code>%files -f %{name}.lang</code>


如果程序使用 GNU info 文件, you need to make sure the installation and uninstallation
如果程序使用 GNU info 文件,你需要确保安装和卸载软件包,不影响系统中的其他软件,按以下步骤操作:
of the package does not interfere with other software on the system, by using this boilerplate:


* delete the 'dir' file in %install:  <code>rm -f %{buildroot}/%{_infodir}/dir</code>
* 在 %install 中添加删除 'dir' 文件的命令: <code>rm -f %{buildroot}/%{_infodir}/dir</code>
* <code>Requires(post): info</code> and <code>Requires(preun): info</code>
* 添加 <code>Requires(post): info</code> <code>Requires(preun): info</code>
* add those steps:
* 添加以下安装脚本:
<pre>
<pre>
%post
%post
Line 131: Line 136:
</pre>
</pre>


=== 一个完整的 <code>hello.spec</code> 文件 ===
=== 完整的 <code>hello.spec</code> 文件 ===


Here's the initial version of <code>hello.spec</code>:
以下为初始版本的 <code>hello.spec</code> 文件:


<pre>
<pre>
Line 140: Line 145:
Release:        1%{?dist}
Release:        1%{?dist}
Summary:        The "Hello World" program from GNU
Summary:        The "Hello World" program from GNU
Summary(zh_CN): GNU "Hello World" 程序


License:        GPLv3+
License:        GPLv3+
Line 153: Line 159:
The "Hello World" program, done with all bells and whistles of a proper FOSS  
The "Hello World" program, done with all bells and whistles of a proper FOSS  
project, including configuration, build, internationalization, help files, etc.
project, including configuration, build, internationalization, help files, etc.
%description -l zh_CN
"Hello World" 程序, 包含 FOSS 项目所需的所有部分, 包括配置, 构建, 国际化, 帮助文件等.


%prep
%prep
%setup -q
%autosetup


%build
%build
Line 185: Line 194:
</pre>
</pre>


With this spec file, you should be able to successfully complete the build process, and create the source and binary RPM packages.
有了 spec 文件,你应该能够顺利完成构建过程,并创建源码和二进制 RPM 包。


Next you should check检查 them for conformance with RPM design rules设计规则, by running <code>rpmlint</code> on the spec file and all RPMs:
接下来,使用 <code>rpmlint</code> 工具检查 spec 文件和所有 RPM 包是否符合 RPM 设计规范:


<pre>
<pre>
Line 193: Line 202:
</pre>
</pre>


If there are no warnings or errors, we've succeeded. Otherwise,  append the error messages to the <code>rpmlint -i</code> command to see a more verbose description of the <code>rpmlint</code> diagnostics.
如果没有警告或错误,我们就成功了。否则,请使用 <code>rpmlint -i</code> 或 <code>rpmlint -I <error_code></code> 命令查看更详细的 <code>rpmlint</code> 诊断说明。


=== 模拟编译The <code>mock</code> builds ===
=== 使用 <code>mock</code> 构建 RPMs ===


To check that the package build will succeed in the Fedora restricted build environment, check it with mock.
使用 <code>mock</code> 在 Fedora 严格的构建环境中,测试构建软件包。默认 <code>mock</code> 将构建针对 Rawhide (Fedora 开发分支) 的软件包。


<pre>
<pre>
$ mock -r fedora-17-i386 --rebuild ../SRPMS/hello-2.8-1.fc17.src.rpm
$ mock --verbose -r fedora-22-i386 --rebuild ../SRPMS/hello-2.8-1.fc22.src.rpm
</pre>
</pre>
其中:


* fedora-17-i386:构建的环境
mock 选项:
* ../SRPMS/hello-2.8-1.fc17.src.rpm :生成的源代码包
 
* --rebuild:重建指定 SRPM
* --buildsrpm:从 spec (--spec ...) 和源码 (--sources ...) 或从 SCM 构建 SRPM
* --shell:在 chroot 内以交互方式运行指定命令。默认命令:/bin/sh
* --chroot:在 chroot 内以非交互方式运行指定命令
* --clean:彻底删除指定 chroot
* --scrub=[all|chroot|cache|root-cache|c-cache|yum-cache]:彻底删除指定 chroot 或缓存目录或所有 chroot 和缓存
* --init:初始化 chroot,不执行构建
* --installdeps:为指定 SRPM 安装编译依赖
* -i, --install:使用包管理安装软件包
* --update:使用包管理更新已安装软件包
* --remove:使用包管理删除软件包
* --orphanskill:杀死指定 buildroot 的所有进程
* --copyin:向指定 chroot 拷贝文件
* --copyout:从指定 chroot 拷贝文件
* --pm-cmd:执行包管理命令 (yum 或 dnf)
* --yum-cmd:执行 yum 包管理命令
* --dnf-cmd:执行 dnf 包管理命令
* --snapshot:使用指定名称创建 LVM 快照
* --remove-snapshot:删除指定 LVM 快照
* --rollback-to:回滚到指定快照
* --umount:卸载 buildroot,如果它已从独立设备挂载 (LVM)
* --mount:挂载 buildroot,如果它已从独立设备挂载 (LVM)
* -r CONFIG, --root=CONFIG:chroot 配置文件名或路径。路径以 .cfg 结尾,否则查找配置目录。默认:default
* --offline:激活 'offline' 模式
* -n, --no-clean:构建前不清理 chroot
* --cleanup-after:Clean chroot after building. Use with --resultdir. Only active for 'rebuild'.
* --no-cleanup-after:Dont clean chroot after building. If automatic cleanup is enabled, use this to disable.
* --cache-alterations:Rebuild the root cache after making alterations to the chroot (i.e. --install). Only useful when using tmpfs plugin.
* --nocheck:pass --nocheck to rpmbuild to skip 'make check' tests
* --arch=ARCH:Sets kernel personality().
* --target=RPMBUILD_ARCH:passed to rpmbuild as --target
* -D 'MACRO EXPR', --define='MACRO EXPR':定义 rpm 宏 (可多次指定)
* --macro-file=MACROFILE:使用预定义 rpm 宏文件
* --with=option:为构建启用配置选项 (可多次指定)
* --without=option:为构建禁用配置选项 (可多次指定)
* --resultdir=RESULTDIR:保存构建结果的目录
* --rootdir=ROOTDIR:chroot 根目录
* --uniqueext=UNIQUEEXT:Arbitrary, unique extension to append to buildroot directory name
* --configdir=CONFIGDIR:Change where config files are found
* --rpmbuild_timeout=RPMBUILD_TIMEOUT:Fail build if rpmbuild takes longer than 'timeout' seconds
* --unpriv:Drop privileges before running command when using --chroot
* --cwd=DIR:Change to the specified directory (relative to the chroot) before running command when using --chroot
* --spec=SPEC:指定 spec 文件用于构建 SRPM (仅用于 --buildsrpm)
* --sources=SOURCES:指定源 (文件/目录) 用于构建 SRPM (仅用于 --buildsrpm)
* --symlink-dereference:跟随软链接 (仅用于 --buildsrpm)
* --short-circuit=SHORT_CIRCUIT:Pass short-circuit option to rpmbuild to skip already complete stages. Warning: produced packages are unusable. Implies --no-clean. Valid options: build, install, binary
* --rpmbuild-opts=RPMBUILD_OPTS:指定额外 rpmbuild 选项
* --enablerepo=[repo]:使用 enablerepo 选项启用源
* --disablerepo=[repo]:使用 disablerepo 选项禁用源
* --old-chroot:使用旧 chroot 代替 systemd-nspawn
* --new-chroot:使用新 chroot (systemd-nspawn)
* --postinstall:在构建后,尝试在同一个 buildroot 安装软件包
* -v, --verbose:详细信息
* -q, --quiet:安静模式
* --trace:启用内部 mock 跟踪输出
* --enable-plugin=ENABLED_PLUGINS:启用插件。可用插件: ['tmpfs', 'root_cache', 'yum_cache', 'bind_mount', 'ccache', 'selinux', 'package_state', 'chroot_scan', 'lvm_root', 'compress_logs', 'sign', 'pm_request']
* --disable-plugin=DISABLED_PLUGINS:禁用插件。可用插件: ['tmpfs', 'root_cache', 'yum_cache', 'bind_mount', 'ccache', 'selinux', 'package_state', 'chroot_scan', 'lvm_root', 'compress_logs', 'sign', 'pm_request']
* --plugin-option=PLUGIN:KEY=VALUE:定义插件选项 (可多次指定)
* -p, --print-root-path:打印 chroot 根路径
* -l, --list-snapshots:列出 LVM 快照与 buildroot 的关联
* --scm-enable:从 SCM 库构建
* --scm-option=SCM_OPTS:定义 SCM 选项 (可多次指定)
* --yum:使用 yum 包管理
* --dnf:使用 dnf 包管理


== 参考文献 ==
== 参考文献 ==


* https://fedoraproject.org/wiki/How_to_create_an_RPM_package
* [[How to create an RPM package]]
* https://fedoraproject.org/wiki/Building_RPM_packages_%2820090405%29
* [[Building RPM packages (20090405)]]
* https://fedoraproject.org/wiki/Using_Mock_to_test_package_builds
* [[Using Mock to test package builds]]
* https://fedoraproject.org/wiki/Using_the_Koji_build_system
* [[Using the Koji build system]]


== 修改历史 ==
== 修改历史 ==

Revision as of 14:24, 29 June 2015

如何创建一个GNU Hello RPM包

这是一个编写 RPM 文件的简明教程, 展示了如何快速创建一个简单的源代码包和二进制软件包。这里假设您已对使用预编译的 RPM 软件包以及 FOSS (Free and Open Source Software:自由及开源软件) 的编译过程有所了解。

关于如何创建 RPM 文件的完整信息(包括更详细的技巧),请参考How to create an RPM package。如果你打算为 Fedora 仓库创建一个 RPM 包,请按照How to join the Fedora Package Collection Maintainers中的流程,并遵照各种 Fedora 的指南。

本教程演示了 GNU“Hello World” 项目的打包过程。虽然用 C 语言程序打印 “Hello World” 到标准输出是小菜一碟,但 GNU 版本包含了与一个典型的 FOSS 软件项目相关的最常用的外围组件,包括配置/编译/安装环境、文档、国际化等等。GNU 版本包含了一个由源代码和 configure/make 脚本组成的 tar 文件,但并不包含打包信息。因此,这是一个很好的 RPM 包打包示例。

开发环境

为了创建 RPM 包,我们需要一套开发工具。开发环境仅需要配置一次即可,使用系统管理员 (root) 账户运行以下命令:

# dnf install @development-tools
# dnf install fedora-packager

如果您想在一个干净的 chroot 环境中测试编译过程,您需要将您的非特权账户配置为 'mock' 用户组的成员:

# usermod -a -G mock <用户名>

只有以上命令需要 root 权限。所有其他操作应使用非特权账户完成,甚至可为开发工作创建专用的隔离账户。现代基于 RPM 的系统中,包括 Fedora,都在一个非特权账户中编译和测试 RPM 包。执行以下命令

$ rpmdev-setuptree

将会创建 ~/rpmbuild 目录作为 RPM 编译目录。此目录包含若干子目录,用于保存项目源码、RPM 配置文件以及最终的源码和二进制包。

创建 "Hello World" RPM 包

我们打包所需的项目源码通常称为 'upstream' 源。我们将从项目网站上,将源码下载到

~/rpmbuild/SOURCE

目录中。我们获得了一个 tarball 压缩文件, 这正是大多数 FOSS 项目首选发布形式。

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

RPM 包通过 .spec 文件进行配置。在 ~/rpmbuild/SPECS 目录创建模板文件 hello.spec

$ cd ~/rpmbuild/SPECS
$ rpmdev-newspec hello

Emacsvi 的最新版本有 .spec 文件编辑模式,它会在创建新文件时打开一个类似的模板。所以可使用以下命令来自动使用模板文件。

$ vi hello.spec

深入 .spec 文件

在我们 .spec 文件中的字段需要稍作修改。字段说明请参考 Fedora rules。在本例中,文件如下所示:

Name:     hello
Version:  2.8
Release:  1%{?dist}
Summary:  The "Hello World" program from GNU
Summary(zh_CN):  GNU "Hello World" 程序
License:  GPLv3+
URL:      http://ftp.gnu.org/gnu/hello    
Source0:  http://ftp.gnu.org/gnu/hello/hello-2.8.tar.gz

%description
The "Hello World" program, done with all bells and whistles of a proper FOSS 
project, including configuration, build, internationalization, help files, etc.

%description -l zh_CN
"Hello World" 程序, 包含 FOSS 项目所需的所有部分, 包括配置, 构建, 国际化, 帮助文件等.

%changelog
* Thu Jul 07 2011 The Coon of Ty <Ty@coon.org> - 2.8-1
- Initial version of the package
  • Version 标签应与 'upstream' 源的版本号一致,而 Release 是在 Fedora 中使用的发布编号。
  • Summary 标签的第一个字母应大写,以避免 rpmlint 工具警告。
  • 审查软件的 License 状态是打包者的职责,这可以通过检查源码或 LICENSE 文件,或与作者沟通来完成。
  • Group 标签过去用于按照 /usr/share/doc/rpm-<version>/GROUPS 分类软件包。目前该标记已丢弃,默认不会添加。添加该标记也不会有任何影响。
  • %changelog 标签应包含每个 Release 所做的更改日志,尤其应包含上游的安全/漏洞补丁的说明。Changelog 日志可使用 rpm --changelog -q <packagename> 查询,通过查询可得知已安装的软件是否包含指定漏洞和安全补丁,感谢勤劳的 Fedora 打包者添加相关 CVE 编号。
  • %changelog 条目应包含版本字符串,以避免 rpmlint 工具警告。
  • 多行的部分,如 %changelog%description 由指令下一行开始,空行结束。
  • 一些不需要的行 (如 BuildRequiresRequires) 可使用 '#' 注释。
  • 多数情况下,模板中的许多行不需要修改。

构建 RPM 软件包

现在,我们可以尝试执行以下命令,以构建源码、二进制和包含调试信息的软件包:

$ rpmbuild -ba hello.spec

命令执行后,提示并列出未打包的文件,例如那些需要安装在系统中,但又未在 .spec 中声明的文件。我们需要在 %files 中声明它们。注意不要使用形如 /usr/bin/ 的硬编码, 应使用类似 %{_bindir}/hello 这样的宏来替代。手册页应在 %doc 中声明 : %doc %{_mandir}/man1/hello.1.gz

这是一个反复的过程:编辑 .spec 文件后,重新运行 rpmbuild

由于我们的程序使用了翻译和国际化,因此会看到很多未声明的 i18 文件。 使用 推荐方法 来声明它们:

  • 查找 %install 中的语言文件: %find_lang %{name}
  • 添加编译依赖: BuildRequires: gettext
  • 声明找到的文件: %files -f %{name}.lang

如果程序使用 GNU info 文件,你需要确保安装和卸载软件包,不影响系统中的其他软件,按以下步骤操作:

  • 在 %install 中添加删除 'dir' 文件的命令: rm -f %{buildroot}/%{_infodir}/dir
  • 添加 Requires(post): infoRequires(preun): info
  • 添加以下安装脚本:
%post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :

%preun
if [ $1 = 0 ] ; then
/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi

完整的 hello.spec 文件

以下为初始版本的 hello.spec 文件:

Name:           hello
Version:        2.8
Release:        1%{?dist}
Summary:        The "Hello World" program from GNU
Summary(zh_CN): GNU "Hello World" 程序

License:        GPLv3+
URL:            http://ftp.gnu.org/gnu/%{name}
Source0:        http://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.gz

BuildRequires: gettext
      
Requires(post): info
Requires(preun): info

%description 
The "Hello World" program, done with all bells and whistles of a proper FOSS 
project, including configuration, build, internationalization, help files, etc.

%description -l zh_CN
"Hello World" 程序, 包含 FOSS 项目所需的所有部分, 包括配置, 构建, 国际化, 帮助文件等.

%prep
%autosetup

%build
%configure
make %{?_smp_mflags}

%install
%make_install
%find_lang %{name}
rm -f %{buildroot}/%{_infodir}/dir

%post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :

%preun
if [ $1 = 0 ] ; then
/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi

%files -f %{name}.lang
%doc AUTHORS ChangeLog COPYING NEWS README THANKS TODO
%{_mandir}/man1/hello.1.gz
%{_infodir}/%{name}.info.gz
%{_bindir}/hello

%changelog
* Tue Sep 06 2011 The Coon of Ty <Ty@coon.org> 2.8-1
- Initial version of the package

有了 spec 文件,你应该能够顺利完成构建过程,并创建源码和二进制 RPM 包。

接下来,使用 rpmlint 工具检查 spec 文件和所有 RPM 包是否符合 RPM 设计规范:

$ rpmlint hello.spec ../SRPMS/hello* ../RPMS/*/hello*

如果没有警告或错误,我们就成功了。否则,请使用 rpmlint -irpmlint -I <error_code> 命令查看更详细的 rpmlint 诊断说明。

使用 mock 构建 RPMs

使用 mock 在 Fedora 严格的构建环境中,测试构建软件包。默认 mock 将构建针对 Rawhide (Fedora 开发分支) 的软件包。

$ mock --verbose -r fedora-22-i386 --rebuild ../SRPMS/hello-2.8-1.fc22.src.rpm

mock 选项:

  • --rebuild:重建指定 SRPM
  • --buildsrpm:从 spec (--spec ...) 和源码 (--sources ...) 或从 SCM 构建 SRPM
  • --shell:在 chroot 内以交互方式运行指定命令。默认命令:/bin/sh
  • --chroot:在 chroot 内以非交互方式运行指定命令
  • --clean:彻底删除指定 chroot
  • --scrub=[all|chroot|cache|root-cache|c-cache|yum-cache]:彻底删除指定 chroot 或缓存目录或所有 chroot 和缓存
  • --init:初始化 chroot,不执行构建
  • --installdeps:为指定 SRPM 安装编译依赖
  • -i, --install:使用包管理安装软件包
  • --update:使用包管理更新已安装软件包
  • --remove:使用包管理删除软件包
  • --orphanskill:杀死指定 buildroot 的所有进程
  • --copyin:向指定 chroot 拷贝文件
  • --copyout:从指定 chroot 拷贝文件
  • --pm-cmd:执行包管理命令 (yum 或 dnf)
  • --yum-cmd:执行 yum 包管理命令
  • --dnf-cmd:执行 dnf 包管理命令
  • --snapshot:使用指定名称创建 LVM 快照
  • --remove-snapshot:删除指定 LVM 快照
  • --rollback-to:回滚到指定快照
  • --umount:卸载 buildroot,如果它已从独立设备挂载 (LVM)
  • --mount:挂载 buildroot,如果它已从独立设备挂载 (LVM)
  • -r CONFIG, --root=CONFIG:chroot 配置文件名或路径。路径以 .cfg 结尾,否则查找配置目录。默认:default
  • --offline:激活 'offline' 模式
  • -n, --no-clean:构建前不清理 chroot
  • --cleanup-after:Clean chroot after building. Use with --resultdir. Only active for 'rebuild'.
  • --no-cleanup-after:Dont clean chroot after building. If automatic cleanup is enabled, use this to disable.
  • --cache-alterations:Rebuild the root cache after making alterations to the chroot (i.e. --install). Only useful when using tmpfs plugin.
  • --nocheck:pass --nocheck to rpmbuild to skip 'make check' tests
  • --arch=ARCH:Sets kernel personality().
  • --target=RPMBUILD_ARCH:passed to rpmbuild as --target
  • -D 'MACRO EXPR', --define='MACRO EXPR':定义 rpm 宏 (可多次指定)
  • --macro-file=MACROFILE:使用预定义 rpm 宏文件
  • --with=option:为构建启用配置选项 (可多次指定)
  • --without=option:为构建禁用配置选项 (可多次指定)
  • --resultdir=RESULTDIR:保存构建结果的目录
  • --rootdir=ROOTDIR:chroot 根目录
  • --uniqueext=UNIQUEEXT:Arbitrary, unique extension to append to buildroot directory name
  • --configdir=CONFIGDIR:Change where config files are found
  • --rpmbuild_timeout=RPMBUILD_TIMEOUT:Fail build if rpmbuild takes longer than 'timeout' seconds
  • --unpriv:Drop privileges before running command when using --chroot
  • --cwd=DIR:Change to the specified directory (relative to the chroot) before running command when using --chroot
  • --spec=SPEC:指定 spec 文件用于构建 SRPM (仅用于 --buildsrpm)
  • --sources=SOURCES:指定源 (文件/目录) 用于构建 SRPM (仅用于 --buildsrpm)
  • --symlink-dereference:跟随软链接 (仅用于 --buildsrpm)
  • --short-circuit=SHORT_CIRCUIT:Pass short-circuit option to rpmbuild to skip already complete stages. Warning: produced packages are unusable. Implies --no-clean. Valid options: build, install, binary
  • --rpmbuild-opts=RPMBUILD_OPTS:指定额外 rpmbuild 选项
  • --enablerepo=[repo]:使用 enablerepo 选项启用源
  • --disablerepo=[repo]:使用 disablerepo 选项禁用源
  • --old-chroot:使用旧 chroot 代替 systemd-nspawn
  • --new-chroot:使用新 chroot (systemd-nspawn)
  • --postinstall:在构建后,尝试在同一个 buildroot 安装软件包
  • -v, --verbose:详细信息
  • -q, --quiet:安静模式
  • --trace:启用内部 mock 跟踪输出
  • --enable-plugin=ENABLED_PLUGINS:启用插件。可用插件: ['tmpfs', 'root_cache', 'yum_cache', 'bind_mount', 'ccache', 'selinux', 'package_state', 'chroot_scan', 'lvm_root', 'compress_logs', 'sign', 'pm_request']
  • --disable-plugin=DISABLED_PLUGINS:禁用插件。可用插件: ['tmpfs', 'root_cache', 'yum_cache', 'bind_mount', 'ccache', 'selinux', 'package_state', 'chroot_scan', 'lvm_root', 'compress_logs', 'sign', 'pm_request']
  • --plugin-option=PLUGIN:KEY=VALUE:定义插件选项 (可多次指定)
  • -p, --print-root-path:打印 chroot 根路径
  • -l, --list-snapshots:列出 LVM 快照与 buildroot 的关联
  • --scm-enable:从 SCM 库构建
  • --scm-option=SCM_OPTS:定义 SCM 选项 (可多次指定)
  • --yum:使用 yum 包管理
  • --dnf:使用 dnf 包管理

参考文献

修改历史

Przemek Klosowski wrote this tutorial when he worked through Christoph Wickert's IRC session on building RPMs using Rahul Sundaram suggestion of GNU "Hello World" as a test case. After he wrote up his experience, he found out about the excellent and extensive How to create an RPM package page on this wiki, as well as the Christian Lyder Jacobsen's website. However, Christian isn't planning to update his site, and it seemed that a 5-minute 'fast food' alternative to the more extensive article might suit some people. More in-depth information on using and building RPM packages is available from other sources.