From Fedora Project Wiki
No edit summary
Line 3: Line 3:
== 关于本指南 ==
== 关于本指南 ==


{{admon/tip|此页正在翻译,''翻译:[[User:Cicku| Christopher 'Cicku' Meng]]''|此页正进行由英语向中文的翻译,已完成 45%。由于源文档更新错乱,中间中文翻译有大量过时翻译,请他人暂时不要翻译本页!
本指南描述了如何为 Fedora 制作 RPM 包,特别是如何写 .spec 配置文件。不像其它文档,本文档会解释 Fedora 中特殊领域的打包(会有链接指向 Fedora 的特殊打包规定)。并且由于本文档通过 wiki 更新,因此会尽可能的保持最新。除了 Fedora,绝大部分内容也会适用其他基于 RPM 机制的发行版。如果您等不及了,您可以先看看[[How_to_create_a_GNU_Hello_RPM_package|如何创建一个 GNU Hello World 软件包]],这是一个创建 RPM 包的简短总结(不包含详细信息)。
您所看到的可能不是最终页面,请定期访问本页面以取得最新更新,如有发现问题请通知 [https://fedoraproject.org/wiki/User:Cicku Cicku]。}}
 
本指南讲述了如何为 Fedora 制作 RPM 包,特别是如何写 .spec 配置文件。不像其它文档,本文档会解释 Fedora 中特殊领域的打包(会有链接指向 Fedora 的特殊打包规定)。并且由于本文档通过 wiki 更新,因此会尽可能的保持最新。除了 Fedora,绝大部分内容也会适用其他基于 RPM 机制的发行版。如果您等不及了,您可以先看看[[How_to_create_a_GNU_Hello_RPM_package|如何创建一个 GNU Hello World 软件包]],这是一个创建 RPM 包的简短总结(但是没有很详细的信息)。


'''目前 Fedora 文档团队有一份草稿已经发布:
'''目前 Fedora 文档团队有一份草稿已经发布:
Line 18: Line 15:
* [[Packaging:DistTag|Dist 标签使用指南]]
* [[Packaging:DistTag|Dist 标签使用指南]]
* [[Packaging:ReviewGuidelines|软件包审核规定]]
* [[Packaging:ReviewGuidelines|软件包审核规定]]
* [[Packaging:ScriptletSnippets|SPEC 文件编写指南]]
* [[Packaging:ScriptletSnippets|SPEC 文件编写指南]] ([[How_to_create_an_RPM_package/zh-hk|zh_CN]])


'''[[Packaging:Guidelines|打包规定]] 和 [[Packaging:NamingGuidelines|软件包命名规定]] 是主要的两份规定,本指南与这两份规定100%兼容。'''
'''[[Packaging:Guidelines|打包规定]] 和 [[Packaging:NamingGuidelines|软件包命名规定]] 是主要的两份规定,本指南与这两份规定100%兼容。'''


如果您计划为 Fedora 的官方源创建一个 RPM 包,请按照 [[Join the package collection maintainers|如何成为 Fedora 软件包仓库维护人员]] 页面的步骤一步步来。
如果您计划为 Fedora 的官方源创建一个 RPM 包,请按照 [[Join the package collection maintainers/zh-cn|如何成为 Fedora 软件包仓库维护人员]] 页面的步骤一步步来。


== 前期准备 ==
== 准备系统 ==


在您为 Fedora 创建 RPM 包之前,您需要安装一些必须的开发组件并且设置好即将使用的账户:
在您为 Fedora 创建 RPM 包之前,您需要安装一些必须的开发工具并设置账户:


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


您可以新建一个临时用户以便创建 RPM 包。这样,如果有错误发生,构建程序不会破坏您的系统,比如造成文件损失或者您的私人文件/密钥被发送到互联网上。
您可以新建一个临时用户以便创建 RPM 包。这样,如果有错误发生,构建程序不会破坏您的系统,比如造成文件损失或您的私人文件/密钥被发送到互联网上。


{{admon/caution|切记!不要使用 <code>root</code> 用户来执行打包操作,因为这很危险,所有二进制文件都会在被打包前安装至系统中,因此您应该以普通用户身份打包以杜绝系统被破坏。}}
{{admon/caution|切记!不要使用 <code>root</code> 用户来执行打包操作。因为这十分危险,所有二进制文件都会在打包前安装至系统中,因此您应该以普通用户身份打包,以防止系统被破坏。}}


新建一个叫做<code>makerpm</code>的用户,添加至 'mock' 用户组,设置好密码并通过该用户登录:
创建名为 <code>makerpm</code> 的用户,添加至 'mock' 用户组,设置好密码并通过该用户登录:
   # /usr/sbin/useradd makerpm
   # /usr/sbin/useradd makerpm
   # usermod -a -G mock makerpm
   # usermod -a -G mock makerpm
   # passwd makerpm
   # passwd makerpm
然后您可以通过这个临时用户开始执行打包操作。
然后,您可以通过这个临时用户开始打包操作。


一旦登录了 makerpm 用户,您可以使用一下命令在这个用户的家目录下创建一个标准的打包工作目录结构:
一旦以 makerpm 用户登陆,使用以下命令在用户家目录下,创建标准的打包工作目录结构:
   $ rpmdev-setuptree
   $ rpmdev-setuptree


<code>rpmdev-setuptree</code> 程序新建一个位置是 <code>~/rpmbuild</code> 的目录。在 "rpmbuild" 目录下是一些预设好的子目录(比如 <code>SPECS</code> 和 <code>BUILD</code>),它们将会在创建软件包的时候起作用。
<code>rpmdev-setuptree</code> 程序将创建 <code>~/rpmbuild</code> 目录,以及一系列预设的子目录(如 <code>SPECS</code> 和 <code>BUILD</code>),你将使用它们作为打包目录。另外,还会创建 <code>~/.rpmmacros</code> 文件,它用于设置各种选项。
<code>rpmdev-setuptree</code> 也会创建一个位置是 <code>~/.rpmmacros</code> 的文件。它是一个保存有关设置的文件。


[[Packaging:Guidelines#Timestamps|打包指南推荐使用命令保存时间戳]];当然,您在使用 <code>wget</code> 或者 <code>curl</code> 获取软件源代码的时候就会自动保存。
[[Packaging:Guidelines#Timestamps|打包指南建议保留文件时间戳]];当然,您在使用 <code>wget</code> <code>curl</code> 获取软件源代码的时候就会自动保存。如果您使用 <code>wget</code> 来获取源代码,确保 <code>~/.wgetrc</code> 文件包含此行 <code>timestamping = on</code> 。如果您使用 <code>curl</code> ,确保 <code>~/.curlrc</code> 文件包含 <code>-R</code> 选项。
如果您使用 <code>wget</code> 来获取源代码,确保在 <code>~/.wgetrc</code> 文件加入一行 <code>timestamping = on</code> 。类似的,如果您使用 <code>curl</code> ,确保 <code>~/.curlrc</code> 文件包含 "-R"。


一旦设置完毕,通常不需要再次设置。
一旦设置完毕,通常不需要再次设置。
Line 54: Line 48:
== RPM 基础知识 ==
== RPM 基础知识 ==


若要构建一个标准的 RPM 包,您需要新建一个叫做 <code>.spec</code> 的文本文档,里面包含被打包软件的全部信息。然后您对这个文本文档在系统中执行 <code>rpmbuild</code> 命令,经过这一步,系统会按照步骤生成最终的 RPM 包。
若要构建一个标准的 RPM 包,您需要创建 <code>.spec</code> 文件,其中包含软件打包的全部信息。然后,对此文件执行 <code>rpmbuild</code> 命令,经过这一步,系统会按照步骤生成最终的 RPM 包。
 
一般情况,您应该把源代码包,比如由开发者发布的以 <code>.tar.gz</code> 结尾的文件,放入 <code>~/rpmbuild/SOURCES</code> 目录。将<code>.spec</code> 文件放入 <code>~/rpmbuild/SPECS</code> 目录,并命名为 "''软件包名''.spec" 。当然, ''软件包名'' 就是最终 RPM 包的名字。为了创建二进制(Binary RPM)和源码软件包(SRPM),您需要将目录切换至 <code>~/rpmbuild/SPECS</code> 并执行:


按理说您应该把源代码包,比如从软件开发者释放出的那种以 <code>.tar.gz</code> 结尾的文件, 放入 <code>~/rpmbuild/SOURCES</code> 目录下。您应该把 <code>.spec</code> 文件放入 <code>~/rpmbuild/SPECS</code> 目录下,并且把它命名为 "''软件包名字''.spec" 。当然, ''软件包名字'' 就是最终 RPM 包的名字。为了创建所有软件包(包括预编译好的包(Binary RPM)和源代码包(SRPM)),您需要把终端切换至 <code>~/rpmbuild/SPECS</code> 目录然后执行:
   $ rpmbuild -ba ''NAME''.spec
   $ rpmbuild -ba ''NAME''.spec


当您执行这一条命令时,<code>rpmbuild</code> 会自动读取 <code>.spec</code> 文件并尝试按照其中所设定的步骤完成构建。(以下表格中,以 <code>%</code> 开头的短语都是宏,每一个宏起到的作用已经详细列出):
当执行此命令时,<code>rpmbuild</code> 会自动读取 <code>.spec</code> 文件并按照下表列出的步骤完成构建。下表中,以 <code>%</code> 开头的语句为预定义宏,每个宏的作用如下:
{|border="1" cellspacing="0"  
 
||阶段 || 读取的目录 || 写入的目录 || 具体动作
{|border="1" cellspacing="0"
! 阶段 !! 读取的目录 !! 写入的目录 !! 具体动作
|-
|-
|<code>%prep</code>||<code>%_sourcedir</code>||<code>%_builddir</code>||本步读取位于 <code>%_sourcedir</code> 目录的源代码和 patch 。随后会解压源代码至 <code>%_builddir</code> 下的子目录并应用所有 patch。
|<code>%prep</code>||<code>%_sourcedir</code>||<code>%_builddir</code>||读取位于 <code>%_sourcedir</code> 目录的源代码和 patch 。之后,解压源代码至 <code>%_builddir</code> 的子目录并应用所有 patch。
|-
|-
|<code>%build</code>||<code>%_builddir</code>||<code>%_builddir</code>||本步编译位于 <code>%_builddir</code> 构建目录下的全部文件。本步类似于 "<code>./configure && make</code>" 命令。
|<code>%build</code>||<code>%_builddir</code>||<code>%_builddir</code>||编译位于 <code>%_builddir</code> 构建目录下的文件。通过执行类似 "<code>./configure && make</code>" 的命令实现。
|-
|-
|<code>%check</code>||<code>%_builddir</code>||<code>%_builddir</code>||本步检查软件是否正常运行。本步类似于 "<code>make test</code>" 命令。不过很多软件包都不需要这步。
|<code>%install</code>||<code>%_builddir</code>||<code>%_buildrootdir</code>||读取位于 <code>%_builddir</code> 构建目录下的文件并将其安装至 <code>%_buildrootdir</code> 目录。这些文件就是用户安装 RPM 后,最终得到的文件。注意一个奇怪的地方: ''最终安装目录'' '''不是''' ''构建目录''。通过执行类似 "<code>make install</code>" 的命令实现。
|-
|-
|<code>%install</code>||<code>%_builddir</code>||<code>%_buildrootdir</code>||本步读取位于 <code>%_builddir</code> 构建目录下的文件并把它们写入至 <code>%_buildrootdir</code> 最终安装目录。这些文件就是用户安装 RPM 后最终得到的文件。请注意一个奇怪的地方:  ''最终安装目录'' '''不是'''  ''构建目录'' !本步类似于 "<code>make install</code>" 命令。
|<code>%check</code>||<code>%_builddir</code>||<code>%_builddir</code>||检查软件是否正常运行。通过执行类似 "<code>make test</code>" 的命令实现。很多软件包都不需要此步。
|-
|-
|bin||<code>%_buildrootdir</code>||<code>%_rpmdir</code>||本步读取位于 <code>%_buildrootdir</code> 最终安装目录下的文件,以便最终在 <code>%_rpmdir</code> 目录下创建 RPM 包。在该目录下,针对不同架构制作的 RPM 包会分子目录放置,同时一个叫做 "noarch" 的目录放置针对所有架构均适用的 RPM 包。所有 RPM 就是用户最终安装的 RPM 包。
|<code>bin</code>||<code>%_buildrootdir</code>||<code>%_rpmdir</code>||读取位于 <code>%_buildrootdir</code> 最终安装目录下的文件,以便最终在 <code>%_rpmdir</code> 目录下创建 RPM 包。在该目录下,不同架构的 RPM 包会分别保存至不同子目录, "<code>noarch</code>" 目录保存适用于所有架构的 RPM 包。这些 RPM 文件就是用户最终安装的 RPM 包。
|-
|-
|src||<code>%_sourcedir</code>||<code>%_srcrpmdir</code>||本步创建源代码 RPM 包(简称 SRPM,通常以<code>.src.rpm</code>作为后缀名)。然后这些 SRPM 会被放在 <code>%_srcrpmdir</code> 目录下。SRPM 包通常被用于审核和升级软件包。
|<code>src</code>||<code>%_sourcedir</code>||<code>%_srcrpmdir</code>||创建源码 RPM 包(简称 SRPM,以<code>.src.rpm</code> 作为后缀名),并保存至 <code>%_srcrpmdir</code> 目录。SRPM 包通常用于审核和升级软件包。
|}
|}


<!-- Note: The words "in" and "underneath" in the table above have different meanings.  Given file /a/b/c, c is "underneath" but not "in" a. -->
<!-- Note: The words "in" and "underneath" in the table above have different meanings.  Given file /a/b/c, c is "underneath" but not "in" a. -->


<code>rpmbuild</code> 中对上面表格中的每个宏代码有相应的目录对应:
<code>rpmbuild</code> 中,对上表中的每个宏代码都有对应的目录:
{|border="1" cellspacing="0"  
 
||宏代码 || 名称 || 默认位置 || 用途
{|border="1" cellspacing="0"
! 宏代码 !! 名称 !! 默认位置 !! 用途
|-
|-
|<code>%_specdir</code>||Spec 文件目录||<code>~/rpmbuild/SPECS</code>|| RPM 包配置(.spec) 文件存放的目录
|<code>%_specdir</code>||Spec 文件目录||<code>~/rpmbuild/SPECS</code>|| 保存 RPM 包配置(<code>.spec</code>)文件
|-
|-
|<code>%_sourcedir</code>||源代码目录||<code>~/rpmbuild/SOURCES</code>||存放源代码包(比如 .tar包) 和所有 patch 的目录
|<code>%_sourcedir</code>||源代码目录||<code>~/rpmbuild/SOURCES</code>|| 保存源码包(如 .tar 包)和所有 patch 补丁
|-
|-
|<code>%_builddir</code>||构建目录||<code>~/rpmbuild/BUILD</code>||源代码被解压,并在该目录下的子目录下被编译
|<code>%_builddir</code>||构建目录||<code>~/rpmbuild/BUILD</code>|| 源码包被解压至此,并在该目录的子目录完成编译
|-
|-
|<code>%_buildrootdir</code>||最终安装目录||<code>~/rpmbuild/BUILDROOT</code>||%install 阶段最终安装的文件
|<code>%_buildrootdir</code>||最终安装目录||<code>~/rpmbuild/BUILDROOT</code>|| 保存 %install 阶段安装的文件
|-
|-
|<code>%_rpmdir</code>||标准 RPM 包目录||<code>~/rpmbuild/RPMS</code>||二进制 RPM 包在这里生成并存放。
|<code>%_rpmdir</code>||标准 RPM 包目录||<code>~/rpmbuild/RPMS</code>|| 生成/保存二进制 RPM
|-
|-
|<code>%_srcrpmdir</code>||源代码 RPM 包目录||<code>~/rpmbuild/SRPMS</code>||源代码 RPM 包(SRPM)在这里生成并存放。
|<code>%_srcrpmdir</code>||源代码 RPM 包目录||<code>~/rpmbuild/SRPMS</code>|| 生成/保存源码 RPM 包(SRPM)
|}
|}


如果某一阶段失败,请查看输出以了解失败的 ''原因'',然后请按照问题修改 <code>.spec</code> 文件。
如果某一阶段失败,请查看输出信息以了解失败''原因'',并根据需要修改 <code>.spec</code> 文件。


== 做好准备打包一个特殊程序 ==
== 做好准备打包一个特殊程序 ==
Line 102: Line 98:
如果这里有特殊的程序,它们需要被安装或者运行以便让您打包的普通程序正常工作,那么请先安装它们,然后记录下诸如软件包等相关信息。
如果这里有特殊的程序,它们需要被安装或者运行以便让您打包的普通程序正常工作,那么请先安装它们,然后记录下诸如软件包等相关信息。


如果想为 Fedora 源打包一个程序,您必须使用源代码来打包,且包含 patch 以及打包简介。不'''可以'''使用预编译好的代码来进行打包。将源代码(通常是一个 .tar.gz 文件) 放入"<code>~/rpmbuild/SOURCES</code>" 目录(注意用户)。
如果想为 Fedora 源打包一个程序,您必须使用源代码来打包,且包含 patch 以及打包简介;'''不可以'''使用预编译代码进行打包。将源代码(通常是 .tar.gz 文件)放入 "<code>~/rpmbuild/SOURCES</code>" 目录(注意用户)。


仔细阅读该软件的安装说明。我们建议您先手工安装一次以了解具体情况。除了少许例外,所有程序的二进制文件和程序库都必须来源于源代码且基于其构建。
仔细阅读该软件的安装说明。我们建议您先手工安装一次以了解具体情况。除少数情况外,所有二进制文件和程序库都必须由源码包中的源码编译而成。


=== 分离程序 ===
=== 分离程序 ===


程序源代码通常会随着发布而被发布,但是其中也会掺杂着许多其它外部组件。[[Packaging:No_Bundled_Libraries|请不要将外部组件跟随主程序一起打包]],相反您需要对每个组件单独打包。
应用程序的源代码发布时,通常会捆绑许多外部依赖库的源代码。[[Packaging:No_Bundled_Libraries|请不要将外部组件与主程序一起打包]]。相反,您需要拆分每个组件并单独打包。


=== 许可协议 ===
=== 许可协议 ===


您只许打包'''允许您打包'''的软件。请查看 [[Packaging:Guidelines#Legal]]、[[Licensing:Main]] 和 [[Packaging:LicensingGuidelines]]
您只允许打包'''符合协议'''的软件。请查看 [[Packaging:Guidelines#Legal]]、[[Licensing:Main]] 和 [[Packaging:LicensingGuidelines]]。通常情况下,您只可以打包使用开源许可证(如 GNU GPL、LGPL、BSD-new、MIT/X 或 Apache 2.0)发布的开源软件(OSS)。请仔细检查许可证是否名副其实,同时确认软件整体是否均基于开源协议发布(如检查头文件注释、 README 文件等等)。如果软件捆绑外部依赖库,请确保这些库也使用开源协议(这十分重要)。
通常情况下,您只可以打包使用开源许可证(比如GNU GPL、GNU LGPL、BSD-new、MIT/X或者Apache 2.0 许可证)发放的开源软件(OSS)。
请仔细检查许可证是否名副其实,同时软件整体是否均基于开源协议发放。(比如抽查源代码开头的注释、README 文件等等)
如果软件有外部组件被捆绑,请确保这些组件也是开源软件。这步十分重要。


=== 使用已有的信息 ===
=== 使用已有的信息 ===


尽可能利用一切已有的信息!很明显,请您不要打包一些已经存在于源里面的程序!为了防止您犯这种错误,请查阅 [https://admin.fedoraproject.org/pkgdb/ Fedora 软件包数据库]。同时建议查阅 [[PackageMaintainers/InProgressReviewRequests |正在被审核的软件包]]和 [[PackageMaintainers/RetiredPackages |已停止使用的软件包]]列表。
尽可能利用一切已有的信息!很明显,请不要打包源中已存在的程序!为了防止您犯这种错误,请查阅 [https://admin.fedoraproject.org/pkgdb/ Fedora 软件包数据库]。同时建议查阅 [[PackageMaintainers/InProgressReviewRequests |正在被审核的软件包]]和 [[PackageMaintainers/RetiredPackages |已停止使用的软件包]]列表。如果未找到相关信息,请使用 Google 搜索查看是否有类似 rpm 包。您可以直接访问 [http://pkgs.fedoraproject.org/cgit/ Fedora 软件包 Git 源] 查看相关 SPEC 文件(和 Patch)。您可以使用 DNF 插件下载 SRPM 包:
如果以上都没有即将被您打包的程序的信息,请使用 Google 以 "您想打包的程序名 Fedora rpm" 或者类似的关键字进行搜索,您可以直接访问 [http://pkgs.fedoraproject.org/cgit/ Fedora 软件仓库 Git 源] 来查看已存在于 Fedora 源中的 .spec 文件(以及 patch)。您还可以通过 <code>yum-utils</code> 工具包的一个工具,<code>yumdownloader</code>,来获取源代码 RPM 包(SRPM):
  $ dnf download --source sourcepackage-name
  $ yum -y install yum-utils
$ yumdownloader --source 您想要获得的源代码包的名字


或者您可以通过浏览 [http://mirrors.fedoraproject.org/publiclist Fedora 镜像列表] 的 HTTP 或者 FTP 页面以获取 SRPM 包。在 releases/{{FedoraVersion}}/Everything/source/SRPMS 目录内
或通过访问 [http://mirrors.fedoraproject.org/publiclist Fedora 镜像列表] 的 HTTP/FTP 镜像页面,导航至 <code>releases/{{FedoraVersion}}/Everything/source/SRPMS</code> 目录( "<code>{{FedoraVersion}}</code>" 表示 Fedora 版本),手动下载扩展名为 <code>.src.rpm</code> 的 SRPM 包即可。
(请把"{{FedoraVersion}}"换成您想要的 Fedora 版本),然后下载即可。通常源代码包以 .src.rpm 为后缀名。


一旦有了源代码包,请按照下面的步骤安装到 <code>~/rpmbuild</code> 目录下:
一旦有了源码包,执行以下命令安装至 <code>~/rpmbuild</code> 目录:
  $ rpm -ivh 源代码包名字.src.rpm
  $ rpm -ivh 源码包名*.src.rpm


您也可以使用 <code>rpm2cpio</code> 把源代码包解压到任意目录:
您也可以使用 <code>rpm2cpio</code> 将源码包解压至任意目录:
  $ mkdir 源代码包名字_src_rpm
  $ mkdir 源码包名_src_rpm
  $ cd 源代码包名字E_src_rpm
  $ cd 源码包名_src_rpm
  $ rpm2cpio ../源代码包名字.src.rpm | cpio -i
  $ rpm2cpio ../源码包名.src.rpm | cpio -i


使用这些已经存在的信息有助于您更快打包。[http://rpmfind.net/ RPM Find] 和 [http://pkgs.org PKGS.org] 将帮助您找到针对非 Fedora RPM 包。您可以尝试安装它们。如果失败,请查看相应的源代码包(不是 <code>.deb</code>) 结尾的 [http://packages.ubuntu.com/ Ubuntu] 或 [http://www.debian.org/distrib/packages Debian]软件包(源代码包通常是标准的 tar 文件比并且内部包含一个叫做 "<code>debian/</code>" 的子目录)。如果您在 [http://www.freebsd.org/ports/installing.html FreeBSD ports 仓库] 找到想要的文件, [ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/ports.tar.gz 请下载 ports 的源代码包] 并查看是否包含相关信息。有些时候这些操作没什么实际帮助,因为不同的系统有不同的规定。
使用已有的信息以帮助您打包。[http://rpmfind.net/ RPM Find] 和 [http://pkgs.org PKGS.org] 可以搜索非 Fedora 系统的 RPM 包。您可以尝试以相同的方式安装 SRPMS,并进行调试。如果未找到 RPM,可以参考 [http://packages.ubuntu.com/ Ubuntu] 或 [http://www.debian.org/distrib/packages Debian] 的源码包(标准 tar 文件,内部包含 "<code>debian/</code>" 子目录)。如果您在 [http://www.freebsd.org/ports/installing.html FreeBSD ports 仓库] 找到想要的软件, [ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/ports.tar.gz 请下载 ports 源码包] 并查看是否包含相关信息。有时,这些操作没什么实际帮助,因为不同系统有不同的打包规则。


== 新建一个 .spec 文件 ==
== 新建一个 .spec 文件 ==


现在您需要新建一个 ".spec" 文件,并让它位于 <code>~/rpmbuild/SPECS</code> 目录下。您应该把它命名为 "<code>程序名.spec</code>"。程序名根据原始软件包命名或通用命名来填写即可。但是,[[Packaging/NamingGuidelines|软件包命名规定]] 是您必须要遵守的。
现在,您需要在 <code>~/rpmbuild/SPECS</code> 目录下,新建一个 SPEC 文件。文件应命名为 "<code>软件包名.spec</code>"。名称根据软件包名或通用名填写即可。但是,必须要遵守 [[Packaging/NamingGuidelines|软件包命名规定]]


=== 模板和实例 ===
=== 模板和实例 ===
==== 模板 ====
==== 模板 ====
如果您第一次新建 .spec 文件,vim 或 emacs 会自动生成好一个模板:
如果您首次创建 .spec 文件,vim 或 emacs 会自动生成模板:
   $ cd ~/rpmbuild/SPECS
   $ cd ~/rpmbuild/SPECS
   $ vim 程序名.spec
   $ vim program.spec


示例(仅为参考):
示例(仅供参考):
  Name:
  Name:
  Version:
  Version:
  Release: 1%{?dist}
  Release: 1%{?dist}
  Summary:
  Summary:
  Group:
  Group:
  License:
  License:
  URL:
  URL:
  Source0:
  Source0:
  BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
  BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
   
   
  BuildRequires:
  BuildRequires:
  Requires:
  Requires:
   
   
  %description
  %description
Line 183: Line 173:
  %changelog
  %changelog


您可以使用 <code>$RPM_BUILD_ROOT</code> 代替 <code>%{buildroot}</code>,两者都可以使用。


您可以使用 <code>$RPM_BUILD_ROOT</code> 替代 <code>%{buildroot}</code>。两者都可以。
您也可以使用 <code>rpmdev-newspec</code> 命令来创建 SPEC 文件。<code>rpmdev-newspec 软件包名</code> 可以创建一个初始 SPEC 文件,该工具从软件包名判断使用哪个模板,支持指定模板。 <code>/etc/rpmdevtools/spectemplate-*.spec</code> 包含所有可用的模板,使用 <code>rpmdev-newspec --help</code> 命令了解更多信息。例如,为 python 模块创建 SPEC 文件:
 
您也可以使用 <code>rpmdev-newspec</code> 命令来创建新的 SPEC 文件。<code>rpmdev-newspec 软件包名</code> 可以创建一个初始的 SPEC 文件,该工具会从软件包名来判断何种模板适合您,您也可以自行指定。请看 <code>/etc/rpmdevtools/spectemplate-*.spec</code> 来找可用的模板,也可以通过 <code>rpmdev-newspec --help</code> 命令来了解。比如您创建一个 python 模块:


  cd ~/rpmbuild/SPECS
  cd ~/rpmbuild/SPECS
Line 194: Line 183:
==== 实例 ====
==== 实例 ====
===== eject =====
===== eject =====
这是 Fedora 16 <code>eject</code> spec 文件:
这是 Fedora 16 <code>eject</code> 程序的 spec 文件:


<pre>
<pre>
Line 225: Line 214:


%prep
%prep
%setup -q -n %{name}
%autosetup -n %{name}
%patch1 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
%patch5 -p1
%patch6 -p1


%build
%build
Line 238: Line 221:


%install
%install
make DESTDIR=%{buildroot} install
%make_install


install -m 755 -d %{buildroot}/%{_sbindir}
install -m 755 -d %{buildroot}/%{_sbindir}
Line 259: Line 242:
</pre>
</pre>


=====  =====
{{Anchor|Spec_file_pieces_explained}}
{{Anchor|Spec_file_pieces_explained}}


== SPEC 文件综述 ==
== SPEC 文件综述 ==


其他有用的介绍:
其他有用的信息:
* [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch-creating-rpms.html RPM 手册] 讲述了如何写 spec 文件。
* [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch-creating-rpms.html RPM 指南] 描述了如何编写 spec 文件。
* IBM "Packaging software with RPM" 系列 [http://www.ibm.com/developerworks/library/l-rpm1/ Part 1],[http://www.ibm.com/developerworks/library/l-rpm2/ Part 2],和 [http://www.ibm.com/developerworks/library/l-rpm3/ Part 3]。
* IBM "Packaging software with RPM" 系列文章 [http://www.ibm.com/developerworks/library/l-rpm1/ Part 1],[http://www.ibm.com/developerworks/library/l-rpm2/ Part 2][http://www.ibm.com/developerworks/library/l-rpm3/ Part 3]。
* [http://rpm.org/max-rpm-snapshot/ Maximum RPM] 有最多的信息,不过已经过时。
* [http://rpm.org/max-rpm-snapshot/ Maximum RPM] 包含详细信息,但内容已经过时。


您也将需要按照这些规定来做:[[Packaging/NamingGuidelines|软件包命名规定]],[[Packaging/Guidelines|打包规定]] 和 [[Packaging/ReviewGuidelines|软件包审核规定]]。
您需要遵守这些规定:[[Packaging/NamingGuidelines|软件包命名规定]],[[Packaging/Guidelines|打包规定]] 和 [[Packaging/ReviewGuidelines|软件包审核规定]]。


Insert comments with a leading "<code>#</code>" character, but avoid macros (beginning with <code>%</code>) that are potentially multiline (as they are expanded first). If commenting out a line, double the percent signs (<code>%%</code>). Also avoid inline comments on the same line as a script command.
"<code>#</code>" 字符表示注释,但需要避免注释宏(以 <code>%</code> 开头),因为它们会首先被替换展开。使用 <code>%%</code> 注释宏。另外,还要避免在脚本命令的相同行中使用行内注释。


主要的标签都在下方介绍。注意 <code>%{name}</code>,<code>%{version}</code> 和 <code>%{release}</code> 代表 Name, Version 和 Release 这三个标签,在 SPEC 中修改它们,构建会自动抓去新的内容。
以下介绍了主要的标签。注意 <code>%{name}</code>,<code>%{version}</code> 和 <code>%{release}</code> 代表 Name, Version 和 Release 这三个标签。只要更改标签,宏就会使用新值。
* '''Name''': 软件包名,应与 SPEC 文件名一致。命名必须符合 [[Packaging/NamingGuidelines|软件包命名规定]]。
* '''Name''': 软件包名,应与 SPEC 文件名一致。命名必须符合 [[Packaging/NamingGuidelines|软件包命名规定]]。
* '''Version''': 上游版本号。请查看 [[Packaging/NamingGuidelines#Version_Tag|版本标签规定]]。如果包含非数字字符,您可能需要将它们包含在 Release 标签中。如果上有采用日期区分版本号,请考虑这个格式:<code>yy.mm[dd]</code> (例如 <code>2008-05-01</code> 可变为 <code>8.05</code>)
* '''Version''': 上游版本号。请查看 [[Packaging/NamingGuidelines#Version_Tag|版本标签规定]]。如果包含非数字字符,您可能需要将它们包含在 Release 标签中。如果上游采用日期作为版本号,请考虑以:<code>yy.mm[dd]</code> (例如 <code>2008-05-01</code> 可变为 <code>8.05</code>) 格式作为版本号。
* '''Release''': 初始值为 <code>1%{?dist}</code>。每次您制作新的包时,请递增该数字。当上游发布新版本时,请修改 Version 标签并重置 Release 的数字为 <code>1</code>。具体请看打包规定中的 [[Packaging/NamingGuidelines#Release_Tag|Release 标签部分]][[Packaging/DistTag|Dist tag]] 可能也会帮助您。
* '''Release''': 发行编号。初始值为 <code>1%{?dist}</code>。每次制作新包时,请递增该数字。当上游发布新版本时,请修改 Version 标签并重置 Release 的数字为 <code>1</code>。具体参考打包规定中的 [[Packaging/NamingGuidelines#Release_Tag|Release 标签部分]],以及 [[Packaging/DistTag|Dist tag]]
* '''Summary''': 一行简短的软件包介绍。请使用美式英语。'''请勿在结尾加上句号!'''
* '''Summary''': 一行简短的软件包介绍。请使用美式英语。'''请勿在结尾添加标点!'''
* '''Group''': 需要在一个一存在的组中,就像 "Applications/Engineering";执行 "<code>less /usr/share/doc/rpm-*/GROUPS</code>" 来查看完整的组列表。使用组 "Documentation" 为所有包含文档的子软件包(例如 <code>kernel-doc</code>)。'''''注意这个标签从 Fedora 17 后已被废除。[[http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/Packagers_Guide/chap-Packagers_Guide-Spec_File_Reference-Preamble.html|Spec 文件参考手册]] 有介绍'''''
* '''Group''': 指定软件包组,例如 "Applications/Engineering";执行 "<code>less /usr/share/doc/rpm-*/GROUPS</code>" 查看完整的组列表。任何包含文档的子软件包,使用 "Documentation" 组(如 <code>kernel-doc</code>)。'''''注意 Fedora 17+ 后已废除此标签。[http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/Packagers_Guide/chap-Packagers_Guide-Spec_File_Reference-Preamble.html Spec 文件参考手册] 有介绍'''''
* '''License''': 必须是开源软件许可证。请''不要''使用 Copyright 标签来注明许可证。请使用一个标准(例如 "<code>GPLv2+</code>")且清楚的的描述(例如,使用 "<code>GPLv2+</code>" 来注明 GPL version 2 及更高而不是 "<code>GPL</code>" 或 "<code>GPLv2</code>" 这种不准确的描述)。请见 [[Licensing]] 和 [[Packaging/LicensingGuidelines|Licensing Guidelines]]。如果一个软件包包含多个协议,您可以使用 "<code>and</code>" 和 "<code>or</code>" (例如 "<code>GPLv2 and BSD</code>")来描述。
* '''License''': 授权协议,必须是开源许可证。请''不要''使用旧的 Copyright 标签。协议采用标准缩写(如 "<code>GPLv2+</code>")并且描述明确(如, "<code>GPLv2+</code>" 表示 GPL 2 及后续版本,而不是 "<code>GPL</code>" 或 "<code>GPLv2</code>" 这种不准确的写法)。参考 [[Licensing]] 和 [[Packaging/LicensingGuidelines|Licensing Guidelines]]。如果一个软件采用多个协议,可以使用 "<code>and</code>" 和 "<code>or</code>"(例如 "<code>GPLv2 and BSD</code>")来描述。
* '''URL''': 有关该软件包的详细信息(例如项目主页)。'''''注意:这不是源代码的来源地址,后者是下文将要讲述的 Source0。'''''
* '''URL''': 该软件包的项目主页。'''''注意:源码包 URL 请使用 Source0 指定。'''''
* '''Source0''': 软件源代码原始下载地址。"<code>Source</code>" is synonymous with "<code>Source0</code>".  If you give a full URL (and you should), its basename will be used when looking in the <code>SOURCES</code> directory. If possible, embed <code>%{name}</code> and <code>%{version}</code>, so that changes to either will go to the right place. [[Packaging:Guidelines#Timestamps|Preserve timestamps]] when downloading source files. If there is more than one source, name them <code>Source1</code>, <code>Source2</code> and so on. If you're adding whole new files in addition to the pristine sources, list them as sources ''after'' the pristine sources. A copy of each of these sources will be included in any SRPM you create, unless you specifically direct otherwise.  See [[Packaging/SourceURL|Source URL]] for more information on special cases (e.g. revision control).
* '''Source0''': 软件源码包的 URL 地址。"<code>Source</code>" "<code>Source0</code>" 相同。'''强烈建议'''提供完整 URL 地址,文件名用于查找 <code>SOURCES</code> 目录。如果可能,建议使用 <code>%{name}</code> <code>%{version}</code> 替换 URL 中的名称/版本,这样更新时就会自动对应。下载源码包时,需要 [[Packaging:Guidelines#Timestamps|保留时间戳]]。如果有多个源码包,请用 <code>Source1</code><code>Source2</code> 等依次列出。如果你需要添加额外文件,请将它们列在后面。更多特殊案例(如 revision control),请参考 [[Packaging/SourceURL|Source URL]]
* '''Patch0''': The name of the first patch to apply to the source code.  If you need to patch the files after they've been uncompressed, you should edit the files and save their differences as a "patch" file in your <code>~/rpmbuild/SOURCES</code> directory. Patches should make only one logical change each, so it's quite possible to have multiple patch files.
* '''Patch0''': 用于源码的补丁名称。如果你需要在源码包解压后对一些代码做修改,你应该修改代码并使用 diff 命令生成 patch 文件,然后放在 <code>~/rpmbuild/SOURCES</code> 目录下。一个 Patch 应该只做一种修改,所以可能会包含多个 patch 文件。
* '''BuildArch''': If you're packaging files that are architecture-independent (e.g. shell scripts, data files), then add "<code>BuildArch: noarch</code>". The architecture for the binary RPM will then be "<code>noarch</code>".
* '''BuildArch''': 如果你要打包的文件不依赖任何架构(例如 shell 脚本,数据文件),请使用 "<code>BuildArch: noarch</code>"。RPM 架构会变成 "<code>noarch</code>"
* '''BuildRoot''': This is where files will be "installed" during the %install process (after the %build process). This is now redundant in Fedora and is only needed for EPEL5. By default, the build root is placed in "<code>%{_topdir}/BUILDROOT/</code>".
* '''BuildRoot''': %install 阶段(%build 阶段后)文件需要安装至此位置。Fedora 不需要此标签,只有 EPEL5 还需要它。默认情况下,根目录为 "<code>%{_topdir}/BUILDROOT/</code>"
* '''BuildRequires''': A comma-separated list of packages required for building (compiling) the program. This field can be (and is commonly) repeated on multiple lines. These dependencies are ''not'' automatically determined, so you need to include ''everything'' needed to build the program. [[Packaging/Guidelines#BuildRequires|Some common packages can be omitted]], such as <code>gcc</code>. You can specify a minimum version if necessary (e.g. "<code>ocaml >= 3.08</code>"). If you need the file <code>/EGGS</code>, determine the package that owns it by running "<code>rpm -qf /EGGS</code>". If you need the program <code>EGGS</code>, determine the package that owns it by running "<code>rpm -qf `which EGGS`</code>". Keep dependencies to a minimum (e.g. use <code>sed</code> instead of <code>perl</code> if you don't really need perl's abilities), but beware that some applications permanently disable functions if the associated dependency is not present; in those cases you may need to include the additional packages. The {{package|auto-buildrequires}} package may be helpful.
* '''BuildRequires''': 编译软件包所需的依赖包列表,以逗号分隔。此标签可以多次指定。编译依赖 ''不会'' 自动判断,所以需要列出编译所需的''所有''依赖包。[[Packaging/Guidelines#Exceptions_2|常见的软件包可省略]],例如 <code>gcc</code>。如果有必要,你可以指定需要的最低版本(例:"<code>ocaml >= 3.08</code>")。如果你需要找到包含 <code>/EGGS</code> 文件的软件包,可执行 "<code>rpm -qf /EGGS</code>"。如果你需要找到包含 <code>EGGS</code> 程序的软件包,可执行 "<code>rpm -qf `which EGGS`</code>"。请保持最小依赖(例如,如果你不需要 perl 的功能,可使用 <code>sed</code> 代替),但请注意,如果不包含相关依赖,某些程序会禁用一些功能;此时,你需要添加这些依赖。{{package|auto-buildrequires}} 软件包可能会有帮助。
* '''Requires''': A comma-separate list of packages that are required when the program is installed. Note that the BuildRequires tag lists what is required to build the binary RPM, while the Requires tag lists what is required when installing/running the program; a package may be in one list or in both. In many cases, <code>rpmbuild</code> automatically detects dependencies so the Requires tag is not always necessary. However, you may wish to highlight some specific packages as being required, or they may not be automatically detected.
* '''Requires''': 安装软件包时所需的依赖包列表,以逗号分隔。请注意, BuildRequires 标签是编译所需的依赖,而 Requires 标签是安装/运行程序所需的依赖。大多数情况下,<code>rpmbuild</code> 会自动探测依赖,所以可能不需要 Requires 标签。然而,你也可以明确标明需要哪些软件包,或由于未自动探测所需依赖而需要手动标明。
* '''%description''':一个较长的/多行的软件介绍部分。请使用美式英语。All lines must be 80 characters or less. Blank lines indicate a new paragraph. Some graphical user interface installation programs will reformat paragraphs; lines that start with whitespace will be treated as preformatted text and displayed as is, normally with a fixed-width font. See [http://docs.fedoraproject.org/drafts/rpm-guide-en/ch09s03.html RPM Guide].
* '''%description''': 程序的详细/多行描述,请使用美式英语。每行必须小于等于 80 个字符。空行表示开始新段落。使用图形安装软件时会重新格式化段落;以空格开头的行被视为已格式化的格式,一般使用等宽字体显示。参考 [http://docs.fedoraproject.org/drafts/rpm-guide-en/ch09s03.html RPM Guide]
* '''%prep''': Script commands to "prepare" the program (e.g. to uncompress it) so that it will be ready for building.  Typically this is just "<code>%setup -q</code>"; a common variation is "<code>%setup -q -n NAME</code>" if the source file unpacks into <code>NAME</code>. See the %prep section below for more.
* '''%prep''': 打包准备阶段执行一些命令(如,解压源码包,打补丁等),以便开始编译。一般仅包含 "<code>%autosetup</code>";如果源码包需要解压并切换至 <code>NAME</code> 目录,则输入 "<code>%autosetup -n NAME</code>"。查看 %prep 部分了解更多信息。
* '''%build''': "构建" 程序并使其接下来能够被安装的命令(例如,编译)。具体请见下文的 %build 部分。
* '''%build''': 包含构建阶段执行的命令,构建完成后便开始后续安装。程序应该包含有如何编译的介绍。查看 %build 部分了解更多信息。
* '''%check''': "测试" 程序的命令。 This is run between the %build and %install procedures, so place it there if you have this section. Often it simply contains "<code>make test</code>" or "<code>make check</code>". This is separated from %build so that people can skip the self-test if they desire.
* '''%install''': 包含安装阶段执行的命令。命令将文件从 <code>%{_builddir}</code> 目录安装至 <code>%{buildroot}</code> 目录。查看 %install 部分了解更多信息。
* '''%install''': "安装" 程序的命令。The commands should copy the files from the <code>BUILD</code> directory <code>%{_builddir}</code> into the buildroot directory, <code>%{buildroot}</code>. 具体请见下文的 %install 部分。
* '''%check''': 包含测试阶段执行的命令。此阶段在 %install 之后执行,通常包含 "<code>make test</code>" 或 "<code>make check</code>" 命令。此阶段要与 %build 分开,以便在需要时忽略测试。
* '''%clean''': "清理" 安装目录的命令。注意这一步目前在 Fedora 中是多余的,仅针对 EPEL。基本上只包含一句:
* '''%clean''': 清理安装目录的命令。此阶段在 Fedora 中是多余的,仅针对 EPEL。一般只包含:
  rm -rf %{buildroot}
  rm -rf %{buildroot}
* '''%files''': 将要被安装的文件的列表。具体请见下文的 %files 部分。
* '''%files''': 需要被打包/安装的文件列表。查看 %files 部分了解更多信息。
* '''%changelog''': 请按照上面 eject 的例子,使用这个标签列出 RPM 的变更日志。'''请注意不是软件本身的变更日志。'''
* '''%changelog''': RPM 包变更日志。请使用示例中的格式。'''注意,不是软件本身的变更日志。'''
* '''ExcludeArch''': 如果该软件包在某些架构上不能被正常编译/构建或工作,请通过该标签列出。
* '''ExcludeArch''': 排除某些架构。如果该软件不能在某些架构上正常编译或工作,通过该标签列出。
* You can add sections so that code will run when packages are installed or removed on the real system (as opposed to just running the %install script, which only does a pseudo-install to the build root).  These are called "scriptlets", and they are usually used to update the running system with information from the package.  See the "Scriptlets" section below for more.
* '''ExclusiveArch''': 列出该软件包独占的架构。
* 你可以加入一些代码片段,以便在真实系统上安装/删除包时执行这些代码(相反,%install 脚本仅将文件虚拟【pseudo】安装至 build root 目录)。这些代码称为 "scriptlets",通常用于从软件包更新系统信息。查看 "Scriptlets" 部分了解更多信息。


RPM also supports the creation of several packages (called [[How_to_create_an_RPM_package#Subpackages|subpackages]]) from a single SPEC file, such as <code>name-libs</code> and <code>name-devel</code> packages.
RPM 还支持使用一个 SPEC 文件制作多个软件包(这称为 [[How_to_create_an_RPM_package#Subpackages|子软件包]]),例如 <code>name-libs</code> <code>name-devel</code> 等软件包。


{{admon/caution|请'''不要'''使用这些标签|
{{admon/caution|请'''不要'''使用这些标签|
Line 306: Line 289:
* Copyright}}
* Copyright}}


Do '''not''' create a "relocatable" package; they don't add value in Fedora and make things more complicated.
'''不要'''制作 "relocatable" 软件包(不遵守FHS);它们不会为 Fedora 加分,反而把事情搞得更复杂。


== SPEC 具体含义 ==
== SPEC 文件剖析 ==


=== %prep 部分 ===
=== %prep 部分 ===


%prep 部分描述了解压缩源代码的方法。一般来讲,其中包含了 "<code>%setup</code>" 和 "<code>%patch</code>" 两个命令。具体请见 [http://rpm.org/max-rpm-snapshot/s1-rpm-inside-macros.html Maximum RPM section on %setup and %patch] 以了解更多。
%prep 部分描述了解压源码包的方法。一般而言,其中包含 "<code>%autosetup</code>" 命令。另外,还可以使用 "<code>%setup</code>" 和 "<code>%patch</code>" 命令来指定操作 Source0 等标签的文件。查看 [http://rpm.org/max-rpm-snapshot/s1-rpm-inside-macros.html Maximum RPM %setup and %patch 小节] 了解更多信息。


<code>%{patches}</code> 和 <code>%{sources}</code> 宏在 RPM 4.4.2 后已经可用。如果您的软件包包含了许多 patch 或 source,那么可以这么做:
自 RPM 4.4.2 开始,可使用 <code>%{patches}</code> 和 <code>%{sources}</code> 宏。如果您的软件包包含了许多 patch 或 source,并且你不想使用 %autosetup,那么可以这么做:
  for p in %{patches}; do
  for p in %{patches}; do
     ...
     ...
  done
  done


不过,记住在 RHEL 和其它基于 RPM 机制的发行版中,这是无法识别的。
注意,RHEL 和其它基于 RPM 的发行版,并不支持这种用法。


==== %prep 部分:%setup 命令 ====
==== %prep 部分:%autosetup 命令 ====


The "<code>%setup</code>" command unpacks a source package. Switches include:
"<code>%autosetup</code>" 命令用于解压源码包。可用选项包括:
* '''<code>-q</code>''' : Suppress unecessary output. This is commonly used.
* '''<code>-n</code> ''name''''' : 如果源码包解压后的目录名称与 RPM 名称不同,此选项用于指定正确的目录名称。例如,如果 tarball 解压目录为 FOO,则使用 "<code>%autosetup -n FOO</code>"
* '''<code>-n</code> ''name''''' : If the Source tarball unpacks into a directory whose name is not the RPM name, this switch can be used to specify the correct directory name. For example, if the tarball unpacks into the directory FOO, use "<code>%setup -q -n FOO</code>".
* '''<code>-c</code> ''name''''' : 如果源码包解压后包含多个目录,而不是单个目录时,此选项可以创建名为 ''name'' 的目录,并在其中解压。
* '''<code>-c</code> ''name''''' : If the Source tarball unpacks into multiple directories instead of a single directory, this switch can be used to create a directory named ''name'' and then unpack into it.


There are [http://rpm.org/max-rpm-snapshot/s1-rpm-inside-macros.html more %spec options if you are unpacking multiple files], which is primarily useful if you
如果使用 "<code>%setup</code>" 命令,通常使用 ''<code>-q</code>''' 抑止不必要的输出。
are creating subpackages (see below).  The key ones are:
 
[http://rpm.org/max-rpm-snapshot/s1-rpm-inside-macros.html 如果需要解压多个文件,有更多 %spec 选项可用],这对于创建子包很有用。常用选项如下:


{|
{|
|-
|-
| <code>-a number</code> || Only unpack the Source directive of the given number after changing directory (e.g. "<code>–a 0</code>" for Source0).
| <code>-a number</code> || 在切换目录后,只解压指定序号的 Source 文件(例如 "<code>-a 0</code>" 表示 Source0)
|-
|-
| <code>-b number</code> || Only unpack the Source directive of the given number before changing directory (e.g. "<code>–b 0</code>" for Source0).
| <code>-b number</code> || 在切换目录前, 只解压指定序号的 Source 文件(例如 "<code>-b 0</code>" 表示 Source0)
|-
|-
| <code>-D</code> || Do not delete the directory before unpacking.
| <code>-D</code> || 解压前,不删除目录。
|-
|-
| <code>-T</code> || Disable the automatic unpacking of the archives.
| <code>-T</code> || 禁止自动解压归档。
|}
|}


==== %prep 部分:%patch 命令 ====
==== %prep 部分:%patch 命令 ====


"<code>%patch0</code>" 命令用来打补丁“Patch0” (%patch1 打 Patch1 以此类推)。Patches 是修改源代码的最佳方式。The usual "<code>-pNUMBER</code>" option applies, which passes that argument onto the program <code>patch</code>.
如果使用 "<code>%autosetup</code>" 命令,则不需要手动进行补丁管理。如果你的需求很复杂,或需要与 EPEL 兼容,需要用到此部分的内容。"<code>%patch0</code>" 命令用于应用 Patch0(%patch1 应用 Patch1,以此类推)。Patches 是修改源码的最佳方式。常用的 "<code>-pNUMBER</code>" 选项,向 <code>patch</code> 程序传递参数,表示跳过 NUM 个路径前缀。


Patch file names often look like "<code>telnet-0.17-env.patch</code>", which is the format <code>%{name} - %{version} - REASON.patch</code>" (though sometimes version is omitted). Patch files are typically the result of "<code>diff -u</code>"; if you do this from the subdirectory of <code>~/rpmbuild/BUILD</code> then you won't have to specify a <code>-p</code> level later.
补丁文件名通常像这样 "<code>telnet-0.17-env.patch</code>",命名格式为 <code>%{name} - %{version} - REASON.patch</code>"(有时省略 version 版本)。补丁文件通常是 "<code>diff -u</code>" 命令的输出;如果你在 <code>~/rpmbuild/BUILD</code> 子目录执行此命令,则之后便不需要指定 <code>-p</code> 选项。


简单的创建 patch 方法:
为一个文件制作补丁的步骤:
  cp foo/bar foo/bar.orig
  cp foo/bar foo/bar.orig
  vim foo/bar
  vim foo/bar
  diff -u foo/bar.orig foo/bar > ~/rpmbuild/SOURCES/PKGNAME.REASON.patch
  diff -u foo/bar.orig foo/bar > ~/rpmbuild/SOURCES/PKGNAME.REASON.patch


If editing many files, one easy method is to copy the whole subdirectory underneath <code>BUILD</code> and then do subdirectory diffs.  After you have changed directory to "<code>~rpmbuild/BUILD/NAME</code>", do the following:
如果需要修改多个文件,简单方法是复制 <code>BUILD</code> 下的整个子目录,然后在子目录执行 diff。切换至 "<code>~rpmbuild/BUILD/NAME</code>" 目录后,执行以下命令:
  cp -pr ./ ../PACKAGENAME.orig/
  cp -pr ./ ../PACKAGENAME.orig/
  ... many edits ...
  ... 执行修改 ...
  diff -u ../PACKAGENAME.orig . > ~/rpmbuild/SOURCES/''NAME''.''REASON''.patch
  diff -ur ../PACKAGENAME.orig . > ~/rpmbuild/SOURCES/''NAME''.''REASON''.patch


If you edit many files in one patch, you can also copy the original files using some consistent ending such as "<code>.orig</code>" before editing them. Then, you can use "<code>gendiff</code>" (in the <code>rpm-build</code> package) to create a patch with the differences.
如果你想在一个补丁中编辑多个文件,你可以在编辑之前,使用 "<code>.orig</code>" 扩展名复制原始文件。然后,使用 "<code>gendiff</code>"(在 <code>rpm-build</code> 包中)创建补丁文件。


Try to ensure that your patch match the context exactly. The default "fuzz" value is "<code>0</code>", requiring matches to be exact. You can work around this by adding "<code>%global _default_patch_fuzz 2</code>" to revert to the value found in older versions of RPM in Fedora, but it is generally recommended to avoid doing this.
需要确保你的补丁精确匹配上下文。默认 "fuzz" 值为 "<code>0</code>",表示要求精确匹配。你可以添加 "<code>%global _default_patch_fuzz 2</code>" 将 fuzz 设为旧版 Fedora RPM 所采用的值,但我们建议你尽量避免这样做。


As explained in [[Packaging/PatchUpstreamStatus]], all patches should have a comment above them in the SPEC file about their upstream status. This should document the upstream bug/email that includes it (including the date). If it is unique to Fedora, you should mention why it is unique. The Fedora Project tries not to deviate from upstream; see [[PackageMaintainers/WhyUpstream]] for the importance of this.
[[Packaging/PatchUpstreamStatus]] 所述,SPEC 文件中的所有补丁都需要注释来描述补丁的上游状态。其中应包括上游 bug/email 文档(包含日期)。如果是 Fedora 特别需要的补丁,应描述为何需要它。Fedora 项目致力于贴近上游;查看 [[PackageMaintainers/WhyUpstream]] 了解其重要性。


==== %prep 部分:Unmodified files ====
==== %prep 部分:未修改文件 ====


Sometimes, one or more of the Source files do not need to be uncompressed. You can "prep" those into the build directory like this (where <code>SOURCE1</code> refers to the relevant Source file):
有时,一个或多个源码包不需要解压。你可以使用以下命令,将文件复制到 build 目录中,如( <code>SOURCE1</code> 表示对应的源码包):
  cp -p %SOURCE1 .
  cp -p %SOURCE1 .


=== %build 部分 ===
=== %build 部分 ===


The "%build" section is sometimes complicated; here you configure and compile/build the files to be installed.
"%build" 部分有时会有点复杂;在这里你可以配置,并编译用于安装的文件。


Many programs follow the GNU <code>configure</code> approach (or some variation). By default, they will install to a prefix of "<code>/usr/local</code>", which is reasonable for unpackaged files. However, since you are packaging it, change the prefix to "<code>/usr</code>". Libraries should be installed to either <code>/usr/lib</code> or <code>/usr/lib64</code> depending on the architecture.
许多程序使用 GNU <code>configure</code> 进行配置。默认情况下,文件会安装到前缀为 "<code>/usr/local</code>" 的路径下,对于手动安装很合理。然而,打包时需要修改前缀为 "<code>/usr</code>"。共享库路径视架构而定,安装至 <code>/usr/lib</code> <code>/usr/lib64</code> 目录。


Since GNU <code>configure</code> is so common, the macro "<code>%configure</code>" can be used to automatically invoke the correct options (e.g. change the prefix to <code>/usr</code>). Some variation of this often works:
由于 GNU <code>configure</code> 很常见,可使用 "<code>%configure</code>" 宏来自动设置正确选项(例如,设置前缀为 <code>/usr</code>)。一般用法如下:
   %configure
   %configure
   make %{?_smp_mflags}
   make %{?_smp_mflags}


To override makefile variables, pass them as parameters to <code>make</code>:
若需要覆盖 makefile 变量,请将变量作为参数传递给 <code>make</code>
  make %{?_smp_mflags} CFLAGS="%{optflags}" BINDIR=%{_bindir}
  make %{?_smp_mflags} CFLAGS="%{optflags}" BINDIR=%{_bindir}


More more information, see [http://sourceware.org/autobook/ "GNU autoconf, automake, and libtool"] and [http://www.suse.de/~sh/automake/automake.pdf "Open Source Development Tools: An Introduction to Make, Configure, Automake, Autoconf" by Stefan Hundhammer].
更多详细信息,请参考 [http://sourceware.org/autobook/ "GNU autoconf, automake libtool"] 以及 [http://www.suse.de/~sh/automake/automake.pdf "开源开发工具:Make, Configure, Automake, Autoconf 介绍" by Stefan Hundhammer]
 
Some programs use <code>cmake</code>.  See [[Packaging/cmake]].
 
=== %check 部分 ===


If self-tests are available, it is generally a good idea to include them. They should be placed in the %check section (which immediately follows the %build section) instead of within the %build section itself, so that they can be easily skipped when necessary.
一些程序使用 <code>cmake</code>。请参考 [[Packaging/cmake]]。
 
Often, this section contains:
make test


=== %install 部分 ===
=== %install 部分 ===


This section involves script commands to "install" the program, copying the relevant files from <code>%{_builddir}</code> to <code>%{buildroot}</code> (which usually means from <code>~/rpmbuild/BUILD</code> to <code>~/rpmbuild/BUILDROOT</code>) and creating directories inside <code>%{buildroot}</code> as necessary.
此部分包含安装阶段需要执行的命令,即从 <code>%{_builddir}</code> 复制相关文件到 <code>%{buildroot}</code> 目录(通常表示从 <code>~/rpmbuild/BUILD</code> 复制到 <code>~/rpmbuild/BUILDROOT</code>) 目录,并根据需要在 <code>%{buildroot}</code> 中创建必要目录。


Some of the terminology can be misleading:
容易混淆的术语:
* The "build directory", also known as <code>%{_builddir}</code> is not the same as the "build root", also known as <code>%{buildroot}</code>. Compilation occurs in the former directory, while files to be packaged are copied from the former to the latter.
* "build 目录",也称为 <code>%{_builddir}</code>,实际上与 "build root",又称为 <code>%{buildroot}</code>,是不同的目录。在前者中进行编译,并将需要打包的文件从前者复制到后者。
* During the %build section, the current directory will start at <code>%{buildsubdir}</code>, which is the subdirectory within <code>%{_builddir}</code> that was created during %prep stage. This is usually something like <code>~/rpmbuild/BUILD/%{name}-%{version}</code>.
* %build 阶段,当前目录为 <code>%{buildsubdir}</code>,是 %prep 阶段中在 <code>%{_builddir}</code> 下创建的子目录。这些目录通常名为 <code>~/rpmbuild/BUILD/%{name}-%{version}</code>
* The %install section is '''not''' run when the binary RPM package is installed by the end-user, but is only run when creating a package.
* %install 阶段的命令'''不会'''在用户安装 RPM 包时执行,此阶段仅在打包时执行。


Normally, some variation of "<code>make install</code>" is performed here:
一般,这里执行 "<code>make install</code>" 之类的命令:
  %install
  %install
  rm -rf %{buildroot}
  rm -rf %{buildroot} # 仅用于 RHEL 5
  make DESTDIR=%{buildroot} install
  %make_install
 
Removal of <code>%{buildroot}</code> is no longer necessary, except for EPEL 5.


Ideally you should use [http://www.gnu.org/prep/standards/html_node/DESTDIR.html <code>DESTDIR=%{buildroot}</code>] if the program supports it, as it redirects file installations to the specified directory and is exactly what we want to happen during the %install section.
理想情况下,对于支持的程序,你应该使用 %make_install,它等同于 [http://www.gnu.org/prep/standards/html_node/DESTDIR.html <code>DESTDIR=%{buildroot}</code>],它会将文件安装到 %{buildroot} 目录中。


If the program does not support <code>DESTDIR</code> (and only if), you can workaround it in one of several (inferior) ways:
如果程序不支持 <code>DESTDIR</code>,使用以下方法避开此问题:
* Patch the makefile so that is supports <code>DESTDIR</code>. Create directories inside <code>DESTDIR</code> where necessary and submit the patch upstream.
* 修补 makefile 以便支持 <code>DESTDIR</code>。请在 <code>DESTDIR</code> 根据需要创建必要目录,并向上游提交补丁。
* Use the "<code>%makeinstall</code>" macro. This method might work, but can lead to subtle failures. It expands to something like "<code>make prefix=%{buildroot}%{_prefix} bindir=%{buildroot}%{_bindir} ... install</code>", which can result in some programs failing to work properly. Create directories inside <code>%{buildroot}</code> where necessary.
* 使用 "<code>%makeinstall</code>" 宏。此方法可能有效,但也可能失败。该宏会展开为 "<code>make prefix=%{buildroot}%{_prefix} bindir=%{buildroot}%{_bindir} ... install</code>",可能导致某些程序无法正常工作。请在 <code>%{buildroot}</code> 根据需要创建必要目录。
* Consider using the <code>auto-destdir</code> package. This requires "<code>BuildRequires: auto-destdir</code>", and changing "<code>make install</code>" to "<code>make-redir DESTDIR=%{buildroot} install</code>". This only works well if the installation uses only certain common commands to install files, like <code>cp</code> and <code>install</code>.
* 使用 <code>auto-destdir</code> 软件包。它需要 "<code>BuildRequires: auto-destdir</code>",并将 "<code>make install</code>" 修改为 "<code>make-redir DESTDIR=%{buildroot} install</code>"。这仅适用于使用常用命令安装文件的情况,例如 <code>cp</code> <code>install</code>
* Perform the installation by hand. This would involve creating the necessary directories under <code>%{buildroot}</code> and copying files from <code>%{_builddir}</code> to <code>%{buildroot}</code>. Be especially careful with updates, which often contain new or changed filenames. An example of this procedure:
* 手动执行安装。这需要在 <code>%{buildroot}</code> 下创建必要目录,并从 <code>%{_builddir}</code> 复制文件至 <code>%{buildroot}</code> 目录。要特别注意更新,通常会包含新文件。示例如下:
  %install
  %install
  rm -rf %{buildroot}
  rm -rf %{buildroot}
Line 421: Line 395:
  cp -p mycommand %{buildroot}%{_bindir}/
  cp -p mycommand %{buildroot}%{_bindir}/


As noted in [[Packaging:Guidelines#Timestamps]], try to preserve timestamps if the makefile lets you override commands:
=== %check 部分 ===
make INSTALL="install -p" CP="cp -p" DESTDIR=%{buildroot} install
 
如果需要执行测试,使用 %check 是个好主意。测试代码应写入 %check 部分(紧接在 %install 之后,因为需要测试 %buildroot 中的文件),而不是写入 %build 部分,这样才能在必要时忽略测试。
 
通常,此部分包含:
make test
 
有时候也可以用:
make check
 
请熟悉 Makefile 的用法,并选择适当的方式。


=== %files 部分 ===
=== %files 部分 ===
This section declares which files and directories are owned by the package, and thus which files and directories will be placed into the binary RPM.


==== %files basics ====
此部分列出了需要被打包的文件和目录。


The <code>%defattr</code> set the default file permissions, and is often found at the start of the <code>%files</code> section. Note that this is no longer necessary unless the permissions need to be altered. The format of this is:
==== %files 基础 ====
%defattr(<file permissions>, <user>, <group>, <directory permissions>)
The fourth parameter is often omitted. Usually one uses <code>%defattr(-,root,root,-)</code>, where "<code>-</code>" uses the default permissions.


You should then list all the files and directories to be owned by the package. Use macros for directory names where possible, which can be viewed at [[Packaging:RPMMacros]] (e.g. use <code>%{_bindir}/mycommand</code> instead of <code>/usr/bin/mycommand</code>). If the pattern begins with a "<code>/</code>" (or when expanded from the macro) then it is taken from the <code>%{buildroot}</code> directory. Otherwise, the file is presumed to be in the current directory (e.g. inside <code>%{_builddir}</code>, such as documentation files that you wish to include). If your package only installs a single file <code>/usr/sbin/mycommand</code>, then the <code>%files</code> section can simply be:
<code>%defattr</code> 用于设置默认文件权限,通常可以在 <code>%files</code> 的开头看到它。注意,如果不需要修改权限,则不需要使用它。其格式为:
%defattr(<文件权限>, <用户>, <用户组>, <目录权限>)
第 4 个参数通常会省略。常规用法为 <code>%defattr(-,root,root,-)</code>,其中 "<code>-</code>" 表示默认权限。
 
您应该列出该软件包拥有的所有文件和目录。尽量使用宏代替目录名,查看宏列表 [[Packaging:RPMMacros]](例如:使用 <code>%{_bindir}/mycommand</code> 代替 <code>/usr/bin/mycommand</code>)。如果路径以 "<code>/</code>" 开头(或从宏扩展),则从 <code>%{buildroot}</code> 目录取用。否则,假设文件在当前目录中(例如:在 <code>%{_builddir}</code> 中,包含需要的文档)。如果您的包仅安装一个文件,如 <code>/usr/sbin/mycommand</code>,则 <code>%files</code> 部分如下所示:
  %files
  %files
  %{_sbindir}/mycommand
  %{_sbindir}/mycommand


To make your package less sensitive to upstream changes, declare all files within a directory to be owned by the package with a pattern match:
若要使软件包不受上游改动的影响,可使用通配符匹配所有文件:
  %{_bindir}/*
  %{_bindir}/*


To include a single directory:
包含一个目录:
  %{_datadir}/%{name}/
  %{_datadir}/%{name}/


Note that <code>%{_bindir}/*</code> does not claim that this package owns the <code>/usr/bin</code> directory, but only the files contained within. If you list a directory, then you are claiming that the package owns that directory and all files and subdirectories contained within. Thus, do '''not''' list <code>%{_bindir}</code> and be careful of directories that may be shared with other packages.
注意,<code>%{_bindir}/*</code> 不会声明此软件包拥有 <code>/usr/bin</code> 目录,而只包含其中的文件。如果您列出一个目录,则该软件包拥有这个目录,及该目录内的所有文件和子目录。因此,'''不要'''列出 <code>%{_bindir}</code>,并且要小心的处理那些可能和其他软件包共享的目录。


如果满足下列条件,会有错误发生:
如果存在以下情况,可能引发错误:
* a pattern match does not match any file or directory
* 通配符未匹配到任何文件或目录
* a file or directory is listed or matched more than once
* 文件或目录被多次列出
* a file or directory in the <code>%{buildroot}</code> has not been listed
* 未列出 <code>%{buildroot}</code> 下的某个文件或目录


It is also possible to exclude files from a previous match by using the <code>%exclude</code> glob. This can be useful for including almost all of the files included by a different pattern match, but note that it will also fail if it does not match anything.


==== %files prefixes ====
You may need to add one or more prefixes to lines in the <code>%files</code> section; seperate them with a space. See [http://www.rpm.org/max-rpm/s1-rpm-inside-files-list-directives.html Max RPM section on %files directives].


Usually, "<code>%doc</code>" is used to list documentation files within <code>%{_builddir}</code> that were not copied to <code>%{buildroot}</code>. A <code>README</code> and <code>INSTALL</code> file is usually included. They will be placed in the directory <code>/usr/share/doc/%{name}-%{version}</code>, whose ownership does not need to be declared.
您也可以使用 <code>%exclude</code> 来排除文件。这对于使用通配符来列出全部文件时会很有用,注意如果未匹配到任何文件也会造成失败。


'''Note:''' If specifying a <code>%doc</code> entry, then you can't copy files into the documentation directory during the <code>%install</code> section. If, for example, you want an "examples" subdirectory within the documentation directory, don't use <code>%doc</code>, but instead create the directories and copy files over manually into <code>%{buildroot}%{_defaultdocdir}/%{name}-%{version}</code> during the %install section. They will be correctly marked as documentation. Make sure you include <code>%{_defaultdocdir}/%{name}-%{version}/</code> as an entry in the %files section.
==== %files 前缀 ====
您可能需要在 <code>%files</code> 部分添加一个或多个前缀;请用空格分隔。详情请查看 [http://www.rpm.org/max-rpm/s1-rpm-inside-files-list-directives.html Max RPM section on %files directives]。


Configuration files should be placed in <code>/etc</code> and are normally specified like this (which makes sure user changes aren't overwritten on update):
通常,"<code>%doc</code>" 用于列出 <code>%{_builddir}</code> 内,但未复制到 <code>%{buildroot}</code> 中的文档。通常包括 <code>README</code> 和 <code>INSTALL</code>。它们会保存至 <code>/usr/share/doc</code> 下适当的目录中,不需要声明 <code>/usr/share/doc</code> 的所有权。
 
'''注意:''' 如果指定 <code>%doc</code> 条目,rpmbuild < 4.9.1 在安装前会将 %doc 目录删除。这表明已保存至其中的文档,例如,在 <code>%install</code> 中安装的文档会被删除,因此最终不会出现在软件包中。如果您想要在 <code>%install</code> 中安装一些文档,请将它们临时安装到 build 目录(不是 build root 目录)中,例如 <code>_docs_staging</code>,接着在 <code>%files</code> 中列出,如 <code>%doc _docs_staging/*</code> 这样。
 
配置文件保存在 <code>/etc</code> 中,一般会这样指定(确保用户的修改不会在更新时被覆盖):
  %config(noreplace) %{_sysconfdir}/foo.conf
  %config(noreplace) %{_sysconfdir}/foo.conf
If the update uses a non-backwards-compatible configuration format, then specify them like this:
如果更新的配置文件无法与之前的配置兼容,则应这样指定:
  %config %{_sysconfdir}/foo.conf
  %config %{_sysconfdir}/foo.conf


"<code>%attr(mode, user, group)</code>" can be used for finer control over permissions, where a "<code>-</code>" means use the default:
"<code>%attr(mode, user, group)</code>" 用于对文件进行更精细的权限控制,"<code>-</code>" 表示使用默认值:
  %attr(0644, root, root) FOO.BAR
  %attr(0644, root, root) FOO.BAR


If a file is in particular natural language, use <code>%lang</code> to note that:
"<code>%caps(capabilities)</code>" 用于为文件分配 POSIX [http://linux.die.net/man/7/capabilities capabilities]。例如:
%caps(cap_net_admin=pe) FOO.BAR
 
如果包含特定语言编写的文件,请使用 <code>%lang</code> 来标注:
  %lang(de) %{_datadir}/locale/de/LC_MESSAGES/tcsh*
  %lang(de) %{_datadir}/locale/de/LC_MESSAGES/tcsh*


Programs using Locale files should follow the [[Packaging:Guidelines#Handling_Locale_Files|recommended method of handling i18n files]]:
使用区域语言(Locale)文件的程序应遵循 [[Packaging:Guidelines#Handling_Locale_Files|处理 i18n 文件的建议方法]]
* 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>


These prefixes are '''not''' valid in Fedora: <code>%license</code> and <code>%readme</code>.
以下前缀在 Fedora 中'''无效'''<code>%license</code> <code>%readme</code>


==== %files and Filesystem Hierarchy Standard (FHS) ====
==== %files 和文件系统层次标准 (FHS) ====


You should follow the [http://www.pathname.com/fhs/ Filesystem Hierarchy Standard (FHS)]. Executables go in <code>/usr/bin</code>, global configuration files go in <code>/etc</code>, libraries go into <code>/usr/lib</code> (or <code>/usr/lib64</code>) and so on. There is one exception: executables that should not normally be executed directly by users or administrators should go in a subdirectory of <code>/usr/libexec</code>, which is referred to as <code>%{_libexecdir}/%{name}</code>.
您应该遵守 [http://www.pathname.com/fhs/ 文件系统层次标准(FHS, Filesystem Hierarchy Standard)]。可执行文件保存在 <code>/usr/bin</code>,配置文件保存在 <code>/etc</code>, 共享库保存在 <code>/usr/lib</code>(或 <code>/usr/lib64</code>)等等。只有一个例外:不需要用户或管理员直接执行的可执行文件,应保存至 <code>/usr/libexec</code> 子目录,子目录通过 <code>%{_libexecdir}/%{name}</code> 宏来引用。


Do '''not''' install files into <code>/opt</code> or <code>/usr/local</code>.
'''不要''' 将文件安装到 <code>/opt</code> <code>/usr/local</code> 目录中。


Unfortunately, many programs do not follow the FHS by default. In particular, architecture-independent libraries get placed in <code>/usr/lib</code> instead of <code>/usr/share</code>. The former is for architecture-dependent libraries, while the latter is for architecture-independent libraries, which means that systems with different CPU architectures can share <code>/usr/share</code>. There are many exceptions in Fedora (such as Python and Perl), but Fedora applies this rule more strictly than some distributions. <code>rpmlint</code> will generally complain if you put anything other than ELF files into <code>/usr/lib</code>.
不幸的是,许多程序默认情况下并不遵守 FHS。尤其是,架构无关的共享库被保存至 <code>/usr/lib</code> 而非 <code>/usr/share</code> 之中。前者供依赖架构的共享库使用,后者供架构无关的共享库使用;这表示不同 CPU 架构的系统都能共享 <code>/usr/share</code> 目录。Fedora 中也有一些例外(如 Python 和 Perl),总的来说,Fedora 比其他发行版更严格遵守标准规范。<code>rpmlint</code> 会在将 ELF 以外的文件保存至 <code>/usr/lib</code> 目录时返回警告。


==== %files 示例 ====
==== %files 示例 ====


Here's a simple example of a %files section:
以下为 %files 部分的简单示例:
  %files
  %files
  %doc README LICENSE
  %doc README
%license LICENSE COPYING
  %{_bindir}/*
  %{_bindir}/*
  %{_sbindir}/*
  %{_sbindir}/*
Line 497: Line 487:
==== 找出重复内容 ====
==== 找出重复内容 ====


You can list any duplicates of two binary packages by doing:
您可以列出任意两个二进制软件包的重复文件,执行以下命令:
  cd ~/rpmbuild/RPMS/ARCH # Substitute "ARCH" for your architecture
  cd ~/rpmbuild/RPMS/ARCH # "ARCH" 替换为您的系统架构
  rpm -qlp PACKAGE1.*.rpm | sort > ,1
  rpm -qlp PACKAGE1.*.rpm | sort > ,1
  rpm -qlp PACKAGE2.*.rpm | sort > ,2
  rpm -qlp PACKAGE2.*.rpm | sort > ,2
Line 505: Line 495:
=== Scriptlets ===
=== Scriptlets ===


When an end-user installs the RPM, you may want some commands to be run. This can be achieved through scriptlets. See [[Packaging/ScriptletSnippets]].
当用户安装 RPM 时,您可能想要执行一些命令。这可以通过 scriptlets 完成。请查看 [[Packaging/ScriptletSnippets]]


Scriptlets can be run:
脚本片段可以:
* before ('''<code>%pre</code>''') or after ('''<code>%post</code>''') a package is installed
* 在软体包安装之前 ('''<code>%pre</code>''') 或之后 ('''<code>%post</code>''') 执行
* before ('''<code>%preun</code>''') or after ('''<code>%postun</code>''') a package is uninstalled
* 在软体包卸载之前 ('''<code>%preun</code>''') 或之后 ('''<code>%postun</code>''') 执行
* at the start ('''<code>%pretrans</code>''') or end ('''<code>%posttrans</code>''') of a transaction
* 在事务开始 ('''<code>%pretrans</code>''') 或结束 ('''<code>%posttrans</code>''') 时执行


For example, every binary RPM package that stores shared library files in any of the dynamic linker's default paths, must call <code>ldconfig</code> in <code>%post</code> and <code>%postun</code>. If the package has multiple subpackages with libraries, each subpackage should also perform the same actions.
例如,每个二进制 RPM 包都会在动态链接器的默认路径中存储共享库文件,并在 <code>%post</code> <code>%postun</code> 中调用 <code>ldconfig</code> 来更新库缓存。如果软件包有多个包含共享库的子包,则每个软体包也需要执行相同动作。
  %post -p /sbin/ldconfig
  %post -p /sbin/ldconfig
  %postun -p /sbin/ldconfig
  %postun -p /sbin/ldconfig


If only running a single command, then the "<code>-p</code>" option runs the adjacent command without invoking the shell. However, for several commands, omit this option and include the shell commands beneath.
如果仅执行一个命令,则 "<code>-p</code>" 选项会直接执行,而不启用 shell。然而,若有许多命令时,不要使用此选项,按正常编写 shell 脚本即可。


If you run any programs within the scriptlets, then you must specify any requirements in the form "<code>Requires(CONTEXT)</code>" (e.g. <code>Requires(post)</code>).
如果你在脚本片段中执行任何程序,就必须以 "<code>Requires(CONTEXT)</code>"(例: <code>Requires(post)</code>)的形式列出所有依赖。


<code>%pre</code>, <code>%post</code>, <code>%preun</code>, and <code>%postun</code> provide the argument <code>$1</code>, which is the number of packages of this name which will be left on the system when the action completes. Don't compare for equality with <code>2</code>, but instead check if they are greater than or equal to <code>2</code>. For <code>%pretrans</code> and <code>%posttrans</code>, <code>$1</code> is always <code>0</code>.
<code>%pre</code><code>%post</code><code>%preun</code> <code>%postun</code> 提供 <code>$1</code> 参数,表示动作完成后,系统中保留的此名称的软件包数量。不要比较此参数值是否等于 <code>2</code>,而是比较是否大于等于 <code>2</code><code>%pretrans</code> <code>%posttrans</code><code>$1</code> 的值恒为 <code>0</code>


For example, if the package installs an info manual, then the info manual index must be updated with <code>install-info</code> from the <code>info</code> package. Firstly, there is no guarantee that the <code>info</code> package will be available unless we explicitly declare it as required, and secondly, we don't want to fail completely if <code>install-info</code> fails:
例如,如果软件包安装了一份 info 手册,那么可以用 <code>info</code> 包提供的 <code>install-info</code> 来更新 info 手册索引。首先,我们不保证系统已安装 <code>info</code> 软件包,除非明确声明需要它;其次,我们不想在 <code>install-info</code> 执行失败时,使软件包安装失败:
  Requires(post): info
  Requires(post): info
  Requires(preun): info
  Requires(preun): info
Line 533: Line 523:
  fi
  fi


There is one other glitch related to installing info manuals. The <code>install-info</code> command will update the info directory, so we should delete the useless empty directory from the %{buildroot} during the <code>%install</code> section:
还有一个安装 info 手册时的小问题。<code>install-info</code> 命令会更新 info 目录,所以我们应该在 <code>%install</code> 阶段删除 %{buildroot} 中无用的空目录:
 
  rm -f %{buildroot}%{_infodir}/dir
  rm -f %{buildroot}%{_infodir}/dir


Another scriptlet-like abilility are "triggers", which can be run for your package when other packages are installed or uninstalled. See [http://rpm.org/api/4.4.2.2/triggers.html RPM Triggers].
另一个类似代码片段的功能是 "triggers"(触发器),它可以在其他软件包安装或删除时,为你的包执行一些动作。请参考 [http://rpm.org/api/4.4.2.2/triggers.html RPM Triggers]


=== 宏 ===
=== 宏 ===


宏通常以 <code>%{string}</code> 的格式出现,下面介绍常见的宏:
宏通常以 <code>%{string}</code> 格式出现,以下介绍常见的宏:


{|
{|
! 名称 !! 通常含义 !! 解释
! 宏名称 !! 典型扩展 !! 意义
|-
|-
| <code>%{_bindir}</code> || <code>/usr/bin</code> || 二进制文件存储目录,通常可执行。
| <code>%{_bindir}</code> || <code>/usr/bin</code> || 二进制目录:保存可执行文件
|-
|-
| <code>%{_builddir}</code> || <code>~/rpmbuild/BUILD</code> || 构建目录:构建软件包的目录,具体请见 <code>%buildsubdir</code>
| <code>%{_builddir}</code> || <code>~/rpmbuild/BUILD</code> || 构建目录:软件在 build 的子目录被编译。参考 <code>%buildsubdir</code>
|-
|-
| <code>%{buildroot}</code> || <code>~/rpmbuild/BUILDROOT</code> || Build root: where files are "installed" during the <code>%install</code> stage, which copies files from a subdirectory of <code>%{_builddir}</code> to a subdirectory of <code>%{buildroot}</code>. (Historically, <code>%{buildroot}</code> was in "/var/tmp/".)
| <code>%{buildroot}</code> || <code>~/rpmbuild/BUILDROOT</code> || Build root:<code>%install</code> 阶段中,将 <code>%{_builddir}</code> 子目录下的文件复制到 <code>%{buildroot}</code> 的子目录(之前,<code>%{buildroot}</code> 使用的位置为 "/var/tmp/"
|-
|-
| <code>%{buildsubdir}</code> || <code>%{_builddir}/%{name}</code> || Build subdirectory: a subdirectory within <code>%{_builddir}</code> where files are compiled during the <code>%build</code> stage. It is set after <code>%setup</code>.
| <code>%{buildsubdir}</code> || <code>%{_builddir}/%{name}</code> || 构建子目录:<code>%build</code> 阶段中,文件会在 <code>%{_builddir}</code> 的子目录中编译。此宏在 <code>%autosetup</code> 之后设置
|-
|-
| <code>%{_datadir}</code> || <code>/usr/share</code> || 系统共享目录
| <code>%{_datadir}</code> || <code>/usr/share</code> || 共享数据目录
|-
|-
| <code>%{_defaultdocdir}</code> || <code>/usr/share/doc</code> || 默认文档目录
| <code>%{_defaultdocdir}</code> || <code>/usr/share/doc</code> || 默认文档目录
|-
|-
| <code>%{dist}</code> || <code>.fc''NUMBER''</code> || 发行版名称+版本号(例如 "<code>.fc{{FedoraVersion}}</code>")
| <code>%{dist}</code> || <code>.fc''NUMBER''</code> || 发行版名称+版本号(例如 "<code>.fc{{FedoraVersion}}</code>"
|-
|-
| <code>%{fedora}</code> || <code>''NUMBER''</code> || 发行版版本号(例如 "<code>9</code>")
| <code>%{fedora}</code> || <code>''NUMBER''</code> || Fedora 发行版本号(例如 "<code>{{FedoraVersion}}</code>"
|-
|-
| <code>%{_includedir}</code> || <code>/usr/include</code>
| <code>%{_includedir}</code> || <code>/usr/include</code> || 程序头文件目录
|-
|-
| <code>%{_infodir}</code> || <code>/usr/share/info</code>
| <code>%{_infodir}</code> || <code>/usr/share/info</code> || info 手册目录
|-
|-
| <code>%{_initrddir}</code> || <code>/etc/rc.d/init.d</code>
| <code>%{_initrddir}</code> || <code>/etc/rc.d/init.d</code> || init 脚本目录
|-
|-
| <code>%{_libdir}</code> || <code>/usr/lib</code>
| <code>%{_libdir}</code> || <code>/usr/lib</code> || 共享库目录
|-
|-
| <code>%{_libexecdir}</code> || <code>/usr/libexec</code>
| <code>%{_libexecdir}</code> || <code>/usr/libexec</code> || 仅由系统调用执行该目录中的命令
|-
|-
| <code>%{_localstatedir}</code> || <code>/var</code>
| <code>%{_localstatedir}</code> || <code>/var</code> || 保存缓存/日志/lock等信息的目录
|-
|-
| <code>%{_mandir}</code> || <code>/usr/share/man</code>
| <code>%{_mandir}</code> || <code>/usr/share/man</code> || man 手册目录
|-
|-
| <code>%{name}</code> || || 软件包名称。set by Name: tag
| <code>%{name}</code> || || 软件包名称,通过 Name: tag 设置
|-
|-
| <code>%{_sbindir}</code> || <code>/usr/sbin</code>
| <code>%{_sbindir}</code> || <code>/usr/sbin</code> || 保存管理员可执行命令
|-
|-
| <code>%{_sharedstatedir}</code> || <code>/var/lib</code>
| <code>%{_sharedstatedir}</code> || <code>/var/lib</code> || 保存程序运行所处理的文件
|-
|-
| <code>%{_sysconfdir}</code> || <code>/etc</code>
| <code>%{_sysconfdir}</code> || <code>/etc</code> || 配置文件目录
|-
|-
| <code>%{version}</code> || || 软件包版本。set by Version: tag
| <code>%{version}</code> || || 软件包版本,通过 Version: tag 设置
|}
|}


Learn more about macros by looking in <code>/etc/rpm/*</code> and <code>/usr/lib/rpm</code>, especially <code>/usr/lib/rpm/macros</code>. Also use <code>rpm --showrc</code> to show values that RPM will use for macros (altered by <code>rpmrc</code> and macro configuration files).
您可以查看 <code>/etc/rpm/*</code> <code>/usr/lib/rpm</code>,以及 <code>/usr/lib/rpm/macros</code> 以进一步了解宏。或使用 <code>rpm --showrc</code> 显示当前 RPM 所使用的宏变量和值(根据 <code>rpmrc</code> 和宏配置文件)。


You can set your own macro values using %global, but be sure to define them before you use them. (Macro definitions can also refer to other macros.)  For example:
您可以使用 %global 来定义自己的宏,但在使用前需要先进行定义。(宏变量定义时,可以利用嵌套来引用其他宏。)例如:
  %global date 2012-02-08
  %global date 2012-02-08


Use the "<code>-E</code>" option of <code>rpmbuild</code> to find the value of a macro in a SPEC file:
使用 <code>rpmbuild</code> "<code>-E</code>" 选项查找 SPEC 文件中宏变量的值:
  rpmbuild -E '%{_bindir}' myfile.spec
  rpmbuild -E '%{_bindir}' myfile.spec


Also see [[Packaging/RPMMacros]] and [https://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch09s07.html RPM Guide chapter 9].
参考 [[Packaging/RPMMacros]] [https://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch09s07.html RPM 指南 - 第 9 ]


=== 其它标签 ===
=== 其它标签 ===


In addition to Requires and BuildRequires tags, you can also use these for controlling dependencies:
除了 Requires BuildRequires 标签外,你还可以使用以下标签控制依赖关系:
* '''Provides''': list virtual package names that this package provides. For example, there might be a package "<code>foo</code>" that demands a particular functionality "bar" from another program. If there are several packages that can satisfy that demand, those packages can specify "<code>Provides: bar</code>" and the "<code>foo</code>" package can specify "<code>Requires: bar</code>". You could also use the [http://dailypackage.fedorabook.com/index.php?/archives/6-Wednesday-Why-The-Alternatives-System.html "alternatives" system], but avoid if multiple users on the same system might want different default, as these settings are system-wide. Use "<code>rpm -q --provides PACKAGENAME</code>" to see what a given package provides. Some examples of virtual packages in Fedora:
 
** MTA: Used for mail transport agents, such as sendmail.
* '''Provides''': 列出此软件包提供的虚拟软件包名称。例如,可能有个 "<code>foo</code>" 软件包需要其他程序的 "bar" 功能;如果有许多软件包可以满足该需求,则这些包可以指定 "<code>Provides: bar</code>",而 "<code>foo</code>" 包可以指定 "<code>Requires: bar</code>"。你也可以使用 [http://dailypackage.fedorabook.com/index.php?/archives/6-Wednesday-Why-The-Alternatives-System.html "alternatives" 系统],但是若一个系统中有多个用户,不同人可能希望使用不同设置,此时请避免使用它,毕竟此设置为系统全局设置。使用 "<code>rpm -q --provides PACKAGENAME</code>" 查看指定包提供哪些虚拟包。Fedora 中的一些虚拟包示例:
** tex(latex): Used for latex
** MTA: 邮件传输代理(mail transport agent, MTA),例如 sendmail。
* '''Obsoletes''': remove another named package(s) when this package is installed. Use when the package name changes or when it totally replaces a different package.
** tex(latex): 用于 latex
* '''Conflicts''': state what other packages cannot be installed simultaneously to this one. Avoid this if you can. See [[Packaging/Conflicts]].
* '''Obsoletes''': 当软件包安装时卸载另一个指定的包。用于软件包改名时,或是用该软件包完全取代另一个不同的包时使用。
* '''BuildConflicts''': state what packages cannot be installed when building this package. Avoid this if you can.
* '''Conflicts''': 表示安装此包时冲突的软件包。如果可以请避免使用此标签。查看 [[Packaging/Conflicts]]
* '''BuildConflicts''': 表示编译此包时冲突的软件包。如果可以请避免使用此标签。


To manage different architectures, there are two tags:
若要处理不同的系统架构,可使用以下 2 个标签:
* '''ExcludeArch''': to exclude an architecture on which the package doesn't build. For example:
* '''ExcludeArch''': 排除无法构建此软件包的系统架构。例如:
  ExcludeArch: ppc
  ExcludeArch: ppc
* '''ExclusiveArch''': to include only the specified architecture. Avoid this unless absolutely correct.
* '''ExclusiveArch''': 仅包含指定的系统架构。除非绝对正确,否则请避免使用。
Valid architectures are listed at [[Architectures]].
可用的系统架构在 [[Architectures]] 中列出。


=== 子软件包 ===
=== 子软件包 ===


A SPEC file can define several binary package. In other words, one SRPM with one SPEC file can result in several RPMS. Note that there is still only one creation (%prep, %build, %install etc.) process. <code>name-doc</code> and <code>name-devel</code> subpackages are common for documentation and development files respectively.
一个 SPEC 文件可以定义多个 RPM 包。换句话说,一个 SRPM 文件可以制作出多个 RPM 包。注意,这仍然只需要一个构建(%prep、%build、%install 等)过程。<code>name-doc</code> <code>name-devel</code> 是最常见的文档和开发文件子软件包。


Use the <code>%package</code> directive to start defining a subpackage:
使用 <code>%package</code> 宏指令来定义子软件包:
  %package subpackage_name
  %package subpackage_name


After each <code>%package</code> directive, list the tags for the subpackage. This should at least include the Summary and Group tags, as well as the <code>%description subpackage_name</code> and <code>%files subpackage_name</code> directives:
在每个 <code>%package</code> 指令后,需要列出该子包的必要标签。至少应包括 Summary Group 标签,以及 <code>%description subpackage_name</code> <code>%files subpackage_name</code> 指令:


Anything not specified by the subpackage will be inherited from its parent.
%package foo
Summary: 简介
Group: 包组
%description foo
相关描述
%files foo
该包需要包含的文件
 
任何子包中未指定的标签,都会从主包继承。


By default, if the package name is "<code>foo</code>" and the subpackage name is "<code>bar</code>", then the resulting subpackage will be "<code>foo-bar</code>". You can override it with the "<code>-n</code>" option (but you'll need to use it in all other directives too if you specify it here):
默认情况下,如果软件包名为 "<code>foo</code>",而子包名为 "<code>bar</code>",则生成的子包为 "<code>foo-bar</code>"。可以使用 "<code>-n</code>" 选项指定包名(但需要在所有其它部分添加此选项):
  %package -n new_subpackage_name
  %package -n new_subpackage_name


[http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch10s04.html See the RPM Guide section on subpackages] for more information.
查看 [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch10s04.html RPM 指南 针对子包的章节] 了解更多信息。


=== Conditionals ===
=== 条件判断 ===


You can insert conditional statements, for example to test if you are creating a binary for a certain architecture:
你可以插入条件判断语句,例如可根据特定系统架构执行不同动作:
  %ifarch ARCHITECTURE_NAME
  %ifarch ARCHITECTURE_NAME
the negated version with:
相反的用法为:
  %ifnarch ARCHITECTURE_NAME
  %ifnarch ARCHITECTURE_NAME
or the more general conditional:
通用的条件判断用法为:
  %if TRUE_OR_FALSE
  %if TRUE_OR_FALSE


There is an optional "<code>%else</code>" section; all of these are closed with "<code>%endif</code>".
可以选择使用 "<code>%else</code>" 字段;条件判断使用 "<code>%endif</code>" 结束。


=== Application Specific Guidelines ===
=== 应用程序具体规定 ===


There are many application-specific guidelines that can help you (e.g., for specific programming languages, applications, libraries, and build systems).  Many of them are listed as part of the
有许多应用程序的具体规定可以帮助你(例:程序语言、应用程序、共享库、构建系统等)。大多都列在 [[Packaging/Guidelines#Application_Specific_Guidelines|应用程序专用打包规定]]。一些应用程序具体规定的例子有:
[[Packaging/Guidelines#Application_Specific_Guidelines|Application Specific Guidelines of Packaging/Guidelines]].  Examples of application-specific guidelines are those for:
* [[Packaging:Cmake|Cmake]]
* [[Packaging:Cmake|Cmake]]
* [[Packaging:Emacs|Emacs]]
* [[Packaging:Emacs|Emacs]]


Failing that, some other ways of finding application-specific help are:
另外,还有一些可以帮你找到应用程序专用规定的方法:
* The 'SEARCH' command on Fedoraproject.org.
* Fedoraproject.org 的 'SEARCH' 命令
* [[PackagingDrafts]]
* [[PackagingDrafts]]
* A [[SIGs|Special Interest Group (SIG)]]
* [[SIGs|特别兴趣小组(Special Interest Group, SIG)]]
* [http://fedoraproject.org/wiki/Special:PrefixIndex/Packaging Wiki pages prefixed with 'Packaging']
* [http://fedoraproject.org/wiki/Special:PrefixIndex/Packaging 'Packaging' 开头的 Wiki 页面]


=== 小提示 ===
=== 其他注意事项 ===


[[Packaging/FrequentlyMadeMistakes]] has information on frequently-made mistakes. There are also some recommendations and controversial tricks on
[[Packaging/FrequentlyMadeMistakes]] 包含常见错误的相关信息。[[PackageMaintainers/Packaging Tricks]] 包含一些推荐,以及有争议的技巧。
[[PackageMaintainers/Packaging Tricks]].


Try to write your SPEC files so that it is likely to work when a new release is made upstream, without any changes aside from bumping the version number and refreshing the source files. For example, if it contains *.txt files with execute bits, instead of doing
请尝试编写你的 SPEC 文件,尽可能在上游更新时使一切都能水到渠成,使你除了修改版本号并刷新源文件外,不需要做其他任何修改。例如,如果要为 *.txt 文件设置执行权限,请不要用:
   chmod a-x Filename1.txt Filename2.txt Filename3.txt
   chmod a-x Filename1.txt Filename2.txt Filename3.txt
consider doing this, which will handle new filenames that use the same file naming convention:
而是,考虑使用以下方式处理,可直接处理使用相同命名规则的新文件:
   chmod a-x *.txt
   chmod a-x *.txt


If you want to see lots of examples of scriptlets, you can show all the scriptlets on installed programs using:
如果你想查看大量脚本片段,使用以下命令显示所有已安装包的脚本片段:
   rpm -qa --queryformat "\n\nPACKAGE: %{name}\n" --scripts | less
   rpm -qa --queryformat "\n\nPACKAGE: %{name}\n" --scripts | less


Don't try to interact with the user; RPM is designed to support batch installs.  If an application needs to show a EULA, that needs to be part of its initial execution, not its installation.
不要尝试和用户交互;RPM 以支持批量安装为设计核心。如果有程序需要显示 EULA 用户授权协议,则应在初次执行时执行该动作,而非安装时。


You might not want to start services, because in a big install that could slow things down. If you install an init or systemd script, consider using <code>chkconfig</code> or <code>systemctl</code> to arrange for the service to be started/stopped on the next reboot.  Before uninstalling, you should normally try to stop its services if they are running.
建议不要试图启动服务,因为这会使安装过程变得缓慢。如果你安装 init systemd 脚本,请考虑使用 <code>chkconfig</code> <code>systemctl</code> 安排服务在下次重启时启动/停止该服务。卸载前,如果服务正在运行,一般需要先尝试停止这些服务。


Uninstalling should reverse most changes made during installation, but don't remove any user-created files.
卸载要尽可能撤消安装阶段中所做的更改,但不要删除任何用户创建的文件。


Normally, if there are binary executables, then debugging symbols are stripped from the normal binary packages and placed into a <code>name-debug</code> subpackage. If this shouldn't happen, you can disable the stripping and creation of this subpackage by putting this at the top of your SPEC:
一般而言,如果有二进制文件,则会剥离其中包含的调试信息,并将调试信息保存至 <code>name-debug</code> 子包中。如果需要禁用此动作,可以在 SPEC 文件顶部添加以下指令:
  %global _enable_debug_package 0
  %global _enable_debug_package 0
  %global debug_package %{nil}
  %global debug_package %{nil}
  %global __os_install_post /usr/lib/rpm/brp-compress %{nil}
  %global __os_install_post /usr/lib/rpm/brp-compress %{nil}


To prevent stripping you may also need to do this in the <code>%install</code> section:
若要避免执行剥离动作,还需要在 <code>%install</code> 部分添加以下变量:
  export DONT_STRIP=1
  export DONT_STRIP=1


A way to check for the version of Fedora in a SPEC file for conditional builds is:
通过条件判断的方式,在 SPEC 文件中检查 Fedora 版本:
  %if 0%{?fedora} <= <version>
  %if 0%{?fedora} <= <version>
The <code>?</code> causes the macro to evaluate to evaluate to blank if <code>%fedora</code> is not defined. This causes the end result to be the <code>0</code> (which is a number and thus fine), while not interfering with the result if there is actually a value for <code>%fedora</code>. (Note that this trick does not work in Koji "scratch" builds, where <code>%fedora</code> is set during the creation of a SRPM.)
<code>?</code> 使 <code>%fedora</code> 宏在未定义时返回空。这样会使结果为 <code>0</code> ,而 <code>%fedora</code> 宏若存在数值时也不会有干扰。(注意,这种做法在 Koji "scratch" 编译中不起作用,<code>%fedora</code> 的值在创建 SRPM 时已设定)


GUI programs must have a desktop entry so that people can invoke it from the graphical desktop menu. For <code>.desktop</code> files, see [[Packaging/Guidelines#Desktop_files|Fedora packaging guidelines for desktop files]] and [http://standards.freedesktop.org/desktop-entry-spec/latest/ desktop entry spec]. For icons within <code>/usr/share/icons</code>, see [http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html icon theme spec].
GUI 程序必须有桌面条目(desktop entry),以便用户通过图形化菜单启动程序。对于 <code>.desktop</code> 文件,请参考 [[Packaging/Guidelines#Desktop_files|Fedora packaging guidelines for desktop files]] [http://standards.freedesktop.org/desktop-entry-spec/latest/ desktop entry spec]。对于 <code>/usr/share/icons</code> 中的图标,请参考 [http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html icon theme spec]


== 构建 ==
== 构建 RPM 包 ==


=== 使用 rpmlint 测试 ===
=== 使用 rpmlint 测试 ===


请先使用 <code>rpmlint</code> 查找 SPEC 文件的错误:
为避免常见错误,请先使用 <code>rpmlint</code> 查找 SPEC 文件的错误:
  $ rpmlint program.spec
  $ rpmlint program.spec
如果没什么异常,使用 "<code>-i</code>" 参数查看长输出。
如果返回错误/警告,使用 "<code>-i</code>" 选项查看更详细的信息。


某些情况下 <code>rpmlint</code> 的报错不一定代表有问题,请查看 [[Packaging/Guidelines#Use_rpmlint|打包规定]]
有时,<code>rpmlint</code> 也会有误报的情况发生。请查看 [[Packaging/Guidelines#Use_rpmlint|打包规定]] 了解哪些错误可以忽略。
了解具体有哪些。


=== 从 SPEC 构建 RPM ===
=== 从 SPEC 构建 RPM ===


一旦 SPEC 编写完毕,请通过 rpmbuild 来构建:
一旦 SPEC 编写完毕,请执行以下命令来构建 SRPM 和 RPM 包:
  $ rpmbuild -ba program.spec
  $ rpmbuild -ba program.spec


成功的话,RPM 会保存至 <code>~/rpmbuild/RPMS</code>,SRPM 会保存至 <code>~/rpmbuild/SRPMS</code>。
如果成功,RPM 会保存至 <code>~/rpmbuild/RPMS</code>,SRPM 会保存至 <code>~/rpmbuild/SRPMS</code>。


If it fails, go to the appropriate directory and see what is left over. To help debug, you can skip earlier stages that succeeded with the "<code>--short-circuit</code> option. For example, to restart at the <code>%install</code> stage (skipping earlier stages), do this:
如果失败,请查看 BUILD 目录的相应编译日志。为了帮助调试,可以用 "<code>--short-circuit</code>" 选项来忽略成功的阶段。例如,若想要(略过更早的阶段)重新从 <code>%install</code> 阶段开始,请执行:
  $ rpmbuild -bi --short-circuit program.spec
  $ rpmbuild -bi --short-circuit program.spec


如果只想创建 SRPM(也就是不需要执行 <code>%prep</code> 或 <code>%build</code> 或其他阶段),请运行:
如果只想创建 SRPM(不需要执行 <code>%prep</code> 或 <code>%build</code> 或其他阶段),请执行:
  rpmbuild -bs program.spec
  rpmbuild -bs program.spec


=== 使用 rpmlint 测试已构建的 RPM ===
=== 使用 rpmlint 测试已构建的 RPM ===


<code>rpmlint</code> 可用于检查 SPEC/RPM/SRPM 的错误。You need to eliminate or justify warnings before posting a package. If you are in the SPECS directory, do this:
<code>rpmlint</code> 用于检查 SPEC/RPM/SRPM 是否存在错误。你需要在发布软件包之前,解决这些警告。[[Common_Rpmlint_issues|此页面]] 提供一些常见问题的解释。如果你位于 SPEC 目录中,请执行:
  $ rpmlint ''NAME''.spec ../RPMS/*/''NAME''*.rpm ../SRPMS/''NAME''*.rpm
  $ rpmlint ''NAME''.spec ../RPMS/*/''NAME''*.rpm ../SRPMS/''NAME''*.rpm


进入 <code>~/rpmbuild/RPMS</code> 目录下的特定架构目录,您会发现有许多二进制 RPM 包。Quickly see their files and permissions (to check whether they are correct) by doing:
进入 <code>~/rpmbuild/RPMS</code> 下的特定架构目录中,您会发现有许多二进制 RPM 包。使用以下命令快速查看 RPM 包含的文件和权限:
  $ rpmls *.rpm
  $ rpmls *.rpm


如果看上去正常,以 root 权限安装它们:
如果看上去正常,以 root 身份安装它们:
  # rpm -ivp package1.rpm package2.rpm package3.rpm ...
  # rpm -ivp package1.rpm package2.rpm package3.rpm ...


Test the programs in a few different ways to see if everything works correctly. If it is a GUI tool, make sure it shows up in the desktop menu, otherwise the <code>.desktop</code> entry is wrong.
以不同方式来测试程序,看看是否全部都正常工作。如果是 GUI 工具,请确认其是否出现在桌面菜单中,否则表示 <code>.desktop</code> 条目可能有错。


最后删除:
最后卸载软件包:
  # rpm -e package1 package2 package3
  # rpm -e package1 package2 package3


== Mock 和 Koji ==
== Mock 和 Koji ==


[[Projects/Mock|Mock]] is a powerful tool that uses the SRPM you have created to build binary packages within a nearly empty environment. This can reveal if you have accurate build dependencies. If it fails, then you forgot to list something in BuildRequires. See [[Using Mock to test package builds]]. Once your account is a member of the "<code>mock</code>" group, you can run commands like this to do local testing:
[[Projects/Mock|Mock]] 用于在标准环境下,使用 SRPM 来构建二进制 RPM 包的强大工具。这可以暴露出包的构建依赖是否存在问题。如果构建失败,表示可能缺少某些 BuildRequires 编译依赖。请参考 [[Using Mock to test package builds|使用 Mock 测试构建软件包]]。一旦你的账户属于 "<code>mock</code>" 组,执行以下命令进行本地构建测试:
  $ mock -r fedora-9-i386 rebuild path_to_source_RPM
  $ mock -r fedora-9-i386 rebuild path_to_source_RPM


You can use Koji (which uses <code>mock</code>) to do builds on many different systems, some of which you may not have. [[PackageMaintainers/Join]] and [[PackageMaintainers/UsingKoji]] have more information about Koji. Once it's set up, you can test your SRPM on a variety of platforms by running commands like:
你可以使用 Koji(会使用 <code>mock</code>)在各种不同的系统上执行构建,包括你没有的系统架构。[[PackageMaintainers/Join]] [[PackageMaintainers/UsingKoji]] 包含更多有关 Koji 的信息。一旦设置完成,你就可以使用以下命令,在各种平台上测试你的 SRPM:
  $ koji build --scratch dist-f9 path_to_source_RPM
  $ koji build --scratch dist-f9 path_to_source_RPM


Replace <code>dist-f9</code> with any later release of Fedora, but don't use <code>dist-rawhide</code>. Remember, the values of <code>%fedora</code>, <code>%fc9</code> and so on will not be correct for a scratch build, so this won't work if your SPEC file does something different based on those values.
请将 <code>dist-f9</code> 替换为任意 Fedora 发行版本,但不要使用 <code>dist-rawhide</code>。记住,<code>%fedora</code><code>%fc9</code> 等宏变量的值都不会在 scratch build 中得到修正,所以如果你的 SPEC 会根据宏变量值来执行不同操作,则不能起作用。


Your Koji builds can only depend on packages that are actually in the TARGET distribution repository.  Thus, you can't use Koji to build for released distributions if your package depends on other new packages that Bodhi hasn't released yet. If you need to build against a package that is not yet a stable released update, you can file a ticket with rel-eng at: https://fedorahosted.org/rel-eng/newticket and request that that package be added as a buildroot override.
你的 Koji 构建只依赖 TARGET 发行版软件源中实际存在的软件包。因此,如果你的软件包依赖 Bodhi 中尚未发行的其它软件包,则不能使用 Koji 为已发行的版本进行构建。如果你需要为尚未稳定的版本构建软件包,请通过 Bodhi 提交 Koji buildroot override 请求。如果你的软件包依赖其他人维护的软件包,请联系其维护者。[在 Bodhi 可以处理 Koji buildroot override 请求之前,以前的旧方法是在此处提交 rel-eng 请求:https://fedorahosted.org/rel-eng/newticket ,并请求将该软件包加入成为 buildroot override。]


== 有帮助的工具 ==
== 有用的工具 ==


<code>rpmdevtools</code> 软件包包含大量有用的工具;"<code>rpm -qil rpmdevtools</code>" 将会显示所有可用命令。
<code>rpmdevtools</code> 软件包包含各种有用的工具;"<code>rpm -qil rpmdevtools</code>" 将显示此包的相关信息和文件列表。
* <code>rpmdev-bumpspec</code> : bump the release tag in the spec file and add a changelog comment with the right date and version 格式:
* <code>rpmdev-bumpspec</code> : 增加 spec 文件的发行版本号,并以当前时间和版本格式添加 changelog 日志:
  rpmdev-bumpspec --comment=COMMENT --userstring=NAME+EMAIL_STRING SPECFILES
  rpmdev-bumpspec --comment=COMMENT --userstring=NAME+EMAIL_STRING SPECFILES


The <code>yum-utils</code> package also has some useful tools:
DNF 下载插件(DNF 核心插件)也十分有用:
* <code>yumdownloader</code> : download the SRPM of the package by running:
* <code>dnf download</code> : 下载指定软件包的 SRPM 包,示例如下:
yumdownloader --source PACKAGENAME
dnf download --source PACKAGENAME
 
<code>auto-buildrequires</code> 软件包有一个好用的工具,可以帮助我们找到合适的 BuildRequires 条目。安装此包后,使用 "<code>auto-br-rpmbuild</code>"  替换 "<code>rpmbuild</code>",你就会看到自动生成的 BuildRequires 列表。
 
你可能发现 [http://rust.sourceforge.net/ RUST] 蛮好用的(GPL),但是它不能生成符合 Fedora 软件包质量的 SPEC 文件。[http://kitenet.net/~joey/code/alien/ Alien] 可以转换软件包格式;它不能生成干净的 SRPM,但转换已有的软件包或许可以提供一些有用的信息。
 
最后,[https://github.com/alanfranz/docker-rpm-builder docker-rpm-builder] (APL 2.0) 使用 [http://www.docker.com Docker] 构建 RPM 包;使用 rpmbuild 构建,目标架构需要与系统架构相同。另外,mock 对于任何目标结构的 Fedora/Centos/RHEL 发行版都能完美工作,'''无论 Docker 是否能运行'''。


The <code>auto-buildrequires</code> package has a pair of nice tools for helping to figure out the proper BuildRequires entries. After installing this package, replace "<code>rpmbuild</code>" with "<code>auto-br-rpmbuild</code>" and you'll see an automatically generated BuildRequires list.
如果你想要为不同发行版和系统架构来编译软件包,并且提供公开访问的 dnf 软件源,你可以提交你的 src.rpm 到 [https://copr.fedoraproject.org Copr]。


You might find [http://rust.sourceforge.net/ RUST] useful (GPL), though it does not create SPEC files of suitable quality for Fedora packages. [http://kitenet.net/~joey/code/alien/ Alien] converts between package formats. It won't produce clean SRPMS, but converting an existing package might provide helpful information.
如果你需要签名你的软件包,可以使用 <code>rpm-sign</code> 软件包的 <code>rpmsign</code> 工具。


== 相关规定 ==
== 相关规定 ==


在您创建软件包的时候,请遵守下列规定:
在您创建软件包的时候,请遵守以下规定:
* [[Join the package collection maintainers|如何成为 Fedora 软件包仓库维护人员]] - 描述成为 packager 的流程。
* [[Join the package collection maintainers|如何成为 Fedora 软件包仓库维护人员]] - 描述成为 Packager 的流程。
* [[Packaging:Guidelines |打包规定]]
* [[Packaging:Guidelines|打包规定]]
* [[Packaging:NamingGuidelines|软件包命名规定]]
* [[Packaging:NamingGuidelines|软件包命名规定]]
* [[Packaging:LicensingGuidelines|软件包许可协议规定]]
* [[Packaging:LicensingGuidelines|软件包许可协议规定]]
* [[Packaging:DistTag|Dist 标签使用指南]]  
* [[Packaging:DistTag|Dist 标签使用指南]]
* [[Packaging:ReviewGuidelines|软件包审核规定]]
* [[Packaging:ReviewGuidelines|软件包审核规定]]


这里也有许多针对特殊环境打包的规定(Java 程序,OCaml 程序,GNOME 程序等等);您也可以从 [[SIGs]] 获取许多有价值的帮助。[[:Category:Package Maintainers|软件包维护人员]] 分类是所有部分的总结页面。
这里也有许多针对特殊环境打包的规定(如 Java 程序,OCaml 程序,GNOME 程序等等);您也可以从 [[SIGs]] [[:Category:Package Maintainers|软件包维护人员]] 获取许多有价值的帮助。


[https://fedoraproject.org/wiki/Special:Prefixindex/Packaging 这里还有一份所有有关打包的页面列表]。
[https://fedoraproject.org/wiki/Special:Prefixindex/Packaging 这里还有一份所有有关打包的 Wiki 页面列表]。


除了这些,还有许多非官方的指南帮助,比如
除了这些,还有许多非官方的指南,比如
[https://fedoraproject.org/wiki/Special:Search?ns0=1&search=PackagingDrafts%2F&searchx=Search 打包规定草稿],[https://fedoraproject.org/wiki/PackagingDrafts 今后需要制定的打包规定]。
[https://fedoraproject.org/wiki/Special:Search?ns0=1&search=PackagingDrafts%2F&searchx=Search 打包规定草稿],[[PackagingDrafts|今后需要制定的打包规定]]。


还有其它地方也可供参考,比如 [http://en.opensuse.org/Packaging SuSE 的打包规定],
还有其它信息也可供参考,比如 [http://en.opensuse.org/Packaging SuSE 打包规定],
[http://www.debian.org/doc/debian-policy/ Debian 的打包规定],但是 [http://www.mail-archive.com/distributions@lists.freedesktop.org/msg00156.html 每个发行版都有不一样的地方],所以不要直接生搬硬套。
[http://www.debian.org/doc/debian-policy/ Debian 打包规定],但是 [http://www.mail-archive.com/distributions@lists.freedesktop.org/msg00156.html 每个发行版都有所不同],所以不要直接生搬硬套。


'''请注意所有 spec 文件必须是有关开源的软件,就像[[Legal:Fedora_Project_Contributor_Agreement|FPCA]] 声明所描述的一样。'''
'''请注意所有 spec 文件必须与开源软件有关,就像 [[Legal:Fedora_Project_Contributor_Agreement|FPCA]] 声明所描述的一样。'''


== 维护软件包 ==
== 维护软件包 ==


一旦您的软件包被批准放入官方软件源,您需要维护或者协助维护它。
一旦您的软件包被批准加入官方软件源,您和您的副维护者需要共同维护它。请查看 [[Package update HOWTO]] 和 [[Package update guidelines]] 了解更多信息。如果您为多个不同的 Fedora 版本维护软件包,有空一定要为之前的 Fedora 版本也制作一个。
请见 [[Package update HOWTO]] 和 [[Package update guidelines]] 以便了解更多。
如果您为多个不同的 Fedora 版本维护软件包,有空一定要为之前的 Fedora 版本也制作一个。


Encourage the upstream developers to use standard source code release conventions.  Using standard conventions makes packaging ''much'' easier.  For more information, see:
请鼓励上游开发者使用标准源码发行惯例。使用标准惯例可以使打包流程更轻松。了解更多信息,请查看:
* [http://www.dwheeler.com/essays/releasing-floss-software.html Releasing Free/Libre/Open Source Software (FLOSS) for Source Installation] (a quick summary)
* [http://www.dwheeler.com/essays/releasing-floss-software.html Releasing Free/Libre/Open Source Software (FLOSS) for Source Installation] (简明摘要)
* [http://www.gnu.org/prep/standards/html_node/Managing-Releases.html GNU Coding Standards release process]
* [http://www.gnu.org/prep/standards/html_node/Managing-Releases.html GNU Coding Standards release process]
* [http://en.tldp.org/HOWTO/Software-Release-Practice-HOWTO/ Software Release Practice HOWTO]
* [http://en.tldp.org/HOWTO/Software-Release-Practice-HOWTO/ Software Release Practice HOWTO]
Line 789: Line 791:
== 更多信息 ==
== 更多信息 ==


[[:Category:Package Maintainers|软件包维护人员]] 页面加入了许多有用页面的链接,您不妨去看看。[[Package update HOWTO|如何升级您维护的软件包]] 页面描述了如何升级您维护的在 Fedora 源里面的软件包。
[[:Category:Package Maintainers|软件包维护人员]] 页面加入了许多有用页面的链接,您不妨去看看。[[Package update HOWTO|如何更新您维护的软件包]] 页面描述了如何更新您在 Fedora 中维护的软件包。
 
更多在维基之外的页面,请见以下:


* [http://www.g-loaded.eu/2006/04/05/how-to-build-rpm-packages-on-fedora/ 如何在 Fedora 系统中制作RPM包]——很简单的贯通介绍
Fedora Wiki 之外的更多其他信息,请查看:
* 使用 RPM 为软件打包(developerWorks): [http://www.ibm.com/developerworks/library/l-rpm1/ 第一章], [http://www.ibm.com/developerworks/library/l-rpm2/ 第二章], and [http://www.ibm.com/developerworks/library/l-rpm3 第三章]
* [http://www.g-loaded.eu/2006/04/05/how-to-build-rpm-packages-on-fedora/ 如何在 Fedora 系统中制作 RPM 包]——很简单明了的介绍
* Fedora Classroom had a IRC session on packaging and you can refer to the logs at https://fedoraproject.org/wiki/Building_RPM_packages_%2820090405%29
* 使用 RPM 为软件打包 (developerWorks): [http://www.ibm.com/developerworks/library/l-rpm1/ 第一章], [http://www.ibm.com/developerworks/library/l-rpm2/ 第二章], [http://www.ibm.com/developerworks/library/l-rpm3 第三章]
* Fedora 课堂包含一个关于打包的 IRC 会话,参考日志在 https://fedoraproject.org/wiki/Building_RPM_packages_%2820090405%29
* [http://koti.welho.com/vskytta/packagers-handbook/packagers-handbook.html Fedora 打包者手册]
* [http://koti.welho.com/vskytta/packagers-handbook/packagers-handbook.html Fedora 打包者手册]
* [http://www.redhatmagazine.com/2008/02/28/when-sally-met-eddie-the-fedora-package-story/ 当 Sally 遇见 Eddie]------一个较详细的 RPM 故事
* [http://www.redhatmagazine.com/2008/02/28/when-sally-met-eddie-the-fedora-package-story/ 当 Sally 遇见 Eddie] — 一个详细的 RPM 故事
* [http://rpm.org/max-rpm-snapshot/ Maximum RPM Book]——一个内容虽然最丰富,可惜早已过时的 RPM 教程 (由 Red Hat 公司撰写)
* [http://rpm.org/max-rpm-snapshot/ Maximum RPM Book] — 内容虽然最丰富,可惜早已过时的 RPM 教程(由 Red Hat 撰写)
* [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch-creating-rpms.html RPM 指南创建 RPM 部分]——虽然只是一份草稿,但是内容很新
* [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch-creating-rpms.html RPM 指南, 创建 RPM 部分] — 虽然只是一份草稿,但是内容很新
* [http://docs.fedoraproject.org/developers-guide/ch-rpm-building.html Developer's guide, section on building RPMs]
* [http://docs.fedoraproject.org/developers-guide/ch-rpm-building.html Developer's guide, section on building RPMs]
* [http://www.gurulabs.com/GURULABS-RPM-LAB/GURULABS-RPM-GUIDE-v1.0.PDF Creating RPMS slides] from Guru Labs
* [http://www.gurulabs.com/GURULABS-RPM-LAB/GURULABS-RPM-GUIDE-v1.0.PDF Creating RPMS slides] from Guru Labs
* [http://freshrpms.net/docs/fight/ The fight, my first attempt to make a readable rpm package building introduction.]
* [http://freshrpms.net/docs/fight/ 第一次尝试编写一个易读的 rpm 包构建介绍]
* [http://genetikayos.com/code/repos/rpm-tutorial/trunk/rpm-tutorial.html RPM Tutorial (Fullhart)]
* [http://www-uxsup.csx.cam.ac.uk/talks/rpmbuild/rpmbuild.pdf Cambridge RPM tutorial] 创建基本 RPM 包的演示
* [http://www-uxsup.csx.cam.ac.uk/talks/rpmbuild/rpmbuild.pdf Cambridge RPM tutorial] is a presentation on creating basic RPMs
* [http://en.tldp.org/HOWTO/RPM-HOWTO/index.html 由 Donnie Barnes 撰写的 RPM 指南]
* [http://en.tldp.org/HOWTO/RPM-HOWTO/index.html 由 Donnie Barnes 撰写的 RPM 指南]
* [http://home.fnal.gov/~dawson/rpms/howto/index.html 由 DawsonRPM 撰写的 RPM 指南]
* [http://home.fnal.gov/~dawson/rpms/howto/index.html 由 Dawson 撰写的 RPM 指南]
* [http://en.opensuse.org/SUSE_Build_Tutorial SuSE 构建向导]SuSE 系统的 RPM 打包教程,不是针对 Fedora 的但是可以参考;[http://en.opensuse.org/Build_Service/cross_distribution_package_how_to Cross-distribution package HOWTO] 倒是有很多针对不同发行版制作 RPM 包的指导。
* [http://en.opensuse.org/SUSE_Build_Tutorial SuSE 构建向导] SuSE 系统的 RPM 打包教程,可以参考;[http://en.opensuse.org/Build_Service/cross_distribution_package_how_to Cross-distribution package HOWTO] 有很多针对不同发行版制作 RPM 包的指导。
* [http://wiki.mandriva.com/en/Development/Howto/RPM Mandriva RPM 指南] ([http://www.mandrivaclub.com/xwiki/bin/view/KB/MandrivaRpmHowTo alt]) Mandriva 系统的 RPM 打包指南。注意: 在 Fedora 中, ''万万不可''重新压缩软件源代码,正如 Mandriva 官方提示,这样会导致软件无法完成自身校验。
* [http://wiki.mandriva.com/en/Development/Howto/RPM Mandriva RPM 指南] ([http://www.mandrivaclub.com/xwiki/bin/view/KB/MandrivaRpmHowTo alt]) Mandriva 系统的 RPM 打包指南。注意:在 Fedora 中, ''万万不可''重新压缩软件源代码,正如 Mandriva 官方提示,这样会导致软件无法完成自身校验。
* [http://linuxshellaccount.blogspot.com/2008/03/creating-your-own-linux-rpms-initial.html Creating Your Own Linux RPM's - The Initial Software Build] is another brief intro, but it makes the point that "The process of building RPM's is much simpler than creating packages for Solaris... Fewer steps, and the ability to add all of your software information into one specification file, makes for a much tighter (and easier to modify or reproduce) software packaging system."
* [http://linuxshellaccount.blogspot.com/2008/03/creating-your-own-linux-rpms-initial.html 创建你的 Linux RPM - 初次编译软件] 简单介绍 RPM 构建,但它使 "构建 RPM 的过程比创建 Solaris 软件包的过程... 步骤更少更简单,并教会你添加软件的所有信息到 spec 文件,使维护/修改更容易。"
* [http://fedoranews.org/alex/tutorial/rpm/ All you need to know about RPM] (more about installing packages than creating them)
* [http://fedoranews.org/alex/tutorial/rpm/ 关于 RPM 所有你应该知道的] 包含更多的安装信息
* [http://wiki.rpm.org/ rpm.org的维基页面] 也含有大量对您有用的信息,比如 [http://wiki.rpm.org/Problems 已知的一些有关 RPM 机制的问题]。
* [http://wiki.rpm.org/ rpm.org 维基页面] 包含大量对您有用的信息,比如 [http://wiki.rpm.org/Problems 一些有关 RPM 机制的已知问题]。


注意: [http://rpm5.org/ rpm5.org] 也有一些文档,但是请不要过度依赖这个网站的内容。这个网站是由 Jeff Johnson 维护的一个 RPM 5.x 版本的网站。事实上RPM 5.x 版本并不适用于 Fedora 系统!和 Novell/SuSE 系统一样,Fedora 采用的 RPM 安装器基于 [http://www.rpm.org rpm.org] 维护的版本。
注意:[http://rpm5.org/ rpm5.org] 包含一些文档,但不要过度依赖这个网站的内容。这个网站是由 Jeff Johnson 维护的一个 RPM 5.x 版本的网站。事实上 RPM 5.x 版本并不适用于 Fedora 系统!Fedora(和 Novell/SuSE)采用的 RPM 基于 [http://www.rpm.org rpm.org] 维护的版本。
在 [http://lwn.net/Articles/236029/ lwn.net] 有一个关于这两者的简短区别介绍。
在 [http://lwn.net/Articles/236029/ lwn.net] 有一个关于这两者区别的介绍。


[[Category:Package Maintainers]]
[[Category:Package Maintainers]]
[[Category:How to]]
[[Category:How to]]

Revision as of 21:31, 25 August 2015

关于本指南

本指南描述了如何为 Fedora 制作 RPM 包,特别是如何写 .spec 配置文件。不像其它文档,本文档会解释 Fedora 中特殊领域的打包(会有链接指向 Fedora 的特殊打包规定)。并且由于本文档通过 wiki 更新,因此会尽可能的保持最新。除了 Fedora,绝大部分内容也会适用其他基于 RPM 机制的发行版。如果您等不及了,您可以先看看如何创建一个 GNU Hello World 软件包,这是一个创建 RPM 包的简短总结(不包含详细信息)。

目前 Fedora 文档团队有一份草稿已经发布:

Packager 指南

注意,本指南并不是 Fedora 官方的打包规定打包委员会 制定的 Fedora 所有打包规定如下:

打包规定软件包命名规定 是主要的两份规定,本指南与这两份规定100%兼容。

如果您计划为 Fedora 的官方源创建一个 RPM 包,请按照 如何成为 Fedora 软件包仓库维护人员 页面的步骤一步步来。

准备系统

在您为 Fedora 创建 RPM 包之前,您需要安装一些必须的开发工具并设置账户:

 # dnf install @development-tools fedora-packager rpmdevtools

您可以新建一个临时用户以便创建 RPM 包。这样,如果有错误发生,构建程序不会破坏您的系统,比如造成文件损失或您的私人文件/密钥被发送到互联网上。

Stop (medium size).png
切记!不要使用 root 用户来执行打包操作。因为这十分危险,所有二进制文件都会在打包前安装至系统中,因此您应该以普通用户身份打包,以防止系统被破坏。

创建名为 makerpm 的用户,添加至 'mock' 用户组,设置好密码并通过该用户登录:

 # /usr/sbin/useradd makerpm
 # usermod -a -G mock makerpm
 # passwd makerpm

然后,您可以通过这个临时用户开始打包操作。

一旦以 makerpm 用户登陆,使用以下命令在用户家目录下,创建标准的打包工作目录结构:

 $ rpmdev-setuptree

rpmdev-setuptree 程序将创建 ~/rpmbuild 目录,以及一系列预设的子目录(如 SPECSBUILD),你将使用它们作为打包目录。另外,还会创建 ~/.rpmmacros 文件,它用于设置各种选项。

打包指南建议保留文件时间戳;当然,您在使用 wgetcurl 获取软件源代码的时候就会自动保存。如果您使用 wget 来获取源代码,确保 ~/.wgetrc 文件包含此行 timestamping = on 。如果您使用 curl ,确保 ~/.curlrc 文件包含 -R 选项。

一旦设置完毕,通常不需要再次设置。

RPM 基础知识

若要构建一个标准的 RPM 包,您需要创建 .spec 文件,其中包含软件打包的全部信息。然后,对此文件执行 rpmbuild 命令,经过这一步,系统会按照步骤生成最终的 RPM 包。

一般情况,您应该把源代码包,比如由开发者发布的以 .tar.gz 结尾的文件,放入 ~/rpmbuild/SOURCES 目录。将.spec 文件放入 ~/rpmbuild/SPECS 目录,并命名为 "软件包名.spec" 。当然, 软件包名 就是最终 RPM 包的名字。为了创建二进制(Binary RPM)和源码软件包(SRPM),您需要将目录切换至 ~/rpmbuild/SPECS 并执行:

 $ rpmbuild -ba NAME.spec

当执行此命令时,rpmbuild 会自动读取 .spec 文件并按照下表列出的步骤完成构建。下表中,以 % 开头的语句为预定义宏,每个宏的作用如下:

阶段 读取的目录 写入的目录 具体动作
%prep %_sourcedir %_builddir 读取位于 %_sourcedir 目录的源代码和 patch 。之后,解压源代码至 %_builddir 的子目录并应用所有 patch。
%build %_builddir %_builddir 编译位于 %_builddir 构建目录下的文件。通过执行类似 "./configure && make" 的命令实现。
%install %_builddir %_buildrootdir 读取位于 %_builddir 构建目录下的文件并将其安装至 %_buildrootdir 目录。这些文件就是用户安装 RPM 后,最终得到的文件。注意一个奇怪的地方: 最终安装目录 不是 构建目录。通过执行类似 "make install" 的命令实现。
%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 中,对上表中的每个宏代码都有对应的目录:

宏代码 名称 默认位置 用途
%_specdir Spec 文件目录 ~/rpmbuild/SPECS 保存 RPM 包配置(.spec)文件
%_sourcedir 源代码目录 ~/rpmbuild/SOURCES 保存源码包(如 .tar 包)和所有 patch 补丁
%_builddir 构建目录 ~/rpmbuild/BUILD 源码包被解压至此,并在该目录的子目录完成编译
%_buildrootdir 最终安装目录 ~/rpmbuild/BUILDROOT 保存 %install 阶段安装的文件
%_rpmdir 标准 RPM 包目录 ~/rpmbuild/RPMS 生成/保存二进制 RPM 包
%_srcrpmdir 源代码 RPM 包目录 ~/rpmbuild/SRPMS 生成/保存源码 RPM 包(SRPM)

如果某一阶段失败,请查看输出信息以了解失败原因,并根据需要修改 .spec 文件。

做好准备打包一个特殊程序

如果这里有特殊的程序,它们需要被安装或者运行以便让您打包的普通程序正常工作,那么请先安装它们,然后记录下诸如软件包等相关信息。

如果想为 Fedora 源打包一个程序,您必须使用源代码来打包,且包含 patch 以及打包简介;不可以使用预编译代码进行打包。将源代码(通常是 .tar.gz 文件)放入 "~/rpmbuild/SOURCES" 目录(注意用户)。

仔细阅读该软件的安装说明。我们建议您先手工安装一次以了解具体情况。除少数情况外,所有二进制文件和程序库都必须由源码包中的源码编译而成。

分离程序

应用程序的源代码发布时,通常会捆绑许多外部依赖库的源代码。请不要将外部组件与主程序一起打包。相反,您需要拆分每个组件并单独打包。

许可协议

您只允许打包符合协议的软件。请查看 Packaging:Guidelines#LegalLicensing:MainPackaging:LicensingGuidelines。通常情况下,您只可以打包使用开源许可证(如 GNU GPL、LGPL、BSD-new、MIT/X 或 Apache 2.0)发布的开源软件(OSS)。请仔细检查许可证是否名副其实,同时确认软件整体是否均基于开源协议发布(如检查头文件注释、 README 文件等等)。如果软件捆绑外部依赖库,请确保这些库也使用开源协议(这十分重要)。

使用已有的信息

尽可能利用一切已有的信息!很明显,请不要打包源中已存在的程序!为了防止您犯这种错误,请查阅 Fedora 软件包数据库。同时建议查阅 正在被审核的软件包已停止使用的软件包列表。如果未找到相关信息,请使用 Google 搜索查看是否有类似 rpm 包。您可以直接访问 Fedora 软件包 Git 源 查看相关 SPEC 文件(和 Patch)。您可以使用 DNF 插件下载 SRPM 包:

$ dnf download --source sourcepackage-name

或通过访问 Fedora 镜像列表 的 HTTP/FTP 镜像页面,导航至 releases/39/Everything/source/SRPMS 目录( "39" 表示 Fedora 版本),手动下载扩展名为 .src.rpm 的 SRPM 包即可。

一旦有了源码包,执行以下命令安装至 ~/rpmbuild 目录:

$ rpm -ivh 源码包名*.src.rpm

您也可以使用 rpm2cpio 将源码包解压至任意目录:

$ mkdir 源码包名_src_rpm
$ cd 源码包名_src_rpm
$ rpm2cpio ../源码包名.src.rpm | cpio -i

使用已有的信息以帮助您打包。RPM FindPKGS.org 可以搜索非 Fedora 系统的 RPM 包。您可以尝试以相同的方式安装 SRPMS,并进行调试。如果未找到 RPM,可以参考 UbuntuDebian 的源码包(标准 tar 文件,内部包含 "debian/" 子目录)。如果您在 FreeBSD ports 仓库 找到想要的软件, 请下载 ports 源码包 并查看是否包含相关信息。有时,这些操作没什么实际帮助,因为不同系统有不同的打包规则。

新建一个 .spec 文件

现在,您需要在 ~/rpmbuild/SPECS 目录下,新建一个 SPEC 文件。文件应命名为 "软件包名.spec"。名称根据软件包名或通用名填写即可。但是,必须要遵守 软件包命名规定

模板和实例

模板

如果您首次创建 .spec 文件,vim 或 emacs 会自动生成模板:

 $ cd ~/rpmbuild/SPECS
 $ vim program.spec

示例(仅供参考):

Name:
Version:
Release:	1%{?dist}
Summary:
Group:
License:
URL:
Source0:
BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

BuildRequires:
Requires:

%description

%prep
%setup -q

%build
%configure
make %{?_smp_mflags}

%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}

%clean
rm -rf %{buildroot}

%files
%defattr(-,root,root,-)
%doc

%changelog

您可以使用 $RPM_BUILD_ROOT 代替 %{buildroot},两者都可以使用。

您也可以使用 rpmdev-newspec 命令来创建 SPEC 文件。rpmdev-newspec 软件包名 可以创建一个初始 SPEC 文件,该工具从软件包名判断使用哪个模板,支持指定模板。 /etc/rpmdevtools/spectemplate-*.spec 包含所有可用的模板,使用 rpmdev-newspec --help 命令了解更多信息。例如,为 python 模块创建 SPEC 文件:

cd ~/rpmbuild/SPECS
rpmdev-newspec python-antigravity
vi python-antigravity.spec

实例

eject

这是 Fedora 16 eject 程序的 spec 文件:

Summary:            A program that ejects removable media using software control
Name:               eject
Version:            2.1.5
Release:            21%{?dist}
License:            GPLv2+
Group:              System Environment/Base
Source:             %{name}-%{version}.tar.gz
Patch1:             eject-2.1.1-verbose.patch
Patch2:             eject-timeout.patch
Patch3:             eject-2.1.5-opendevice.patch
Patch4:             eject-2.1.5-spaces.patch
Patch5:             eject-2.1.5-lock.patch
Patch6:             eject-2.1.5-umount.patch
URL:                http://www.pobox.com/~tranter
ExcludeArch:        s390 s390x
BuildRequires:      gettext
BuildRequires:      libtool

%description
The eject program allows the user to eject removable media (typically
CD-ROMs, floppy disks or Iomega Jaz or Zip disks) using software
control. Eject can also control some multi-disk CD changers and even
some devices' auto-eject features.

Install eject if you'd like to eject removable media using software
control.

%prep
%autosetup -n %{name}

%build
%configure
make %{?_smp_mflags}

%install
%make_install

install -m 755 -d %{buildroot}/%{_sbindir}
ln -s ../bin/eject %{buildroot}/%{_sbindir}

%find_lang %{name}

%files -f %{name}.lang
%doc README TODO COPYING ChangeLog
%{_bindir}/*
%{_sbindir}/*
%{_mandir}/man1/*

%changelog
* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.1.5-21
- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild

* Fri Jul 02 2010 Kamil Dudka <kdudka@redhat.com> 2.1.5-20
- handle multi-partition devices with spaces in mount points properly (#608502)

SPEC 文件综述

其他有用的信息:

您需要遵守这些规定:软件包命名规定打包规定软件包审核规定

"#" 字符表示注释,但需要避免注释宏(以 % 开头),因为它们会首先被替换展开。使用 %% 注释宏。另外,还要避免在脚本命令的相同行中使用行内注释。

以下介绍了主要的标签。注意 %{name}%{version}%{release} 代表 Name, Version 和 Release 这三个标签。只要更改标签,宏就会使用新值。

  • Name: 软件包名,应与 SPEC 文件名一致。命名必须符合 软件包命名规定
  • Version: 上游版本号。请查看 版本标签规定。如果包含非数字字符,您可能需要将它们包含在 Release 标签中。如果上游采用日期作为版本号,请考虑以:yy.mm[dd] (例如 2008-05-01 可变为 8.05) 格式作为版本号。
  • Release: 发行编号。初始值为 1%{?dist}。每次制作新包时,请递增该数字。当上游发布新版本时,请修改 Version 标签并重置 Release 的数字为 1。具体参考打包规定中的 Release 标签部分,以及 Dist tag
  • Summary: 一行简短的软件包介绍。请使用美式英语。请勿在结尾添加标点!
  • Group: 指定软件包组,例如 "Applications/Engineering";执行 "less /usr/share/doc/rpm-*/GROUPS" 查看完整的组列表。任何包含文档的子软件包,使用 "Documentation" 组(如 kernel-doc)。注意 Fedora 17+ 后已废除此标签。Spec 文件参考手册 有介绍
  • License: 授权协议,必须是开源许可证。请不要使用旧的 Copyright 标签。协议采用标准缩写(如 "GPLv2+")并且描述明确(如, "GPLv2+" 表示 GPL 2 及后续版本,而不是 "GPL" 或 "GPLv2" 这种不准确的写法)。参考 LicensingLicensing Guidelines。如果一个软件采用多个协议,可以使用 "and" 和 "or"(例如 "GPLv2 and BSD")来描述。
  • URL: 该软件包的项目主页。注意:源码包 URL 请使用 Source0 指定。
  • Source0: 软件源码包的 URL 地址。"Source" 与 "Source0" 相同。强烈建议提供完整 URL 地址,文件名用于查找 SOURCES 目录。如果可能,建议使用 %{name}%{version} 替换 URL 中的名称/版本,这样更新时就会自动对应。下载源码包时,需要 保留时间戳。如果有多个源码包,请用 Source1Source2 等依次列出。如果你需要添加额外文件,请将它们列在后面。更多特殊案例(如 revision control),请参考 Source URL
  • Patch0: 用于源码的补丁名称。如果你需要在源码包解压后对一些代码做修改,你应该修改代码并使用 diff 命令生成 patch 文件,然后放在 ~/rpmbuild/SOURCES 目录下。一个 Patch 应该只做一种修改,所以可能会包含多个 patch 文件。
  • BuildArch: 如果你要打包的文件不依赖任何架构(例如 shell 脚本,数据文件),请使用 "BuildArch: noarch"。RPM 架构会变成 "noarch"。
  • BuildRoot: 在 %install 阶段(%build 阶段后)文件需要安装至此位置。Fedora 不需要此标签,只有 EPEL5 还需要它。默认情况下,根目录为 "%{_topdir}/BUILDROOT/"。
  • BuildRequires: 编译软件包所需的依赖包列表,以逗号分隔。此标签可以多次指定。编译依赖 不会 自动判断,所以需要列出编译所需的所有依赖包。常见的软件包可省略,例如 gcc。如果有必要,你可以指定需要的最低版本(例:"ocaml >= 3.08")。如果你需要找到包含 /EGGS 文件的软件包,可执行 "rpm -qf /EGGS"。如果你需要找到包含 EGGS 程序的软件包,可执行 "rpm -qf which EGGS"。请保持最小依赖(例如,如果你不需要 perl 的功能,可使用 sed 代替),但请注意,如果不包含相关依赖,某些程序会禁用一些功能;此时,你需要添加这些依赖。Package-x-generic-16.pngauto-buildrequires 软件包可能会有帮助。
  • Requires: 安装软件包时所需的依赖包列表,以逗号分隔。请注意, BuildRequires 标签是编译所需的依赖,而 Requires 标签是安装/运行程序所需的依赖。大多数情况下,rpmbuild 会自动探测依赖,所以可能不需要 Requires 标签。然而,你也可以明确标明需要哪些软件包,或由于未自动探测所需依赖而需要手动标明。
  • %description: 程序的详细/多行描述,请使用美式英语。每行必须小于等于 80 个字符。空行表示开始新段落。使用图形安装软件时会重新格式化段落;以空格开头的行被视为已格式化的格式,一般使用等宽字体显示。参考 RPM Guide
  • %prep: 打包准备阶段执行一些命令(如,解压源码包,打补丁等),以便开始编译。一般仅包含 "%autosetup";如果源码包需要解压并切换至 NAME 目录,则输入 "%autosetup -n NAME"。查看 %prep 部分了解更多信息。
  • %build: 包含构建阶段执行的命令,构建完成后便开始后续安装。程序应该包含有如何编译的介绍。查看 %build 部分了解更多信息。
  • %install: 包含安装阶段执行的命令。命令将文件从 %{_builddir} 目录安装至 %{buildroot} 目录。查看 %install 部分了解更多信息。
  • %check: 包含测试阶段执行的命令。此阶段在 %install 之后执行,通常包含 "make test" 或 "make check" 命令。此阶段要与 %build 分开,以便在需要时忽略测试。
  • %clean: 清理安装目录的命令。此阶段在 Fedora 中是多余的,仅针对 EPEL。一般只包含:
rm -rf %{buildroot}
  • %files: 需要被打包/安装的文件列表。查看 %files 部分了解更多信息。
  • %changelog: RPM 包变更日志。请使用示例中的格式。注意,不是软件本身的变更日志。
  • ExcludeArch: 排除某些架构。如果该软件不能在某些架构上正常编译或工作,通过该标签列出。
  • ExclusiveArch: 列出该软件包独占的架构。
  • 你可以加入一些代码片段,以便在真实系统上安装/删除包时执行这些代码(相反,%install 脚本仅将文件虚拟【pseudo】安装至 build root 目录)。这些代码称为 "scriptlets",通常用于从软件包更新系统信息。查看 "Scriptlets" 部分了解更多信息。

RPM 还支持使用一个 SPEC 文件制作多个软件包(这称为 子软件包),例如 name-libsname-devel 等软件包。

Stop (medium size).png
不要使用这些标签
  • Packager
  • Vendor
  • Copyright

不要制作 "relocatable" 软件包(不遵守FHS);它们不会为 Fedora 加分,反而把事情搞得更复杂。

SPEC 文件剖析

%prep 部分

%prep 部分描述了解压源码包的方法。一般而言,其中包含 "%autosetup" 命令。另外,还可以使用 "%setup" 和 "%patch" 命令来指定操作 Source0 等标签的文件。查看 Maximum RPM 的 %setup and %patch 小节 了解更多信息。

自 RPM 4.4.2 开始,可使用 %{patches}%{sources} 宏。如果您的软件包包含了许多 patch 或 source,并且你不想使用 %autosetup,那么可以这么做:

for p in %{patches}; do
    ...
done

注意,RHEL 和其它基于 RPM 的发行版,并不支持这种用法。

%prep 部分:%autosetup 命令

"%autosetup" 命令用于解压源码包。可用选项包括:

  • -n name : 如果源码包解压后的目录名称与 RPM 名称不同,此选项用于指定正确的目录名称。例如,如果 tarball 解压目录为 FOO,则使用 "%autosetup -n FOO"。
  • -c name : 如果源码包解压后包含多个目录,而不是单个目录时,此选项可以创建名为 name 的目录,并在其中解压。

如果使用 "%setup" 命令,通常使用 -q' 抑止不必要的输出。

如果需要解压多个文件,有更多 %spec 选项可用,这对于创建子包很有用。常用选项如下:

-a number 在切换目录后,只解压指定序号的 Source 文件(例如 "-a 0" 表示 Source0)
-b number 在切换目录前, 只解压指定序号的 Source 文件(例如 "-b 0" 表示 Source0)
-D 解压前,不删除目录。
-T 禁止自动解压归档。

%prep 部分:%patch 命令

如果使用 "%autosetup" 命令,则不需要手动进行补丁管理。如果你的需求很复杂,或需要与 EPEL 兼容,需要用到此部分的内容。"%patch0" 命令用于应用 Patch0(%patch1 应用 Patch1,以此类推)。Patches 是修改源码的最佳方式。常用的 "-pNUMBER" 选项,向 patch 程序传递参数,表示跳过 NUM 个路径前缀。

补丁文件名通常像这样 "telnet-0.17-env.patch",命名格式为 %{name} - %{version} - REASON.patch"(有时省略 version 版本)。补丁文件通常是 "diff -u" 命令的输出;如果你在 ~/rpmbuild/BUILD 子目录执行此命令,则之后便不需要指定 -p 选项。

为一个文件制作补丁的步骤:

cp foo/bar foo/bar.orig
vim foo/bar
diff -u foo/bar.orig foo/bar > ~/rpmbuild/SOURCES/PKGNAME.REASON.patch

如果需要修改多个文件,简单方法是复制 BUILD 下的整个子目录,然后在子目录执行 diff。切换至 "~rpmbuild/BUILD/NAME" 目录后,执行以下命令:

cp -pr ./ ../PACKAGENAME.orig/
... 执行修改 ...
diff -ur ../PACKAGENAME.orig . > ~/rpmbuild/SOURCES/NAME.REASON.patch

如果你想在一个补丁中编辑多个文件,你可以在编辑之前,使用 ".orig" 扩展名复制原始文件。然后,使用 "gendiff"(在 rpm-build 包中)创建补丁文件。

需要确保你的补丁精确匹配上下文。默认 "fuzz" 值为 "0",表示要求精确匹配。你可以添加 "%global _default_patch_fuzz 2" 将 fuzz 设为旧版 Fedora RPM 所采用的值,但我们建议你尽量避免这样做。

Packaging/PatchUpstreamStatus 所述,SPEC 文件中的所有补丁都需要注释来描述补丁的上游状态。其中应包括上游 bug/email 文档(包含日期)。如果是 Fedora 特别需要的补丁,应描述为何需要它。Fedora 项目致力于贴近上游;查看 PackageMaintainers/WhyUpstream 了解其重要性。

%prep 部分:未修改文件

有时,一个或多个源码包不需要解压。你可以使用以下命令,将文件复制到 build 目录中,如( SOURCE1 表示对应的源码包):

cp -p %SOURCE1 .

%build 部分

"%build" 部分有时会有点复杂;在这里你可以配置,并编译用于安装的文件。

许多程序使用 GNU configure 进行配置。默认情况下,文件会安装到前缀为 "/usr/local" 的路径下,对于手动安装很合理。然而,打包时需要修改前缀为 "/usr"。共享库路径视架构而定,安装至 /usr/lib/usr/lib64 目录。

由于 GNU configure 很常见,可使用 "%configure" 宏来自动设置正确选项(例如,设置前缀为 /usr)。一般用法如下:

 %configure
 make %{?_smp_mflags}

若需要覆盖 makefile 变量,请将变量作为参数传递给 make

make %{?_smp_mflags} CFLAGS="%{optflags}" BINDIR=%{_bindir}

更多详细信息,请参考 "GNU autoconf, automake 和 libtool" 以及 "开源开发工具:Make, Configure, Automake, Autoconf 介绍" by Stefan Hundhammer

一些程序使用 cmake。请参考 Packaging/cmake

%install 部分

此部分包含安装阶段需要执行的命令,即从 %{_builddir} 复制相关文件到 %{buildroot} 目录(通常表示从 ~/rpmbuild/BUILD 复制到 ~/rpmbuild/BUILDROOT) 目录,并根据需要在 %{buildroot} 中创建必要目录。

容易混淆的术语:

  • "build 目录",也称为 %{_builddir},实际上与 "build root",又称为 %{buildroot},是不同的目录。在前者中进行编译,并将需要打包的文件从前者复制到后者。
  • 在 %build 阶段,当前目录为 %{buildsubdir},是 %prep 阶段中在 %{_builddir} 下创建的子目录。这些目录通常名为 ~/rpmbuild/BUILD/%{name}-%{version}
  • %install 阶段的命令不会在用户安装 RPM 包时执行,此阶段仅在打包时执行。

一般,这里执行 "make install" 之类的命令:

%install
rm -rf %{buildroot} # 仅用于 RHEL 5
%make_install

理想情况下,对于支持的程序,你应该使用 %make_install,它等同于 DESTDIR=%{buildroot},它会将文件安装到 %{buildroot} 目录中。

如果程序不支持 DESTDIR,使用以下方法避开此问题:

  • 修补 makefile 以便支持 DESTDIR。请在 DESTDIR 根据需要创建必要目录,并向上游提交补丁。
  • 使用 "%makeinstall" 宏。此方法可能有效,但也可能失败。该宏会展开为 "make prefix=%{buildroot}%{_prefix} bindir=%{buildroot}%{_bindir} ... install",可能导致某些程序无法正常工作。请在 %{buildroot} 根据需要创建必要目录。
  • 使用 auto-destdir 软件包。它需要 "BuildRequires: auto-destdir",并将 "make install" 修改为 "make-redir DESTDIR=%{buildroot} install"。这仅适用于使用常用命令安装文件的情况,例如 cpinstall
  • 手动执行安装。这需要在 %{buildroot} 下创建必要目录,并从 %{_builddir} 复制文件至 %{buildroot} 目录。要特别注意更新,通常会包含新文件。示例如下:
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_bindir}/
cp -p mycommand %{buildroot}%{_bindir}/

%check 部分

如果需要执行测试,使用 %check 是个好主意。测试代码应写入 %check 部分(紧接在 %install 之后,因为需要测试 %buildroot 中的文件),而不是写入 %build 部分,这样才能在必要时忽略测试。

通常,此部分包含:

make test

有时候也可以用:

make check

请熟悉 Makefile 的用法,并选择适当的方式。

%files 部分

此部分列出了需要被打包的文件和目录。

%files 基础

%defattr 用于设置默认文件权限,通常可以在 %files 的开头看到它。注意,如果不需要修改权限,则不需要使用它。其格式为:

%defattr(<文件权限>, <用户>, <用户组>, <目录权限>)

第 4 个参数通常会省略。常规用法为 %defattr(-,root,root,-),其中 "-" 表示默认权限。

您应该列出该软件包拥有的所有文件和目录。尽量使用宏代替目录名,查看宏列表 Packaging:RPMMacros(例如:使用 %{_bindir}/mycommand 代替 /usr/bin/mycommand)。如果路径以 "/" 开头(或从宏扩展),则从 %{buildroot} 目录取用。否则,假设文件在当前目录中(例如:在 %{_builddir} 中,包含需要的文档)。如果您的包仅安装一个文件,如 /usr/sbin/mycommand,则 %files 部分如下所示:

%files
%{_sbindir}/mycommand

若要使软件包不受上游改动的影响,可使用通配符匹配所有文件:

%{_bindir}/*

包含一个目录:

%{_datadir}/%{name}/

注意,%{_bindir}/* 不会声明此软件包拥有 /usr/bin 目录,而只包含其中的文件。如果您列出一个目录,则该软件包拥有这个目录,及该目录内的所有文件和子目录。因此,不要列出 %{_bindir},并且要小心的处理那些可能和其他软件包共享的目录。

如果存在以下情况,可能引发错误:

  • 通配符未匹配到任何文件或目录
  • 文件或目录被多次列出
  • 未列出 %{buildroot} 下的某个文件或目录


您也可以使用 %exclude 来排除文件。这对于使用通配符来列出全部文件时会很有用,注意如果未匹配到任何文件也会造成失败。

%files 前缀

您可能需要在 %files 部分添加一个或多个前缀;请用空格分隔。详情请查看 Max RPM section on %files directives

通常,"%doc" 用于列出 %{_builddir} 内,但未复制到 %{buildroot} 中的文档。通常包括 READMEINSTALL。它们会保存至 /usr/share/doc 下适当的目录中,不需要声明 /usr/share/doc 的所有权。

注意: 如果指定 %doc 条目,rpmbuild < 4.9.1 在安装前会将 %doc 目录删除。这表明已保存至其中的文档,例如,在 %install 中安装的文档会被删除,因此最终不会出现在软件包中。如果您想要在 %install 中安装一些文档,请将它们临时安装到 build 目录(不是 build root 目录)中,例如 _docs_staging,接着在 %files 中列出,如 %doc _docs_staging/* 这样。

配置文件保存在 /etc 中,一般会这样指定(确保用户的修改不会在更新时被覆盖):

%config(noreplace) %{_sysconfdir}/foo.conf

如果更新的配置文件无法与之前的配置兼容,则应这样指定:

%config %{_sysconfdir}/foo.conf

"%attr(mode, user, group)" 用于对文件进行更精细的权限控制,"-" 表示使用默认值:

%attr(0644, root, root) FOO.BAR

"%caps(capabilities)" 用于为文件分配 POSIX capabilities。例如:

%caps(cap_net_admin=pe) FOO.BAR

如果包含特定语言编写的文件,请使用 %lang 来标注:

%lang(de) %{_datadir}/locale/de/LC_MESSAGES/tcsh*

使用区域语言(Locale)文件的程序应遵循 处理 i18n 文件的建议方法

  • %install 步骤中找出文件名: %find_lang ${name}
  • 添加必要的编译依赖: BuildRequires: gettext
  • 使用找到的文件名: %files -f ${name}.lang

以下前缀在 Fedora 中无效%license%readme

%files 和文件系统层次标准 (FHS)

您应该遵守 文件系统层次标准(FHS, Filesystem Hierarchy Standard)。可执行文件保存在 /usr/bin,配置文件保存在 /etc, 共享库保存在 /usr/lib(或 /usr/lib64)等等。只有一个例外:不需要用户或管理员直接执行的可执行文件,应保存至 /usr/libexec 子目录,子目录通过 %{_libexecdir}/%{name} 宏来引用。

不要 将文件安装到 /opt/usr/local 目录中。

不幸的是,许多程序默认情况下并不遵守 FHS。尤其是,架构无关的共享库被保存至 /usr/lib 而非 /usr/share 之中。前者供依赖架构的共享库使用,后者供架构无关的共享库使用;这表示不同 CPU 架构的系统都能共享 /usr/share 目录。Fedora 中也有一些例外(如 Python 和 Perl),总的来说,Fedora 比其他发行版更严格遵守标准规范。rpmlint 会在将 ELF 以外的文件保存至 /usr/lib 目录时返回警告。

%files 示例

以下为 %files 部分的简单示例:

%files
%doc README
%license LICENSE COPYING
%{_bindir}/*
%{_sbindir}/*
%{_datadir}/%{name}/
%config(noreplace) %{_sysconfdir}/*.conf

找出重复内容

您可以列出任意两个二进制软件包的重复文件,执行以下命令:

cd ~/rpmbuild/RPMS/ARCH  # 将 "ARCH" 替换为您的系统架构
rpm -qlp PACKAGE1.*.rpm | sort > ,1
rpm -qlp PACKAGE2.*.rpm | sort > ,2
comm -12 ,1 ,2

Scriptlets

当用户安装 RPM 时,您可能想要执行一些命令。这可以通过 scriptlets 完成。请查看 Packaging/ScriptletSnippets

脚本片段可以:

  • 在软体包安装之前 (%pre) 或之后 (%post) 执行
  • 在软体包卸载之前 (%preun) 或之后 (%postun) 执行
  • 在事务开始 (%pretrans) 或结束 (%posttrans) 时执行

例如,每个二进制 RPM 包都会在动态链接器的默认路径中存储共享库文件,并在 %post%postun 中调用 ldconfig 来更新库缓存。如果软件包有多个包含共享库的子包,则每个软体包也需要执行相同动作。

%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig

如果仅执行一个命令,则 "-p" 选项会直接执行,而不启用 shell。然而,若有许多命令时,不要使用此选项,按正常编写 shell 脚本即可。

如果你在脚本片段中执行任何程序,就必须以 "Requires(CONTEXT)"(例: Requires(post))的形式列出所有依赖。

%pre%post%preun%postun 提供 $1 参数,表示动作完成后,系统中保留的此名称的软件包数量。不要比较此参数值是否等于 2,而是比较是否大于等于 2%pretrans%posttrans$1 的值恒为 0

例如,如果软件包安装了一份 info 手册,那么可以用 info 包提供的 install-info 来更新 info 手册索引。首先,我们不保证系统已安装 info 软件包,除非明确声明需要它;其次,我们不想在 install-info 执行失败时,使软件包安装失败:

Requires(post): info
Requires(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

还有一个安装 info 手册时的小问题。install-info 命令会更新 info 目录,所以我们应该在 %install 阶段删除 %{buildroot} 中无用的空目录:

rm -f %{buildroot}%{_infodir}/dir

另一个类似代码片段的功能是 "triggers"(触发器),它可以在其他软件包安装或删除时,为你的包执行一些动作。请参考 RPM Triggers

宏通常以 %{string} 格式出现,以下介绍常见的宏:

宏名称 典型扩展 意义
%{_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} .fcNUMBER 发行版名称+版本号(例如 ".fc39")
%{fedora} NUMBER Fedora 发行版本号(例如 "39")
%{_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 和宏配置文件)。

您可以使用 %global 来定义自己的宏,但在使用前需要先进行定义。(宏变量定义时,可以利用嵌套来引用其他宏。)例如:

%global date 2012-02-08

使用 rpmbuild 的 "-E" 选项查找 SPEC 文件中宏变量的值:

rpmbuild -E '%{_bindir}' myfile.spec

参考 Packaging/RPMMacrosRPM 指南 - 第 9 章

其它标签

除了 Requires 和 BuildRequires 标签外,你还可以使用以下标签控制依赖关系:

  • Provides: 列出此软件包提供的虚拟软件包名称。例如,可能有个 "foo" 软件包需要其他程序的 "bar" 功能;如果有许多软件包可以满足该需求,则这些包可以指定 "Provides: bar",而 "foo" 包可以指定 "Requires: bar"。你也可以使用 "alternatives" 系统,但是若一个系统中有多个用户,不同人可能希望使用不同设置,此时请避免使用它,毕竟此设置为系统全局设置。使用 "rpm -q --provides PACKAGENAME" 查看指定包提供哪些虚拟包。Fedora 中的一些虚拟包示例:
    • MTA: 邮件传输代理(mail transport agent, MTA),例如 sendmail。
    • tex(latex): 用于 latex
  • Obsoletes: 当软件包安装时卸载另一个指定的包。用于软件包改名时,或是用该软件包完全取代另一个不同的包时使用。
  • Conflicts: 表示安装此包时冲突的软件包。如果可以请避免使用此标签。查看 Packaging/Conflicts
  • BuildConflicts: 表示编译此包时冲突的软件包。如果可以请避免使用此标签。

若要处理不同的系统架构,可使用以下 2 个标签:

  • ExcludeArch: 排除无法构建此软件包的系统架构。例如:
ExcludeArch: ppc
  • ExclusiveArch: 仅包含指定的系统架构。除非绝对正确,否则请避免使用。

可用的系统架构在 Architectures 中列出。

子软件包

一个 SPEC 文件可以定义多个 RPM 包。换句话说,一个 SRPM 文件可以制作出多个 RPM 包。注意,这仍然只需要一个构建(%prep、%build、%install 等)过程。name-docname-devel 是最常见的文档和开发文件子软件包。

使用 %package 宏指令来定义子软件包:

%package subpackage_name

在每个 %package 指令后,需要列出该子包的必要标签。至少应包括 Summary 和 Group 标签,以及 %description subpackage_name%files subpackage_name 指令:

%package foo
Summary: 简介
Group: 包组

%description foo
相关描述

%files foo
该包需要包含的文件

任何子包中未指定的标签,都会从主包继承。

默认情况下,如果软件包名为 "foo",而子包名为 "bar",则生成的子包为 "foo-bar"。可以使用 "-n" 选项指定包名(但需要在所有其它部分添加此选项):

%package -n new_subpackage_name

查看 RPM 指南 针对子包的章节 了解更多信息。

条件判断

你可以插入条件判断语句,例如可根据特定系统架构执行不同动作:

%ifarch ARCHITECTURE_NAME

相反的用法为:

%ifnarch ARCHITECTURE_NAME

通用的条件判断用法为:

%if TRUE_OR_FALSE

可以选择使用 "%else" 字段;条件判断使用 "%endif" 结束。

应用程序具体规定

有许多应用程序的具体规定可以帮助你(例:程序语言、应用程序、共享库、构建系统等)。大多都列在 应用程序专用打包规定。一些应用程序具体规定的例子有:

另外,还有一些可以帮你找到应用程序专用规定的方法:

其他注意事项

Packaging/FrequentlyMadeMistakes 包含常见错误的相关信息。PackageMaintainers/Packaging Tricks 包含一些推荐,以及有争议的技巧。

请尝试编写你的 SPEC 文件,尽可能在上游更新时使一切都能水到渠成,使你除了修改版本号并刷新源文件外,不需要做其他任何修改。例如,如果要为 *.txt 文件设置执行权限,请不要用:

 chmod a-x Filename1.txt Filename2.txt Filename3.txt

而是,考虑使用以下方式处理,可直接处理使用相同命名规则的新文件:

 chmod a-x *.txt

如果你想查看大量脚本片段,使用以下命令显示所有已安装包的脚本片段:

 rpm -qa --queryformat "\n\nPACKAGE: %{name}\n" --scripts | less

不要尝试和用户交互;RPM 以支持批量安装为设计核心。如果有程序需要显示 EULA 用户授权协议,则应在初次执行时执行该动作,而非安装时。

建议不要试图启动服务,因为这会使安装过程变得缓慢。如果你安装 init 或 systemd 脚本,请考虑使用 chkconfigsystemctl 安排服务在下次重启时启动/停止该服务。卸载前,如果服务正在运行,一般需要先尝试停止这些服务。

卸载要尽可能撤消安装阶段中所做的更改,但不要删除任何用户创建的文件。

一般而言,如果有二进制文件,则会剥离其中包含的调试信息,并将调试信息保存至 name-debug 子包中。如果需要禁用此动作,可以在 SPEC 文件顶部添加以下指令:

%global _enable_debug_package 0
%global debug_package %{nil}
%global __os_install_post /usr/lib/rpm/brp-compress %{nil}

若要避免执行剥离动作,还需要在 %install 部分添加以下变量:

export DONT_STRIP=1

通过条件判断的方式,在 SPEC 文件中检查 Fedora 版本:

%if 0%{?fedora} <= <version>

? 使 %fedora 宏在未定义时返回空。这样会使结果为 0 ,而 %fedora 宏若存在数值时也不会有干扰。(注意,这种做法在 Koji "scratch" 编译中不起作用,%fedora 的值在创建 SRPM 时已设定)

GUI 程序必须有桌面条目(desktop entry),以便用户通过图形化菜单启动程序。对于 .desktop 文件,请参考 Fedora packaging guidelines for desktop filesdesktop entry spec。对于 /usr/share/icons 中的图标,请参考 icon theme spec

构建 RPM 包

使用 rpmlint 测试

为避免常见错误,请先使用 rpmlint 查找 SPEC 文件的错误:

$ rpmlint program.spec

如果返回错误/警告,使用 "-i" 选项查看更详细的信息。

有时,rpmlint 也会有误报的情况发生。请查看 打包规定 了解哪些错误可以忽略。

从 SPEC 构建 RPM 包

一旦 SPEC 编写完毕,请执行以下命令来构建 SRPM 和 RPM 包:

$ rpmbuild -ba program.spec

如果成功,RPM 会保存至 ~/rpmbuild/RPMS,SRPM 会保存至 ~/rpmbuild/SRPMS

如果失败,请查看 BUILD 目录的相应编译日志。为了帮助调试,可以用 "--short-circuit" 选项来忽略成功的阶段。例如,若想要(略过更早的阶段)重新从 %install 阶段开始,请执行:

$ rpmbuild -bi --short-circuit program.spec

如果只想创建 SRPM(不需要执行 %prep%build 或其他阶段),请执行:

rpmbuild -bs program.spec

使用 rpmlint 测试已构建的 RPM 包

rpmlint 用于检查 SPEC/RPM/SRPM 是否存在错误。你需要在发布软件包之前,解决这些警告。此页面 提供一些常见问题的解释。如果你位于 SPEC 目录中,请执行:

$ rpmlint NAME.spec ../RPMS/*/NAME*.rpm ../SRPMS/NAME*.rpm

进入 ~/rpmbuild/RPMS 下的特定架构目录中,您会发现有许多二进制 RPM 包。使用以下命令快速查看 RPM 包含的文件和权限:

$ rpmls *.rpm

如果看上去正常,以 root 身份安装它们:

# rpm -ivp package1.rpm package2.rpm package3.rpm ...

以不同方式来测试程序,看看是否全部都正常工作。如果是 GUI 工具,请确认其是否出现在桌面菜单中,否则表示 .desktop 条目可能有错。

最后卸载软件包:

# rpm -e package1 package2 package3

Mock 和 Koji

Mock 用于在标准环境下,使用 SRPM 来构建二进制 RPM 包的强大工具。这可以暴露出包的构建依赖是否存在问题。如果构建失败,表示可能缺少某些 BuildRequires 编译依赖。请参考 使用 Mock 测试构建软件包。一旦你的账户属于 "mock" 组,执行以下命令进行本地构建测试:

$ mock -r fedora-9-i386 rebuild path_to_source_RPM

你可以使用 Koji(会使用 mock)在各种不同的系统上执行构建,包括你没有的系统架构。PackageMaintainers/JoinPackageMaintainers/UsingKoji 包含更多有关 Koji 的信息。一旦设置完成,你就可以使用以下命令,在各种平台上测试你的 SRPM:

$ koji build --scratch dist-f9 path_to_source_RPM

请将 dist-f9 替换为任意 Fedora 发行版本,但不要使用 dist-rawhide。记住,%fedora%fc9 等宏变量的值都不会在 scratch build 中得到修正,所以如果你的 SPEC 会根据宏变量值来执行不同操作,则不能起作用。

你的 Koji 构建只依赖 TARGET 发行版软件源中实际存在的软件包。因此,如果你的软件包依赖 Bodhi 中尚未发行的其它软件包,则不能使用 Koji 为已发行的版本进行构建。如果你需要为尚未稳定的版本构建软件包,请通过 Bodhi 提交 Koji buildroot override 请求。如果你的软件包依赖其他人维护的软件包,请联系其维护者。[在 Bodhi 可以处理 Koji buildroot override 请求之前,以前的旧方法是在此处提交 rel-eng 请求:https://fedorahosted.org/rel-eng/newticket ,并请求将该软件包加入成为 buildroot override。]

有用的工具

rpmdevtools 软件包包含各种有用的工具;"rpm -qil rpmdevtools" 将显示此包的相关信息和文件列表。

  • rpmdev-bumpspec : 增加 spec 文件的发行版本号,并以当前时间和版本格式添加 changelog 日志:
rpmdev-bumpspec --comment=COMMENT --userstring=NAME+EMAIL_STRING SPECFILES

DNF 下载插件(DNF 核心插件)也十分有用:

  • dnf download : 下载指定软件包的 SRPM 包,示例如下:
dnf download --source PACKAGENAME

auto-buildrequires 软件包有一个好用的工具,可以帮助我们找到合适的 BuildRequires 条目。安装此包后,使用 "auto-br-rpmbuild" 替换 "rpmbuild",你就会看到自动生成的 BuildRequires 列表。

你可能发现 RUST 蛮好用的(GPL),但是它不能生成符合 Fedora 软件包质量的 SPEC 文件。Alien 可以转换软件包格式;它不能生成干净的 SRPM,但转换已有的软件包或许可以提供一些有用的信息。

最后,docker-rpm-builder (APL 2.0) 使用 Docker 构建 RPM 包;使用 rpmbuild 构建,目标架构需要与系统架构相同。另外,mock 对于任何目标结构的 Fedora/Centos/RHEL 发行版都能完美工作,无论 Docker 是否能运行

如果你想要为不同发行版和系统架构来编译软件包,并且提供公开访问的 dnf 软件源,你可以提交你的 src.rpm 到 Copr

如果你需要签名你的软件包,可以使用 rpm-sign 软件包的 rpmsign 工具。

相关规定

在您创建软件包的时候,请遵守以下规定:

这里也有许多针对特殊环境打包的规定(如 Java 程序,OCaml 程序,GNOME 程序等等);您也可以从 SIGs软件包维护人员 获取许多有价值的帮助。

这里还有一份所有有关打包的 Wiki 页面列表

除了这些,还有许多非官方的指南,比如 打包规定草稿今后需要制定的打包规定

还有其它信息也可供参考,比如 SuSE 打包规定Debian 打包规定,但是 每个发行版都有所不同,所以不要直接生搬硬套。

请注意所有 spec 文件必须与开源软件有关,就像 FPCA 声明所描述的一样。

维护软件包

一旦您的软件包被批准加入官方软件源,您和您的副维护者需要共同维护它。请查看 Package update HOWTOPackage update guidelines 了解更多信息。如果您为多个不同的 Fedora 版本维护软件包,有空一定要为之前的 Fedora 版本也制作一个。

请鼓励上游开发者使用标准源码发行惯例。使用标准惯例可以使打包流程更轻松。了解更多信息,请查看:

更多信息

软件包维护人员 页面加入了许多有用页面的链接,您不妨去看看。如何更新您维护的软件包 页面描述了如何更新您在 Fedora 中维护的软件包。

Fedora Wiki 之外的更多其他信息,请查看:

注意:rpm5.org 包含一些文档,但不要过度依赖这个网站的内容。这个网站是由 Jeff Johnson 维护的一个 RPM 5.x 版本的网站。事实上 RPM 5.x 版本并不适用于 Fedora 系统!Fedora(和 Novell/SuSE)采用的 RPM 基于 rpm.org 维护的版本。 在 lwn.net 有一个关于这两者区别的介绍。