From Fedora Project Wiki
m (internal link cleaning)
 
(96 intermediate revisions by 6 users not shown)
Line 1: Line 1:
{{autolang}}
{{autolang}}


== 关于本指南==
== 关于本指南 ==


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


本指南详细讲述了如何针对Fedora系统制作RPM包(特别是如何写.spec配置文件),同时还给您许多实际的提示,帮助您减少不必要的时间损耗。
'''目前 Fedora 文档团队有一份草稿已经发布:


但是,本指南并不是Fedora官方的打包指南(官方指南在此:[[Packaging:Guidelines|Fedora打包官方指南]]和[[Packaging:NamingGuidelines|软件包命名向导]])。尽管如此,本指南与前两者100%兼容,您不必担心内容是否不准确。
[http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/Packagers_Guide Packager 指南]'''


请您注意:本指南不会在某些方面作深入讨论,但是指南会引入相关链接以帮助您理解这些内容。而且,本指南特殊之处在于,不像有些RPM指南不解释详细。本文档会解释在Fedora中RPM的特殊之处(会有大量链接指向Fedora的特殊指南),并且尽可能的保持最新。
注意,本指南并不是 Fedora 官方的打包'''规定''',[[Packaging:Committee|打包委员会]] 制定的 Fedora 所有打包规定如下:
* [[Packaging:Guidelines|打包规定]]
* [[Packaging:LicensingGuidelines|软件许可协议规定]]
* [[Packaging:NamingGuidelines|软件包命名规定]]
* [[Packaging:DistTag|Dist 标签使用指南]]
* [[Packaging:ReviewGuidelines|软件包审核规定]]
* [[Packaging:ScriptletSnippets|SPEC 文件编写指南]] ([[How_to_create_an_RPM_package/zh-hk|zh_CN]])


举个例子,本指南会提醒 [http://lists.autistici.org/message/20080709.212842.3f8877db.de.html RPM version 4.4.2.x版本] 的影响。由于本文档是主要针对Fedora编写,因此绝大部分内容也会适用其他基于RPM机制的发行版。如果您等不及了,您可以先看看[[How_to_create_a_GNU_Hello_RPM_package|如何创建一个GNU Hello World软件包]],这是一个创建RPM包的简短总结(但是没有很详细的信息)。
'''[[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包之前,您需要安装一些必须的开发组件并且设置好您即将使用的账户。请在root权限下执行(不要输入"#"):
在您为 Fedora 创建 RPM 包之前,您需要安装一些必须的开发工具并设置账户:


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


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


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


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


一旦您登陆了要执行打包操作的用户,您可以使用一下命令在您的主文件夹下创建一个标准的打包工作目录结构(不要输入"$"):
一旦以 makerpm 用户登陆,使用以下命令在用户家目录下,创建标准的打包工作目录结构:
   $ rpmdev-setuptree
   $ rpmdev-setuptree


"rpmdev-setuptree" 程序新建一个位置是 ~/rpmbuild 的目录。在"rpmbuild"目录下是一些预设好的子目录(比如SPECS 和 BUILD),它们将会在创建软件包的时候起作用。
<code>rpmdev-setuptree</code> 程序将创建 <code>~/rpmbuild</code> 目录,以及一系列预设的子目录(如 <code>SPECS</code> <code>BUILD</code>),你将使用它们作为打包目录。另外,还会创建 <code>~/.rpmmacros</code> 文件,它用于设置各种选项。
"rpmdev-setuptree" 也会创建一个位置是 ~/.rpmmacros 的文件。它将会使 rpm 和 rpmbuild 程序调能够正确识别目录结构。
 
[[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> 选项。
 
一旦设置完毕,通常不需要再次设置。


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


一旦您设置好了,通常不需要再次设置。
若要构建一个标准的 RPM 包,您需要创建 <code>.spec</code> 文件,其中包含软件打包的全部信息。然后,对此文件执行 <code>rpmbuild</code> 命令,经过这一步,系统会按照步骤生成最终的 RPM 包。


== 构建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> 并执行:


要想构建一个标准的RPM包,您需要新建一个 ".spec" 文本文档,里面包含即将被安装的软件的所有详细信息。然后您对这个文本文档在系统中执行 "rpmbuild" 命令,经过这一步,系统会按照步骤生成最终的RPM包。
  $ rpmbuild -ba ''NAME''.spec


按理说您应该把源代码包,比如从软件开发者释放出的那种以 ".tar.gz" 结尾的文件, 放入 "~/rpmbuild/SOURCES" 目录下。您应该把 ".spec" 文件放入 "~/rpmbuild/SPECS" 目录下,并且把它命名为 "''软件包名字''.spec" 。当然, ''软件包名字'' 就是最终RPM包的名字。为了创建所有软件包(包括预编译好的包(Binary RPM)和源代码包(SRPM)),您需要把终端切换至 "~/rpmbuild/SPECS" 目录然后执行:
当执行此命令时,<code>rpmbuild</code> 会自动读取 <code>.spec</code> 文件并按照下表列出的步骤完成构建。下表中,以 <code>%</code> 开头的语句为预定义宏,每个宏的作用如下:
rpmbuild -ba ''软件包名字''.spec


When invoked this way, rpmbuild will read the .spec file and attempt to go through the following stages (in this order; names beginning with % are predefined macros as described below, and the directories each stage normally reads and writes are listed):
{|border="1" cellspacing="0"
{|
! 阶段 !! 读取的目录 !! 写入的目录 !! 具体动作
! 阶段 !! 读取 !! 写入 !! 具体动作
|-
|-
|%prep||%_sourcedir||%_builddir||This reads the sources and patches in the source directory %_sourcedir (usually ~/rpmbuild/SOURCES).  It unpackages the sources to a subdirectory underneath the build directory %_builddir (usually ~/rpmbuild/BUILD/) and applies the patches.
|<code>%prep</code>||<code>%_sourcedir</code>||<code>%_builddir</code>||读取位于 <code>%_sourcedir</code> 目录的源代码和 patch 。之后,解压源代码至 <code>%_builddir</code> 的子目录并应用所有 patch。
|-
|-
|%build||%_builddir||%_builddir||This compiles the files underneath the build directory %_builddir (usually ~/rpmbuild/BUILD/).  This is often implemented by running some variation of "./configure ; make".
|<code>%build</code>||<code>%_builddir</code>||<code>%_builddir</code>||编译位于 <code>%_builddir</code> 构建目录下的文件。通过执行类似 "<code>./configure && make</code>" 的命令实现。
|-
|-
|%check||%_builddir||%_builddir||Check that the software works properly.  This is often implemented by running some variation of "make test".  Many packages don't implement this stage.
|<code>%install</code>||<code>%_builddir</code>||<code>%_buildrootdir</code>||读取位于 <code>%_builddir</code> 构建目录下的文件并将其安装至 <code>%_buildrootdir</code> 目录。这些文件就是用户安装 RPM 后,最终得到的文件。注意一个奇怪的地方: ''最终安装目录'' '''不是''' ''构建目录''。通过执行类似 "<code>make install</code>" 的命令实现。
|-
|-
|%install||%_builddir||%_buildrootdir||This reads the files underneath the build directory %_builddir (usually ~/rpmbuild/BUILD/) and writes to a directory underneath the build root directory %_buildrootdir (usually ~/rpmbuild/BUILDROOT).  The files that are written are the files that are supposed to be installed when the binary package is installed by an end-user.  Beware of the weird terminology: The ''build root directory'' is '''not''' the same as the ''build directory''.  This is often implemented by running "make install".
|<code>%check</code>||<code>%_builddir</code>||<code>%_builddir</code>||检查软件是否正常运行。通过执行类似 "<code>make test</code>" 的命令实现。很多软件包都不需要此步。
|-
|-
|bin||%_buildrootdir||%_rpmdir||This reads the files underneath the build root directory %_buildrootdir (usually ~/rpmbuild/BUILDROOT/) to create binary RPM packages underneath the RPM directory %_rpmdir (usually ~/rpmbuild/RPMS/).  Inside the RPM directory is a directory for each architecture, and a "noarch" directory for packages that apply to any architecture.  These RPM files are the packages for users to install.
|<code>bin</code>||<code>%_buildrootdir</code>||<code>%_rpmdir</code>||读取位于 <code>%_buildrootdir</code> 最终安装目录下的文件,以便最终在 <code>%_rpmdir</code> 目录下创建 RPM 包。在该目录下,不同架构的 RPM 包会分别保存至不同子目录, "<code>noarch</code>" 目录保存适用于所有架构的 RPM 包。这些 RPM 文件就是用户最终安装的 RPM 包。
|-
|-
|src||%_sourcedir||%_srcrpmdir||This creates a source RPM package (.src.rpm) inside the source RPM directory %_srcrpmdir (usually ~/rpmbuild/SRPMS).  These files are needed for reviewing and updating packages.
|<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. -->


As you can tell, certain directories have certain purposes in rpmbuild.  These are:
在 <code>rpmbuild</code> 中,对上表中的每个宏代码都有对应的目录:
{|
 
! 宏指令 !! 名字 !! 通常用途 !! 目的
{|border="1" cellspacing="0"
! 宏代码 !! 名称 !! 默认位置 !! 用途
|-
|-
|%_specdir||Specification directory||~/rpmbuild/SPECS||RPM specifications (.spec) files
|<code>%_specdir</code>||Spec 文件目录||<code>~/rpmbuild/SPECS</code>|| 保存 RPM 包配置(<code>.spec</code>)文件
|-
|-
|%_sourcedir||Source directory||~/rpmbuild/SOURCES||Pristine source package (e.g., tarballs) and patches
|<code>%_sourcedir</code>||源代码目录||<code>~/rpmbuild/SOURCES</code>|| 保存源码包(如 .tar 包)和所有 patch 补丁
|-
|-
|%_builddir||Build directory||~/rpmbuild/BUILD||Source files are unpacked and compiled in a subdirectory underneath this.
|<code>%_builddir</code>||构建目录||<code>~/rpmbuild/BUILD</code>|| 源码包被解压至此,并在该目录的子目录完成编译
|-
|-
|%_buildrootdir||Build root directory||~/rpmbuild/BUILDROOT||Files are installed under here during the %install stage.
|<code>%_buildrootdir</code>||最终安装目录||<code>~/rpmbuild/BUILDROOT</code>|| 保存 %install 阶段安装的文件
|-
|-
|%_rpmdir||Binary RPM directory||~/rpmbuild/RPMS||Binary RPMs are created and stored under here.
|<code>%_rpmdir</code>||标准 RPM 包目录||<code>~/rpmbuild/RPMS</code>|| 生成/保存二进制 RPM 包
|-
|-
|%_srcrpmdir||Source RPM directory||~/rpmbuild/SRPMS||Source RPMs are created and stored here.
|<code>%_srcrpmdir</code>||源代码 RPM 包目录||<code>~/rpmbuild/SRPMS</code>|| 生成/保存源码 RPM 包(SRPM)
|}
|}


Should a stage fail, you'll need to look at the output to see ''why'' it failed, and change the .spec file (or other input) as needed.
如果某一阶段失败,请查看输出信息以了解失败''原因'',并根据需要修改 <code>.spec</code> 文件。


== Getting ready to package a particular program ==
== 做好准备打包一个特殊程序 ==


If there are special programs that are required to build or run the program you are packaging, install those
如果这里有特殊的程序,它们需要被安装或者运行以便让您打包的普通程序正常工作,那么请先安装它们,然后记录下诸如软件包等相关信息。
other programs and write down what they were (you'll need that information).


To package a program for the Fedora repository, you ''must'' package pristine (original) sources, along
如果想为 Fedora 源打包一个程序,您必须使用源代码来打包,且包含 patch 以及打包简介;'''不可以'''使用预编译代码进行打包。将源代码(通常是 .tar.gz 文件)放入 "<code>~/rpmbuild/SOURCES</code>" 目录(注意用户)。
with the patches and build instructions;
it's ''not'' okay to start with pre-compiled code.
Install the file with the original source (usually a .tar.gz file) in the
"~/rpmbuild/SOURCES" directory (of the rpm building user account).


Read through the manual installation instructions for your program;
仔细阅读该软件的安装说明。我们建议您先手工安装一次以了解具体情况。除少数情况外,所有二进制文件和程序库都必须由源码包中的源码编译而成。
you're going to be automating this by editing a ".spec" file, so you have
to understand what you're supposed to do first.
It's probably best if you try a "dry run", going through its build and installation
procedure without trying to do it via RPM first
(that's especially true if you're not familiar with RPM).
With a few exceptions, all program binaries and program libraries included in Fedora packages must be built from the source code that is included in the source package.


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


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


请参阅:[[Packaging:No_Bundled_Libraries|把绑入的外部组件分离成单独程序]] 以了解更多信息。请您把绑入的组件从主程序分离,制作成单独的包再发布。
=== 许可协议 ===


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


您只可以打包'''允许您合法打包'''的软件。
=== 使用已有的信息 ===


就像在 [[Packaging/Guidelines#Legal|packaging guidelines' legal section]] 声明的那样,如果您打算把软件包放入 Fedora 源,一定遵守[[Licensing:Main]] [[Packaging:LicensingGuidelines]] 。
尽可能利用一切已有的信息!很明显,请不要打包源中已存在的程序!为了防止您犯这种错误,请查阅 [https://admin.fedoraproject.org/pkgdb/ Fedora 软件包数据库]。同时建议查阅 [[PackageMaintainers/InProgressReviewRequests |正在被审核的软件包]][[PackageMaintainers/RetiredPackages |已停止使用的软件包]]列表。如果未找到相关信息,请使用 Google 搜索查看是否有类似 rpm 包。您可以直接访问 [http://pkgs.fedoraproject.org/cgit/ Fedora 软件包 Git 源] 查看相关 SPEC 文件(和 Patch)。您可以使用 DNF 插件下载 SRPM 包:
通常情况下,您只可以打包使用开源许可证(比如GNU GPL、GNU LGPL、BSD-new、MIT/X或者Apache 2.0 许可证)发放的开源软件(OSS)。
$ dnf download --source sourcepackage-name
请仔细检查许可证是否名副其实,同时是否软件全体均满足开源要求。(比如抽查源代码开头的注释、README 文件等等)。
如果软件有附加组件被绑入软件,请确定它们也是开源软件。这十分重要。


=== Reuse existing package information ===
或通过访问 [http://mirrors.fedoraproject.org/publiclist Fedora 镜像列表] 的 HTTP/FTP 镜像页面,导航至 <code>releases/{{FedoraVersion}}/Everything/source/SRPMS</code> 目录( "<code>{{FedoraVersion}}</code>" 表示 Fedora 版本),手动下载扩展名为 <code>.src.rpm</code> 的 SRPM 包即可。


Try to reuse what you can.
一旦有了源码包,执行以下命令安装至 <code>~/rpmbuild</code> 目录:
Obviously, make sure you aren't packaging something that is already packaged;
$ rpm -ivh 源码包名*.src.rpm
you can find a list of existing packages in Fedora Package Collection in the [https://admin.fedoraproject.org/pkgdb/ Fedora Package Database].
Also check the [[PackageMaintainers/InProgressReviewRequests | In Progress Review Requests]] (for packages that are currently being reviewed)
and the [[PackageMaintainers/RetiredPackages | Retired Packages]] list.
Failing that, see if someone has already started to package it for Fedora.
Google for "PROGRAMNAME Fedora rpm" or similar... maybe you can pick up where
they started.
You can use
http://pkgs.fedoraproject.org/gitweb/
directly to view .spec files (and patches) of any similar packages already in Fedora.
You can download the source RPMs using a program from the yum-utils package, by:
$ yumdownloader --source sourcepackage-name


Alternatively, an existing source rpm can be retrieved manually by browsing to the [http://mirrors.fedoraproject.org/publiclist Fedora mirror]'s http or ftp page,
您也可以使用 <code>rpm2cpio</code> 将源码包解压至任意目录:
select releases/11/Everything/source/SRPMS
$ mkdir 源码包名_src_rpm
(replace "11" with the Fedora release you want),
$ cd 源码包名_src_rpm
and downloading the source RPMs you want (they end in .src.rpm).
$ rpm2cpio ../源码包名.src.rpm | cpio -i


Once you have the source RPM, running the following command installs the src.rpm
使用已有的信息以帮助您打包。[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 源码包] 并查看是否包含相关信息。有时,这些操作没什么实际帮助,因为不同系统有不同的打包规则。
$ rpm -ivh sourcepackage-name*.src.rpm
which places the package's .spec file into ~/rpmbuild/SPECS and other source and patch files in ~/rpmbuild/SOURCES).
 
You can also unpack the .src.rpm in a directory using rpm2cpio:
$ mkdir PROGRAMNAME_src_rpm
$ cd PROGRAMNAME_src_rpm
$ rpm2cpio ../PROGRAMNAME-*.src.rpm | cpio -i
 
''Sometimes'' it's easiest to start with an existing package, and then clean it up for Fedora.
[http://rpmfind.net/ RPM Find] may help you find rpm's for non-Fedora systems.
(You can install source RPMs for other systems the same way as for Fedora).
Failing that, you might look at the source package files (not the .deb binary package files)
for [http://packages.ubuntu.com/ Ubuntu] or
[http://www.debian.org/distrib/packages Debian]
(source package files are standard tarballs with a "debian/" subdirectory, possibly
associated with patch files).
If the [http://www.freebsd.org/ports/installing.html FreeBSD ports collection] has it,
you could
[ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/ports.tar.gz download the FreeBSD ports tarball]
and see if their packaging information helps as a starting point.
'''However''', this is sometimes not helpful at all.
Different distributions have different rules, and what they do may be quite
inappropriate for Fedora.


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


You now need to create a ".spec" file in the "~/rpmbuild/SPECS" directory.
现在,您需要在 <code>~/rpmbuild/SPECS</code> 目录下,新建一个 SPEC 文件。文件应命名为 "<code>软件包名.spec</code>"。名称根据软件包名或通用名填写即可。但是,必须要遵守 [[Packaging/NamingGuidelines|软件包命名规定]]
You should name it after the program name, e.g., "program.spec".  Use the archive name or the name advocated by the software author where you can, but be sure to follow the
[[Packaging/NamingGuidelines| Package Naming Guidelines]].


=== 新建一个空白的 .spec 文件 ===
=== 模板和实例 ===
 
==== 模板 ====
When you're creating a spec file for the first time, you can create its initial version using emacs or vim (as of 7.1.270-1),
如果您首次创建 .spec 文件,vim 或 emacs 会自动生成模板:
they will automatically create a template for you.  E.G.:
   $ cd ~/rpmbuild/SPECS
   $ cd ~/rpmbuild/SPECS
   $ vi program.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 216: Line 173:
  %changelog
  %changelog


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


You may have $RPM_BUILD_ROOT instead of %{buildroot}; just be consistent.
您也可以使用 <code>rpmdev-newspec</code> 命令来创建 SPEC 文件。<code>rpmdev-newspec 软件包名</code> 可以创建一个初始 SPEC 文件,该工具从软件包名判断使用哪个模板,支持指定模板。 <code>/etc/rpmdevtools/spectemplate-*.spec</code> 包含所有可用的模板,使用 <code>rpmdev-newspec --help</code> 命令了解更多信息。例如,为 python 模块创建 SPEC 文件:
 
You may also use the <code>rpmdev-newspec</code> command to create a spec file for you.  <code>rpmdev-newspec NAME-OF-NEW-PACKAGE</code> can create an initial spec file for a new package, tailored to various types of packages.  It will guess what kind of template to use based on the package name, or you can specify a particular template; see <code>/etc/rpmdevtools/spectemplate-*.spec</code> for available templates.  See <code>rpmdev-newspec --help</code> for more information. For example, to create a new spec file for a python module:


  cd ~/rpmbuild/SPECS
  cd ~/rpmbuild/SPECS
Line 225: Line 181:
  vi python-antigravity.spec
  vi python-antigravity.spec


=== 示例: eject ===
==== 实例 ====
===== eject =====
这是 Fedora 16 <code>eject</code> 程序的 spec 文件:
 
<pre>
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}


Here's a simple example, a Fedora 9 package for the "eject" program:
%install
%make_install


Summary: A program that ejects removable media using software control
install -m 755 -d %{buildroot}/%{_sbindir}
Name: eject
ln -s ../bin/eject %{buildroot}/%{_sbindir}
Version: 2.1.5
 
Release: 11%{dist}
%find_lang %{name}
License: GPL
 
Group: System Environment/Base
%files -f %{name}.lang
Source: http://metalab.unc.edu/pub/Linux/utils/disk-management/%{name}-%{version}.tar.gz
%doc README TODO COPYING ChangeLog
Source1: eject.pam
%{_bindir}/*
Patch1: eject-2.1.1-verbose.patch
%{_sbindir}/*
Patch2: eject-timeout.patch
%{_mandir}/man1/*
Patch3: eject-2.1.5-opendevice.patch
 
Patch4: eject-2.1.5-spaces.patch
%changelog
Patch5: eject-2.1.5-lock.patch
* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.1.5-21
Patch6: eject-2.1.5-umount.patch
- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
 
URL: http://www.pobox.com/~tranter
* Fri Jul 02 2010 Kamil Dudka <kdudka@redhat.com> 2.1.5-20
ExcludeArch: s390 s390x
- handle multi-partition devices with spaces in mount points properly (#608502)
BuildRequires: gettext
</pre>
BuildRequires: automake
 
BuildRequires: autoconf
{{Anchor|Spec_file_pieces_explained}}
BuildRequires: libtool
 
== SPEC 文件综述 ==
%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
%setup -q -n %{name}
%patch1 -p1 -b .versbose
%patch2 -p1 -b .timeout
%patch3 -p0 -b .opendevice
%patch4 -p0 -b .spaces
%patch5 -p0 -b .lock
%patch6 -p1 -b .umount
%build
%configure
make
%install
rm -rf %{buildroot}
make DESTDIR=%{buildroot} install
# pam stuff
install -m 755 -d %{buildroot}/%{_sysconfdir}/pam.d
install -m 644 %{SOURCE1} %{buildroot}/%{_sysconfdir}/pam.d/%{name}
install -m 755 -d %{buildroot}/%{_sysconfdir}/security/console.apps/
echo "FALLBACK=true" > %{buildroot}/%{_sysconfdir}/security/console.apps/%{name}
install -m 755 -d %{buildroot}/%{_sbindir}
pushd %{buildroot}/%{_bindir}
mv eject ../sbin
ln -s consolehelper eject
popd
%find_lang %{name}
%clean
rm -rf %{buildroot}
%files -f %{name}.lang
%defattr(-,root,root)
%doc README TODO COPYING ChangeLog
%attr(644,root,root) %{_sysconfdir}/security/console.apps/*
%attr(644,root,root) %{_sysconfdir}/pam.d/*
%{_bindir}/*
%{_sbindir}/*
%{_mandir}/man1/*
%changelog
* Wed Apr 02 2008 Zdenek Prikryl &lt;zprikryl at, redhat.com&gt; 2.1.5-11
- Added check if device is hotpluggable
- Resolves #438610


=== .Spec 文件具体内容解析 ===
其他有用的信息:
* [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch-creating-rpms.html RPM 指南] 描述了如何编写 spec 文件。
The [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch-creating-rpms.html RPM Guide, section on creating RPMs],
* 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]
describes the details of how to fill in a spec file.
* [http://rpm.org/max-rpm-snapshot/ Maximum RPM] 包含详细信息,但内容已经过时。
The developerWorks series "Packaging software with RPM" [http://www.ibm.com/developerworks/library/l-rpm1/ Part 1], [http://www.ibm.com/developerworks/library/l-rpm2/ Part 2], and [http://www.ibm.com/developerworks/library/l-rpm3.html Part 3] is also handy.
[http://rpm.org/max-rpm-snapshot/ Maximum RPM] has the most complete information, but is dated.


You will need to follow the Fedora guidelines, such as the
您需要遵守这些规定:[[Packaging/NamingGuidelines|软件包命名规定]][[Packaging/Guidelines|打包规定]] [[Packaging/ReviewGuidelines|软件包审核规定]]
[[Packaging/NamingGuidelines| Package Naming Guidelines]],
[[Packaging/Guidelines| Packaging Guidelines]], and
[[Packaging/ReviewGuidelines|Package review guidelines]].


You can insert comments with a leading "#" character, but
"<code>#</code>" 字符表示注释,但需要避免注释宏(以 <code>%</code> 开头),因为它们会首先被替换展开。使用 <code>%%</code> 注释宏。另外,还要避免在脚本命令的相同行中使用行内注释。
don't insert potentially-multiline-macros (words beginning with "%") in a comment
(macros are expanded first); if you're commenting out a line, double the percent signs ("%%").  Also, don't use inline comments ("#") on the same line after
a script command.


Here are the major fields/areas that you will need to fill in:
以下介绍了主要的标签。注意 <code>%{name}</code>,<code>%{version}</code> 和 <code>%{release}</code> 代表 Name, Version 和 Release 这三个标签。只要更改标签,宏就会使用新值。
* Name: The (base) name of the package.  It must follow the [[Packaging/NamingGuidelines| Package Naming Guidelines]].  In many cases, this will be in all lower case.  Elsewhere in the spec file, you can refer to the name using the macro %{name} - that way, if the name changes, the new name will be used by those other locations. This name should match the spec file name.
* '''Name''': 软件包名,应与 SPEC 文件名一致。命名必须符合 [[Packaging/NamingGuidelines|软件包命名规定]]
* Version: The upstream version number. See [http://fedoraproject.org/wiki/Packaging/NamingGuidelines#Package_Version Packaging/Naming guidelines - package version] for more information. If the version is non-numeric (contains tags that are not numbers or digits), you may need to include the additional non-numeric characters in the release field. If upstream uses full dates to distinguish versions, consider using version numbers of the form yy.mm[dd] (so a 2008-05-01 release becomes 8.05). Elsewhere in the spec file, refer to this value as %{version}.
* '''Version''': 上游版本号。请查看 [[Packaging/NamingGuidelines#Version_Tag|版本标签规定]]。如果包含非数字字符,您可能需要将它们包含在 Release 标签中。如果上游采用日期作为版本号,请考虑以:<code>yy.mm[dd]</code> (例如 <code>2008-05-01</code> 可变为 <code>8.05</code>) 格式作为版本号。
* Release: The initial value of the release should normally be "1%{?dist}".  Then, increment the number every time you release a new package for the same version of software.  If a new version of the software being packaged is released, the version number should be changed to reflect the new software version, and the release number should be reset to 1.  See [http://fedoraproject.org/wiki/Packaging/NamingGuidelines#Package_Release Name Guidelines - package release] for more. [[Packaging/DistTag]] describes the "dist" tag, which isn't required but can be useful. Use %{release} to reuse this value.
* '''Release''': 发行编号。初始值为 <code>1%{?dist}</code>。每次制作新包时,请递增该数字。当上游发布新版本时,请修改 Version 标签并重置 Release 的数字为 <code>1</code>。具体参考打包规定中的 [[Packaging/NamingGuidelines#Release_Tag|Release 标签部分]],以及 [[Packaging/DistTag|Dist tag]]
* Summary: A brief, one-line summary of the package. Use American English, and ''do not'' end in a period.
* '''Summary''': 一行简短的软件包介绍。请使用美式英语。'''请勿在结尾添加标点!'''
* Group: This needs to be a pre-existing group, like "Applications/Engineering"; run "less /usr/share/doc/rpm-*/GROUPS" to see the complete list.  If you create a sub-package "...-doc" with documentation, use the group "Documentation".
* '''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: Its license; for software, this must be an open source software license.  Use a standard abbreviation, e.g., "GPLv2+".  Try to be specific, e.g., use "GPLv2+" (GPL version 2 or greater) instead of just "GPL" or "GPLv2" where it's true.  See [[Licensing]] and the [[Packaging/LicensingGuidelines| Licensing Guidelines]] for more information.  You can list multiple licenses by combining them with "and" and "or", e.g., "GPLv2 and BSD".  Call this tag "License"; don't use the older, inaccurately named tag "Copyright".
* '''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: The URL for more information about the program, e.g., the project website.  Note: This is NOT where the original source code came from, see "Source" (next!).
* '''URL''': 该软件包的项目主页。'''''注意:源码包 URL 请使用 Source0 指定。'''''
* Source0: The URL for the compressed archive containing (original) pristine source code, as upstream released it. "Source" is synonymous with "Source0".  If you give a full URL (and you should), its basename will be used when looking in the SOURCES directory. If possible, embed %{name} and %{version}, so that changes to either will go to the right place.  '''Warning:''' ''Source0:'' and ''URL:'' are different - normally they are both URLs, but the "URL:" entry points to the project website, while the "Source0:" entry points to the actual file containing the source code (and is typically a .tar.gz file).  As noted in the guidelines, "When downloading sources, patches etc, consider using a client that preserves the upstream timestamps. For example wget -N or curl -R. To make the change global for wget, add this to your ~/.wgetrc: timestamping = on, and for curl, add to your ~/.curlrc: -R." If there is more than one source, name them Source1, Source2, and so on. If you're adding whole new files in addition to the pristine sources, you can list each of them as sources as well, but list them ''after'' the pristine sources. A copy of each of these sources will be included in any source package you create (unless you specially direct otherwise).  See [[Packaging/SourceURL]] for more information on special cases (using revision control, when upstream uses prohibited code, etc.).
* '''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 that you will apply to the source code.  If you need to patch the files after they've been uncompressed, you should edit the files, save their differences as a "patch" file in your ~/rpmbuild/SOURCES directory.  Patches should make only one logical change, 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, etc.), then add "BuildArch: noarch".  The architecture for the binary RPM will then be "noarch".
* '''BuildArch''': 如果你要打包的文件不依赖任何架构(例如 shell 脚本,数据文件),请使用 "<code>BuildArch: noarch</code>"。RPM 架构会变成 "<code>noarch</code>"
* BuildRoot: This is where files will be "installed" during the "%install" process (which happens after the %build compilation process).  Normally you should just leave this line alone; under the usual Fedora setup, this will be a macro that will create a new special directory under /var/tmp. Newer versions of RPM will ignore this value, and instead place the build root in "%{_topdir}/BUILDROOT/".
* '''BuildRoot''': %install 阶段(%build 阶段后)文件需要安装至此位置。Fedora 不需要此标签,只有 EPEL5 还需要它。默认情况下,根目录为 "<code>%{_topdir}/BUILDROOT/</code>"
* BuildRequires: A comma-separated list of packages required for building (compiling) the program.  These are ''not'' automatically determined, so you need to include ''everything'' needed to build the program.  There are a few packages that are so common in builds that you don't need to mention them, such as "gcc"; see the [[Packaging/Guidelines| Packaging Guidelines]] for the complete list of the packages you may omit.  You can also specify minimum versions, if necessary, like this: "ocaml >= 3.08". You can have more than one line of BuildRequires (in which case they are all required for building).  If you need file /EGGS, you can get its package by running "rpm -qf /EGGS"; if EGGS is a program, you determine its package quickly by running "rpm -qf `which EGGS`".  Try to specify only the minimal set of packages necessary to properly build the package, since each one will slow down a "mock"-based build (e.g., try to use sed instead of perl if you don't really need perl's abilities).  Watch out: Some applications permanently disable functions if their package isn't detected during the build; in those cases you may need to include those additional packages.  If you have trouble figuring out this list, the "auto-br-rpmbuild" command (from the 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 list of packages for ''Requires'' (what's required when installing/running) and ''BuildRequires'' (what's required to build the binary RPM) are independent; a package may be in one list but not the other, or it could be in both.  The dependencies of binary packages are in many cases automatically detected by rpmbuild, so it is often the case that you don't need to specify the ''Requires'' tag at all. But if you want to highlight some specific packages as being required, or require a package that rpm can't detect should be required, then add it here.
* '''Requires''': 安装软件包时所需的依赖包列表,以逗号分隔。请注意, BuildRequires 标签是编译所需的依赖,而 Requires 标签是安装/运行程序所需的依赖。大多数情况下,<code>rpmbuild</code> 会自动探测依赖,所以可能不需要 Requires 标签。然而,你也可以明确标明需要哪些软件包,或由于未自动探测所需依赖而需要手动标明。
* %description - A longer, multi-line description of the program.  Use American English. All lines must be 80 characters or less. "Blank lines are assumed to separate paragraphs. Some graphical user interface installation programs will reformat paragraphs...  (lines that) start with whitespace, such as a space or tab, will be treated as preformatted text and displayed as is, normally with a fixed-width font." (per the [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, that is, to uncompress it so that it will be ready for building (compiling).  Typically this is just "%setup -q" or some variation of it; a common variation is "%setup -q -n NAME" if the source file unpacks into NAME. See the "%prep" section below for more.
* '''%prep''': 打包准备阶段执行一些命令(如,解压源码包,打补丁等),以便开始编译。一般仅包含 "<code>%autosetup</code>";如果源码包需要解压并切换至 <code>NAME</code> 目录,则输入 "<code>%autosetup -n NAME</code>"。查看 %prep 部分了解更多信息。
* %build - Script commands to "build" the program, that is, to compile it and get it ready for installing.  The program should come with instructions on how to do this.  See the "%build" section below for more.
* '''%build''': 包含构建阶段执行的命令,构建完成后便开始后续安装。程序应该包含有如何编译的介绍。查看 %build 部分了解更多信息。
* %check - Script commands to self-test the program. This is run after %build and before %install, so you should place it there if you have this section.  Often it simply contains "make test" or "make check".  This is separated from %build so that people can skip the self-test if they desire.  This isn't documented in many places.
* '''%install''': 包含安装阶段执行的命令。命令将文件从 <code>%{_builddir}</code> 目录安装至 <code>%{buildroot}</code> 目录。查看 %install 部分了解更多信息。
* %install - Script commands to "install" the program.  The commands should copy the files from the "build directory" %{_builddir} (which would be under ~/rpmbuild/BUILD) into the buildroot directory, %{buildroot} (which would normally be under /var/tmp). See the "%install" section below for more.
* '''%check''': 包含测试阶段执行的命令。此阶段在 %install 之后执行,通常包含 "<code>make test</code>" 或 "<code>make check</code>" 命令。此阶段要与 %build 分开,以便在需要时忽略测试。
* %clean - instructions to clean out the build root. Typically:
* '''%clean''': 清理安装目录的命令。此阶段在 Fedora 中是多余的,仅针对 EPEL。一般只包含:
  rm -rf %{buildroot}
  rm -rf %{buildroot}
* %files - the list of files that will be installed.  See the "%files" section below for more.
* '''%files''': 需要被打包/安装的文件列表。查看 %files 部分了解更多信息。
* %changelog - Changes in the package.  Use the format example above.
* '''%changelog''': RPM 包变更日志。请使用示例中的格式。'''注意,不是软件本身的变更日志。'''
* ExcludeArch: If the package does not successfully compile, build or work on an architecture, then those architectures should be listed in the spec in an ExcludeArch tag.
* '''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 还支持使用一个 SPEC 文件制作多个软件包(这称为 [[How_to_create_an_RPM_package#Subpackages|子软件包]]),例如 <code>name-libs</code> 和 <code>name-devel</code> 等软件包。
 
{{admon/caution|请'''不要'''使用这些标签|
* Packager
* Vendor
* Copyright}}


Don't use the tags "Packager" or "Vendor".  Don't use "Copyright" - use "License" instead.
请'''不要'''制作 "relocatable" 软件包(不遵守FHS);它们不会为 Fedora 加分,反而把事情搞得更复杂。
Don't create a "relocatable" package - they don't add value in Fedora yet they make things more complicated.


RPM supports subpackages, that is, a single spec file can generate many
== SPEC 文件剖析 ==
binary packages.  For example, if the documentation is very large, you might generate
a separate "-doc" subpackage.
See below for more.


=== %prep section ===
=== %prep 部分 ===
The "%prep" section describes how to unpack the compressed packages so that they can be built.
Typically, this is a set of "%setup" and/or %patch commands, which reference the Source0:, Source1:, etc. lines above.
See the [http://rpm.org/max-rpm-snapshot/s1-rpm-inside-macros.html Maximum RPM section on %setup and %patch] for more details.


'''Warning:''' In spec files, don't use in-line comments (a "#" comment on the same line after a command), and don't put macros (words beginning with "%") in a comment unless you quote the "%" as "%%". Macros can cause failures if they are in a comment, because they are always expanded (even when in a comment) and they can expand to multiple lines. This is true for %prep, %build, and so on.
%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 小节] 了解更多信息。


The new RPM 4.4.2.x series adds two new macros, %{patches} and %{sources}, so you can do things like:
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
These new macros can be very useful if you have a large list of patches or sources.
However, keep in mind that using these will make your spec
incompatible with the rpm used in Fedora 9 and earlier, RHEL, and
many other RPM-based distros.


==== %prep section: %setup command ====
注意,RHEL 和其它基于 RPM 的发行版,并不支持这种用法。


The "%setup" command unpacks a source package, and takes several switches.  Normally you should use "-q" (quiet) to prevent setup from babbling about every file it unpacks.  Here are a few switches besides -q:
==== %prep 部分:%autosetup 命令 ====
* -n ''name'': If the name of the rpm is something other than what the Source unpacks to, use this switch to state the ''name'' it unpacks to.  E.G., if the tarball unpacks into a directory MYNAME, use %setup -q -n MYNAME
* -c ''name'': If the tarball doesn't unpack into a single directory, this creates a directory named ''name'' and then unpacks into it.  Useful if you have one of those annoying tarballs that doesn't have a single common subdirectory embedded in it.


There are
"<code>%autosetup</code>" 命令用于解压源码包。可用选项包括:
[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>-n</code> ''name''''' : 如果源码包解压后的目录名称与 RPM 名称不同,此选项用于指定正确的目录名称。例如,如果 tarball 解压目录为 FOO,则使用 "<code>%autosetup -n FOO</code>"。
are creating subpackages (see below).  The key ones are:
* '''<code>-c</code> ''name''''' : 如果源码包解压后包含多个目录,而不是单个目录时,此选项可以创建名为 ''name'' 的目录,并在其中解压。
 
如果使用 "<code>%setup</code>" 命令,通常使用 ''<code>-q</code>''' 抑止不必要的输出。
 
[http://rpm.org/max-rpm-snapshot/s1-rpm-inside-macros.html 如果需要解压多个文件,有更多 %spec 选项可用],这对于创建子包很有用。常用选项如下:


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


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


The "%patch0" command applies patch 0 (similar for 1, 2, etc.).  Patches are the normal way to change to the source code if necessary to package it.  The normal "-pNUMBER" option applies, which simply passes that argument on to ''patch''.
如果使用 "<code>%autosetup</code>" 命令,则不需要手动进行补丁管理。如果你的需求很复杂,或需要与 EPEL 兼容,需要用到此部分的内容。"<code>%patch0</code>" 命令用于应用 Patch0(%patch1 应用 Patch1,以此类推)。Patches 是修改源码的最佳方式。常用的 "<code>-pNUMBER</code>" 选项,向 <code>patch</code> 程序传递参数,表示跳过 NUM 个路径前缀。


Patch file names often look like "telnet-0.17-env.patch", that is, ''%{name}''-''%{version}''-''patch_purpose''.patch (some people omit -''%{version}''). Patch files are typically the result of a "diff -u"; if you do this from the subdirectory of ~/rpmbuild/BUILD, you won't have to specify a -p level later.  You can use all the normal ways of creating a patch file.
补丁文件名通常像这样 "<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> 选项。


If you're creating a patch file a single file FILENAME, a common way is to copy it to FILENAME.orig, modify it, and then save the results of "diff -u FILENAME.orig FILENAME".  If you change directory to "~/rpmbuild/BUILD/''NAME''", you could create a patch file to change a single file by doing:
为一个文件制作补丁的步骤:
  cp X/Y.Z X/Y.Z.orig
  cp foo/bar foo/bar.orig
  vim X/Y.Z
  vim foo/bar
  diff -u X/Y.Z.orig X/Y.Z > ~/rpmbuild/SOURCES/PKGNAME.REASON.patch
  diff -u foo/bar.orig foo/bar > ~/rpmbuild/SOURCES/PKGNAME.REASON.patch


If you're going to edit many files, one easy method is to copy the whole subdirectory underneath BUILD, and then do subdirectory diffs; once you change directory to "~rpmbuild/BUILD/''NAME''", you can:
如果需要修改多个文件,简单方法是复制 <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 ".orig" before editing them. Then, you can use "gendiff" (in the rpm package) to create a patch with the differences.  Do "man gendiff" for more information.
如果你想在一个补丁中编辑多个文件,你可以在编辑之前,使用 "<code>.orig</code>" 扩展名复制原始文件。然后,使用 "<code>gendiff</code>"(在 <code>rpm-build</code> 包中)创建补丁文件。


Try to ensure that in your patch the "context" matches exactly.
需要确保你的补丁精确匹配上下文。默认 "fuzz" 值为 "<code>0</code>",表示要求精确匹配。你可以添加 "<code>%global _default_patch_fuzz 2</code>" 将 fuzz 设为旧版 Fedora RPM 所采用的值,但我们建议你尽量避免这样做。
In old versions of Fedora, the default "fuzz" value was 2, which meant that imprecise matches were acceptable.
However, the version of [http://lwn.net/Articles/289235/ RPM used by Fedora 10 and later] have a default fuzz to 0, requiring that matches be exact.
You can work around this by adding "%global _default_patch_fuzz 2", but it's better to not
have the problem by making the patch match the context exactly.


As explained in [[Packaging/PatchUpstreamStatus]], all patches in Fedora spec files SHOULD have a comment above them about their upstream status.  This should document the upstream bug/email that includes it (including the date), or if it's Fedora-unique, why it is unique.
[[Packaging/PatchUpstreamStatus]] 所述,SPEC 文件中的所有补丁都需要注释来描述补丁的上游状态。其中应包括上游 bug/email 文档(包含日期)。如果是 Fedora 特别需要的补丁,应描述为何需要它。Fedora 项目致力于贴近上游;查看 [[PackageMaintainers/WhyUpstream]] 了解其重要性。
The Fedora Project focuses, as much as possible, on not deviating from upstream in the software it includes in the repository - see [[PackageMaintainers/WhyUpstream|Staying close to upstream projects]] for more about why it's important to do this.


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


Sometimes, you'll package just a straight file that doesn't need to be uncompressed, e.g., a "Source1:" that is just a simple PDF file.  These might not be from external sources, e.g., perhaps you've had to create a few additional files that weren't in the original sources so that the package cleanly installs in Fedora.  You can "prep" those into the build directory by doing this (replace "1" with whatever number it is):
有时,一个或多个源码包不需要解压。你可以使用以下命令,将文件复制到 build 目录中,如( <code>SOURCE1</code> 表示对应的源码包):
  cp -p %SOURCE1 .
cp -p %SOURCE1 .


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


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


Many programs follow the GNU configure approach (or some variation).  By default, they will install to a prefix of "/usr/local" (/usr/local/bin, /usr/local/lib, etc.), which is a reasonable default for unpackaged files.  However, since you ''are'' packaging it, you will want to change the prefix to "/usr", since this is now a package maintained by the system itself.  If there are any libraries, they'll need to be installed in the right directory, which is either /usr/lib or /usr/lib64 depending on the architecture (the actual value is in %{_libdir}).
许多程序使用 GNU <code>configure</code> 进行配置。默认情况下,文件会安装到前缀为 "<code>/usr/local</code>" 的路径下,对于手动安装很合理。然而,打包时需要修改前缀为 "<code>/usr</code>"。共享库路径视架构而定,安装至 <code>/usr/lib</code> 或 <code>/usr/lib64</code> 目录。


Since the GNU "configure" system is so common, rpm pre-defines a macro named "%configure", which invokes GNU configure with the right options (e.g., it changes --prefix to /usr).  This means that some variation of this will often work as a build command:
由于 GNU <code>configure</code> 很常见,可使用 "<code>%configure</code>" 宏来自动设置正确选项(例如,设置前缀为 <code>/usr</code>)。一般用法如下:
   %configure
   %configure
   make %{?_smp_mflags}
   make %{?_smp_mflags}


Sometimes you'll want to override the variables of a makefile; you can easily do that by passing them as parameters to make, like this:
若需要覆盖 makefile 变量,请将变量作为参数传递给 <code>make</code>:
  make %{?_smp_mflags} CFLAGS="%{optflags}" BINDIR=%{_bindir}
  make %{?_smp_mflags} CFLAGS="%{optflags}" BINDIR=%{_bindir}


If you need to do something complicated with GNU-generated configure, take a look at [http://sourceware.org/autobook/ "GNU autoconf, automake, and libtool"].  A good presentation on these as well as "make" is [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 Cmake.  See [[Packaging/cmake]] for some suggestions.
一些程序使用 <code>cmake</code>。请参考 [[Packaging/cmake]]


If you include some self-tests (and that's a good idea), put them in a separate "%check" section that immediately follows the "%build" area, instead of including them in %build.  That way, it will be easy for the system to skip unnecessary self-tests.
=== %install 部分 ===


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


The "%check" section does testing, often it's "make test".
容易混淆的术语:
This is not documented in many other sources of RPM info.
* "build 目录",也称为 <code>%{_builddir}</code>,实际上与 "build root",又称为 <code>%{buildroot}</code>,是不同的目录。在前者中进行编译,并将需要打包的文件从前者复制到后者。
* 在 %build 阶段,当前目录为 <code>%{buildsubdir}</code>,是 %prep 阶段中在 <code>%{_builddir}</code> 下创建的子目录。这些目录通常名为 <code>~/rpmbuild/BUILD/%{name}-%{version}</code>。
* %install 阶段的命令'''不会'''在用户安装 RPM 包时执行,此阶段仅在打包时执行。


=== %install section ===
一般,这里执行 "<code>make install</code>" 之类的命令:
 
The "%install" section is a set of script commands to "install" the program. The commands in this section should copy the files from a directory inside the "build directory" %{_builddir} (normally ~/rpmbuild/BUILD/''something'') into the build root directory, %{buildroot} (normally /var/tmp/''something''), creating the directories inside %{buildroot} as necessary.
 
'''Watch out''': Some of the terminology is very misleading:
* The ''build directory'' (under which compilations occur during %build) and the ''build root'' (where files are copied into during the %install process) are '''different'''.  The point of the %install process is to copy files, such as those under the build directory, to the right place in the build root. Perhaps "buildroot" should be called "installroot", but it's too late now, the terminology is entrenched.
* The build directory is normally ~/rpmbuild/BUILD, while the build root (where files get installed to during %install) is normally ~/rpmbuild/BUILDROOT.  The %prep stage will normally create a subdirectory underneath the build directory as part of %setup, and populate the build directory with files (based on the source information in %_sourcedir, which is typically in ~/rpmbuild/SOURCES).  During %build, the current directory will actually start at %{buildsubdir}, that newly-created  subdirectory under the build directory.  Typically %{buildsubdir} is something like ~/rpmbuild/BUILD/%{name}-%{version}.
* The "%install" script is ''not'' used when the binary rpm package is installed by the end-user!! The term "%install" is misleading, in fact, the script must ''not'' install the programs in the REAL final locations (e.g., in /usr/bin), but under the buildroot %{buildroot}.
 
Normally, the install script would first erase the %{buildroot} directory, and then do some variation of "make install" (ideally using DESTDIR=%{buildroot}, if the program supports it). Here's an example of an %install section:
  %install
  %install
  rm -rf %{buildroot}
  rm -rf %{buildroot} # 仅用于 RHEL 5
  make DESTDIR=%{buildroot} INSTALL="install -p" CP="cp -p" install
  %make_install


Ideally, every program would have a "make install" command that supported the [http://www.gnu.org/prep/standards/html_node/DESTDIR.html ''DESTDIR'' convention].  If the program includes a "make install" that supports DESTDIR, where possible, ''use it''. The DESTDIR convention supports redirecting file installations to descend from a specific directory, which is exactly what we want during %install.
理想情况下,对于支持的程序,你应该使用 %make_install,它等同于 [http://www.gnu.org/prep/standards/html_node/DESTDIR.html <code>DESTDIR=%{buildroot}</code>],它会将文件安装到 %{buildroot} 目录中。


Installing a program that does not support DESTDIR can be much harder, and no option is as good as native DESTDIR support.  Consider these alternatives:
如果程序不支持 <code>DESTDIR</code>,使用以下方法避开此问题:
* Patch the makefile so that it ''does'' support DESTDIR.  Create directories inside DESTDIR where necessary (feel free to use "mkdir -p", the "-p" option of mkdir is now standard and widely supported).  Be sure to submit the patch upstream.
* 修补 makefile 以便支持 <code>DESTDIR</code>。请在 <code>DESTDIR</code> 根据需要创建必要目录,并向上游提交补丁。
* Use "%makeinstall".  Many older RPM documents suggest using "%makeinstall", which ''might'' work if "make install" doesn't support DESTDIR.  However, as noted in the Fedora guidelines, the %makeinstall macro "must NOT be used when make install DESTDIR=%{buildroot} works. %makeinstall is (merely) a kludge that can work with Makefiles that don't make use of the DESTDIR variable...".  Unfortunately, this sometimes has subtle failures, which is why %makeinstall should not be used if DESTDIR works.  The reason is based on how %makeinstall works.  The "%makeinstall" macro expands to something like "<tt>make prefix=%{buildroot}%{_prefix} bindir=%{buildroot}%{_bindir} ... install</tt>".  Many programs will quietly recompile or change parts of the program when values like prefix are changed, resulting in an incorrect installation.  See the Fedora guidelines if you want the details on why this approach can fail.  You will probably need to create appropriate directories inside %buildroot before calling %makeinstall (e.g., <tt>mkdir -p %{buildroot}%{_bindir}/</tt>).
* 使用 "<code>%makeinstall</code>" 宏。此方法可能有效,但也可能失败。该宏会展开为 "<code>make prefix=%{buildroot}%{_prefix} bindir=%{buildroot}%{_bindir} ... install</code>",可能导致某些程序无法正常工作。请在 <code>%{buildroot}</code> 根据需要创建必要目录。
* Consider using the auto-destdir package.  This requires "BuildRequires: auto-destdir", and changing "make install" to "make-redir DESTDIR=%{buildroot} install".  This only works well if the installation uses only certain common commands to install files, like cp and install; see "man make-redir" for details.
* 使用 <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>。
* Do the installation "by hand", that is, instead of invoking a build system, copy the files to the correct locations.  Basically, this would be a sequence that would create directories that weren't already created by the "BuildRequires" packages (typically using install -d or mkdir -p), followed by copying of files from the current directory (inside the build directory) into the buildroot directory (typically using "cp -p" and/or "install -p"). Running "make -n install" may make it easy to determine what this sequence should be.  Be sure to create directories inside %buildroot where necessary.  One serious problem with this approach is that it's easy to fail to install new or renamed files during an update&mdash;so if there's a better approach, use it instead.  If you ''do'' perform the installation "by hand", be ''especially'' careful with updates when using this approach.  For example:
* 手动执行安装。这需要在 <code>%{buildroot}</code> 下创建必要目录,并从 <code>%{_builddir}</code> 复制文件至 <code>%{buildroot}</code> 目录。要特别注意更新,通常会包含新文件。示例如下:
  %install
  %install
  rm -rf %{buildroot}
  rm -rf %{buildroot}
Line 479: Line 395:
  cp -p mycommand %{buildroot}%{_bindir}/
  cp -p mycommand %{buildroot}%{_bindir}/


As noted in the [[Packaging:Guidelines#Timestamps|packaging guidelines' timestamp section]],  "when adding file copying commands in the spec file, consider using a command that preserves the files' timestamps, eg. cp -p or install -p". So, if the makefile lets you override the install command (typically named INSTALL), you might want something like INSTALL="install -p" CP="cp -p" as make parameters, like this:
=== %check 部分 ===
  make INSTALL="install -p" CP="cp -p" DESTDIR=%{buildroot} install
 
如果需要执行测试,使用 %check 是个好主意。测试代码应写入 %check 部分(紧接在 %install 之后,因为需要测试 %buildroot 中的文件),而不是写入 %build 部分,这样才能在必要时忽略测试。
 
通常,此部分包含:
  make test
 
有时候也可以用:
  make check
 
请熟悉 Makefile 的用法,并选择适当的方式。
 
=== %files 部分 ===


=== %files section ===
此部分列出了需要被打包的文件和目录。
The %files section identifies what files and directories were added by the package - and thus, which files and directories are ''owned'' by the package.  Ownership is important - when you type "rpm -qif ''blah''",  you'll see who owns ''blah''.  This section is used when performing the ''bin'' stage, to determine which files are placed into each binary RPM file.


==== %files Basics ====
==== %files 基础 ====


The %files section normally begins with a %defattr line which sets the default file permissions. The format of this is %defattr(<file permissions>, <user>, <group>, <directory permissions>), that is, one can specify the permissions to apply to files and directories in the %files section. The fourth parameter is often omitted. Usually one uses %defattr(-,root,root,-), where "-" means "use the default permissions".
<code>%defattr</code> 用于设置默认文件权限,通常可以在 <code>%files</code> 的开头看到它。注意,如果不需要修改权限,则不需要使用它。其格式为:
%defattr(<文件权限>, <用户>, <用户组>, <目录权限>)
第 4 个参数通常会省略。常规用法为 <code>%defattr(-,root,root,-)</code>,其中 "<code>-</code>" 表示默认权限。


This is followed by names or patterns of the directories or files to be installed and owned by this package. You should use macros for directory names, e.g., use %{_bindir}/myfile instead of /usr/bin/myfile, and %{_sbindir}/killaccount instead of /usr/sbin/killaccount.  If a name or pattern begins with "/" when expanded, then it is presumed to have been copied into the %{buildroot} followed by that pattern; when installed on the final system, it will be copied into that name ''without'' the buildroot prefix.  If you don't precede the pattern with "/", then it is presumed to be in the current directory (e.g., inside the build directory) - this is used for "documentation" files.  So if your package just installs /usr/sbin/mycommand, then your %files section ''could'' simply say:
您应该列出该软件包拥有的所有文件和目录。尽量使用宏代替目录名,查看宏列表 [[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
%defattr(-,root,root,-)
  %{_sbindir}/mycommand
  %{_sbindir}/mycommand


Any file or directory identified in the %files section is ''owned'' by the defining package. You should make sure that you declare ownership of every new file or directory the package creates.  You can use wildcards (*) which match a set of files - this makes the package less sensitive to changes. For example, you can declare that all the files that were copied into %{buildroot}/usr/bin are owned by this package by declaring:
若要使软件包不受上游改动的影响,可使用通配符匹配所有文件:
  %{_bindir}/*
  %{_bindir}/*


Note that "%{_bindir}/*" does not claim that this package owns the /usr/bin directory - it claims that all the files that were installed inside the ''build root'' 's /usr/bin are owned by the package.
包含一个目录:
If you list a ''directory'' in the %files section, then you are claiming that this package owns that subdirectory and all files and directories in it, recursively (''all'' the way down) if they are present in the build root.  Do not list the "/usr/bin" or "%{_bindir}" directories directly in your %files list, because that would claim ownership of /usr/bin and everything inside it.  Claiming ownership of "%{_bindir}/*" is fine, though; that just claims ownership of the subdirectories and files you placed ''under'' %{buildroot}/%{_bindir}.  If you create a subdirectory such as %{_datadir}/%{name}, (/usr/share/NAME), you ''should'' include that directory in the %files list:
  %{_datadir}/%{name}/
  %{_datadir}/%{name}/


It's usually easier to use wildcards for filenames, and that's also better at coping with changes in upstream.  Older RPM documentation typically shows long lists under %files with individual names, such as /usr/bin/program1 followed by /usr/bin/program2.  Because of the way Fedora now uses buildroots, that is no longer necessary.
注意,<code>%{_bindir}/*</code> 不会声明此软件包拥有 <code>/usr/bin</code> 目录,而只包含其中的文件。如果您列出一个目录,则该软件包拥有这个目录,及该目录内的所有文件和子目录。因此,'''不要'''列出 <code>%{_bindir}</code>,并且要小心的处理那些可能和其他软件包共享的目录。


It's an error if ''no'' file matches the wildcard of a line, so only note the directories that actually matter.  Also, you can't identify the same file or directory more than once.  Finally, it's an error to have something in the buildroot and ''not'' listed under %files; the whole point of copying something into the buildroot is because you intend to have it installed in the final system.  If you don't intend that, remove those files during the %install process.
如果存在以下情况,可能引发错误:
* 通配符未匹配到任何文件或目录
* 文件或目录被多次列出
* 未列出 <code>%{buildroot}</code> 下的某个文件或目录


It is also possible to exclude files from a previous match by using a %exclude glob. This can be useful for including "almost all" of the files that match a different glob. However, note that, like any other file glob, even a %exclude glob will fail if it matches nothing. (This might be considered counterintuitive, as the whole point is essentially to ensure that a certain file ISN'T there, so this rule is especially important to remember.)


==== %files prefixes ====
You may need to add one or more prefixes to a %files entry (if more than one, use a space to separate them).


Typically there is a "%doc" entry with a list of documentation files that didn't get copied into the buildroot; usually there is at least a README and LICENSE file.  You ''must'' include the license file, if there is one. You may prefix some of these with %attr(mode, user, group) to set the file permission mode, user, or group.  You don't need to claim ownership of the /usr/share/doc/%{name} directory, that's automatic if there's a %doc entry.  Any %doc entry must not affect the runtime of the application (if it is in %doc, the program must run properly if it is not present).
您也可以使用 <code>%exclude</code> 来排除文件。这对于使用通配符来列出全部文件时会很有用,注意如果未匹配到任何文件也会造成失败。


There is a potential 'gotcha' with %doc entries: if you have a %doc entry, then you can't use commands during %install to copy files into the documentation directory descending from %_defaultdocdir. That's because if there's a %doc entry, rpmbuild will automatically remove the docdir files created by %install before installing the files listed with %doc.  This can hit you if, for example, you want an "examples" subdirectory in the documentation directory. In this case, don't use "%doc" to mark documentation. Instead, create the directories and copy the files into %{buildroot}%{_defaultdocdir}/%{name}-%{version}/ during %install, and make sure that %files includes an entry for "%{_defaultdocdir}/%{name}-%{version}/".  They will still be correctly marked as documentation.
==== %files 前缀 ====
您可能需要在 <code>%files</code> 部分添加一个或多个前缀;请用空格分隔。详情请查看 [http://www.rpm.org/max-rpm/s1-rpm-inside-files-list-directives.html Max RPM section on %files directives]。


If you save configuration files (under /etc - don't put them under /usr), you should normally prefix them with %config(noreplace) unless this program version uses a non-backwards-compatible configuration format (in which case, prefix them with %config).
通常,"<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> 的所有权。


Prefixing a %files entry with "%attr(mode, user, group)" lets you set the permissions for particular file(s), e.g., "%attr(0644, root, root)".  A "-" means "use the default".
'''注意:''' 如果指定 <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> 这样。


If a file is in particular natural language, use %lang to note that. E.G.:
配置文件保存在 <code>/etc</code> 中,一般会这样指定(确保用户的修改不会在更新时被覆盖):
%config(noreplace) %{_sysconfdir}/foo.conf
如果更新的配置文件无法与之前的配置兼容,则应这样指定:
%config %{_sysconfdir}/foo.conf
 
"<code>%attr(mode, user, group)</code>" 用于对文件进行更精细的权限控制,"<code>-</code>" 表示使用默认值:
%attr(0644, root, root) FOO.BAR
 
"<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
使用区域语言(Locale)文件的程序应遵循 [[Packaging:Guidelines#Handling_Locale_Files|处理 i18n 文件的建议方法]]
[[Packaging:Guidelines#Handling_Locale_Files|recommended method of handling the i18n files]]:
* 在 <code>%install</code> 步骤中找出文件名: <code> %find_lang ${name}</code>
* 添加必要的编译依赖: <code>BuildRequires: gettext</code>
* 使用找到的文件名: <code>%files -f ${name}.lang</code>


* find the filenames in the <code>%install</code> step: <code> %find_lang ${name}</code>
以下前缀在 Fedora 中'''无效''':<code>%license</code> <code>%readme</code>
* add the required build dependencies: <code>BuildRequires: gettext</code>
* use the found filenames: <code>%files -f ${name}.lang</code>


Some documentation claims that %license and %readme are valid prefixes; they are ''not'' valid in Fedora.  Use %doc instead.
==== %files 和文件系统层次标准 (FHS) ====


==== %files and Filesystem Hierarchy Standard (FHS) ====
您应该遵守 [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> 宏来引用。


You should follow the [http://www.pathname.com/fhs/ Filesystem Hierarchy Standard (FHS)], i.e., ordinary application executables go into /usr/bin, global configuration files go into /etc, ordinary libraries go into /usr/lib, and so on, with one exception: executables that should ''not'' normally be executed directly by users or administrators should go into a subdirectory of /usr/libexec; usually you'd refer to the necessary directory as "%{_libexecdir}/%{name}".
请 '''不要''' 将文件安装到 <code>/opt</code> 或 <code>/usr/local</code> 目录中。


You shouldn't be installing files under /usr/local; that is where ''unpackaged'' files go.
不幸的是,许多程序默认情况下并不遵守 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> 目录时返回警告。
Typically there will be a "prefix" attribute that lets you set the prefix to be "/usr" instead of "/usr/local".


Unfortunately, many programs' "normal" installation routines do not follow the FHS.
==== %files 示例 ====
In particular, many programs normally place architecture-independent libraries under /usr/lib, instead of
under /usr/share as the FHS requires.
The [http://www.pathname.com/fhs/pub/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA FHS /usr/lib section] says that
/usr/lib is for architecture-''dependent'' data (e.g., ELF files like .so files), while
[http://www.pathname.com/fhs/pub/fhs-2.3.html#USRSHAREARCHITECTUREINDEPENDENTDATA /usr/share] is for
architecture-''independent'' data.  That way, systems with different CPUs can share /usr/share.
There are many exceptions to this rule in Fedora (e.g., Python and Perl), but Fedora applies this rule more
strictly than some distributions.  Note, for example, that rpmlint will complain if you
put just about anything other than ELF files into /usr/lib.


==== %files example ====
以下为 %files 部分的简单示例:
 
Here's a simple example of a %files section:
  %files
  %files
  %defattr(-,root,root,-)
  %doc README
  %doc README LICENSE
  %license LICENSE COPYING
  %{_bindir}/*
  %{_bindir}/*
  %{_sbindir}/*
  %{_sbindir}/*
  %{_datadir}/%{name}/
  %{_datadir}/%{name}/
%config(noreplace) %{_sysconfdir}/*.conf


==== Finding duplicates ====
==== 找出重复内容 ====
 
The Fedora guidelines require that
"A Fedora package must not list a file more than once in the spec
file's %files listings."


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 571: Line 494:


=== Scriptlets ===
=== Scriptlets ===
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.


The scriptlets in %pre and %post are run before and after a package is installed (respectively). The scriptlets %preun and %postun are run before and after a package is uninstalled. The scriptlets %pretrans and %posttrans are run at start and end of a transaction.  See [[Packaging/ScriptletSnippets]] for more examples and details.  For example, every binary RPM package which stores shared library files (not just symlinks) in any of the dynamic linker's default paths, must call ldconfig in %post and %postun (post-install and post-uninstall). If the package has multiple subpackages with libraries, each subpackage should also have a %post/%postun section that calls /sbin/ldconfig. For example:
当用户安装 RPM 时,您可能想要执行一些命令。这可以通过 scriptlets 完成。请查看 [[Packaging/ScriptletSnippets]]。
 
脚本片段可以:
* 在软体包安装之前 ('''<code>%pre</code>''') 或之后 ('''<code>%post</code>''') 执行
* 在软体包卸载之前 ('''<code>%preun</code>''') 或之后 ('''<code>%postun</code>''') 执行
* 在事务开始 ('''<code>%pretrans</code>''') 或结束 ('''<code>%posttrans</code>''') 时执行
 
例如,每个二进制 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


''Beware'': The "-p" option specifies what ''command processor'' to use for the commands on the following lines.
如果仅执行一个命令,则 "<code>-p</code>" 选项会直接执行,而不启用 shell。然而,若有许多命令时,不要使用此选项,按正常编写 shell 脚本即可。
If there are no following lines, then using /sbin/ldconfig as the "command processor" is a minor efficiency improvement compared to putting "/sbin/ldconfig" on the next line, and letting the shell invoke it.
That's because by using "-p",
the shell isn't invoked simply to invoke a single program.
But if you have multiple shell commands,
don't use "-p" or /sbin/ldconfig after it!  Instead, leave it blank, and include the shell commands under it.
 
If you are going to run programs in scriptlets, they must be installed before you run them.
You have to use special variants of the "Requires:" tag, so that
the program will be installed before you try to use it.  These are of the form "Requires(CONTEXT):", e.g., "Requires(post)".


Most scriptlets (%pre, %post, %preun, and %postun) provide an argument you can use,
如果你在脚本片段中执行任何程序,就必须以 "<code>Requires(CONTEXT)</code>"(例: <code>Requires(post)</code>)的形式列出所有依赖。
accessed via $1, 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 2; check if they are ''greater than or equal than'' 2, since
users can arrange to have multiple versions of a package installed simultaneously.
For %pretrans and %posttrans, $1 is always 0.


For example, after adding an info manual to the system the dir file
<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>。
which indexes the info manuals should be updated.
Basically, after you install the info manual, you need to run the program install-info.  That's fine, except that install-info is part of package info, and there's no guarantee that info is installed unless we require it. Also, if "install-info" fails, we don't want to fail ''all'' processing. Here's one way to do that:


例如,如果软件包安装了一份 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 609: Line 523:
  fi
  fi


There is another glitch related to installing info files. The <code>install-info</code> command will update
还有一个安装 info 手册时的小问题。<code>install-info</code> 命令会更新 info 目录,所以我们应该在 <code>%install</code> 阶段删除 %{buildroot} 中无用的空目录:
the info directory, but we have to prevent the install of a useless, empty directory file from the
RPM_BUILD_ROOT environment, by deleting it in  the <code>%install</code> section:
rm -f $RPM_BUILD_ROOT%{_infodir}/dir
 
Another scriptlet-like abilility are ''triggers''.  You can define triggers for when ''other'' packages
are installed or uninstalled.  See ''Maximum RPM'' for more information about triggers.


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


Spec files may contain "macro" references (text beginning with "%"), which are replaced with other values.
另一个类似代码片段的功能是 "triggers"(触发器),它可以在其他软件包安装或删除时,为你的包执行一些动作。请参考 [http://rpm.org/api/4.4.2.2/triggers.html RPM Triggers]。
You can follow % by a word, e.g., "%name", but just like shell variables you must bracket the name with {...}
if letters or digits immediately follow, e.g., "%{name}".


As noted in the
=== 宏 ===
[[Packaging/Guidelines| Packaging Guidelines]],
There are two styles for referring some values such as the rpm Build Root and Optimization Flags:
* "macro style": %{buildroot}, %{optflags}
* "variable style": $RPM_BUILD_ROOT, $RPM_OPT_FLAGS
Pick a style and use it consistently throughout your packaging; this document uses "macro style".


Here are some typical macros:
宏通常以 <code>%{string}</code> 格式出现,以下介绍常见的宏:


{|
{|
! Macro !! Typical Expansion !! Meaning
! 宏名称 !! 典型扩展 !! 意义
|-
|-
| %{_bindir} || /usr/bin || Binary directory (where executables are usually stored)
| <code>%{_bindir}</code> || <code>/usr/bin</code> || 二进制目录:保存可执行文件
|-
|-
| %{_builddir} || ~/rpmbuild/BUILD || Build directory; files are compiled a subdirectory of the build directory.  See %buildsubdir.
| <code>%{_builddir}</code> || <code>~/rpmbuild/BUILD</code> || 构建目录:软件在 build 的子目录被编译。参考 <code>%buildsubdir</code>
|-
|-
| %{buildroot} || ~/rpmbuild/BUILDROOT || Build root, where files are "installed" during %install. The %install stage copies files from a subdirectory of %{_builddir} to a subdirectory of %{buildroot}. Historically %{buildroot} 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/"
|-
|-
| %{buildsubdir} || %{_builddir}/%{name} || Build subdirectory, where files are compiled during %build. It's under %{_builddir}, set after %setup.
| <code>%{buildsubdir}</code> || <code>%{_builddir}/%{name}</code> || 构建子目录:<code>%build</code> 阶段中,文件会在 <code>%{_builddir}</code> 的子目录中编译。此宏在 <code>%autosetup</code> 之后设置
|-
|-
| %{_datadir} || /usr/share || Share directory.
| <code>%{_datadir}</code> || <code>/usr/share</code> || 共享数据目录
|-
|-
| %{_defaultdocdir} || /usr/share/doc || Default documentation directory.
| <code>%{_defaultdocdir}</code> || <code>/usr/share/doc</code> || 默认文档目录
|-
|-
| %{dist} || .fc''NUMBER'' || Distribution+version short name (e.g., ".fc9")
| <code>%{dist}</code> || <code>.fc''NUMBER''</code> || 发行版名称+版本号(例如 "<code>.fc{{FedoraVersion}}</code>"
|-
|-
| %{fedora} || ''NUMBER'' || Number of fedora release (e.g., 9)
| <code>%{fedora}</code> || <code>''NUMBER''</code> || Fedora 发行版本号(例如 "<code>{{FedoraVersion}}</code>")
|-
|-
| %{_includedir} || /usr/include
| <code>%{_includedir}</code> || <code>/usr/include</code> || 程序头文件目录
|-
|-
| %{_infodir} || /usr/share/info
| <code>%{_infodir}</code> || <code>/usr/share/info</code> || info 手册目录
|-
|-
| %{_initrddir} || /etc/rc.d/init.d
| <code>%{_initrddir}</code> || <code>/etc/rc.d/init.d</code> || init 脚本目录
|-
|-
| %{_libdir} || /usr/lib
| <code>%{_libdir}</code> || <code>/usr/lib</code> || 共享库目录
|-
|-
| %{_libexecdir} || /usr/libexec
| <code>%{_libexecdir}</code> || <code>/usr/libexec</code> || 仅由系统调用执行该目录中的命令
|-
|-
| %{_localstatedir} || /var
| <code>%{_localstatedir}</code> || <code>/var</code> || 保存缓存/日志/lock等信息的目录
|-
|-
| %{_mandir} || /usr/share/man
| <code>%{_mandir}</code> || <code>/usr/share/man</code> || man 手册目录
|-
|-
| %{name} || || Name of package, set by Name: tag
| <code>%{name}</code> || || 软件包名称,通过 Name: tag 设置
|-
|-
| %{_sbindir} || /usr/sbin
| <code>%{_sbindir}</code> || <code>/usr/sbin</code> || 保存管理员可执行命令
|-
|-
| %{_sharedstatedir} || /var/lib
| <code>%{_sharedstatedir}</code> || <code>/var/lib</code> || 保存程序运行所处理的文件
|-
|-
| %{_sysconfdir} || /etc
| <code>%{_sysconfdir}</code> || <code>/etc</code> || 配置文件目录
|-
|-
| %{version} || || Version of package, set by Version: tag
| <code>%{version}</code> || || 软件包版本,通过 Version: tag 设置
|}
|}


To see more about macros you
您可以查看 <code>/etc/rpm/*</code> 和 <code>/usr/lib/rpm</code>,以及 <code>/usr/lib/rpm/macros</code> 以进一步了解宏。或使用 <code>rpm --showrc</code> 显示当前 RPM 所使用的宏变量和值(根据 <code>rpmrc</code> 和宏配置文件)。
can look in /etc/rpm/* and the "macros" files under "/usr/lib/rpm/",
especially /usr/lib/rpm/macros.
You can also use "rpm --showrc" to show the values rpm will use for all of the options
currently set in rpmrc and macro configuration files.


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


You can use rpmbuild to find the value of some macro, using its "-E" (--eval) option. For example, to find the current expansion of %{_bindir} in myfile.spec, you can run:
使用 <code>rpmbuild</code> 的 "<code>-E</code>" 选项查找 SPEC 文件中宏变量的值:
  rpmbuild -E '%{_bindir}' myfile.spec
  rpmbuild -E '%{_bindir}' myfile.spec


[[Packaging/RPMMacros]] has more information on macros, as does
参考 [[Packaging/RPMMacros]] [https://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch09s07.html RPM 指南 - 第 9 ]
[http://docs.fedoraproject.org/drafts/rpm-guide-en/ch09s07.html RPM Guide chapter 9].


=== Other tags ===
=== 其它标签 ===


We noted the "Requires" and "BuildRequires" tags earlier.
除了 Requires BuildRequires 标签外,你还可以使用以下标签控制依赖关系:
There are a few other tags for controlling dependencies:
Provides, Obsoletes, Conflicts, and BuildConflicts.
* "Provides:" lets you list ''virtual'' package names that this package provides.  Sometimes there are several different packages that can provide a function, and using packages won't care which one.  In that case, each of the packages that provide the function should "provide" a virtual package, and then using packages can list the virtual package name under "Requires:".  For example, several different packages might provide "latex"; if you depend on the virtual package "tex(latex)", then users can choose which package to get "latex" from.  If you provide virtual packages, you might also want to use the [http://dailypackage.fedorabook.com/index.php?/archives/6-Wednesday-Why-The-Alternatives-System.html "alternatives" system], but be careful: "alternatives" settings are ''system-wide'', so if multiple users on the same system might want different defaults, don't use the alternatives system. You can find out what a given package provides (both virtual and non-virtual names) by querying "rpm -q --provides PACKAGENAME".  Some virtual packages in Fedora are:
** ''MTA'' : Used for mail transport agents, such as sendmail.
** ''tex(latex)'' : Used for latex
* "Obsoletes:" lets you state that installing this package should (normally) cause the removal of the other named package(s).  This is useful when a package's name changes, or when a package wholly replaces a different package.
* "Conflicts:" lets you state what packages cannot be installed simultaneously this one.  Obviously, try to avoid this if you can; see [[Packaging/Conflicts]] if you think you need to use it.
* "BuildConflicts:" lets you state what packages cannot be installed when building this package. Obviously, try to avoid this if you can.


You can control which architectures a package builds (or doesn't build). For example, if your package can't compile on ppc, you can do this:
* '''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 中的一些虚拟包示例:
** MTA: 邮件传输代理(mail transport agent, MTA),例如 sendmail。
** tex(latex): 用于 latex
* '''Obsoletes''': 当软件包安装时卸载另一个指定的包。用于软件包改名时,或是用该软件包完全取代另一个不同的包时使用。
* '''Conflicts''': 表示安装此包时冲突的软件包。如果可以请避免使用此标签。查看 [[Packaging/Conflicts]]。
* '''BuildConflicts''': 表示编译此包时冲突的软件包。如果可以请避免使用此标签。
 
若要处理不同的系统架构,可使用以下 2 个标签:
* '''ExcludeArch''': 排除无法构建此软件包的系统架构。例如:
  ExcludeArch: ppc
  ExcludeArch: ppc
There's also an "ExclusiveArch" tag. The valid architectures one can specify in these tags are listed in the [[Architectures]] section.
* '''ExclusiveArch''': 仅包含指定的系统架构。除非绝对正确,否则请避免使用。
可用的系统架构在 [[Architectures]] 中列出。


=== Subpackages ===
=== 子软件包 ===
A spec file can define more than one binary package, e.g., client and server,
or runtime and developer packages.
If there's a large amount of documentation, it may be split into a NAME-doc subpackage.
You will always have one spec file and one source RPM (SRPM), even if there are
multiple binary RPMs that they generate.
A spec file that produces multiple binary packages still has only
one creation process, so there is only one
%prep, %build, %check, and %install section that creates all the files
for all the packages.


In a spec file, use the %package directive to start defining a subpackage:
一个 SPEC 文件可以定义多个 RPM 包。换句话说,一个 SRPM 文件可以制作出多个 RPM 包。注意,这仍然只需要一个构建(%prep、%build、%install 等)过程。<code>name-doc</code> 和 <code>name-devel</code> 是最常见的文档和开发文件子软件包。
%package sub_package_name


By default, the subpackage name is PACKAGE_NAME, "-", SUBPACKAGE_NAME; you can
使用 <code>%package</code> 宏指令来定义子软件包:
use "-n" to override this and make a new name:
  %package subpackage_name
  %package -n new_sub_package_name


After the %package directive, list the tags for the subpackage.
在每个 <code>%package</code> 指令后,需要列出该子包的必要标签。至少应包括 Summary Group 标签,以及 <code>%description subpackage_name</code> 和 <code>%files subpackage_name</code> 指令:
This should include at least the "Summary:" and "Group:" tags and directives
"%description SUBPACKAGE_NAME" and "%files SUBPACKAGE_NAME".
Anything not specified by the subpackage will be inherited from its parent.
For the directives, if you used "-n" with %package,
you'll need it again for these directives.
You need to specify the name for the other directives, e.g., %pre and %post,
if you use them in the subpackage.


[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.
%package foo
Summary: 简介
Group: 包组
%description foo
相关描述
%files foo
该包需要包含的文件


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


You can insert conditional statements.
默认情况下,如果软件包名为 "<code>foo</code>",而子包名为 "<code>bar</code>",则生成的子包为 "<code>foo-bar</code>"。可以使用 "<code>-n</code>" 选项指定包名(但需要在所有其它部分添加此选项):
E.G., you can test if you are creating a binary for a certain architecture
%package -n new_subpackage_name
with:
 
查看 [http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch10s04.html RPM 指南 针对子包的章节] 了解更多信息。
 
=== 条件判断 ===
 
你可以插入条件判断语句,例如可根据特定系统架构执行不同动作:
  %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 "%else" section; all of these are closed with "%endif".
可以选择使用 "<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']
* [[Special:PrefixIndex/Packaging|以 'Packaging' 开头的 Wiki 页面]]
 
=== 其他注意事项 ===


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


Try to write your scripts so that when upstream makes changes, the packaging is likely to work when you change the version number and reload the source file(s).  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


[[Packaging/FrequentlyMadeMistakes]] has information on frequently-made mistakes.
不要尝试和用户交互;RPM 以支持批量安装为设计核心。如果有程序需要显示 EULA 用户授权协议,则应在初次执行时执行该动作,而非安装时。
 
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.


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


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


Normally, if there are binary executables, a separate "debug" package is created with the symbols, and the symbols are stripped from the normal binary packages.  If this shouldn't happen, you can disable the package-creation and stripping with:
一般而言,如果有二进制文件,则会剥离其中包含的调试信息,并将调试信息保存至 <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 %install 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 ? causes the macro to evaluate to blank if %fedora is not defined, and this causes the end result to be "0", which is a number and thus ok, while not interfering with the result if there is actually a value for %fedora.)
<code>?</code> 使 <code>%fedora</code> 宏在未定义时返回空。这样会使结果为 <code>0</code> ,而 <code>%fedora</code> 宏若存在数值时也不会有干扰。(注意,这种做法在 Koji "scratch" 编译中不起作用,<code>%fedora</code> 的值在创建 SRPM 时已设定)
 
Note that the previous trick DOES NOT work in Koji "scratch" builds - %fedora is set during the creation of a source RPM. (Thus, this trick does work in actual Koji builds as the system extracts sources from the source RPM and rebuilds the source RPM with the appropriate %fedora value.)
 
There are also some recommendations and controversial tricks on
[[PackageMaintainers/Packaging Tricks]].


GUI programs ''must'' have a desktop entry (so that people can invoke it from a graphical menu). The
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]
[http://fedoraproject.org/wiki/Packaging/Guidelines#Desktop_files Fedora packaging guidelines discuss desktop files].  See also the [http://standards.freedesktop.org/desktop-entry-spec/latest/ desktop entry spec] (for .desktop files) and
[http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html icon theme spec] (for icon-related materials such as those in /usr/share/icon).


=== Older RPM documents ===
== 构建 RPM ==
Some older documents about RPM have the most information, but some older documents make claims that are no longer true:
* rpm files are no longer placed in a shared /usr/src/redhat directory.  This is an obsolete way of using rpm and ''not'' recommended; modern systems set a %{_topdir} instead like ~/rpmbuild.
* the %install process does not install files in their final location.  Instead, it "installs" files to the buildroot.
* The "rpm" command no longer creates packages (e.g., "rpm -ba" was once legal). Use the separate "rpmbuild" program instead.
* Many historical specs use the "%define" command to define macros.  However, "%define" creates a locally defined submacro within other macro definitions; this is very rarely needed, and using %define incorrectly can cause subtle bugs.  For nearly all uses, use "%global" instead.  (See [[PackagingDrafts/global_preferred_over_define]].)
* The "BuildRoot:" value is now ignored.


== Quick test with rpmlint ==
=== 使用 rpmlint 测试 ===


Before trying to build anything from it, you should run ''rpmlint'' on the spec file:
为避免常见错误,请先使用 <code>rpmlint</code> 查找 SPEC 文件的错误:
  rpmlint program.spec
  $ rpmlint program.spec
This will catch many errors early. If the reported error doesn't make sense,
如果返回错误/警告,使用 "<code>-i</code>" 选项查看更详细的信息。
run it again with the "-i" option (this gives longer messages).


Generally, you should not have errors from rpmlint, but sometimes
有时,<code>rpmlint</code> 也会有误报的情况发生。请查看 [[Packaging/Guidelines#Use_rpmlint|打包规定]] 了解哪些错误可以忽略。
rpmlint is excessively noisy.
The
[http://fedoraproject.org/wiki/Packaging/Guidelines#Use_rpmlint Fedora packaging guidelines]
explain which ones to ignore, e.g., ignore "no-packager-tag" and "no-signature" errors.


== Creating RPMs from the spec file ==
=== 从 SPEC 构建 RPM 包 ===


Once you've create a spec file, say "program.spec", you can create
一旦 SPEC 编写完毕,请执行以下命令来构建 SRPM 和 RPM 包:
source and binary RPMs by simply running this:
$ rpmbuild -ba program.spec
  $ rpmbuild -ba program.spec


If this works, then your binary RPM files will be created underneath ~/rpmbuild/RPMS/ and the source RPM will be in ~/rpmbuild/SRPMS.
如果成功,RPM 会保存至 <code>~/rpmbuild/RPMS</code>,SRPM 会保存至 <code>~/rpmbuild/SRPMS</code>。


When things go wrong, you can "cd" into the appropriate directory and see what's left over.
如果失败,请查看 BUILD 目录的相应编译日志。为了帮助调试,可以用 "<code>--short-circuit</code>" 选项来忽略成功的阶段。例如,若想要(略过更早的阶段)重新从 <code>%install</code> 阶段开始,请执行:
If you want to skip earlier stages, use the "--short-circuit" option; this is handy if you had a successful build, but have an error in the %install section. For example, to restart at the %install stage (skipping earlier stages), do this:
  $ rpmbuild -bi --short-circuit program.spec
  $ rpmbuild -bi --short-circuit program.spec


If you just want to create a source RPM (.src.rpm), do this in the SPECS directory:
如果只想创建 SRPM(不需要执行 <code>%prep</code> 或 <code>%build</code> 或其他阶段),请执行:
  rpmbuild -bs program.spec
  rpmbuild -bs program.spec
This will create the source RPM in ~/rpmbuild/SRPMS.  Creating ''only'' a source rpm (.src.rpm) is quite quick, because rpm simply needs to copy the .spec file and associated SOURCES files into a .src.rpm file.  Creating a binary rpm typically takes ''much'' longer, because this requires running the %prep, %build, and %install scripts.


== Testing RPMs you've built (including rpmlint) ==
=== 使用 rpmlint 测试已构建的 RPM 包 ===


Run rpmlint on the .spec files, generated ''binary'' RPM, and generated source RPM. Rpmlint works on .spec files, binary RPMs, and source RPMs, finding different things in each.  You need to eliminate or justify rpmlint 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


Normally rpmbuild will build a binary RPM with debugging information - this will handle that.
进入 <code>~/rpmbuild/RPMS</code> 下的特定架构目录中,您会发现有许多二进制 RPM 包。使用以下命令快速查看 RPM 包含的文件和权限:
$ rpmls *.rpm


If you "cd" to the "~/rpmbuild/RPMS" directory, and then cd to the architecture subdirectory,
如果看上去正常,以 root 身份安装它们:
you'll find some binary rpms. You can quickly see their files and their permissions by using rpmls
# rpm -ivp package1.rpm package2.rpm package3.rpm ...
(check to see that they are what you expect):
$ rpmls *.rpm


If those look okay, you can become root and try to install them:
以不同方式来测试程序,看看是否全部都正常工作。如果是 GUI 工具,请确认其是否出现在桌面菜单中,否则表示 <code>.desktop</code> 条目可能有错。
# rpm -ivp XYZ1.rpm XYZ2.rpm XYZ3.rpm ...


Then, you can test them out. Use it a few different ways and see if it works correctly.  If it's a GUI tool, make sure it shows up in the menu (if it doesn't, something is wrong with your .desktop entry).
最后卸载软件包:
  # rpm -e package1 package2 package3


You can uninstall packages later using:
== Mock 和 Koji ==
# rpm -e XYZ1 XYZ2 XYZ3


If that works, you can use Mock to do a more rigorous test that you have accurate build dependencies.  Basically, mock will create a nearly-empty environment and try to rebuild the package; if it fails, then you forgot to list something in a "BuildRequires:" statement. See [[Using Mock to test package builds]] for more information about how to use Mock; once your account is a member of the "mock" 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


If a mock build fails, or the resulting program doesn't work correctly, then you almost certainly have one or more missing BuildRequires packages.
你可以使用 Koji(会使用 <code>mock</code>)在各种不同的系统上执行构建,包括你没有的系统架构。[[PackageMaintainers/Join]] [[PackageMaintainers/UsingKoji]] 包含更多有关 Koji 的信息。一旦设置完成,你就可以使用以下命令,在各种平台上测试你的 SRPM:
 
Once Mock works on your system, you can use Koji (which uses Mock) 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 source RPM
on a variety of platforms by running commands like:
  $ koji build --scratch dist-f9 path_to_source_RPM
  $ koji build --scratch dist-f9 path_to_source_RPM


You can replace dist-f9 with dist-f8, dist-f10, etc., to try other releases. Don't use "dist-rawhide", that's not really rawhide.  Remember, the values of %fedora, %fc9, etc., 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.  You ''can'' use koji to build for rawhide (the next unreleased version), even if it depends on other new packages, as long as the other packages were built in the CVS "devel" section as described below.
你的 Koji 构建只依赖 TARGET 发行版软件源中实际存在的软件包。因此,如果你的软件包依赖 Bodhi 中尚未发行的其它软件包,则不能使用 Koji 为已发行的版本进行构建。如果你需要为尚未稳定的版本构建软件包,请通过 Bodhi 提交 Koji buildroot override 请求。如果你的软件包依赖其他人维护的软件包,请联系其维护者。[在 Bodhi 可以处理 Koji buildroot override 请求之前,以前的旧方法是在此处提交 rel-eng 请求:https://fedorahosted.org/rel-eng/newticket ,并请求将该软件包加入成为 buildroot override。]
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.


== Helpful tools ==
== 有用的工具 ==


The "rpmdevtools" package has a number of helpful tools; "rpm -qil rpmdevtools" will show you what it installs.
<code>rpmdevtools</code> 软件包包含各种有用的工具;"<code>rpm -qil rpmdevtools</code>" 将显示此包的相关信息和文件列表。
One particularly useful tool is rpmdev-bumpspec, which has this form:
* <code>rpmdev-bumpspec</code> : 增加 spec 文件的发行版本号,并以当前时间和版本格式添加 changelog 日志:
  rpmdev-bumpspec --comment=COMMENT --userstring=NAME+EMAIL_STRING SPECFILES
  rpmdev-bumpspec --comment=COMMENT --userstring=NAME+EMAIL_STRING SPECFILES
rpmdev-bumpspec will bump the release tag in the spec file(s), and add a changelog comment with the right datetime and version format.  COMMENT should typically start with "- ".


Similarly, "yum-utils" has a number of yum-specific tools.  "yumdownloader" is especially helpful; you can download the source RPM of a package by simply running "yumdownloader --source PACKAGENAME". You can then use "rpm -U SOURCEPACKAGENAME" to install the source files. E.G., "yumdownloader --source glib; rpm -Uvh glib*.src.rpm".
DNF 下载插件(DNF 核心插件)也十分有用:
* <code>dnf download</code> : 下载指定软件包的 SRPM 包,示例如下:
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,但转换已有的软件包或许可以提供一些有用的信息。
 
如果你想为 Fedora 打包,请确保您的包已通过 [https://fedorahosted.org/FedoraReview/ Fedora Review] 检查,这有助于确保您的包完全遵守 [[Packaging:Guidelines?rd=Packaging/Guidelines|Packaging Guideline]]。
 
最后,[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 是否能运行'''。
 
如果你想要为不同发行版和系统架构来编译软件包,并且提供公开访问的 dnf 软件源,你可以提交你的 src.rpm 到 [https://copr.fedoraproject.org Copr]。


The auto-buildrequires package has a pair of nice tools for helping to figure out the proper BuildRequires entries.  After installing this package, replace "rpmbuild" with "auto-br-rpmbuild" and you'll see an automatically-generated buildrequires list.
如果你需要签名你的软件包,可以使用 <code>rpm-sign</code> 软件包的 <code>rpmsign</code> 工具。


You might find [http://rust.sourceforge.net/ RUST] useful (GPL).
== 相关规定 ==
It is "a drag &amp; drop RPM creation GUI and a 'sandboxing' toolkit that allows you to do software installations within a chrooted environment and automatically generate RPMs from arbitrary source code, without ever seeing a spec file."
If you're creating spec files, it can help you determine the %files.
Note, however, that it does not create .spec files, nor does it create packages of adequate quality for the Fedora repository; it is primarily a tool for making quick-and-dirty binary RPM packages.
(Note: it is no longer at "rusthq.com".)


[http://kitenet.net/~joey/code/alien/ Alien] converts between package formats.
在您创建软件包的时候,请遵守以下规定:
It won't produce clean source RPMs, but converting an existing package
* [[Join the package collection maintainers|如何成为 Fedora 软件包仓库维护人员]] - 描述成为 Packager 的流程。
might provide helpful information.
* [[Packaging:Guidelines|打包规定]]
* [[Packaging:NamingGuidelines|软件包命名规定]]
* [[Packaging:LicensingGuidelines|软件包许可协议规定]]
* [[Packaging:DistTag|Dist 标签使用指南]]
* [[Packaging:ReviewGuidelines|软件包审核规定]]


== Guidelines and rules ==
这里也有许多针对特殊环境打包的规定(如 Java 程序,OCaml 程序,GNOME 程序等等);您也可以从 [[SIGs]] 和 [[:Category:Package Maintainers|软件包维护人员]] 获取许多有价值的帮助。


When you create your packages, you'll need to follow the following rules and guidelines:
[[Special:Prefixindex/Packaging|这里还有一份所有有关打包的 Wiki 页面列表]]
* [[Join the package collection maintainers| How to join the Fedora Package Collection Maintainers]] - describes the process for becoming a Fedora package maintainer
* [[Packaging:Guidelines | Packaging Guidelines]]
* [[Packaging:NamingGuidelines| Package Naming Guidelines]]
* [[Packaging:DistTag| Dist Tag Guidelines]]
* [[Packaging:ReviewGuidelines| Package Review Guidelines]]


There are many official guidelines that will help you with specific circumstances
除了这些,还有许多非官方的指南,比如
(Java programs, OCaml programs, GNOME programs, etc.); the
[[Special:Search?ns0=1&search=PackagingDrafts%2F&searchx=Search|打包规定草稿]][[PackagingDrafts|今后需要制定的打包规定]]
[[Packaging:Guidelines|Packaging Guidelines]] include cross-references to those guidelines.
You can also learn more from the [[SIGs]] and
[[:Category:Package Maintainers|Package Maintainers]] sections.
[https://fedoraproject.org/wiki/Special:Prefixindex/Packaging You can also see the list of all Wiki pages about Packaging] to see if any apply.


Failing that, you might find some useful recommendations in the unofficial
还有其它信息也可供参考,比如 [http://en.opensuse.org/Packaging SuSE 打包规定]
[https://fedoraproject.org/wiki/Special:Search?ns0=1&search=PackagingDrafts%2F&searchx=Search Packaging Drafts] and [https://fedoraproject.org/wiki/PackagingDrafts Packaging Drafts To Do].
[http://www.debian.org/doc/debian-policy/ Debian 打包规定],但是 [http://www.mail-archive.com/distributions@lists.freedesktop.org/msg00156.html 每个发行版都有所不同],所以不要直接生搬硬套。
These are unofficial, obviously.
You might find ideas from [http://en.opensuse.org/Packaging SuSE],
[http://www.debian.org/doc/debian-policy/ Debian], but
[http://www.mail-archive.com/distributions@lists.freedesktop.org/msg00156.html distributions differ in their rules], so do not presume they can be used directly.


The .spec files that you create must be open source software, as noted in the
'''请注意所有 spec 文件必须与开源软件有关,就像 [[Legal:Fedora_Project_Contributor_Agreement|FPCA]] 声明所描述的一样。'''
[http://fedoraproject.org/wiki/Legal/Licenses/CLA CLA].


== Maintaining the package ==
== 维护软件包 ==


Once your package is accepted, you (or your co-maintainers) need to maintain it.
一旦您的软件包被批准加入官方软件源,您和您的副维护者需要共同维护它。请查看 [[Package update HOWTO]] [[Package update guidelines]] 了解更多信息。如果您为多个不同的 Fedora 版本维护软件包,有空一定要为之前的 Fedora 版本也制作一个。
See the [[Package update HOWTO]] and [[Package update guidelines]] for more information.
If you update the version in multiple releases of Fedora, do it "backwards" in time, e.g.,
release for Fedora N, then once that's accepted, Fedora N-1
(the system presumes that later versions of Fedora have the same or later versions of programs).


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 941: Line 793:
== 更多信息 ==
== 更多信息 ==


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


更多在维基之外的页面,请见以下:
Fedora Wiki 之外的更多其他信息,请查看:
* [http://www.g-loaded.eu/2006/04/05/how-to-build-rpm-packages-on-fedora/ 如何在Fedora系统中制作RPM包]——很简单的贯通介绍
* [http://www.g-loaded.eu/2006/04/05/how-to-build-rpm-packages-on-fedora/ 如何在 Fedora 系统中制作 RPM 包]——很简单明了的介绍
* Packaging software with 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.html 第三部分]
* 使用 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 Classroom had a IRC session on packaging and you can refer to the logs at https://fedoraproject.org/wiki/Building_RPM_packages_%2820090405%29
* Fedora 课堂包含一个关于打包的 IRC 会话,参考日志在 https://fedoraproject.org/wiki/Building_RPM_packages_%2820090405%29
* [http://koti.welho.com/vskytta/packagers-handbook/packagers-handbook.html Fedora Packager's Handbook]
* [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/ When Sally met Eddie] - a simple tale, but little detail
* [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] - most complete information, but in some cases old/obsolete
* [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 Guide, section on creating RPMs] - this has lots of good information, and is slightly more up-to-date, but is a draft
* [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 RPM HOWTO: RPM at Idle by Donnie Barnes]
* [http://home.fnal.gov/~dawson/rpms/howto/index.html 由 Dawson 撰写的 RPM 指南]
* [http://home.fnal.gov/~dawson/rpms/howto/index.html RPM HowTo] by Dawson
* [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://en.opensuse.org/SUSE_Build_Tutorial SuSE build tutorial] - but about SuSE, not Fedora. [http://en.opensuse.org/Build_Service/cross_distribution_package_how_to Cross-distribution package HOWTO] has hints if you're building one RPM for many distributions.
* [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 HowTo (en)] ([http://www.mandrivaclub.com/xwiki/bin/view/KB/MandrivaRpmHowTo alt]) is an RPM tutorial, though for Mandriva (nee Mandrake).  Note: In Fedora, do ''not'' recompress original tarballs, as Mandriva suggests, because that would change their cryptographic hashes.
* [http://linuxshellaccount.blogspot.com/2008/03/creating-your-own-linux-rpms-initial.html 创建你的 Linux RPM - 初次编译软件] 简单介绍 RPM 构建,但它使 "构建 RPM 的过程比创建 Solaris 软件包的过程... 步骤更少更简单,并教会你添加软件的所有信息到 spec 文件,使维护/修改更容易。"
* [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://fedoranews.org/alex/tutorial/rpm/ 关于 RPM 所有你应该知道的] 包含更多的安装信息
* [http://fedoranews.org/alex/tutorial/rpm/ All you need to know about RPM] (more about installing packages than creating them)
* [http://wiki.rpm.org/ rpm.org 维基页面] 包含大量对您有用的信息,比如 [http://wiki.rpm.org/Problems 一些有关 RPM 机制的已知问题]
* The [http://wiki.rpm.org/ rpm.org Wiki] has some useful information, such as the [http://wiki.rpm.org/Problems list of known RPM problems]


Note: The [http://rpm5.org/ rpm5.org] site has some documentation, but do not depend on it; that is the home of a ''fork'' of RPM maintained by Jeff Johnson. The RPM used by Fedora (and Novell/SuSE) is instead based at [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 has a brief article] about this.
[http://lwn.net/Articles/236029/ lwn.net] 有一个关于这两者区别的介绍。


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

Latest revision as of 21:00, 19 September 2016

关于本指南

本指南描述了如何为 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 包。这样,如果有错误发生,构建程序不会破坏您的系统,比如造成文件损失或您的私人文件/密钥被发送到互联网上。

切记!不要使用 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/41/Everything/source/SRPMS 目录( "41" 表示 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 代替),但请注意,如果不包含相关依赖,某些程序会禁用一些功能;此时,你需要添加这些依赖。auto-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 等软件包。

不要使用这些标签
  • 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 发行版名称+版本号(例如 ".fc41")
%{fedora} NUMBER Fedora 发行版本号(例如 "41")
%{_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,但转换已有的软件包或许可以提供一些有用的信息。

如果你想为 Fedora 打包,请确保您的包已通过 Fedora Review 检查,这有助于确保您的包完全遵守 Packaging Guideline

最后,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 有一个关于这两者区别的介绍。