<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//FreeBSD//DTD DocBook XML V5.0-Based Extension//EN"
"http://www.FreeBSD.org/XML/share/xml/freebsd50.dtd">
<!--
The FreeBSD Documentation Project
The FreeBSD Simplified Chinese Project
Original Revision: r37884
$FreeBSD$
-->
<book xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:lang="zh_cn">
<info><title>FreeBSD Porter 手册</title>
<authorgroup>
<author><orgname>The FreeBSD Documentation Project</orgname></author>
</authorgroup>
<pubdate>2000 年 4 月</pubdate>
<copyright>
<year>2000</year>
<year>2001</year>
<year>2002</year>
<year>2003</year>
<year>2004</year>
<year>2005</year>
<year>2006</year>
<year>2007</year>
<year>2008</year>
<year>2009</year>
<year>2010</year>
<year>2011</year>
<holder role="mailto:doc@FreeBSD.org">The FreeBSD Documentation
Project</holder>
</copyright>
<authorgroup>
<author><orgname>&cnproj.freebsd.org;</orgname></author>
</authorgroup>
<pubdate>2005 年 11 月</pubdate>
<copyright>
<year>2005</year>
<year>2006</year>
<year>2007</year>
<year>2008</year>
<year>2009</year>
<year>2010</year>
<year>2011</year>
<holder>&cnproj.freebsd.org;</holder>
</copyright>
&trademarks;
&legalnotice;
<releaseinfo>$FreeBSD$</releaseinfo>
</info>
<chapter xml:id="why-port">
<title>介绍</title>
<para>几乎每个人都是通过 FreeBSD Ports Collection
在 FreeBSD 上面装应用程序 (“ports”)的。
就像FreeBSD的其它部分一样, 它主要来自于志愿者的努力。
所以在阅读这份文档的时候请务必记住这些。</para>
<para>在 FreeBSD 的世界里, 任何人都能提交新的 port,
或志愿地维护一个已有的 port, 如果那个 port 没人维护的话 —
不需要任何特殊的权限来做这件事情。</para>
</chapter>
<chapter xml:id="own-port">
<title>自行制作新 port</title>
<para>那么, 您有兴趣创建自己的 port 或升级现有的 port?
太好了。</para>
<para>下面的内容将会提供一些创建FreeBSD port的指导。
如果想升级一个现有的 port, 那么您应该在看完这些内容并阅读
<xref linkend="port-upgrading"/>。</para>
<para>因为这份文档不是十分详细, 您还应该再参考一下
<filename>/usr/ports/Mk/bsd.port.mk</filename>, 所有 port
的 Makefile 文件都会包含它。 即使不是每天都去摆弄 Makefile,
您也会从那个文件里面获得很多知识, 里面的注释非常详细。
还有要补充一下,如果您有其它的问题, 可以给&a.ports;
这个 mailing list 发信。</para>
<note>
<para>在这份文档里提到的大部分的变量
(<varname><replaceable>VAR</replaceable></varname>)
是不能修改的。 大多 (但不是全部) 都在
<filename>/usr/ports/Mk/bsd.port.mk</filename>
的开始部分进行了介绍; 其它一些也应该可以在那里找到。
注意这些文件使用了非标准的制表符:
<application>Emacs</application> 和
<application>Vim</application> 应该能在打开文件的时候自动识别它,
而 &man.vi.1; 和 &man.ex.1; 则需要在打开文件的时候通过
<command>:set tabstop=4</command> 来修正默认的设置。</para>
</note>
<para>
想练练手吗? 请参阅我们的
<link xlink:href="http://wiki.freebsd.org/WantedPorts">希望移植的软件列表</link>
来看看您是否有兴趣完成其中的任务。
</para>
</chapter>
<chapter xml:id="quick-porting">
<title>简单的 port</title>
<para>这一章将介绍如何快速创建一个全新的 port。
很多时候, 这点内容是不够的,
您需要阅读这份文档中更深入的内容。</para>
<para>首先, 需要取得包含源代码的 tar包, 并把它放到
<varname>DISTDIR</varname>变量所指的地方。 默认的情况下, 这应该是
<filename>/usr/ports/distfiles</filename>。</para>
<note>
<para>下面的内容假定您不需要修改软件的源代码就能在 FreeBSD
上编译通过。 如果需要修改代码, 就需要参考下一章的内容了。</para>
</note>
<sect1 xml:id="porting-makefile">
<title>编写 <filename>Makefile</filename></title>
<para>最简单的 <filename>Makefile</filename>
应该是这个样子的:</para>
<programlisting># New ports collection makefile for: oneko
# Date created: 5 December 1994
# Whom: asami
#
# $FreeBSD$
#
PORTNAME= oneko
PORTVERSION= 1.1b
CATEGORIES= games
MASTER_SITES= ftp://ftp.cs.columbia.edu/archives/X11R5/contrib/
MAINTAINER= asami@FreeBSD.org
COMMENT= A cat chasing a mouse all over the screen
MAN1= oneko.1
MANCOMPRESSED= yes
USE_IMAKE= yes
.include <bsd.port.mk></programlisting>
<para>看看您是否能够看懂。 不必担心
<literal>$FreeBSD$</literal>
那一行, 当这个 port 被导入到 ports 树里的时候,
CVS 会自动填写它。 您可以在 <link linkend="porting-samplem">
示范的 Makefile</link>那章找到更多的细节。</para>
</sect1>
<sect1 xml:id="porting-desc">
<title>创建描述文件</title>
<para>有 2 个描述文件对于任何一个 port 来说是必须的,
不论它是不是打算成为 package。 它们是
<filename>pkg-descr</filename> 和
<filename>pkg-plist</filename>。 这两个文件使用 <filename>pkg-</filename>
前缀以区别于其它文件。</para><!-- NOT GOOD -->
<sect2>
<title><filename>pkg-descr</filename> (关于 port 的冗长描述文件)</title>
<para>这是 port 里一个较长的描述文件。
使用一段或几段文件文字来简明的描述这个 ports 是用来做什么的。</para>
<note>
<para>这 <emphasis> 不是</emphasis> 手册或者对如何
深入使用/编译这个port的说明! <emphasis>要是您从
<filename>README</filename> 或者联机手册里面中复制文字的话,
请务必小心</emphasis>; 通常, 它们不是对这个 port
简明扼要的描述, 或者用了难以使用的格式 (比如,
联机手册里有迫使两端对齐的空格)。
如果要移植的软件有官方的WWW网页, 您应该在这里列出来。
使用 <literal>WWW:</literal> 作为前缀来表示
<emphasis>一个</emphasis>网站,
这样其它的自动工具就能正常工作了。</para>
</note>
<para>下面是一个简单的
<filename>pkg-descr</filename> 例子:</para>
<programlisting>This is a port of oneko, in which a cat chases a poor mouse all over
the screen.
:
(etc.)
WWW: http://www.oneko.org/</programlisting>
</sect2>
<sect2>
<title><filename>pkg-plist</filename> (port 的装箱单)</title>
<para>这份文件列出了 port 所要安装的所有文件。 由于 package
也是据此进行打包, 因此它也被称作 <quote>装箱单(packing list)</quote>.
这个文件中, 路径是相对于安装的路径的 (通常是
<filename>/usr/local</filename> 或
<filename>/usr/X11R6</filename>)。 如果您使用
<varname>MAN<replaceable>n</replaceable></varname>
变量的话, 请不要在这里列出任何联机手册。 假如 port
在安装过程中会创建一些目录, 请务必增加对应的
<literal>@dirrm</literal> 行,
以便在 package 被卸载时予以自动删除。</para>
<para>下面是一个简单的例子:</para>
<programlisting>bin/oneko
lib/X11/app-defaults/Oneko
lib/X11/oneko/cat1.xpm
lib/X11/oneko/cat2.xpm
lib/X11/oneko/mouse.xpm
@dirrm lib/X11/oneko</programlisting>
<para>参考 &man.pkg.create.1; 的联机手册以获得更多有关装箱单的细节</para>
<note>
<para>建议您将这个文件里的所有的文件名按字母排序。
这样, 在升级这个port的时候就能够更方便地核实所做的修改。</para>
</note>
<note>
<para>手工创建这样一份列表可能是一件非常枯燥的事情。
如果您的 port 需要安装大量的文件, <link linkend="plist-autoplist">自动创建装箱单</link>
会帮您省下不少时间。</para>
</note>
<para>只有一种情况可以不用 <filename>pkg-plist</filename>文件。
如果这个 port 只安装很少量的一些文件或目录的话,
这些文件和目录就可以分别列在 <filename>Makefile</filename> 的
<varname>PLIST_FILES</varname>和<varname>PLIST_DIRS</varname>
变量里。 举个例子来说, 我们可以在上面那个
<filename>oneko</filename> port 里面不用
<filename>pkg-plist</filename>, 而把下面的这几行加到
<filename>Makefile</filename> 里面:</para>
<programlisting>PLIST_FILES= bin/oneko \
lib/X11/app-defaults/Oneko \
lib/X11/oneko/cat1.xpm \
lib/X11/oneko/cat2.xpm \
lib/X11/oneko/mouse.xpm
PLIST_DIRS= lib/X11/oneko</programlisting>
<para>当然, 如果一个 port 不需要给它自己创建目录的话,
就不用设置 <varname>PLIST_DIRS</varname> 变量了。</para>
<para>不过, 如果用这种方式来列出 port 要安装的文件和目录的话,
也就无法利用在 &man.pkg.create.1; 里介绍的命令来制作 package 了。
因此, 这种方法只适用于那些简单的 port, 使它们更为简化。 同时,
这种做法也有助于减少 ports collection 中的文件数量。
在采用 <filename>pkg-plist</filename> 之前,
请考虑一下使用这种方法。</para>
<para>稍后我们将看到 <filename>pkg-plist</filename>
以及 <varname>PLIST_FILES</varname> 如何处理
<link linkend="plist">更复杂的任务</link>。</para>
</sect2>
</sect1>
<sect1 xml:id="porting-checksum">
<title>创建校验和文件</title>
<para>只要键入 <command>make makesum</command>,
port 便会自动创建 <filename>distinfo</filename>文件。</para>
<para>如果下载的文件的校验和经常变化,
而您又能确保它们的来源可靠 (比如, 来自于CD制造商,
或每天联编生成的文档文件), 就应该在 <varname>IGNOREFILES</varname>
里面标明这些文件。 这样, 再运行
<command>make makesum</command> 的时候便不会把这些标记
<literal>IGNORE</literal> 的文件计算在内了。</para>
</sect1>
<sect1 xml:id="porting-testing">
<title>测试 port</title>
<para>应当确定您的 port 确实做了您希望它们做的事情,
包括打包。下面是需要重点检查的一些重要的工作。</para>
<itemizedlist>
<listitem>
<para><filename>pkg-plist</filename> 中没有包括任何不想安装的文件</para>
</listitem>
<listitem>
<para><filename>pkg-plist</filename> 包含了所有应该安装的文件</para>
</listitem>
<listitem>
<para>您的 port 能够使用 <buildtarget>reinstall</buildtarget>
多次安装。</para>
</listitem>
<listitem>
<para>您的 port 能在卸载 (deinstall) 时,
自动完成 <link linkend="plist-cleaning">清理</link></para>
</listitem>
</itemizedlist>
<procedure>
<title>推荐的测试顺序</title>
<step>
<para><command>make install</command></para>
</step>
<step>
<para><command>make package</command></para>
</step>
<step>
<para><command>make deinstall</command></para>
</step>
<step>
<para><command>pkg_add package-name
</command></para>
</step>
<step>
<para><command>make deinstall</command></para>
</step>
<step>
<para><command>make reinstall</command></para>
</step>
<step>
<para><command>make package</command></para>
</step>
</procedure>
<para>确信在 <buildtarget>package</buildtarget> 和
<buildtarget>deinstall</buildtarget> 阶段没有任何警告。
第三步以后, 检查是否所有新建的目录都被正确删除了。
在第四步以后, 试着运行一下所装的软件,
确保当它以 package 方式安装的时候也能正常工作。</para>
<para>自动化这些步骤最简单的方法是通过 <application>ports tinderbox</application>
来进行测试。 它可以维护 <literal>jails</literal> 并在其中完成全部测试工作,
而不会破坏正在运行的系统的状态。 请参见
<filename>ports/ports-mgmt/tinderbox</filename>
以了解更多的信息。</para>
</sect1>
<sect1 xml:id="porting-portlint">
<title>用 <command>portlint</command> 来检查 port</title>
<para>请使用 <command>portlint</command> 命令来检查您的 port
是否符合我们的规范。 <package>ports-mgmt/portlint</package>
程序是 ports 套件的一部分。
这个程序的主要功能是帮助您检查
<link linkend="porting-samplem">Makefile</link> 的样式是否符合规范,
以及 <link linkend="porting-pkgname">package</link> 的命名是否得体。</para>
</sect1>
<sect1 xml:id="porting-submitting">
<title>提交新 port</title>
<para>在提交新 port 之前, 应先阅读 <link linkend="porting-dads"> 该做什么和不该做什么</link> 一节。</para>
<para>既然已经对所制作的 port 相当满意了, 剩下的工作,
便是将它放进 &os; 的主 ports 树, 以便让更多的人从中受益。
我们并不需要您的 <filename>work</filename>
目录以及 <filename>pkgname.tgz</filename> 包, 因此现在可以删除它们了。
假定您的 port 的名字是 oneko, 接下来要做的是
<command>cd</command> 到
<literal>oneko</literal> 所在的目录, 然后输入命令:
<command>shar `find oneko` > oneko.shar</command></para>
<para>将这个 <literal>oneko.shar</literal> 文件作为附件,
使用 &man.send-pr.1; 程序提交 (请参阅
<link xlink:href="&url.articles.contributing;/contrib-how.html#CONTRIB-GENERAL">Bug
Reports and General Commentary</link> 以了解关于
&man.send-pr.1; 的进一步详情) 将其送出。 请务必将您的 bug 报告分类 (category) 为
<literal>ports</literal> 并把子分类 (class) 设置为
<literal>change-request</literal> (不要把报告表及为机密的, 即
<literal>confidential</literal>!)。 此外, 在 PR
的描述 (<quote>Description</quote>) 一栏中的内容应该是 port 的简要介绍
(例如 <varname>COMMENT</varname> 内容的简化版本),
而 shar 文件则应填入修正 (<quote>Fix</quote>)
栏中。</para>
<note>
<para>在问题报告里面使用了一段好的描述,
能使我们的工作变得更容易。 习惯上, 我们会使用类似:
<quote>New port: <category>/<portname>
<short description of the port></quote>
这样的标题来说明这是新的 port。 如果您也使用这样的习惯,
那么我们将更容易更方便地阅读您的 PR,
从而加快处理速度。</para>
</note>
<para>再次声明, <emphasis>不要包含原始的distfile,
<filename>work</filename>目录, 或者您用
<command>make package</command> 制作的包</emphasis>;
此外, 对于新的 port 请务必使用 &man.shar.1;
而不是 &man.diff.1;。</para>
<para>在您提交的您的 port 以后请耐心等待。
有时在一个 port 正式加入 &os; 之前需要花费好几个月,
尽管也有可能是几天。 您可以查看
<link xlink:href="http://www.FreeBSD.org/cgi/query-pr-summary.cgi?category=ports">
正等待被 commit 到 &os; 的 port PR</link>。</para>
<para>一旦我们看过了您的报告, 有必要的话我们会联系您,
并把它放到 ports 树里。 您的名字也会出现在
<link xlink:href="&url.articles.contributors;/contrib-additional.html">Additional FreeBSD Contributors</link>
和其它的文件。 不是很棒吗!? <!-- smiley -->
:-)</para>
</sect1>
</chapter>
<chapter xml:id="slow">
<title>复杂的 Porting</title>
<para>好了, 也许工作没那么简单, port 需要做些修改才能够在
FreeBSD 上跑起来。 在这一章里,
我们将会一步步举例来介绍应该如何修改来使您的
port 能在 FreeBSD 上面运行。</para>
<sect1 xml:id="slow-work">
<title>整个系统是如何运转的?</title>
<para>首先, 这一系列的动作是由用户在您的
port 目录里敲入 <command>make</command> 后发生的。
您也许会发现在另外的一个窗口里阅读一下
<filename>bsd.port.mk</filename> 将会有助于您的理解。</para>
<para>要是您不是非常明白 <filename>bsd.port.mk</filename>
是做什么的话, 也不用太担心, 很多人都不知道的...
<!-- smiley --><emphasis>:-></emphasis></para>
<procedure>
<step>
<para><buildtarget>fetch</buildtarget> 会首先被执行。
<buildtarget>fetch</buildtarget> 将检查在本地的
<varname>DISTDIR</varname> 目录里是否存在
tar 包。 如果 <buildtarget>fetch</buildtarget>
没有找到就会查找 Makefile 中定义的
<varname>MASTER_SITES</varname> URL, 还有我们的主
FTP 站点 <uri xlink:href="ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/distfiles/">ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/distfiles/</uri>,
在那里我们备份了所有被认可的 distfile。 假设那个
<varname>MASTER_SITES</varname> 站点是直接连在 Internet 上的,
就会试着用 <varname>FETCH</varname> 指定的程序取回
distfile。 如果成功的话, 文件会被保存在<varname>DISTDIR</varname>
所指定的目录以备稍后使用。</para>
</step>
<step>
<para>接下来会执行 <buildtarget>extract</buildtarget>。
它会在 <varname>DISTDIR</varname> 中寻找您的
tar 包 (通常是用 gzip 压缩的 tar 包),然后解压缩到由
<varname>WRKDIR</varname> 所指定的临时目录里
(默认为<filename>work</filename>目录)。</para>
</step>
<step>
<para>下一步是执行 <buildtarget>patch</buildtarget>。
首先任何在 <varname>PATCHFILES</varname> 中定义的补丁都会被打上。
然后, 在由 <varname>PATCHDIR</varname> 指定的目录
(默认为 <filename>files</filename>目录)
中发现的<filename>patch-*</filename>,
它们将会以文件名的字母顺序被先后打上。</para>
</step>
<step>
<para><buildtarget>configure</buildtarget>会被执行。
这一步骤可能会有以下几种情形。</para>
<orderedlist>
<listitem>
<para>如果存在 <filename>scripts/configure</filename>,
就会执行它</para>
</listitem>
<listitem>
<para>如果定义了 <varname>HAS_CONFIGURE</varname>
或者 <varname>GNU_CONFIGURE</varname>, 就会执行
<filename>WRKSRC/configure</filename>。</para>
</listitem>
<listitem>
<para>如果定义了<varname>USE_IMAGE</varname>, 就会执行
<varname>XMKMF</varname> (默认为: <command>xmkmf
-a</command>)。</para>
</listitem>
</orderedlist>
</step>
<step>
<para><buildtarget>build</buildtarget>会被执行。
这一步将会进入ports的工作目录
(<varname>WRKSRC</varname>)
然后进行编译。如果定义了<varname>USE_GMAKE</varname>,
就会使用 GNU <command>make</command>, 反之,
则会使用系统默认的 <command>make</command>。</para>
</step>
</procedure>
<para>以上都是系统默认的步骤。 您也可以定义
<buildtarget>pre-<replaceable>something</replaceable></buildtarget> 或者
<buildtarget>post-<replaceable>something</replaceable></buildtarget>,
或者把以此命名的脚本放到
<filename>scripts</filename> 目录,
它们会在默认的动作之前或之后被执行。</para>
<para>举个例子, 如果您在您的 <filename>Makefile</filename>
里定义了<buildtarget>post-extract</buildtarget>, 并在
<filename>script</filename> 目录里放了一个
<filename>pre-build</filename> 脚本,
那么在 tar 包解开之后
<buildtarget>post-extract</buildtarget> 将被调用,
<filename>pre-build</filename> 脚本会在默认的编译之前被执行。
我们推荐您在 <filename>Makefile</filename>
定义所有的动作, 如果不是十分复杂的话,
这样, 别人能更容易明白您的 port
需要执行哪些非默认的动作。</para>
<para>默认的行为都是由 <filename>bsd.port.mk</filename> 定义的
<buildtarget>do-<replaceable>something</replaceable></buildtarget>
来表示的。 例如, port 中用来解压缩的命令是由
<buildtarget>do-extract</buildtarget> 来定义的。
如果您对默认的设置不满意, 可以通过在
<filename>Makefile</filename> 重新定义
<buildtarget>do-<replaceable>someting</replaceable></buildtarget>
来做些改变。</para>
<note>
<para><quote>主</quote> 动作 (例如
<buildtarget>extract</buildtarget>、
<buildtarget>configure</buildtarget>, 等等)
仅仅是用来确定所有相应的阶段都完成了,
以及调用真实的动作或脚本, 它们不应被修改。
如果您想要修改解压缩这个动作,
可以修改 <buildtarget>do-extract</buildtarget>,
但永远都不要改变 <buildtarget>extract</buildtarget>
的操作!</para>
</note>
<para>我们已经介绍了在用户敲入 <command>make</command>
之后会发生哪些事情了。 接下来我们将进行进一步的学习,
来看一看如何创建一个理想的 port。</para>
</sect1>
<sect1 xml:id="slow-sources">
<title>获取源代码</title>
<para>获取源代码的 tar 包 (通常是
<filename>foo.tar.gz</filename> 或者
<filename>foo.tar.Z</filename>) 并把它们放进
<varname>DISTDIR</varname>。 最好使用 <emphasis>主流</emphasis> 的版本。</para>
<para>您需要设置变量 <varname>MASTER_SITES</varname>
来指向原始 tar 包的获取位置。 您可以在
<filename>bsd.sites.mk</filename>
里找到一些速度较快的主流站点。 请使用这些站点 —
和相关的定义 — 如果可能的话,
应尽量避免在同一个源代码树里出现大量重复的信息。
这些站点会随着时间而变化,
如果每个人都随意加入的话会使维护变得非常困难。</para>
<para>如果您找不到一个有很好网络连接的
FTP/HTTP 站点, 或者它们使用了非标准的格式,
您也许就会想在您自己的 FTP 或 HTTP
服务器上放上一份副本。</para>
<para>如果您找不到可靠的地方放置 distfiles,
我们也可以提供给您一些空间来保存它。
我们自己的 <systemitem>ftp.FreeBSD.org</systemitem>;
然而这只是一个折衷的办法。 distfile 必须放进某人在
<systemitem>freefall</systemitem> 上的
<filename>~/public_distfiles/</filename>
目录中。 可以要求帮助您 commit port 的人来放这个
distfile, 而这个人也需要把 <varname>MASTER_SITES</varname>、
<varname>MASTER_SITE_LOCAL</varname> 以及
<varname>MASTER_SITE_SUBDIR</varname> 的设置,
改为在 <systemitem>freefall</systemitem> 上的用户名。</para>
<para>如果您的 port 的 distfile 一直在变化,
而作者拒绝改变其版本号, 您可以考虑把 distfiles
放在自己的主页, 并在 <varname>MASTER_SITES</varname>
里把原作者的列为首选位置。 如果可能, 试着与 port
的作者沟通一下让他不要这么做, 这将有助于建立对源代码的控制。
在您的主页上放置您自己的 distfile 会避免用户得到
<errorname>checksum mismatch</errorname>
的错误, 而且能减轻我们 FTP 站点维护人员的工作量。
如果您的port只有一个主站点的话, 我们建议您在自己的网站上做一份备份,
并他列为 <varname>MASTER_SITES</varname>的第2项。</para>
<para>如果您的 port 需要来自网络上的一些补丁, 请把它们放到
<varname>DISTDIR</varname>里。 不用担心它们跟源代码不是来自同一站点。
我们有办法处理 (参阅下面的
<link linkend="porting-patchfiles">补丁文件</link>)。</para>
</sect1>
<sect1 xml:id="slow-modifying">
<title>修改 port</title>
<para>解开 tar 包, 对源代码做出合理的修改使得这个 port
能在最新版本的 &os; 上面运行。
一定要 <emphasis>仔细记录</emphasis> 您所做的每处改动,
包括删除、添加、修改的文件等等, 这些修改以后会在您的 port
中以脚本或补丁的方式出现, 并且能通过运行它们来自动完成您对
port 的改动要求。</para>
<para>如果您的 port 要求用与用户交互/配置来完成编译或安装的话,
您可以看一下 Larry Wall 的经典的 <application>Configure</application>
脚本, 适当地模仿一下。 Port collection 的目的,
就是使每个 port 占用最少的空间,
并做到软件的 <quote>即插即用</quote>。</para>
<note>
<para>除非明确地声明, 否则您提交给
&os; ports collection 的补丁,
脚本和其它的文件都将以标准的 BSD 版权发布。</para>
</note>
</sect1>
<sect1 xml:id="slow-patch">
<title>打补丁</title>
<para>在您准备制作 port 的过程中, 增加或修改的文件,
都可以通过 &man.diff.1; 来做成补丁。 希望应用到源代码上的每个补丁,
都应保存为单独的文件, 并命名为
<filename>patch-*</filename>, 其中
<replaceable>*</replaceable> 表示将要修改的文件的完整路径名,
例如 <filename>patch-Imakefile</filename> 或
<filename>patch-src-config.h</filename>。 这些文件,
都应保存在 <varname>PATCHDIR</varname>
(通常是 <filename>files/</filename>), 这里的补丁都会自动应用到源代码上。
所有的补丁必须是相对于
<varname>WRKSRC</varname> 的 (一般而言, 您的 port 会将其
tarball 解压缩在那里, 并完成余下的工作)。 为了让修正和升级更容易,
您应避免使用多个 patch 去修改同一个文件 (例如,
<filename>patch-file</filename> 以及
<filename>patch-file2</filename> 都修改
<filename>WRKSRC/foobar.c</filename>) 这种情况。
需要注意的是, 如果修改的文件的路径中包含下划线
(<literal>_</literal>) 字符, 则在补丁文件名中应使用两个下划线来代替。
例如, 如果需要修改名为 <filename>src/freeglut_joystick.c</filename> 的文件,
补丁文件的名字应为
<filename>patch-src-freeglut__joystick.c</filename>。</para>
<para>只有 <literal>[-+._a-zA-Z0-9]</literal> 这些字符,
可以出现在补丁的文件名中, 请务必不要使用除这些字符以外的其它字符。
不要把您的补丁命名成 <filename>patch-aa</filename>
或 <filename>patch-ab</filename> 等这样的名字,
最好能在补丁名中提到路径和文件名。</para>
<para>不要把 RCS 字符串放进补丁。 我们把文件放进 ports
树的时候, CVS 会损坏它们, 当我们再 check out 出来的时候,
它们就会和原来的不一样, 从而导致打补丁失败。 RCS 字符串
是由美元符号 (<literal>$</literal>) 围绕的,
通常由 <literal>$Id</literal> 或
<literal>$RCS</literal> 开头。</para>
<para>使用 &man.diff.1; 的递归选项(<option>-r</option>)
很好, 但是请检查一下最后输出的 patch,
确保没有任何的垃圾信息。 特别地, 有 2 种文件不需要 diff,
并且应该删除: 一种是 <filename>Makefile</filename>,
当您的port使用了<command>Imake</command>,
或者 GNU <command>configure</command> 等等的话。
如果您不得不编辑<filename>configure.in</filename>
以使 <command>autoconf</command> 去生成
<command>configure</command>, 不要使用
<command>configure</command> 来做 diff
(这常常会有好几千行长!); 请定义
<literal>USE_AUTOTOOLS=autoconf:261</literal> 并对应
<filename>configure.in</filename> 来制作 diff。</para>
<para>另外, 您还应尽量减少补丁中非功能性的空格及空白变动。
在开源世界中, 遵循不同的编码规范的项目共享大量代码是很常见的事情。
如果您从某个项目中提取一部分功能用来修正另一个程序中的问题时,
请务必小心: 补丁中很可能到处都是非功能性的变动行。 这不仅会导致 CVS
库的膨胀, 而且也会让导致问题的故障点, 以及您到底修改了什么变得不甚清晰。</para>
<para>假如需要删除文件, 则应在
<buildtarget>post-extract</buildtarget> target,
而不是作为补丁的一部分来完成。</para>
<para>除此之外, port 的
<filename>Makefile</filename> 还可以通过 in-place 模式的
&man.sed.1; 来直接进行简单的替换操作。 如果补丁需要使用变量值,
这就非常有用了。 例如:</para>
<programlisting>post-patch:
@${REINPLACE_CMD} -e 's|for Linux|for FreeBSD|g' ${WRKSRC}/README
@${REINPLACE_CMD} -e 's|-pthread|${PTHREAD_LIBS}|' ${WRKSRC}/configure</programlisting>
<para>往往在移植某些软件的时候会遇到这样一种情况,
特别是这个软件是在 &windows; 上开发的时候,
大多数的源代码都需要进行CR/LF的转换。
这很可能会给以后打补丁带来问题, 还可能触发编译警告,
并给脚本的执行带来麻烦 (<command>/bin/sh^M</command> not found),
等等。 要迅速将所有文件中的 CR/LF 改为只用 LF, 可以在 port 的
<filename>Makefile</filename> 中加入
<literal>USE_DOS2UNIX=yes</literal>。 除此之外,
还可以指定一个需要执行这种转换操作的文件列表:</para>
<programlisting>USE_DOS2UNIX= util.c util.h</programlisting>
<para>如果希望转换一系列目录中的一组文件, 也可以使用
<varname>DOS2UNIX_REGEX</varname>。 它的参数是与
<command>find</command> 兼容的正则表达式。
关于这种格式的说明, 请参阅 &man.re.format.7;。
这个选项对转换所有指定扩展名的文件, 例如只转换源代码文件这样的应用非常有用:</para>
<programlisting>USE_DOS2UNIX= yes
DOS2UNIX_REGEX= .*\.(c|cpp|h)</programlisting>
<para>如果您希望基于现存的文件创建补丁, 可以把文件复制为带
<filename>.orig</filename> 扩展名的名字, 然后修改原文件。
然后使用 <buildtarget>makepatch</buildtarget>
目标根据修改在 port 的 <filename>files</filename> 目录中生成补丁文件。</para>
</sect1>
<sect1 xml:id="slow-configure">
<title>配置</title>
<para>把任何附加的配置命令加进您的
<filename>configure</filename> 脚本并把它保存到
<filename>scripts</filename> 子目录。 如前面提到的那样,
您也能在 <filename>Makefile</filename> 和/或
使用 <filename>pre-configure</filename> 或
<filename>post-configure</filename> 的脚本来做同样的事情。</para>
</sect1>
<sect1 xml:id="slow-user-input">
<title>处理用户输入</title>
<para>如果您的 port 要求用户的输入以便配置编译、 或安装配置过程,
就必须在 <filename>Makefile</filename> 里设置
<varname>IS_INTERACTIVE</varname> 变量。
如果用户设置了 <envar>BATCH</envar> 的话,
这将让用户能跳过您的 port 来完成
<quote>通宵编译</quote> (如果用户设置了
<envar>INTERACTIVE</envar>的话, 那么 <emphasis>只有</emphasis>
那些要求互动的 port 才会被编译)
这将给那些不停编译 ports 的机器省下很多时间。</para>
<para>通常我们还建议,
如果对于那些问题能有合理的缺省答案的话, 应检查一下
<varname>PACKAGE_BUILDING</varname> 变量,
并根据其设置决定是否执行关闭交互脚本。
这将允许我们为 CDROM 和 FTP 来编译 package。</para>
</sect1>
</chapter>
<chapter xml:id="makefile">
<title>配置 Makefile</title>
<para>配置 <filename>Makefile</filename> 是相当简单的,
我们在此建议您在开始之前看一下现有的例子。
在这份手册里也有一个
<link linkend="porting-samplem">Makefile例子</link>,
照着里面变量的顺序来写能使得您的 port
更容易地被其它人看懂。</para>
<para>现在, 当您开始编写您新的<filename>Makefile</filename>
的时候, 可以依次思考一下以下的问题:</para>
<sect1 xml:id="makefile-source">
<title>作者发布的代码</title>
<para>放在 <varname>DISTDIR</varname> 中的是不是标准的用 gzip 压缩的
tar 包, 例如 <filename>foozolix-1.2.tar.gz</filename>?
如果是, 可以先略过这一节。 如果不是,
您应当看看是不是要覆盖这些变量: <varname>DISTVERSION</varname>、
<varname>DISTNAME</varname>、
<varname>EXTRACT_CMD</varname>、
<varname>EXTRACT_BEFORE_ARGS</varname>、
<varname>EXTRACT_AFTER_ARGS</varname>、
<varname>EXTRACT_SUFX</varname>,
<varname>DISTFILES</varname>,取决于您 port 的 distfile
格式有多么怪异。 (最常见的一个例子便是
<literal>EXTRACT_SUFX=.tar.Z</literal>, 一般这是因为 tar
包是用 <command>compress</command> 而不是
<command>gzip</command> 压缩的时候。)</para>
<para>最糟的情况是, 您需要自己编写
<buildtarget>do-extract</buildtarget>
来覆盖默认的定义, 尽管这不常见,
但如果遇到了, 还是需要这么做。</para>
</sect1>
<sect1 xml:id="makefile-naming">
<title>命名</title>
<para>Makefile 的第一部分便是 port
的名字、 版本号, 以及它所属的分类。</para>
<sect2>
<title><varname>PORTNAME</varname> 和 <varname>PORTVERSION</varname></title>
<para>您应该把 <varname>PORTNAME</varname> 设置为您
port 的名字, <varname>PORTVERSION</varname>
则是 port 的版本号。</para>
</sect2>
<sect2 xml:id="makefile-naming-revepoch">
<title><varname>PORTREVISION</varname> 和
<varname>PORTEPOCH</varname></title>
<sect3>
<title><varname>PORTREVISION</varname> (port 的修订版本号)</title>
<para><varname>PORTEREVISION</varname>
变量是一个单调递增的值, 如果不为
0, 就会被加到包名的后面,
当 <varname>PORTVERSION</varname> 增加
的时候应被置 0 (也就是当官方有新版本发布的时候)。
<varname>PORTREVISION</varname>
会被自动化工具 (比如 &man.pkg.version.1;)
用来检测是否存在可用的新版本。</para>
<para>每当 port 发生变化并对生成的
package 的内容或结构有显著影响时,
都应增加 <varname>PORTREVISION</varname>
值。</para>
<para>下面是一些应当修改 <varname>PORTREVISION</varname>
的情况:</para>
<itemizedlist>
<listitem>
<para>有新的补丁用来修正安全漏洞、
错误, 或给 port 添加了新的功能。</para>
</listitem>
<listitem>
<para>修改了 <filename>Makefile</filename>
里编译时开启或禁用的选项。</para>
</listitem>
<listitem>
<para>修改了要安装文件的列表或安装时的行为
(例如, 修改了一个用来给 package
初始化数据的脚本, 如 ssh host keys)。</para>
</listitem>
<listitem>
<para>一个port依赖的共享库版本改变
(在这种情况下, 当安装了新版本的共享库,
后再去安装较早的软件就会出错,
因为它们要依赖老的 libfoo.x
而不是libfoo.(x+1))。</para>
</listitem>
<listitem>
<para>原作者修改了 port distfile, 并且 distfile
的新老版本之间用
<command>diff -ru</command> 只能发现一些细微的变化,
这时我们只需要对
<filename>distinfo</filename> 做相应的修正,
而不需要修改
<varname>PORTVERSION</varname>。</para>
</listitem>
</itemizedlist>
<para>不需要修改
<varname>PORTREVISION</varname> 的例子:</para>
<itemizedlist>
<listitem>
<para>port 结构风格的改变,
但对于打成的包没有功能的上的变化。</para>
</listitem>
<listitem>
<para><varname>MASTER_SITES</varname>
发生变化, 或进行了对 port 功能的修改,
但不致影响最后打成的包。</para>
</listitem>
<listitem>
<para>对 distfiles 诸如修正拼写错误之类的补丁,
对用户而言没有升级上的麻烦。</para>
</listitem>
<listitem>
<para>对一个原本编译失败的包的修改,
使其可编译, 而没有加入新功能。 因为
<varname>PORTREVISION</varname>
表示包的内容发生了变化,
如果先前没有可编译的包, 也就不需要修改
<varname>PORTREVISION</varname>
来表示变化。</para>
</listitem>
</itemizedlist>
<para>一个修改并提交 port 的原则是:
使得别人能从中受益 (改进、 修改已有错误,
或使新的 package 能够运行),
您还要权衡一下这是否应让那些经常更新
ports 树的人升级,
如果回答是 <quote>是</quote> 的话,
<varname>PORTREVISION</varname>
就应该修改了。</para>
</sect3>
<sect3>
<title><varname>PORTEPOCH</varname> (port 的加权版本号)</title>
<para>有时软件商或 FreeBSD 的
porter 会使用比旧版的版本号小的数字做为新版本号的情况。
举例来说, 从
foo-20000801 到 foo-1.0 (从形式上来说这是不对的,
因为 20000801 在数值上比1大很多)。</para>
<para>在这种情况下, <varname>PORTEPOCH</varname>
应当增加。 如果
<varname>PORTEPOCH</varname> 非 0,
就应当加到包名字的后面。
<varname>PORTEPOCH</varname>
永远不能被减少或清零,
因为那样会导致与前一时期的 package
比较版本时产生不正确的结果。
(就是说, 那个 package 就不会被检测到已经过时了。)
新的版本号 (比如前面在前面那个例子中的
<literal>1.0,1</literal>) 在数值上比前一个版本
(20000801) 小, 但多数自动化的工具会认为
<literal>,1</literal>
后缀意味着比前一个包的后缀 <literal>,0</literal> 大。</para>
<para>错误的去除或重置
<varname>PORTEPOCH</varname>
会导致很多不幸发生; 如果您还不明白前面的讨论,
请多阅读几次直至明白为止,
或到邮件列表上来提问。</para>
<para>大多数 port 都不会用到
<varname>PORTEPOCH</varname>,
并且如果某个软件的下一个版本改变了版本号结构的话,
用巧妙的方法来设定 <varname>PORTVERSION</varname>
也能避免使用 <varname>PORTEPOCH</varname>。 然而,
FreeBSD porter 也需要注意, 当有新版本的软件发布,
但并非正式版本时 — 比如
<quote>snapshot</quote> 版本,
原作者可能会使用当时的日期来命名,
这在新的 <quote>官方</quote> 版本发布的时候,
就很容易引起前面提到的问题。</para>
<para>举个例子, 如果 snapshot 版本的发布日期是
20000917, 这个软件的上一个版本是1.2,
那么这个版本的 <varname>PORTVERSIN</varname> 应该设为
1.2.20000917 或类似的样子, 而不是20000917,
这样在 1.3 发布以后,
新版本就可以在数值上大于旧的版本了。</para>
</sect3>
<sect3>
<title>关于 <varname>PORTREVISION</varname> 和
<varname>PORTEPOCH</varname> 的用例</title>
<para><literal>gtkmumble</literal> port,版本号
<literal>0.10</literal>, 被提交到
ports collection:</para>
<programlisting>PORTNAME= gtkmumble
PORTVERSION= 0.10</programlisting>
<para><varname>PKGNAME</varname> 变成
<literal>gtkmumble-0.10</literal>。</para>
<para>然后有人发现了一个安全漏洞,
需要用一个FreeBSD的补丁。 <varname>PORTREVISION</varname>
就要相应的增加。</para>
<programlisting>PORTNAME= gtkmumble
PORTVERSION= 0.10
PORTREVISION= 1</programlisting>
<para><varname>PKGNAME</varname>变成了
<literal>gtkmumble-0.10_1</literal></para>
<para>软件的作者发布了新的版本, 版本为
<literal>0.2</literal> (作者本来的意思是,
用 <literal>0.10</literal> 表示
<literal>0.1.0</literal>,<quote>而不是指
0.9 之后的那个版本</quote> - 但是现在太迟了)。
因为现在的次版本号 <literal>2</literal>
在数值上比上一个版本 <literal>10</literal> 小,
<varname>PORTEPOCH</varname> 必须增加,
以使新的 package 被认为是 <quote>更新的</quote>。
由于那是作者发布的一个新版本, 因此
<varname>PORTREVISION</varname> 应被置0 (或者从
<filename>Makefile</filename> 里面删除它)。</para>
<programlisting>PORTNAME= gtkmumble
PORTVERSION= 0.2
PORTEPOCH= 1</programlisting>
<para><varname>PKGNAME</varname> 变成了
<literal>gtkmumble-0.2,1</literal></para>
<para>下一个版本将会是 0.3。
由于 <varname>PORTEPOCH</varname> 从不减少,
那么就无须改动:</para>
<programlisting>PORTNAME= gtkmumble
PORTVERSION= 0.3
PORTEPOCH= 1</programlisting>
<para><varname>PKGNAME</varname> 变成
<literal>gtkmumble-0.3,1</literal></para>
<note>
<para>如果在这次升级中 <varname>PORTEPOCH</varname>
被置为了<literal>0</literal>, 那么在装了
<literal>gtkmumble-0.10_1</literal> 包的机器上就无法检测到
<literal>gtkmumble-0.3</literal> 包的更新,
因为 <literal>3</literal> 在数值上比
<literal>10</literal> 小。 记住, 这是
<varname>PORTEPOCH</varname> 最重要的地方。</para>
</note>
</sect3>
</sect2>
<sect2>
<title><varname>PKGNAMEPREFIX</varname> 和 <varname>PKGNAMESUFFIX</varname></title>
<para>2 个可选的变量, <varname>PKGNAMEPREFIX</varname> 和
<varname>PKGNAMESUFFIX</varname> 可以和
<varname>PORTNAME</varname> 还有
<varname>PORTVERSION</varname> 配合使用,
形成像这样的 <varname>PKGNAME</varname>:
<literal>${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}</literal>。
请确定符合我们的
<link linkend="porting-pkgname">包命名规则</link>。
当然, <emphasis>不</emphasis> 允许在
<varname>PORTVERSION</varname>
中使用连字符 (<literal>-</literal>)。 如果包名有
<replaceable>language-</replaceable> 或
<replaceable>-compiled.specifics</replaceable> 部分 (见下文),
请分别用 <varname>PKGNAMEPREFIX</varname> 和
<varname>PKGNAMESUFFIX</varname>, 不要直接加到
<varname>PORTNAME</varname> 中。</para>
</sect2>
<sect2>
<title><varname>LATEST_LINK</varname></title>
<para><varname>LATEST_LINK</varname> 在编译包的过程中用于确定可以为
<command>pkg_add -r</command> 使用的缩短的名字。 举例来说,
在安装最新版本的 perl 的时候, 只需指定
<command>pkg_add -r perl</command> 而无需知道具体的版本号。
这个名字应该是独一无二的, 并且对用户而言应该是显而易见的名字。</para>
<para>有时, 在 ports 套件中可能会存在同一程序的多个版本。
索引和预编译包的联编系统都需要能够将它们视为不同的软件包, 尽管其
<varname>PORTNAME</varname>、 <varname>PKGNAMEPREFIX</varname>, 甚至
<varname>PKGNAMESUFFIX</varname> 可能是一模一样的。 遇到这种情况时,
就需要将除了 <quote>主</quote> port 之外的其他 port
中的 <varname>LATEST_LINK</varname> 变量设为不同的值
— 请参见 <filename>lang/gcc46</filename> 和
<filename>lang/gcc</filename> port, 以及
<filename>www/apache*</filename> 系列, 以了解它的用法。
如果设置了 <varname>NO_LATEST_LINK</varname>,
则系统便不会生成对应的连接, 对于非
<quote>主</quote> port 来说是一个可行的选择。
需要注意的是, 如何确定 <quote>主</quote> 版本 —
<quote>最流行</quote>、 <quote>受支持最好</quote>,
<quote>变动最少</quote>, 等等 — 已经超过了本书能够给出的建议范围;
这里只是向您介绍在选定了一个
<quote>主</quote> port 之后如何指定其他 port 的版本。</para>
</sect2>
<sect2 xml:id="porting-pkgname">
<title>包命名规则</title>
<para>以下是您在命名您的包时应当遵守的规则。
这将使得我们放包的目录更利于浏览,
因为我们已经有数以万计的包了,
如果用户觉得查看包名很困难的话,
他们会很快走开的。</para>
<para>一个包的名字应该看起来像这样:
<filename>language_region-name-compiled.specifics-version.numbers</filename>。</para>
<para>要像这样来定义包的名字:
<literal>${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}</literal>。
确保所有的变量符合上面的格式。</para>
<orderedlist>
<listitem>
<para>FreeBSD 会尽力去支持用户当地的语言。
如果这个 port 是某种语言专用的, 那么
<replaceable>language-</replaceable> 部分应该是
由 ISO-639 定义的自然语言的 2 个字母缩写。 比如,
<literal>ja</literal>是表示日本, <literal>ru</literal>
是表示俄罗斯, <literal>vi</literal> 表示越南,
<literal>zh</literal> 表示中国, <literal>ko</literal>
表示韩国, <literal>de</literal> 表示德国。</para>
<para>如果是针对某种语言的某一地区的话,
再要加上2个字母的国家代码。 例如,
<literal>en_US</literal> 表示美国英语,
<literal>fr_CH</literal> 表示瑞士法语。</para>
<para><replaceable>language-</replaceable> 部分应该在
<varname>PKGNAMEPREFIX</varname> 变量里设置。</para>
</listitem>
<listitem>
<para><filename>name</filename> 部分的首字母应该
小写。 (余下的部分可以包含大写字母, 所以当您
要转换一个包含大写字母软件的名字时, 您需要
自己做出判断。) 对于 <literal>Perl 5</literal>
模块的命名, 有个传统的规则是, 在前面
加上 <literal>p5-</literal>
并把两个冒号的部分改为连字号, 如:
<literal>Data::Dumper</literal> 模块对应的名字, 就应该是
<literal>p5-Data-Dumper</literal>。</para>
</listitem>
<listitem>
<para>确认 port 的名字和版本之间有清晰的分隔, 并放入 <varname>PORTNAME</varname> 和
<varname>PORTVERSION</varname> 变量。 在
<varname>PORTNAME</varname> 中包含版本部分的唯一理由是上游软件包真的采用这样的命名方式,
类似 <filename>textproc/libxml2</filename> 或
<filename>japanese/kinput2-freewnn</filename> port 这样。 否则,
在 <varname>PORTNAME</varname> 中就不应包含任何版本信息。
许多 port 采用同样的 <varname>PORTNAME</varname> 名字是很正常的,
<filename>www/apache*</filename> port 便是如此; 在这种情况下,
不同的版本 (以及不同的索引项) 是由 <varname>PKGNAMEPREFIX</varname>、
<varname>PKGNAMESUFFIX</varname>, 以及
<varname>LATEST_LINK</varname> 的值的不同而有所区别的。</para>
</listitem>
<listitem>
<para>如果 port 可以使用不同的 <link linkend="makefile-masterdir">硬编码默认配置</link>
进行联编 (通常是一系列 port 的一部分目录名), 则
<replaceable>-compiled.specifics</replaceable>
部分就应该明示编译进去的默认值 (此处连字号是可选的)。
通常的用例包括纸型和不同的字体尺寸。</para>
<para><replaceable>-compiled.specifics</replaceable>
部分应该通过 <varname>PKGNAMESUFFIX</varname>
变量来设置。</para>
</listitem>
<listitem>
<para>版本号应该紧随在连字号
(<literal>-</literal>) 后面并由数字和字母组成。
特别指出, 另外的连字号是不允许出现在版本号里的。
唯一例外的是字符串 <literal>pl</literal>
(表示 <quote>patchlevel</quote>),
<emphasis>只能</emphasis>
用在软件没有主版本号和次版本号的情况下。
如果软件的版本号里出现了像
<quote>alpha</quote>, <quote>beta</quote>,
<quote>rc</quote>, <quote>pre</quote>,
取第一个字母把它放在小数点的后面。
如果在版本号里一直出现那些名字,
那么在数字和字母之间不应有多余的小数点。</para>
<para>这个方法是为了更容易得凭版本号来排序 port。
特别注意的是, 确保版本号之间的每部分都由小数点来分隔,
如果日期也是版本号的一部分, 就用这样的格式,
<literal>0.0.yyyy.mm.dd</literal>
这样的格式, 而非
<literal>dd.mm.yyyy</literal>
甚至
<literal>yy.mm.dd</literal>
这种不适合表示千年的格式。 在版本号上使用
<literal>0.0.</literal> 前缀十分重要, 因为当软件发行正式的版本时,
其版本号数字很可能会小于表示年份的
<literal>yyyy</literal> 数字。</para>
</listitem>
</orderedlist>
<para>这里是一些真实的例子,
我们藉此说明如何把软件作者对软件的命名,
转换为适合我们包的命名方式:</para>
<informaltable frame="none" pgwide="1">
<tgroup cols="6">
<thead>
<row>
<entry>发行版的名字</entry>
<entry><varname>PKGNAMEPREFIX</varname></entry>
<entry><varname>PORTNAME</varname></entry>
<entry><varname>PKGNAMESUFFIX</varname></entry>
<entry><varname>PORTVERSION</varname></entry>
<entry>说明</entry>
</row>
</thead>
<tbody>
<row>
<entry>mule-2.2.2</entry>
<entry>(空)</entry>
<entry>mule</entry>
<entry>(空)</entry>
<entry>2.2.2</entry>
<entry>没什么需要修改的</entry>
</row>
<row>
<entry>EmiClock-1.0.2</entry>
<entry>(空)</entry>
<entry>emiclock</entry>
<entry>(空)</entry>
<entry>1.0.2</entry>
<entry>程序的名字不能使用大写字母</entry>
</row>
<row>
<entry>rdist-1.3alpha</entry>
<entry>(空)</entry>
<entry>rdist</entry>
<entry>(空)</entry>
<entry>1.3.a</entry>
<entry>像 <literal>alpha</literal>
这样的字符串是不允许出现的</entry>
</row>
<row>
<entry>es-0.9-beta1</entry>
<entry>(空)</entry>
<entry>es</entry>
<entry>(空)</entry>
<entry>0.9.b1</entry>
<entry>像 <literal>beta</literal>
这样的字符串是不允许出现的</entry>
</row>
<row>
<entry>mailman-2.0rc3</entry>
<entry>(空)</entry>
<entry>mailman</entry>
<entry>(空)</entry>
<entry>2.0.r3</entry>
<entry>像 <literal>rc</literal>
这样的字符串是不允许出现的</entry>
</row>
<row>
<entry>v3.3beta021.src</entry>
<entry>(空)</entry>
<entry>tiff</entry>
<entry>(空)</entry>
<entry>3.3</entry>
<entry>那个是啥鬼东西?</entry>
</row>
<row>
<entry>tvtwm</entry>
<entry>(空)</entry>
<entry>tvtwm</entry>
<entry>(空)</entry>
<entry>pl11</entry>
<entry>总需要有个版本号吧</entry>
</row>
<row>
<entry>piewm</entry>
<entry>(空)</entry>
<entry>piewm</entry>
<entry>(空)</entry>
<entry>1.0</entry>
<entry>总需要有个版本号吧</entry>
</row>
<row>
<entry>xvgr-2.10pl1</entry>
<entry>(空)</entry>
<entry>xvgr</entry>
<entry>(空)</entry>
<entry>2.10.1</entry>
<entry><literal>pl</literal> 只允许在没有
主/次 版本号的情况下才能出现</entry>
</row>
<row>
<entry>gawk-2.15.6</entry>
<entry>ja-</entry>
<entry>gawk</entry>
<entry>(空)</entry>
<entry>2.15.6</entry>
<entry>日文版</entry>
</row>
<row>
<entry>psutils-1.13</entry>
<entry>(空)</entry>
<entry>psutils</entry>
<entry>-letter</entry>
<entry>1.13</entry>
<entry>纸张大小已经在编译的时候被硬编码到程序里了</entry>
</row>
<row>
<entry>pkfonts</entry>
<entry>(空)</entry>
<entry>pkfonts</entry>
<entry>300</entry>
<entry>1.0</entry>
<entry>300dpi 字体的包</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>如果在原始的代码里没有版本号,
或者原作者并不打算开发另外的版本,
就应把版本号设成 <literal>1.0</literal> (就像前面
<literal>piewm</literal> 的例子那样)。 否则,
要求原始的作者加上版本号或使用日期
(<literal>0.0.yyyy.mm.dd</literal>)
来作为版本号。</para>
</sect2>
</sect1>
<sect1 xml:id="makefile-categories">
<title>分类</title>
<sect2>
<title><varname>CATEGORIES</varname> (所属分类)</title>
<para>在包制作完成之后,
它会被放在 <filename>/usr/ports/packages/All</filename>,
并建立一系列来自
<filename>/usr/ports/packages</filename>
下子目录的符号连接。 这些子目录的名称是由
<varname>CATEGORIES</varname>
指定的。 这将方便于那些用户在 FTP 站点或 CDROM
的一大堆包里面寻找自己想要的包。 请查看一下
<link linkend="porting-categories">目前的分类表</link>,
并找出一个适合您 port 的分类。</para>
<para>此列表也会决定您的 port 在 port
目录中的位置。 如果您在这里设定了 1 个以上的分类,
则认为您 port 文件应放到以第一个分类命名的子目录中。 请参阅
<link linkend="choosing-categories">后面</link>
关于如何选择正确分类的更多讨论。</para>
</sect2>
<sect2 xml:id="porting-categories">
<title>目前的分类表</title>
<para>这是目前 port 中的分类。 那些用星号
(<literal>*</literal>) 标记的是
<emphasis>虚拟</emphasis>分类 —
它们在ports树里没有相应的子目录,
因而只用来做为次要的分类, 用以方便搜索。</para>
<note>
<para>对于非虚拟的分类来说,
您会看到在相对应子目录中的 <filename>Makefile</filename>
里有写在 <varname>COMMENT</varname> 里的单行描述。</para>
</note>
<informaltable frame="none" pgwide="1">
<tgroup cols="3">
<thead>
<row>
<entry>分类</entry>
<entry>描述</entry>
<entry>注意事项</entry>
</row>
</thead>
<tbody>
<row>
<entry><filename>accessibility</filename></entry>
<entry>帮助残障人士的 port。</entry>
<entry/>
</row>
<row>
<entry><filename>afterstep*</filename></entry>
<entry>对于
<link xlink:href="http://www.afterstep.org">AfterStep</link>
窗口管理器的支持。</entry>
<entry/>
</row>
<row>
<entry><filename>arabic</filename></entry>
<entry>阿拉伯语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>archivers</filename></entry>
<entry>压缩与备份工具。</entry>
<entry/>
</row>
<row>
<entry><filename>astro</filename></entry>
<entry>有关天文学的 port。</entry>
<entry/>
</row>
<row>
<entry><filename>audio</filename></entry>
<entry>声音支持。</entry>
<entry/>
</row>
<row>
<entry><filename>benchmarks</filename></entry>
<entry>测评程序。</entry>
<entry/>
</row>
<row>
<entry><filename>biology</filename></entry>
<entry>生物学相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>cad</filename></entry>
<entry>计算机辅助设计工具。</entry>
<entry/>
</row>
<row>
<entry><filename>chinese</filename></entry>
<entry>中文语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>comms</filename></entry>
<entry>通讯软件。</entry>
<entry>大部分是用于串口通讯的。</entry>
</row>
<row>
<entry><filename>converters</filename></entry>
<entry>字符编码转换。</entry>
<entry/>
</row>
<row>
<entry><filename>databases</filename></entry>
<entry>数据库。</entry>
<entry/>
</row>
<row>
<entry><filename>deskutils</filename></entry>
<entry>在发明计算机以前就已经在桌面上使用的东西。</entry>
<entry/>
</row>
<row>
<entry><filename>devel</filename></entry>
<entry>程序开发工具。</entry>
<entry>不要把开发库放在这里 —
除非您再也找不到更合适的分类,
否则就不该放在这个分类里。</entry>
</row>
<row>
<entry><filename>dns</filename></entry>
<entry>DNS 相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>docs*</filename></entry>
<entry>有关 FreeBSD 文档的 Meta-ports。</entry>
<entry/>
</row>
<row>
<entry><filename>editors</filename></entry>
<entry>通用编辑器。</entry>
<entry>有特殊用途的编辑器应该被置于相应的分类中
(比如, 数学-方程式
编辑器应该放在 <filename>math</filename> 分类里。</entry>
</row>
<row>
<entry><filename>elisp*</filename></entry>
<entry>Emacs-lisp相关的port。</entry>
<entry/>
</row>
<row>
<entry><filename>emulators</filename></entry>
<entry>其它操作系统的模拟器。</entry>
<entry>终端模拟器 <emphasis>不应该</emphasis>
属于这个分类 — 基于 X 的应该放在
<filename>x11</filename> 而基于文本模式的应该放到
<filename>comms</filename> 或 <filename>misc</filename>
中去, 取决于具体的功能。</entry>
</row>
<row>
<entry><filename>finance</filename></entry>
<entry>货币、 金融以及相关的应用程序。</entry>
<entry/>
</row>
<row>
<entry><filename>french</filename></entry>
<entry>法语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>ftp</filename></entry>
<entry>FTP 客户端和服务器端的程序。</entry>
<entry>如果您的 port 同时支持 FTP 和 HTTP 的话,
把它放进 <filename>ftp</filename> 并把
<filename>www</filename> 做为第二分类。</entry>
</row>
<row>
<entry><filename>games</filename></entry>
<entry>游戏。</entry>
<entry/>
</row>
<row>
<entry><filename>geography*</filename></entry>
<entry>与地理学有关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>german</filename></entry>
<entry>德语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>gnome*</filename></entry>
<entry>关于
<link xlink:href="http://www.gnome.org">GNOME</link>
项目的支持。</entry>
<entry/>
</row>
<row>
<entry><filename>gnustep*</filename></entry>
<entry>与 GNUstep 桌面环境有关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>graphics</filename></entry>
<entry>图形图象程序。</entry>
<entry/>
</row>
<row>
<entry><filename>hamradio*</filename></entry>
<entry>业余无线电爱好者使用的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>haskell*</filename></entry>
<entry>有关 Haskell 编程语言的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>hebrew</filename></entry>
<entry>希伯来语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>hungarian</filename></entry>
<entry>匈牙利语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>ipv6*</filename></entry>
<entry>IPv6 相关软件。</entry>
<entry/>
</row>
<row>
<entry><filename>irc</filename></entry>
<entry>IRC 相关程序</entry>
<entry/>
</row>
<row>
<entry><filename>japanese</filename></entry>
<entry>日语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>java</filename></entry>
<entry>与 Java™ 编程语言有关的软件。</entry>
<entry><filename>java</filename> 分类对 port 而言不应是其唯一的分类。
除了直接与 Java 语言相关的 port 之外,
开发人员应尽量避免使用 <filename>java</filename> 作为
port 的主分类。</entry>
</row>
<row>
<entry><filename>kde*</filename></entry>
<entry><link xlink:href="http://www.kde.org">K 桌面环境 (KDE)</link>
相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>kld*</filename></entry>
<entry>可加载内核模块。</entry>
<entry/>
</row>
<row>
<entry><filename>korean</filename></entry>
<entry>韩语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>lang</filename></entry>
<entry>编程语言。</entry>
<entry/>
</row>
<row>
<entry><filename>linux*</filename></entry>
<entry>Linux 相关的应用程序。</entry>
<entry/>
</row>
<row>
<entry><filename>lisp*</filename></entry>
<entry>和 Lisp 编程语言有关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>mail</filename></entry>
<entry>电子邮件软件。</entry>
<entry/>
</row>
<row>
<entry><filename>math</filename></entry>
<entry>数值计算和其它数学相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>mbone*</filename></entry>
<entry>MBone 应用程序。</entry>
<entry/>
</row>
<row>
<entry><filename>misc</filename></entry>
<entry>各式各样的实用程序。</entry>
<entry>通常不属于其它的任何分类,
如果可能的话, 尽量为您的 port 选择
<literal>misc</literal>
以外的分类, 因为在这里的
port 比较容易被人忽略。</entry>
</row>
<row>
<entry><filename>multimedia</filename></entry>
<entry>多媒体软件。</entry>
<entry/>
</row>
<row>
<entry><filename>net</filename></entry>
<entry>各种网络相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>net-im</filename></entry>
<entry>即时消息软件。</entry>
<entry/>
</row>
<row>
<entry><filename>net-mgmt</filename></entry>
<entry>网络管理软件。</entry>
<entry/>
</row>
<row>
<entry><filename>net-p2p</filename></entry>
<entry>对等网 (Peer to peer network) 应用程序。</entry>
<entry/>
</row>
<row>
<entry><filename>news</filename></entry>
<entry>USENET新闻组相关软件。</entry>
<entry/>
</row>
<row>
<entry><filename>palm</filename></entry>
<entry><link xlink:href="http://www.palm.com/">Palm™</link> 系列相关软件。</entry>
<entry/>
</row>
<row>
<entry><filename>parallel*</filename></entry>
<entry>并行计算相关软件。</entry>
<entry/>
</row>
<row>
<entry><filename>pear*</filename></entry>
<entry>Pear PHP 架构相关软件。</entry>
<entry/>
</row>
<row>
<entry><filename>perl5*</filename></entry>
<entry><application>Perl</application>5 相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>plan9*</filename></entry>
<entry><link xlink:href="http://www.cs.bell-labs.com/plan9dist/">Plan9</link> 相关程序。</entry>
<entry/>
</row>
<row>
<entry><filename>polish</filename></entry>
<entry>波兰语语言语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>ports-mgmt</filename></entry>
<entry>用于管理、 安装和开发 FreeBSD ports 和预编译包的 port。</entry>
</row>
<row>
<entry><filename>portuguese</filename></entry>
<entry>葡萄牙语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>print</filename></entry>
<entry>打印相关的软件。</entry>
<entry>桌面出版工具 (打印预览工具等等)
也可以放在此分类里。</entry>
</row>
<row>
<entry><filename>python*</filename></entry>
<entry><link xlink:href="http://www.pythong.org/">Python</link> 编程语言相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>ruby*</filename></entry>
<entry><link xlink:href="http://www.ruby-lang.org/">Ruby</link> 编程语言相关的软件。</entry>
<entry/>
</row>
<row>
<entry><filename>rubygems*</filename></entry>
<entry>移植版本的 <link xlink:href="http://www.rubygems.org/">RubyGems</link> 软件包。</entry>
<entry/>
</row>
<row>
<entry><filename>russian</filename></entry>
<entry>俄语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>scheme*</filename></entry>
<entry>与 Scheme 语言有关的 port。</entry>
<entry/>
</row>
<row>
<entry><filename>science</filename></entry>
<entry>科学相关但不适合放在
<filename>astro</filename>、
<filename>biology</filename>, 以及
<filename>math</filename> 分类的 port。</entry>
<entry/>
</row>
<row>
<entry><filename>security</filename></entry>
<entry>安全相关的实用程序。</entry>
<entry/>
</row>
<row>
<entry><filename>shells</filename></entry>
<entry>命令行 shell。</entry>
<entry/>
</row>
<row>
<entry><filename>spanish*</filename></entry>
<entry>西班牙语支持</entry>
<entry/>
</row>
<row>
<entry><filename>sysutils</filename></entry>
<entry>系统相关的实用程序。</entry>
<entry/>
</row>
<row>
<entry><filename>tcl*</filename></entry>
<entry>依赖于 Tcl 运行的 port。</entry>
<entry/>
</row>
<row>
<entry><filename>textproc</filename></entry>
<entry>文本处理的实用程序。</entry>
<entry>这个分类并不适合于那些应该放到
<filename>print</filename> 的桌面出版工具。</entry>
</row>
<row>
<entry><filename>tk*</filename></entry>
<entry>依赖于 Tk 运行的 port。</entry>
<entry/>
</row>
<row>
<entry><filename>ukrainian</filename></entry>
<entry>乌克兰语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>vietnamese</filename></entry>
<entry>越南语语言支持。</entry>
<entry/>
</row>
<row>
<entry><filename>windowmaker*</filename></entry>
<entry>WindowMaker 窗口管理器的相关支持。</entry>
<entry/>
</row>
<row>
<entry><filename>www</filename></entry>
<entry>Word Wide Web的相关软件。</entry>
<entry>HTML语言相关的支持也可以放在这个分类里。</entry>
</row>
<row>
<entry><filename>x11</filename></entry>
<entry>X Window System以及相关软件。</entry>
<entry>这个分类是给那些直接支持X Window System
的软件的。 不要把常规的 X 应用程序也放进这里;
它们中的大多数都应被归类到
<filename>x11-*</filename> (参见下文)。
如果您的 port <emphasis>是</emphasis>
X 应用程序, 应定义 <varname>USE_XLIB</varname>
(使用 <varname>USER_IMAKE</varname> 隐含包括它),
然后把它放到合适的分类里。</entry>
</row>
<row>
<entry><filename>x11-clocks</filename></entry>
<entry>X11 下的时钟程序。</entry>
<entry/>
</row>
<row>
<entry><filename>x11-drivers</filename></entry>
<entry>X11 驱动程序。</entry>
<entry/>
</row>
<row>
<entry><filename>x11-fm</filename></entry>
<entry>X11 下的文件管理器。</entry>
<entry/>
</row>
<row>
<entry><filename>x11-fonts</filename></entry>
<entry>X11 下的字体以及相关工具。</entry>
<entry/>
</row>
<row>
<entry><filename>x11-servers</filename></entry>
<entry>X11 服务器。</entry>
<entry/>
</row>
<row>
<entry><filename>x11-themes</filename></entry>
<entry>X11 主题。</entry>
<entry/>
</row>
<row>
<entry><filename>x11-toolkits</filename></entry>
<entry>X11 工具包。</entry>
<entry/>
</row>
<row>
<entry><filename>x11-wm</filename></entry>
<entry>X11 窗口管理器。</entry>
<entry/>
</row>
<row>
<entry><filename>xfce*</filename></entry>
<entry>与
<link xlink:href="http://www.xfce.org/">Xfce</link>
桌面环境有关的 port。</entry>
<entry/>
</row>
<row>
<entry><filename>zope*</filename></entry>
<entry><link xlink:href="http://www.zope.org/">Zope</link> 相关的支持。</entry>
<entry/>
</row>
</tbody>
</tgroup>
</informaltable>
</sect2>
<sect2 xml:id="choosing-categories">
<title>选择正确的分类</title>
<para>由于不少分类是重复的, 您通常在用哪个分类作为您
port 的主分类上做出选择。 下面有几条规则能帮您解决这个问题。
这是一个带优先级的表, 按优先级降序罗列:</para>
<itemizedlist>
<listitem>
<para>第一个分类必须是个物理的分类 (参阅
<link linkend="porting-categories">前面</link>)。
这对于制作包是必要的。
虚拟分类和物理分类可能在包制作完成后混合在一起。</para>
</listitem>
<listitem>
<para>对于特定语言的分类通常放在第一位。
例如, 如果您的 port 会安装一些 X11 的日文字体,
那么 <varname>CATEGORIES</varname>那行
就应该是 <filename>japanese x11-fonts</filename>。</para>
</listitem>
<listitem>
<para>有特定意义的分类应当被列在无特定意义的前面。
例如, HTML 编辑器应该是这样的 <filename>www
editors</filename>, 而不是其它的什么。 同样地,
您不应该列出 <filename>net</filename>, 如果 port 属于
<filename>irc</filename>、 <filename>mail</filename>、
<filename>news</filename>、
<filename>security</filename>, 或是 <filename>www</filename>,
因为 <filename>net</filename> 可以表示它们的超集。</para>
</listitem>
<listitem>
<para>只有当主要的分类是一门自然语言的时候,
<filename>x11</filename> 能被做为第二分类。
需要特别指出的是, 您不应把 X 的应用程序也归类为
<filename>x11</filename>。</para>
</listitem>
<listitem>
<para><application>Emacs</application>
模式应当于相应的应用程序放在同一个分类里, 而不是
<filename>editors</filename> 分类。 举例来说,
一个用于编辑某种编程语言源代码的
<application>Emacs</application>
模式应该被归为
<filename>lang</filename> 一类。
</para>
</listitem>
<listitem>
<para>需要安装可加载内核模块的 port 应在其 <varname>CATEGORIES</varname>
中归入虚拟分类 <filename>kld</filename>。
</para>
</listitem>
<listitem>
<para><filename>misc</filename>
分类的 port 不能有其它非虚拟的分类。
如果您在您的 <varname>CATEGORIES</varname>
里设了 <literal>misc</literal> 和另外的分类,
那意味着可以安全地删除 <literal>misc</literal>
并把 port 放到其它的子目录中了!</para>
</listitem>
<listitem>
<para>如果您的 port 确实不属于现有的分类,
才把它放到 <filename>misc</filename>。</para>
</listitem>
</itemizedlist>
<para>如果您不能确定使用哪个分类, 请在您提交的
&man.send-pr.1; 里加上一行注释,
这样我们就能在导入进 port 树之前讨论一下。
如果您是 committer, 发一份备忘到 &a.ports;
先讨论一下。 很多情况是新的 port 被加到错误的分类里,
然后又立即被移走。这会造成源代码库不必要和不良的膨胀。</para>
</sect2>
<sect2 xml:id="proposing-categories">
<title>如何提议建立新的分类</title>
<para>由于 Ports Collection 在持续增长, 已经引入了许多新的分类。
新的分类既可以是 <emphasis>虚拟的</emphasis> 分类 —
这些分类在整个 ports 目录中没有属于自己的子目录 —
或 <emphasis>物理的</emphasis> 分类 — 它们有自己的子目录。
接下来我们将讨论与建立新的物理分类有关的事项,
以便帮助您理解如何提议建立新的分类。</para>
<para>我们目前的做法是避免建立新的物理分类, 除非有非常多的 port
应被归入这一分类, 或者 port 属于某一特定的小团体 (例如,
与某种人类语言相关), 或两者皆是。</para>
<para>这样做的原因是这类修改会让 committer 和用户都不得不进行 <link xlink:href="&url.articles.committers-guide;/#ports">许多工作</link>
来在 Ports Collection 进行或追踪修改。 此外,
提议新的分类通常都会引起争论。 (可能这是因为关于某个分类是否
<quote>太大</quote> 一直没有非常一致的意见的缘故,
另一方面, 分类是否能够能够有助于浏览 (以及多少个分类是合适的),
等等, 也都是问题。)</para>
<para>下面是具体的步骤:</para>
<procedure>
<step>
<para>在 &a.ports; 提议新的分类。 您应提供建立新分类的详细依据,
包括为什么认为现有的分类不够, 以及希望移动位置的一系列 port
的名字。 (如果有尚在
<application>GNATS</application> 而未 commit 的 port,
也应一一列出。) 如果您是相关 port 的监护人或提交者,
说明这一情况可能有助于您的提议得到通过。</para>
</step>
<step>
<para>参与讨论。</para>
</step>
<step>
<para>如果有人支持您的建议, 应及时提交一个 PR,
其中包括提议 PR 的理由, 以及需要移动的 port 的列表。
理想情况下, 这个 PR 也应包含针对下列文件的补丁:</para>
<itemizedlist>
<listitem>
<para>进行 repocopy 之后对 <filename>Makefile</filename>
进行的修改</para>
</listitem>
<listitem>
<para>新分类的 <filename>Makefile</filename></para>
</listitem>
<listitem>
<para>旧分类的 <filename>Makefile</filename></para>
</listitem>
<listitem>
<para>依赖于旧 port 的 port 的
<filename>Makefile</filename></para>
</listitem>
<listitem>
<para>(此外, 作为一项加分因素,
您还可以按照 Committer 指南所介绍的流程,
提供一些其它需要修改的文件。)</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>由于这是一项影响 ports 基础设施的变动,
它不仅涉及 repo-copy 的使用,
而且也可能会影响联编集群的回归测试操作,
因此这类 PR 应分派给 &a.portmgr;。</para>
</step>
<step>
<para>如果这一 PR 得到批准, 某个 committer 将按照在
<link xlink:href="&url.articles.committers-guide;/article.html#PORTS">
Committer 指南</link> 中所介绍的步骤来完成余下的工作。</para>
</step>
</procedure>
<para>提议新的虚拟分类和上述过程类似, 但会容易许多,
因为不需要实际地移动任何 port。 这种情况下, PR 应附带的补丁,
就只需要修改影响到的 port 的 Makefile, 以便在其中的
<varname>CATEGORIES</varname> 中加入新的分类了。</para>
</sect2>
<sect2 xml:id="proposing-reorg">
<title>如何提议对分类进行重新组织</title>
<para>有些时候会有一些人提议重新将分类组织为 2-层 或某种基于关键字的结构。
目前为止, 还没有进行任何相关的改变, 因为尽管这些修改比较容易完成,
但修改整个 Ports Collection 所需要进行的工作, 至少也是令人生畏的。
在发表您的观点之前, 请阅读在邮件列表存档中历史上所进行过的提议;
此外, 您也会被要求提供一个可用的原形。</para>
</sect2>
</sect1>
<sect1 xml:id="makefile-distfiles">
<title>源码包文件</title>
<para>在 <filename>Makefile</filename> 中的第二部分是描述用于联编 port
所必需下载的文件, 以及到什么地方去下载它们。</para>
<sect2>
<title><varname>DISTVERSION/DISTNAME</varname> (源码包版本号/名称)</title>
<para><varname>DISTNAME</varname> 是作者称呼您所 port 软件的名字。
<varname>DISTNAME</varname> 的默认值是
<literal>${PORTNAME}-${PORTVERSION}</literal>,
因此只有在需要时才应手工指定。
<varname>DISTNAME</varname> 只在两个地方用到。 第一处是源码包文件列表
(<varname>DISTFILES</varname>), 其默认值是
<varname>${DISTNAME}</varname><varname>${EXTRACT_SUFX}</varname>。
第二处是源码包应被展开到的目录名,
即 <varname>WRKSRC</varname> 所指定的目录,
其默认值是 <filename>work/${DISTNAME}</filename>。</para>
<para>某些软件作者发布源码包的时候并不采取
<literal>${PORTNAME}-${PORTVERSION}</literal> 这样的模式,
这可以通过设置 <varname>DISTVERSION</varname> 来自动处理。
<varname>PORTVERSION</varname> 和 <varname>DISTNAME</varname>
会自动地展开, 当然, 也可以改掉它。 下表给出了一些例子:</para>
<informaltable frame="none" pgwide="1">
<tgroup cols="2">
<thead>
<row>
<entry><varname>DISTVERSION</varname></entry>
<entry><varname>PORTVERSION</varname></entry>
</row>
</thead>
<tbody>
<row>
<entry>0.7.1d</entry>
<entry>0.7.1.d</entry>
</row>
<row>
<entry>10Alpha3</entry>
<entry>10.a3</entry>
</row>
<row>
<entry>3Beta7-pre2</entry>
<entry>3.b7.p2</entry>
</row>
<row>
<entry>8:f_17</entry>
<entry>8f.17</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<note>
<para><varname>PKGNAMEPREFIX</varname> 和
<varname>PKGNAMESUFFIX</varname> 并不影响
<varname>DISTNAME</varname>。 此外还应注意
<varname>WRKSRC</varname> 等于
<filename>work/${PORTNAME}-${PORTVERSION}</filename>,
而源代码的压缩包则可能是
<varname>${PORTNAME}-${PORTVERSION}${EXTRACT_SUFX}</varname>
以外的其它名字。 一般情况下应该保持 <varname>DISTNAME</varname>
不变 — 更好的方法是定义
<varname>DISTFILES</varname> 而不是同时设置
<varname>DISTNAME</varname> 和 <varname>WRKSRC</varname>
(可能还有 <varname>EXTRACT_SUFX</varname>)。</para>
</note>
</sect2>
<sect2>
<title><varname>MASTER_SITES</varname> (主流下载站点)</title>
<para>记录 FTP/HTTP-URL 指向 <varname>MASTER_SITES</varname>
中原始压缩档的目录部分。 不要忘了结尾的斜线
(<filename>/</filename>)!</para>
<para><command>make</command> 宏将尝试使用
<varname>FETCH</varname> 来抓取所指定的源码包文件,
如果无法在本地系统中找到这些文件的话。</para>
<para>建议您指定多个镜像站点, 最好是在不同的大洲上的。
这样将有效地防止由于大范围网络问题所导致无法下载的问题。
我们甚至打算增加自动检测距离最近的站点并从那里下载的功能;
使用多个站点是这样做的重要一步。</para>
<para>如果原始的源码包可以从比较流行的软件下载站点,
例如 SourceForge、 GNU 或是 Perl CPAN 等等来获得,
您可能会希望使用类似
<varname>MASTER_SITE_<replaceable>*</replaceable></varname>
这样的缩写来表示它们
(例如 <varname>MASTER_SITE_SOURCEFORGE</varname>、
<varname>MASTER_SITE_GNU</varname> 以及
<varname>MASTER_SITE_PERL_CPAN</varname>)。 只需将
<varname>MASTER_SITES</varname> 设为这些变量, 并使用
<varname>MASTER_SITE_SUBDIR</varname> 来指定路径就可以了。
下面是一个例子:</para>
<programlisting>MASTER_SITES= ${MASTER_SITE_GNU}
MASTER_SITE_SUBDIR= make</programlisting>
<para>此外, 您还可以用更为简略的格式:</para>
<programlisting>MASTER_SITES= GNU/make</programlisting>
<para>这些变量是在
<filename>/usr/ports/Mk/bsd.sites.mk</filename> 中定义的。
新项目会随时增加, 因此在您提交 port 之前,
应先看一看这个文件的最新版本。</para>
<para>针对常用软件下载站的许多 <emphasis>暗黑魔法</emphasis> 宏,
还能够自动判断目录的结构。 对于这些站点,
只要使用与之对应的缩写, 系统便会自动为您生成相关的子目录配置。</para>
<programlisting>MASTER_SITES= SF</programlisting>
<para>如果系统猜测的路径不对, 则可以使用下面这样的配置来替换。</para>
<programlisting>MASTER_SITES= SF/stardict/WyabdcRealPeopleTTS/${PORTVERSION}</programlisting>
<table frame="none">
<title>常用的魔术 <varname>MASTER_SITES</varname> 宏</title>
<tgroup cols="2">
<thead>
<row>
<entry>宏</entry>
<entry>自动猜测的子目录</entry>
</row>
</thead>
<tbody>
<row>
<entry><varname>APACHE_JAKARTA</varname></entry>
<entry><varname>/dist/jakarta/${PORTNAME:S,-,,/,}/source</varname></entry>
</row>
<row>
<entry><varname>BERLIOS</varname></entry>
<entry><varname>/${PORTNAME:L}</varname></entry>
</row>
<row>
<entry><varname>CHEESESHOP</varname></entry>
<entry><varname>/packages/source/source/${DISTNAME:C/(.).*/\1/}/${DISTNAME:C/(.*)-[0-9].*/\1/}</varname></entry>
</row>
<row>
<entry><varname>DEBIAN</varname></entry>
<entry><varname>/debian/pool/main/${PORTNAME:C/^((lib)?.).*$/\1/}/${PORTNAME}</varname></entry>
</row>
<row>
<entry><varname>GCC</varname></entry>
<entry><varname>/pub/gcc/releases/${DISTNAME}</varname></entry>
</row>
<row>
<entry><varname>GNOME</varname></entry>
<entry><varname>/pub/GNOME/sources/${PORTNAME}/${PORTVERSION:C/^([0-9]+\.[0-9]+).*/\1/}</varname></entry>
</row>
<row>
<entry><varname>GNU</varname></entry>
<entry><varname>/gnu/${PORTNAME}</varname></entry>
</row>
<row>
<entry><varname>MOZDEV</varname></entry>
<entry><varname>/pub/mozdev/${PORTNAME:L}</varname></entry>
</row>
<row>
<entry><varname>PERL_CPAN</varname></entry>
<entry><varname>/pub/CPAN/modules/by-module/${PORTNAME:C/-.*//}</varname></entry>
</row>
<row>
<entry><varname>PYTHON</varname></entry>
<entry><varname>/ftp/python/${PYTHON_PORTVERSION:C/rc[0-9]//}</varname></entry>
</row>
<row>
<entry><varname>RUBYFORGE</varname></entry>
<entry><varname>/${PORTNAME:L}</varname></entry>
</row>
<row>
<entry><varname>SAVANNAH</varname></entry>
<entry><varname>/${PORTNAME:L}</varname></entry>
</row>
<row>
<entry><varname>SF</varname></entry>
<entry><varname>/project/${PORTNAME:L}/${PORTNAME:L}/${PORTVERSION}</varname></entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2>
<title><varname>EXTRACT_SUFX</varname> (压缩包所用的扩展名)</title>
<para>如果您有一个源码包文件,
而它使用了某种怪异的扩展名来表达压缩方法, 应设置
<varname>EXTRACT_SUFX</varname>。</para>
<para>例如, 如果源码包文件的名字是
<filename>foo.tgz</filename> 而非更为一般的
<filename>foo.tar.gz</filename>, 您应写上:</para>
<programlisting>DISTNAME= foo
EXTRACT_SUFX= .tgz</programlisting>
<para><varname>USE_BZIP2</varname> 和 <varname>USE_ZIP</varname>
变量会自动根据需要将 <varname>EXTRACT_SUFX</varname> 设置为
<literal>.tar.bz2</literal> 或 <literal>.zip</literal>。
如果这两个都没设置, 则 <varname>EXTRACT_SUFX</varname> 的
默认值将是 <literal>.tar.gz</literal>。</para>
<note>
<para>任何时候都不需要同时设置 <varname>EXTRACT_SUFX</varname> 和
<varname>DISTFILES</varname>.</para>
</note>
</sect2>
<sect2>
<title><varname>DISTFILES</varname> (全部源代码包)</title>
<para>有些时候所下载的文件名字和 port 的名字没有任何联系。
例如, 可能是 <filename>source.tar.gz</filename>,
或者与此类似的其它名字。 也有一些其它的应用软件,
它们的源代码可能被存放到了不同的压缩包中, 而且全都需要下载。</para>
<para>如果遇到这种情况, 可以将 <varname>DISTFILES</varname>
设置为以空格分隔的一组需要下载的文件列表。</para>
<programlisting>DISTFILES= source1.tar.gz source2.tar.gz</programlisting>
<para>如果没有予以明确的设置, <varname>DISTFILES</varname> 的默认值将是
<literal>${DISTNAME}${EXTRACT_SUFX}</literal>。</para>
</sect2>
<sect2>
<title><varname>EXTRACT_ONLY</varname> (只解压缩部分源文件)</title>
<para>如果只有一部分 <varname>DISTFILES</varname> 需要解压缩
— 例如, 其中的一个是源代码, 而其它则是未压缩的文档 —
此时应把那些需要解压缩的文件加到
<varname>EXTRACT_ONLY</varname> 中。</para>
<programlisting>DISTFILES= source.tar.gz manual.html
EXTRACT_ONLY= source.tar.gz</programlisting>
<para>如果 <varname>DISTFILES</varname> 中 <emphasis>没有</emphasis>
需要解压缩的文件, 则应将 <varname>EXTRACT_ONLY</varname> 设为空串。</para>
<programlisting>EXTRACT_ONLY=</programlisting>
</sect2>
<sect2 xml:id="porting-patchfiles">
<title><varname>PATCHFILES</varname> (通过下载得到的补丁文件)</title>
<para>如果您的 port 需要来自 FTP 或 HTTP 的一些额外的补丁,
应将 <varname>PATCHFILES</varname> 设置为这些文件的名字,
并将 <varname>PATCH_SITES</varname> 指向包含这些文件的目录的 URL
(格式与 <varname>MASTER_SITES</varname> 相同)。</para>
<para>如果这些补丁, 由于包含了其它的目录名,
而导致它们不是相对于源代码目录的顶级目录
(也就是 <varname>WRKSRC</varname>) 的话,
就需要相应地设置 <varname>PATCH_DIST_STRIP</varname> 了。
例如, 如果补丁中所有的目录名前面都有一个多余的
<literal>foozolix-1.0/</literal>, 就应设置
<literal>PATCH_DIST_STRIP=-p1</literal>。</para>
<para>不需要担心补丁文件本身是否是压缩的; 如果文件名以
<filename>.gz</filename> or <filename>.Z</filename>
结尾, 系统会自动解压缩。</para>
<para>如果补丁是同某些其它文件, 例如文档, 一同以 gzip 压缩的 tar
格式发布的, 就不能简单地使用
<varname>PATCHFILES</varname> 了。 这种情况下,
您应将这些补丁包的文件和位置加入到
<varname>DISTFILES</varname> 和 <varname>MASTER_SITES</varname>
中。 然后, 用 <varname>EXTRA_PATCHES</varname> 变量来指出这些文件,
这样 <filename>bsd.port.mk</filename> 就会自动地为您应用这些补丁了。
需要特别注意的是, <emphasis>不要</emphasis> 将补丁文件复制到
<varname>PATCHDIR</varname> 目录中 — 这个目录可能是不可写的。</para>
<note>
<para>压缩包会以同源代码一样的方式解压缩, 因此不需要自行完成解压缩操作,
并复制补丁文件。 如果您一定要这样做, 就要注意,
不要让解压缩出来的文件覆盖先前已经存在的文件。
此外, 这么做还需要手工增加命令,
以便在 <buildtarget>pre-clean</buildtarget> target
中删除这些复制出来的文件。</para>
</note>
</sect2>
<sect2 xml:id="porting-master-sites-n">
<title>来自不同站点的多个源代码包或补丁文件
(<literal>MASTER_SITES:n</literal>)</title>
<para>(这一节在某种程度上应被视作 <quote>进阶话题</quote>;
刚开始阅读这份文档的读者可能会希望先跳过这一部分)。</para>
<para>这一节提供了被称作 <literal>MASTER_SITES:n</literal> 和
<literal>MASTER_SITES_NN</literal> 的下载控制机制。
这里我们把它们称为 <literal>MASTER_SITES:n</literal>。</para>
<para>首先给出一些背景。 OpenBSD 在其 <varname>DISTFILES</varname> 和
<varname>PATCHFILES</varname> 变量中提供了一个很棒的功能,
即, 允许这些文件和补丁拥有 <literal>:n</literal>
后缀, 其中 <literal>n</literal> 可以使用
<literal>[0-9]</literal>, 来表达组。 例如:</para>
<programlisting>DISTFILES= alpha:0 beta:1</programlisting>
<para>在 OpenBSD 中, 源码包文件 <filename>alpha</filename>
应被关联到变量
<varname>MASTER_SITES0</varname> 而不是公共的
<varname>MASTER_SITES</varname> 变量上; 而
<filename>beta</filename> 则应关联到
<varname>MASTER_SITES1</varname> 上。</para>
<para>这是一个很有意思的功能,
它可以避免无休止地搜索正确的下载站点的过程。</para>
<para>想象 <varname>DISTFILES</varname> 中指定了 2 个文件,
而 <varname>MASTER_SITES</varname> 包含了 20 个站点的情形,
这其中许多站点慢如蜗牛, 而 <filename>beta</filename> 可以在
<varname>MASTER_SITES</varname> 的所有站点找到, 而
<filename>alpha</filename> 只能在第 20 个上面找到。
如果监护人了解这一点, 那么检查所有的站点无疑是在浪费时间,
不是吗? 这显然不是开始一个愉快周末的好办法!</para>
<para>现在您有了一个感性的认识了, 想象一下
<varname>DISTFILES</varname> 和更多的
<varname>MASTER_SITES</varname>。 显然, 我们的
<quote>distfiles 调查员先生</quote>
会感谢您减少他浪费在等待下载上所耗费的时间。</para>
<para>下一节中, 将按照 FreeBSD 对上述想法的实现来加以阐释。
我们对 OpenBSD 所提出的概念进行了一些改进。</para>
<sect3>
<title>简化信息</title>
<para>这一节将介绍如何迅速地对从不同的站点以及子目录下载多个源码包和补丁进行精确的控制。
这里, 我们将描述 <literal>MASTER_SITES:n</literal> 的一种简化用法。
对于多数情况而言这样做是足够的。 然而, 如果您需要更多信息,
还需要参考下面的几节。</para>
<para>一些应用程序需要从多个站点下载不同的源码包。 例如,
<application>Ghostscript</application> 包括了程序核心本身,
以及大量的驱动文件, 以及则取决于用户的打印机品牌和型号的驱动程序。
某些驱动文件已经随程序核心附带, 但也有很多需要从其它站点下载。</para>
<para>为了适应这种需要, 每一个
<varname>DISTFILES</varname> 项应跟随一个冒号,
以及一个 <quote>标签名</quote>。 在
<varname>MASTER_SITES</varname> 的每个站点也应跟随冒号和标签名,
以便指定从哪个网站下载源码包文件。</para>
<para>例如, 考虑一个将源代码包分为两部分,
即 <filename>source1.tar.gz</filename>
和 <filename>source2.tar.gz</filename> 的软件,
它必须从两个不同的站点下载。 port 的
<filename>Makefile</filename> 应包括类似
<xref linkend="ports-master-sites-n-example-simple-use-one-file-per-site"/>
的配置。</para>
<example xml:id="ports-master-sites-n-example-simple-use-one-file-per-site">
<title>简化的 <literal>MASTER_SITES:n</literal>
用法, 每个文件来自一个站点</title>
<programlisting>MASTER_SITES= ftp://ftp.example1.com/:source1 \
ftp://ftp.example2.com/:source2
DISTFILES= source1.tar.gz:source1 \
source2.tar.gz:source2</programlisting>
</example>
<para>多个源码包可以使用同一个标签。 继续前面的例子,
假定增加了第三个源码包, <filename>source3.tar.gz</filename>,
应从 <systemitem>ftp.example2.com</systemitem> 下载。
<filename>Makefile</filename> 的这部分应写成
<xref linkend="ports-master-sites-n-example-simple-use-more-than-one-file-per-site"/>
的样子。</para>
<example xml:id="ports-master-sites-n-example-simple-use-more-than-one-file-per-site">
<title>简化的 <literal>MASTER_SITES:n</literal> 用法,
其中同一个站点上提供了不止一个文件</title>
<programlisting>MASTER_SITES= ftp://ftp.example1.com/:source1 \
ftp://ftp.example2.com/:source2
DISTFILES= source1.tar.gz:source1 \
source2.tar.gz:source2 \
source3.tar.gz:source2</programlisting>
</example>
</sect3>
<sect3>
<title>深入介绍</title>
<para>前面的例子无法满足您的需求? 这一节,
我们将详细介绍 <literal>MASTER_SITES:n</literal>
的精细控制是如何工作的, 以及如何修改您的 port
来使用它们。</para>
<orderedlist>
<listitem>
<para>元素可以包含
<literal>:n</literal> 这样的后缀, 其中
<replaceable>n</replaceable> 是
<literal>[^:,]+</literal>, 概念上即
<replaceable>n</replaceable> 可以取任意数字或字母,
但我们目前将其限定为
<literal>[a-zA-Z_][0-9a-zA-Z_]+</literal>。</para>
<para>此外, 字符串匹配时对大小写是敏感的;
换言之, <literal>n</literal> 与
<literal>N</literal> 不同。</para>
<para>但是, 由于表达特殊的意义, 下列单词不能用于后缀:
<literal>default</literal>、 <literal>all</literal> 和
<literal>ALL</literal> (它们会在 <xref linkend="porting-master-sites-n-what-changes-in-port-targets"/>
中介绍的部分用到)。 此外, <literal>DEFAULT</literal>
是一个有特殊用途的词 (请参见 <xref linkend="porting-master-sites-n-DEFAULT-group"/>)。</para>
</listitem>
<listitem>
<para>后缀为 <literal>:n</literal>
的项目属于 <literal>n</literal> 组, 而
<literal>:m</literal> 属于
<literal>m</literal> 组, 依此类推。</para>
</listitem>
<listitem xml:id="porting-master-sites-n-DEFAULT-group">
<para>没有后缀的元素是无组的, 也就是它们都属于那个特殊的
<literal>DEFAULT</literal> 组。 给元素加入
<literal>DEFAULT</literal> 后缀通常是多余的,
除非您有同时属于 <literal>DEFAULT</literal> 和其它组的元素
(参见 <xref linkend="porting-master-sites-n-comma-operator"/>)。</para>
<para>下面的例子是等价的, 但通常应适用第一个:</para>
<programlisting>MASTER_SITES= alpha
MASTER_SITES= alpha:DEFAULT</programlisting>
</listitem>
<listitem>
<para>组之间不是互斥的, 同一元素可以同时隶属于多个组,
而组则可以为空或者有任意多个元素。 同一组中的重复元素,
并不会被自动消去。</para>
</listitem>
<listitem xml:id="porting-master-sites-n-comma-operator">
<para>如果希望同一元素同时属于多个组, 可以用逗号
(<literal>,</literal>) 分开。</para>
<para>这种办法可以避免仅为指定不同的组而多次重复同一元素。 例如
<literal>:m,n,o</literal> 表示这个元素同时属于
<literal>m</literal>、 <literal>n</literal> 和 <literal>o</literal>
这三组。</para>
<para>下面这些写法都是等价的, 但只推荐使用最后一种:</para>
<programlisting>MASTER_SITES= alpha alpha:SOME_SITE
MASTER_SITES= alpha:DEFAULT alpha:SOME_SITE
MASTER_SITES= alpha:SOME_SITE,DEFAULT
MASTER_SITES= alpha:DEFAULT,SOME_SITE</programlisting>
</listitem>
<listitem>
<para>同一组中的所有站点, 会根据
<varname>MASTER_SORT_AWK</varname> 排序。
在 <varname>MASTER_SITES</varname> 和
<varname>PATCH_SITES</varname> 中的组也会进行排序。</para>
</listitem>
<listitem xml:id="porting-master-sites-n-group-semantics">
<para>在 <varname>MASTER_SITES</varname>、
<varname>PATCH_SITES</varname>、
<varname>MASTER_SITE_SUBDIR</varname>、
<varname>PATCH_SITE_SUBDIR</varname>、
<varname>DISTFILES</varname>, 以及
<varname>PATCHFILES</varname> 中, 都可以使用组,
其语法为:</para>
<orderedlist>
<listitem>
<para>所有 <varname>MASTER_SITES</varname>、
<varname>PATCH_SITES</varname>、
<varname>MASTER_SITE_SUBDIR</varname> 以及
<varname>PATCH_SITE_SUBDIR</varname> 的元素, 都必须以
<literal>/</literal> 字符结尾。 如果有元素属于某些组,
则组后缀
<literal>:n</literal>
必须出现在终结符
<literal>/</literal> 之后。
<literal>MASTER_SITES:n</literal> 机制依赖于
<literal>/</literal> 的存在, 以避免在 <literal>:n</literal>
是元素一部分, 而 <literal>:n</literal> 同时又表示组
<literal>n</literal> 时发生混淆。 为了兼容性的考虑,
因为之前 <literal>/</literal> 终结符在
<varname>MASTER_SITE_SUBDIR</varname> 和
<varname>PATCH_SITE_SUBDIR</varname> 元素中都不是必需的,
如果后缀所紧跟的字符不是 <literal>/</literal>,
则 <literal>:n</literal> 将被认为是元素的一部分,
而不被当作组后缀, 即使元素拥有 <literal>:n</literal>
后缀。 请参见 <xref linkend="ports-master-sites-n-example-detailed-use-master-site-subdir"/>
和 <xref linkend="ports-master-sites-n-example-detailed-use-complete-example-master-sites"/>
以了解进一步的细节。</para>
<example xml:id="ports-master-sites-n-example-detailed-use-master-site-subdir">
<title>在 <varname>MASTER_SITE_SUBDIR</varname> 中
<literal>MASTER_SITES:n</literal> 的详细用法</title>
<programlisting>MASTER_SITE_SUBDIR= old:n new/:NEW</programlisting>
<itemizedlist>
<listitem>
<para>组 <literal>DEFAULT</literal> 中的目录
-> old:n</para>
</listitem>
<listitem>
<para>组 <literal>NEW</literal> 中的目录
-> new</para>
</listitem>
</itemizedlist>
</example>
<example xml:id="ports-master-sites-n-example-detailed-use-complete-example-master-sites">
<title>用到逗号分隔符、 多个文件, 多个站点和
不同子目录的 <literal>MASTER_SITES:n</literal>
详细用法</title>
<programlisting>MASTER_SITES= http://site1/%SUBDIR%/ http://site2/:DEFAULT \
http://site3/:group3 http://site4/:group4 \
http://site5/:group5 http://site6/:group6 \
http://site7/:DEFAULT,group6 \
http://site8/%SUBDIR%/:group6,group7 \
http://site9/:group8
DISTFILES= file1 file2:DEFAULT file3:group3 \
file4:group4,group5,group6 file5:grouping \
file6:group7
MASTER_SITE_SUBDIR= directory-trial:1 directory-n/:groupn \
directory-one/:group6,DEFAULT \
directory</programlisting>
<para>前述的例子的结果是下述的对于下载行为的精细控制。
站点的列表按照使用的顺序给出。</para>
<itemizedlist>
<listitem>
<para><filename>file1</filename> 将从</para>
<itemizedlist>
<listitem>
<para><varname>MASTER_SITE_OVERRIDE</varname></para>
</listitem>
<listitem>
<para>http://site1/directory-trial:1/</para>
</listitem>
<listitem>
<para>http://site1/directory-one/</para>
</listitem>
<listitem>
<para>http://site1/directory/</para>
</listitem>
<listitem>
<para>http://site2/</para>
</listitem>
<listitem>
<para>http://site7/</para>
</listitem>
<listitem>
<para><varname>MASTER_SITE_BACKUP</varname></para>
</listitem>
</itemizedlist>
<para>下载。</para>
</listitem>
<listitem>
<para><filename>file2</filename> 将和
<filename>file1</filename> 以同样的方式下载,
因为它们属于同一组</para>
<itemizedlist>
<listitem>
<para><varname>MASTER_SITE_OVERRIDE</varname></para>
</listitem>
<listitem>
<para>http://site1/directory-trial:1/</para>
</listitem>
<listitem>
<para>http://site1/directory-one/</para>
</listitem>
<listitem>
<para>http://site1/directory/</para>
</listitem>
<listitem>
<para>http://site2/</para>
</listitem>
<listitem>
<para>http://site7/</para>
</listitem>
<listitem>
<para><varname>MASTER_SITE_BACKUP</varname></para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para><filename>file3</filename> 将从</para>
<itemizedlist>
<listitem>
<para><varname>MASTER_SITE_OVERRIDE</varname></para>
</listitem>
<listitem>
<para>http://site3/</para>
</listitem>
<listitem>
<para><varname>MASTER_SITE_BACKUP</varname></para>
</listitem>
</itemizedlist>
<para>下载。</para>
</listitem>
<listitem>
<para><filename>file4</filename> 将从</para>
<itemizedlist>
<listitem>
<para><varname>MASTER_SITE_OVERRIDE</varname></para>
</listitem>
<listitem>
<para>http://site4/</para>
</listitem>
<listitem>
<para>http://site5/</para>
</listitem>
<listitem>
<para>http://site6/</para>
</listitem>
<listitem>
<para>http://site7/</para>
</listitem>
<listitem>
<para>http://site8/directory-one/</para>
</listitem>
<listitem>
<para><varname>MASTER_SITE_BACKUP</varname></para>
</listitem>
</itemizedlist>
<para>下载。</para>
</listitem>
<listitem>
<para><filename>file5</filename> 将从</para>
<itemizedlist>
<listitem>
<para><varname>MASTER_SITE_OVERRIDE</varname></para>
</listitem>
<listitem>
<para><varname>MASTER_SITE_BACKUP</varname></para>
</listitem>
</itemizedlist>
<para>下载。</para>
</listitem>
<listitem>
<para><filename>file6</filename> 将从</para>
<itemizedlist>
<listitem>
<para><varname>MASTER_SITE_OVERRIDE</varname></para>
</listitem>
<listitem>
<para>http://site8/</para>
</listitem>
<listitem>
<para><varname>MASTER_SITE_BACKUP</varname></para>
</listitem>
</itemizedlist>
<para>下载。</para>
</listitem>
</itemizedlist>
</example>
</listitem>
</orderedlist>
</listitem>
<listitem>
<para>如何对来自
<filename>bsd.sites.mk</filename> 的特殊变量, 例如
<varname>MASTER_SITE_SOURCEFORGE</varname> 进行分组?</para>
<para>参见 <xref linkend="ports-master-sites-n-example-detailed-use-master-site-sourceforge"/>。</para>
<example xml:id="ports-master-sites-n-example-detailed-use-master-site-sourceforge">
<title><varname>MASTER_SITE_SOURCEFORGE</varname> 中
<literal>MASTER_SITES:n</literal> 的详细用法</title>
<programlisting>MASTER_SITES= http://site1/ ${MASTER_SITE_SOURCEFORGE:S/$/:sourceforge,TEST/}
DISTFILES= something.tar.gz:sourceforge</programlisting>
</example>
<para><filename>something.tar.gz</filename> 将从所有
<varname>MASTER_SITE_SOURCEFORGE</varname> 中的站点下载。</para>
</listitem>
<listitem>
<para>如何与 <varname>PATCH*</varname> 变量连用?</para>
<para>前面的例子介绍的都是
<varname>MASTER*</varname> 变量,
但对于 <varname>PATCH*</varname> 也是完全一样的,
它们在 <xref linkend="ports-master-sites-n-example-detailed-use-patch-sites"/>
有所介绍。</para>
<example xml:id="ports-master-sites-n-example-detailed-use-patch-sites">
<title>简化的 <varname>PATCH_SITES</varname> 中的
<literal>MASTER_SITES:n</literal> 用法。</title>
<programlisting>PATCH_SITES= http://site1/ http://site2/:test
PATCHFILES= patch1:test</programlisting>
</example>
</listitem>
</orderedlist>
</sect3>
<sect3>
<title>会改变 ports 的哪些行为? 哪些不会?</title>
<orderedlist numeration="lowerroman">
<listitem>
<para>所有普通的 ports 的行为都会保持不变。
<literal>MASTER_SITES:n</literal> 功能的代码,
只有在某些元素包含了前述, 特别是 <xref linkend="porting-master-sites-n-group-semantics"/>
中所提及语法的
<literal>:n</literal> 后缀时,
才会启用。</para>
</listitem>
<listitem xml:id="porting-master-sites-n-what-changes-in-port-targets">
<para>不受影响的 port target:
<buildtarget>checksum</buildtarget>、
<buildtarget>makesum</buildtarget>、
<buildtarget>patch</buildtarget>、
<buildtarget>configure</buildtarget>、
<buildtarget>build</buildtarget>, 等等。
显然, <buildtarget>do-fetch</buildtarget>、
<buildtarget>fetch-list</buildtarget>、
<buildtarget>master-sites</buildtarget> 和
<buildtarget>patch-sites</buildtarget> 的行为会发生变化。</para>
<itemizedlist>
<listitem>
<para><buildtarget>do-fetch</buildtarget>: 会按照新的、
带有组后缀的
<varname>DISTFILES</varname> 和
<varname>PATCHFILES</varname> 在
<varname>MASTER_SITES</varname> 和
<varname>PATCH_SITES</varname> 所匹配的组元素, 以及
<varname>MASTER_SITE_SUBDIR</varname> 和
<varname>PATCH_SITE_SUBDIR</varname> 来进行。
请参见 <xref linkend="ports-master-sites-n-example-detailed-use-complete-example-master-sites"/>。</para>
</listitem>
<listitem>
<para><buildtarget>fetch-list</buildtarget>: 和旧式的
<buildtarget>fetch-list</buildtarget> 类似, 但以同
<buildtarget>do-fetch</buildtarget> 相似的方式处理组。</para>
</listitem>
<listitem>
<para><buildtarget>master-sites</buildtarget> 和
<buildtarget>patch-sites</buildtarget>:
(与旧版本不兼容) 仅返回组 <literal>DEFAULT</literal>
的元素; 事实上, 它们会执行
<buildtarget>master-sites-default</buildtarget> 和
<buildtarget>patch-sites-default</buildtarget>
这两个 target。</para>
<para>更进一步, 使用
<buildtarget>master-sites-all</buildtarget> 或
<buildtarget>patch-sites-all</buildtarget> 这两个
target 之一, 要比直接检查
<buildtarget>MASTER_SITES</buildtarget> 或
<buildtarget>PATCH_SITES</buildtarget> 更好。
此外, 未来版本可能不再保证直接检查能够正确工作。
请参见 <xref linkend="porting-master-sites-n-new-port-targets-master-sites-all"/>
以了解关于这些新 target 的更多技术细节。</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>port 中的新 target</para>
<orderedlist>
<listitem>
<para>一系列
<buildtarget>master-sites-<replaceable>n</replaceable></buildtarget>
和
<buildtarget>patch-sites-<replaceable>n</replaceable></buildtarget>
target 可以分别用来列出 <varname>MASTER_SITES</varname> 和
<varname>PATCH_SITES</varname> 中的 <replaceable>n</replaceable>
组的内容。 例如,
<buildtarget>master-sites-DEFAULT</buildtarget> 和
<buildtarget>patch-sites-DEFAULT</buildtarget> 都会返回
<literal>DEFAULT</literal> 组的内容, 而
<buildtarget>master-sites-test</buildtarget> 和
<buildtarget>patch-sites-test</buildtarget> 则返回
<literal>test</literal> 组的内容, 等等。</para>
</listitem>
<listitem xml:id="porting-master-sites-n-new-port-targets-master-sites-all">
<para>新增的
<buildtarget>master-sites-all</buildtarget> 和
<buildtarget>patch-sites-all</buildtarget> 这两个 target,
会完成先前
<buildtarget>master-sites</buildtarget> 和
<buildtarget>patch-sites</buildtarget> 所做的工作。
它们会返回所有组的元素, 就像这些元素都属于同一组一样,
并且会列出与
<varname>MASTER_SITE_BACKUP</varname> 或
<varname>MASTER_SITE_OVERRIDE</varname> 中在
<varname>DISTFILES</varname> 或
<varname>PATCHFILES</varname> 中指定的同样多个; 分别对于
<buildtarget>master-sites-all</buildtarget> 和
<buildtarget>patch-sites-all</buildtarget>。</para>
</listitem>
</orderedlist>
</listitem>
</orderedlist>
</sect3>
</sect2>
<sect2>
<title><varname>DIST_SUBDIR</varname> (独立的源码包子目录)</title>
<para>避免让您的 port 使
<filename>/usr/ports/distfiles</filename> 陷入混乱。
如果您的 port 需要下载很多文件, 或者需要下载可能与其它 port
的源文件名冲突的文件 (例如,
<filename>Makefile</filename>), 则应将 <varname>DIST_SUBDIR</varname>
设置为 port 的名字 (通常可以用 <literal>${PORTNAME}</literal> 或
<literal>${PKGNAMEPREFIX}${PORTNAME}</literal>)。 这将把
<varname>DISTDIR</varname> 从默认的
<filename>/usr/ports/distfiles</filename> 改为
<filename>/usr/ports/distfiles/DIST_SUBDIR</filename>,
并将与您的 port 有关的文件放到那个目录中。</para>
<para>此外, 它也会在备份文件主服务器 <filename>ftp.FreeBSD.org</filename>
上查找同一子目录下的文件 (直接在您的
<varname>Makefile</varname> 中设置 <varname>DISTDIR</varname>
则不会有这样的效果, 因此您应使用
<varname>DIST_SUBDIR</varname>。)</para>
<note>
<para>这一设置并不影响您在 <filename>Makefile</filename>
中定义的 <varname>MASTER_SITES</varname>。</para>
</note>
</sect2>
<sect2>
<title><varname>ALWAYS_KEEP_DISTFILES</varname> (一直保存源码包)</title>
<para>如果您的 port 采用的是预编译的包,
但却采用了某种要求源代码必须与预编译版本一同提供的授权,
例如 GPL, 则应使用 <varname>ALWAYS_KEEP_DISTFILES</varname>
来告诉 &os; 联编集群保留一份在 <varname>DISTFILES</varname>
中文件的副本。 一般来说这些 port 的用户并不需要这些文件,
因此, 只在定义了
<varname>PACKAGE_BUILDING</varname> 符的时候,
才将源代码包文件加入 <varname>DISTFILES</varname> 是个好主意。
</para>
<example xml:id="ports-master-sites-n-example-always-keep-distfiles">
<title>如何使用 <varname>ALWAYS_KEEP_DISTFILES</varname>。</title>
<programlisting>.if defined(PACKAGE_BUILDING)
DISTFILES+= <replaceable>foo.tar.gz</replaceable>
ALWAYS_KEEP_DISTFILES= yes
.endif</programlisting>
</example>
<para>当您在 <varname>DISTFILES</varname> 加入其它文件时,
请务必确保这些文件也出现在了 <filename>distinfo</filename> 中。
此外, 这些额外的文件通常也会展开到
<varname>WRKDIR</varname> 中, 对于某些 ports,
这可能导致一些不希望的副作用, 因而需要进行特别的处理。</para>
</sect2>
</sect1>
<sect1 xml:id="makefile-maintainer">
<title><varname>MAINTAINER</varname> (监护人)</title>
<para>请在此处写上您的电子邮件地址。 <!-- smiley
--><emphasis>:-)</emphasis></para>
<para>需要注意一点, <varname>MAINTAINER</varname>
变量的值只能是一个不包括注释部分的电子邮件地址,
其格式应为 <literal>user@hostname.domain</literal>。
请不要在此处写任何说明性的文字, 例如您的真实姓名 — 这会给
<filename>bsd.port.mk</filename> 带来麻烦。</para>
<para>监护人有责任保持 port 随时更新, 并确保其能够正确地运行。
详细的 port 监护人职责说明, 请参见 <link xlink:href="&url.articles.contributing-ports;/maintain-port.html">
port 监护人面临的挑战</link> 一节。</para>
<para>对于 port 的修改, 应被发给 port 的监护人进行复审,
且在 commit 之前需要获得其监护人的同意。
假如某一 port 的监护人没有在两周之内 (不包括主要的公共假日)
响应来自用户的更新请求, 则可视为监护人超时,
在这种情况下可以在没有监护人明确同意的情形下进行更新。
如果监护人在多达三个月的时间内没有进行任何响应,
则可以认为该监护人不辞而别, 允许对出现此类问题的 port 进行监护人变更。
尽管如此, 监护人为 &a.portmgr; 或者 &a.security-officer; 的 port
不受此限。 对监护人为这些小组的 port 进行未经许可的 commit 是不允许的。</para>
<para>我们保留对监护人所提交修正案进行改动的权力, 以便使其更符合现行的 Ports
Collection 规范, 而无需提交补丁的人明确批准。 此外, 大规模的基础性修改,
也可能使 port 在没有得到监护人同意的情形下进行修改。
但这类修改都不应影响 port 本身的功能。</para>
<para>&a.portmgr; 保留以任何原因收回或绕过任何人监护权的权力,
而 &a.security-officer; 则保留以安全原因收回或绕过监护权的权力。</para>
</sect1>
<sect1 xml:id="makefile-comment">
<title><varname>COMMENT</varname> (一句话说明)</title>
<para>这一变量用于指定 port 的一句话说明。
<emphasis>请</emphasis> 勿将 package 的名字 (或软件的版本)
放在说明中。 这一说明的第一个字母应大写, 结尾不用句点。
下面是一个例子:</para>
<programlisting>COMMENT= A cat chasing a mouse all over the screen</programlisting>
<para><filename>Makefile</filename> 中的 COMMENT 变量应该紧接着 MAINTAINER
变量出现。</para>
<para>请务必将 COMMENT 这行限制在不超过 70 个字符之内,
因为这行内容会成为 &man.pkg.info.1;
呈现给用户的 port 的一句话简介。</para>
</sect1>
<sect1 xml:id="makefile-depend">
<title>依赖关系</title>
<para>许多 ports 会依赖其它 port。 这是包括 &os;
在内的多数 类-Unix 系统的很方便的功能。
这项功能, 可以避免在每个 port 或预编译包中都带上重复的依赖的代码,
而可以以依赖关系的方式去共享它们。
有七个变量用于帮助您确保所需的文件都存在于用户的机器上。
此外, 也提供了用于支持常见情形的依赖关系变量,
以及对依赖关系行为的更多控制。</para>
<sect2>
<title><varname>LIB_DEPENDS</varname> (依赖的函数库/共享库)</title>
<para>这个变量用于指定 port 所依赖的共享库。 其内容是由一系列
<replaceable>lib</replaceable>:<replaceable>dir</replaceable><optional>:target</optional>
元组构成的表, 其中 <replaceable>lib</replaceable> 是共享库的名字,
而 <replaceable>dir</replaceable> 则是在找不到时应该从哪里联编和安装,
最后, <replaceable>target</replaceable> 用于指定在那个目录中调用的
target。 例如,
<programlisting>LIB_DEPENDS= jpeg.9:${PORTSDIR}/graphics/jpeg</programlisting>
会检测主版本号为 9 的 jpeg 共享库, 如果它不存在,
则会进入到您的 ports 目录中的 <filename>graphics/jpeg</filename>
子目录, 并联编和安装它。 如果您指定的 <replaceable>target</replaceable>
就是 <varname>DEPENDS_TARGET</varname> (默认是
<literal>install</literal>), 则可以略去不写。</para>
<note>
<para><replaceable>lib</replaceable> 部分是一个正则表达式, 用于在
<command>ldconfig -r</command> 的输出中进行查找。 可以使用类似
<literal>intl.[5-7]</literal> 和 <literal>intl</literal> 这样的值。
前一种模式, 即
<literal>intl.[5-7]</literal>, 能够匹配
<literal>intl.5</literal>、 <literal>intl.6</literal> 和
<literal>intl.7</literal> 中的任意一个。 第二种模式, 即
<literal>intl</literal> 则可以匹配任意版本的
<literal>intl</literal> 库。</para>
</note>
<para>依赖关系会被检测两次, 一次是在
<buildtarget>extract</buildtarget> target 中, 而另一次则是在
<buildtarget>install</buildtarget> target。 另外,
依赖关系的名字会放到 package 中, 以便让
&man.pkg.add.1; 能够自动地在用户系统上安装所需的未安装的其它
package。</para>
</sect2>
<sect2>
<title><varname>RUN_DEPENDS</varname> (依赖的运行环境)</title>
<para>这个变量可以用来指定 port 在运行时所需要的可执行文件,
以及资源文件。 它是一系列
<replaceable>path</replaceable>:<replaceable>dir</replaceable><optional>:target</optional>
元组的列表, 这里, <replaceable>path</replaceable> 时所需的可执行,
或者资源文件的名字, <replaceable>dir</replaceable> 是在无法找到这些文件或目录时,
去什么地方完成联编和安装以便获得这些文件; 而
<replaceable>target</replaceable> 则用来指定在这个目录中所调用的
target 的名字。 假如 <replaceable>path</replaceable> 以斜线
(<literal>/</literal>) 开始, 则会当作普通文件,
使用 <command>test -e</command> 来测试; 反之,
则系统会假定这是一个可执行文件, 并且用 <command>which -s</command>
来检测程序是否存在于搜索路径中。</para>
<para>例如,</para>
<programlisting>RUN_DEPENDS= ${LOCALBASE}/etc/innd:${PORTSDIR}/news/inn \
xmlcatmgr:${PORTSDIR}/textproc/xmlcatmgr</programlisting>
<para>将检查文件, 或者目录
<filename>/usr/local/etc/innd</filename> 是否存在,
如果找不到, 则将从 port 目录的
<filename>news/inn</filename> 子目录加以安装。
系统也会检查是否能够在搜索路径中找到名为 <command>xmlcatmgr</command>
的文件, 如果找不到的话, 则会进入 ports 目录中的
<filename>textproc/xmlcatmgr</filename> 子目录,
并进行联编和安装的操作。</para>
<note>
<para>这种情况下, <command>innd</command> 实际上是一个可执行文件;
如果可执行文件不会出现在搜索路径中, 您就需要指定完整路径了。</para>
</note>
<note>
<para>ports 联编集群上官方的搜索 <envar>PATH</envar> 是</para>
<programlisting>/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin</programlisting>
</note>
<para>这个依赖关系会在
<buildtarget>install</buildtarget> target 的过程中进行检查。
此外, 依赖关系的名字会被放到 package 中, 以便
&man.pkg.add.1; 能够在用户的系统中尚未安装相关软件时自动地安装那些 package。
如果您希望指定一个的 <replaceable>target</replaceable>
和默认的 <varname>DEPENDS_TARGET</varname> 相同,
则可以略去不写。</para>
<para>一种比较常见的情形是
<varname>RUN_DEPENDS</varname> 和
<varname>BUILD_DEPENDS</varname> 完全一样,
这种情况在移植的软件是采用脚本语言书写,
或联编环境与运行环境需求相同时尤其普遍。
这种情况可以用下面简单明了的方式直接将其中一个变量赋值给另一个变量:</para>
<programlisting>RUN_DEPENDS= ${BUILD_DEPENDS}</programlisting>
<para>不过, 这种赋值有可能会令运行环境被某些没有在
port 原本的 <varname>BUILD_DEPENDS</varname> 明确定义的依赖关系污染。
导致这种情况的原因是 &man.make.1; 计算变量赋值时默认采用的是延后计算 (lazy evaluation)。
例如, 如果在 <filename>Makefile</filename> 中使用了
<varname>USE_<replaceable>*</replaceable></varname> 变量,
这些变量就会由 <filename>ports/Mk/bsd.*.mk</filename> 处理,
并填写与之对应的联编依赖关系。 例如, <literal>USE_GMAKE=yes</literal>
会把 <package>devel/gmake</package> 加入到
<varname>BUILD_DEPENDS</varname>。
如果希望避免这些附加的依赖关系污染 <varname>RUN_DEPENDS</varname>,
在使用赋值的时候就需要小心考虑这类扩展的情况,
例如, 可以在赋值展开之前复制变量的值:</para>
<programlisting>RUN_DEPENDS:= ${BUILD_DEPENDS}</programlisting>
</sect2>
<sect2>
<title><varname>BUILD_DEPENDS</varname> (依赖的联编环境)</title>
<para>此变量用于指定用来联编 port 的可执行文件或资源文件。
与 <varname>RUN_DEPENDS</varname> 类似, 它是一个
<replaceable>path</replaceable>:<replaceable>dir</replaceable><optional>:target</optional>
元组的列表。 例如, <programlisting> BUILD_DEPENDS=
unzip:${PORTSDIR}/archivers/unzip</programlisting> 将检测名为
<command>unzip</command> 的可执行文件是否存在, 如果不存在,
则会进入到您的 ports 目录中的 <filename>archivers/unzip</filename>
并完成联编和安装工作。</para>
<note>
<para>这里的 <quote>build</quote> 表示从解压缩到编译的全部过程。
依赖关系是在 <buildtarget>extract</buildtarget> target 的过程中检测的。
假如您要指定的 <replaceable>target</replaceable> 和
<varname>DEPENDS_TARGET</varname> 相同, 则可以略去不写。</para>
</note>
</sect2>
<sect2>
<title><varname>FETCH_DEPENDS</varname> (依赖的下载环境)</title>
<para>这一变量用于指定 port 在下载时所需的可执行文件或资源文件。
和前两个类似, 它是一组
<replaceable>path</replaceable>:<replaceable>dir</replaceable><optional>:target</optional>
元组。 例如, <programlisting> FETCH_DEPENDS=
ncftp2:${PORTSDIR}/net/ncftp2</programlisting>
将检测名为 <command>ncftp2</command> 的可执行文件是否存在,
如果找不到, 则将进入到您 ports 目录中的
<filename>net/ncftp2</filename> 子目录并加以联编和安装。</para>
<para>这个依赖关系是在
<buildtarget>fetch</buildtarget> target 过程中检查的。
如果与 <varname>DEPENDS_TARGET</varname> 相同,
则可以省略 <replaceable>target</replaceable> 部分。</para>
</sect2>
<sect2>
<title><varname>EXTRACT_DEPENDS</varname> (依赖的解压缩环境)</title>
<para>此变量用于指定 port 在解压缩时所需的可执行文件或其它资源文件。
和前一个变量类似, 它是一系列
<replaceable>path</replaceable>:<replaceable>dir</replaceable><optional>:target</optional>
元组的列表。 例如, <programlisting>EXTRACT_DEPENDS=
unzip:${PORTSDIR}/archivers/unzip</programlisting> 将检查名为
<command>unzip</command> 的可执行文件是否存在, 如果不存在,
则会进入到您的 ports 目录中的 <filename>archivers/unzip</filename>
子目录, 予以联编和安装。</para>
<para>这个依赖关系是在
<buildtarget>extract</buildtarget> target 的过程中检查的。 如果与
<varname>DEPENDS_TARGET</varname> 相同,
则可以略去 <replaceable>target</replaceable> 部分。</para>
<note>
<para>只有在其它方式都不可用 (默认是 <command>gzip</command>)
而且无法通过 <xref linkend="use-vars"/> 所介绍的 <varname>USE_ZIP</varname> 或
<varname>USE_BZIP2</varname> 都不能达到需要时,
才应使用这个变量。</para>
</note>
</sect2>
<sect2>
<title><varname>PATCH_DEPENDS</varname> (依赖的打补丁环境)</title>
<para>这个变量用于指定 port 在进行 patch
操作时所需的可执行文件或其它资源文件。 和前一个变量类似, 它是一组
<replaceable>path</replaceable>:<replaceable>dir</replaceable><optional>:target</optional>
元组的表。 例如, <programlisting> PATCH_DEPENDS=
${NONEXISTENT}:${PORTSDIR}/java/jfc:extract
</programlisting> 表示进入到您的 ports 目录中的
<filename>java/jfc</filename> 子目录, 并将其解压缩。</para>
<para>这个依赖关系是在
<buildtarget>patch</buildtarget> target 的过程中检查的。
<replaceable>target</replaceable> 部分如果和
<varname>DEPENDS_TARGET</varname> 相同, 就可略去不写。</para>
</sect2>
<sect2 xml:id="use-vars">
<title><varname>USE_<replaceable>*</replaceable></varname></title>
<para>提供了一系列变量, 用以封装大量 port 都用到的依赖关系。
虽然使用这些变量是可选的, 但它们能显著减少 port 的
<filename>Makefile</filename> 复杂性。 这些变量的共同特征在于,
它们的名字都是 <varname>USE_<replaceable>*</replaceable></varname>
这样的形式。 这些变量的使用, 应严格限制于 port 的
<filename>Makefile</filename> 以及
<filename>ports/Mk/bsd.*.mk</filename>,
而绝不应用于表达用户能够设置的选项 — 这种情况下应采用
<varname>WITH_<replaceable>*</replaceable></varname> 和
<varname>WITHOUT_<replaceable>*</replaceable></varname>
这样的变量。</para>
<note>
<para>在 <emphasis>任何</emphasis> 情况下,
都不应在 <filename>/etc/make.conf</filename> 中配置任何
<varname>USE_<replaceable>*</replaceable></varname>。
例如, 设置 <programlisting>USE_GCC=3.4</programlisting>
将导致每个 port 都依赖 gcc34, 甚至包括
gcc34 本身!</para>
</note>
<table frame="none">
<title>常用的 <varname>USE_<replaceable>*</replaceable></varname>
变量</title>
<tgroup cols="2">
<thead>
<row>
<entry>变量</entry>
<entry>含义</entry>
</row>
</thead>
<tbody>
<row>
<entry><varname>USE_BZIP2</varname></entry>
<entry>此 port 的源码包是使用
<command>bzip2</command> 压缩的。</entry>
</row>
<row>
<entry><varname>USE_ZIP</varname></entry>
<entry>此 port 的源码包是用
<command>zip</command> 压缩的。</entry>
</row>
<row>
<entry><varname>USE_BISON</varname></entry>
<entry>此 port 在联编时使用 <command>bison</command>。</entry>
</row>
<row>
<entry><varname>USE_CDRTOOLS</varname></entry>
<entry>此 port 需要使用 <application>cdrecord</application>,
根据用户的喜好, 可能是 <package>sysutils/cdrtools</package> 或 <package>sysutils/cdrtools-cjk</package>。</entry>
</row>
<row>
<entry><varname>USE_GCC</varname></entry>
<entry>此 port 需要使用某一特定版本的
<command>gcc</command> 才能完成编译。
可以使用类似 <literal>3.4</literal> 这样的值来精确指定版本。
如果希望使用不低于某一版本的编译器, 则可以用
<literal>3.4+</literal> 这样的形式。 如果与所希望的版本吻合,
则将使用基本系统中所提供的 <command>gcc</command>, 反之,
系统会从 ports 中安装所希望版本的 <command>gcc</command>,
并调整 <varname>CC</varname> 以及
<varname>CXX</varname> 变量的设置。</entry>
</row>
</tbody>
</tgroup>
</table>
<para>与 <application>gmake</application>
和 <filename>configure</filename> 脚本有关的变量在
<xref linkend="building"/> 中进行了介绍, 而
<application>autoconf</application>、
<application>automake</application> 以及
<application>libtool</application> 的介绍则可以在
<xref linkend="using-autotools"/> 找到。 <xref linkend="using-perl"/>
介绍了与 <application>Perl</application> 有关的的变量。
<xref linkend="using-x11"/> 中列出了关于 X11 的变量。 关于 GNOME 的变量在 <xref linkend="using-gnome"/>, 而关于 KDE 的则在 <xref linkend="using-kde"/>。 <xref linkend="using-java"/> 讲述了和 Java 有关的变量, 而 <xref linkend="using-php"/> 则包含了关于
<application>Apache</application>、 <application>PHP</application>
以及 PEAR 的介绍性信息。 关于 <application>Python</application>,
在 <xref linkend="using-python"/> 进行了讨论, 而关于
<application>Ruby</application> 的介绍,
则可以在 <xref linkend="using-ruby"/> 中找到。
<xref linkend="using-sdl"/> 提供了用于
<application>SDL</application> 应用程序的变量介绍, 最后,
<xref linkend="using-xfce"/> 包含了关于
<application>Xfce</application> 的信息。</para>
</sect2>
<sect2>
<title>在依赖关系中指定最低版本</title>
<para>在依赖某个其他 port 时, 可以采用下面的句法, 通过除
<varname>LIB_DEPENDS</varname> 之外的
<varname>*_DEPENDS</varname> 变量来指定最低版本:</para>
<programlisting>p5-Spiffy>=0.26:${PORTSDIR}/devel/p5-Spiffy</programlisting>
<para>第一个字段指明了所依赖 package 的名字,
用以与 package 数据库中的某项匹配, 然后是比较算符,
以及 package 的版本号。 前面的例子中,
如果系统中安装了 p5-Spiffy-0.26 则认为满足了依赖条件。</para>
</sect2>
<sect2>
<title>关于依赖关系的补充说明</title>
<para>如前面所提到的那样, 在需要某一依赖的 port 时,
将调用 <buildtarget>DEPENDS_TARGET</buildtarget> 所指定的 target。
这一变量的默认值是 <literal>install</literal>。 这不是一个用户变量,
它不应在 port 的
<filename>Makefile</filename> 中予以定义。 如果您的 port 需要使用特殊的
target 来处理依赖关系, 应使用 <varname>*_DEPENDS</varname> 的
<literal>:target</literal> 部分, 而不是重定义
<varname>DEPENDS_TARGET</varname> 来完成。</para>
<para>当您输入 <command>make clean</command> 时, 其依赖的 port
也会自动进行清理。 如果您不希望如此, 应定义环境变量
<varname>NOCLEANDEPENDS</varname>。 如果 port
依赖一些重新联编需要花费很长时间的 port 时, 例如 KDE, GNOME
或 Mozilla 时, 这一方法会非常有用。</para>
<para>要无条件地依赖某个 port, 可以使用 <varname>${NONEXISTENT}</varname>
作为 <varname>BUILD_DEPENDS</varname> 或
<varname>RUN_DEPENDS</varname> 的第一部分。 只有在您需要使用其它
port 提供的源代码时才应这样做。 通常也可以通过这样指定来缩短编译所需的时间。
例如
<programlisting>BUILD_DEPENDS= ${NONEXISTENT}:${PORTSDIR}/graphics/jpeg:extract</programlisting>
表示依赖 <literal>jpeg</literal> port 并将其解压缩。</para>
</sect2>
<sect2>
<title>循环的依赖关系是致命的</title>
<important>
<para>不要在 ports tree 中引入任何循环依赖关系!</para>
</important>
<para>ports 联编技术不能够容忍循环依赖关系。 如果您引入了这样的关系,
就一定会有人安装的 FreeBSD 会因此而损坏, 而且这种现象会越来越多。
这些情形很难检测; 如果有疑虑, 在进行这样的修改之前, 务必执行:
<command>cd /usr/ports; make index</command>。 这个过程在旧的机器上会很慢,
但能够让大量的用户 — 也包括您自己 —
拯救于由这种问题所造成的困惑之中。</para>
</sect2>
</sect1>
<sect1 xml:id="makefile-masterdir">
<title><varname>MASTERDIR</varname> (主 port 所在的目录)</title>
<para>如果 port 需要依某些变量的设置 (举例来说, 分辨率或纸型)
来联编略有不同的预编译包, 则可以为每一个这样的包建立不同的目录,
这样可以让用户更容易地看到他们想要安装的版本, 但又能在这些 port
之间共用尽可能多的文件。 一般情况下, 如果运用得当, 除主目录之外都只需要很短的
<filename>Makefile</filename>。 这些 <filename>Makefile</filename> 中,
可以用 <varname>MASTERDIR</varname> 来指定其它文件所在的目录。
另外, 还应使用一个变量作为
<link linkend="porting-pkgname"><varname>PKGNAMESUFFIX</varname></link>
的一部分, 以便为不同的包给出不同的命名。</para>
<para>用例子来阐述这些会更为明晰。 以下是
<filename>japanese/xdvi300/Makefile</filename> 的部分代码:</para>
<programlisting>PORTNAME= xdvi
PORTVERSION= 17
PKGNAMEPREFIX= ja-
PKGNAMESUFFIX= ${RESOLUTION}
:
# default
RESOLUTION?= 300
.if ${RESOLUTION} != 118 && ${RESOLUTION} != 240 && \
${RESOLUTION} != 300 && ${RESOLUTION} != 400
@${ECHO_MSG} "Error: invalid value for RESOLUTION: \"${RESOLUTION}\""
@${ECHO_MSG} "Possible values are: 118, 240, 300 (default) and 400."
@${FALSE}
.endif</programlisting>
<para><package>japanese/xdvi300</package> 也提供了全部常规的补丁,
以及打包用到的文件等等内容。 如果您在那里输入 <command>make</command>,
它将使用默认的分辨率值 (300) 并正常地联编 port。</para>
<para>对于其它分辨率而言, 以下是 <emphasis>完整的</emphasis>
<filename>xdvi118/Makefile</filename>:</para>
<programlisting>RESOLUTION= 118
MASTERDIR= ${.CURDIR}/../xdvi300
.include "${MASTERDIR}/Makefile"</programlisting>
<para>(<filename>xdvi240/Makefile</filename> 和
<filename>xdvi400/Makefile</filename> 是相似的)。
<varname>MASTERDIR</varname> 定义会告诉
<filename>bsd.port.mk</filename> 常规的目录,
例如 <varname>FILESDIR</varname> 以及
<varname>SCRIPTDIR</varname> 应在
<filename>xdvi300</filename> 中查找。 <literal>RESOLUTION=118</literal>
这行将覆盖在 <filename>xdvi300/Makefile</filename> 中所作的
<literal>RESOLUTION=300</literal> 设置, 从而 port
将以分辨率为 118 的设置来联编。</para>
</sect1>
<sect1 xml:id="makefile-manpages">
<title>联机手册</title>
<para><varname>MAN[1-9LN]</varname> 这些变量,
会自动地将联机手册加到 <filename>pkg-plist</filename> (这也意味着
<emphasis>不能</emphasis> 在
<filename>pkg-plist</filename> 中列出联机手册 — 参见 <link linkend="plist-sub">PLIST 的生成</link> 来了解更多细节)。 此外,
这也会让安装阶段自动地根据在 <filename>/etc/make.conf</filename>
中所作的 <varname>NO_MANCOMPRESS</varname>
设置来自动对联机手册文件执行压缩或解压缩操作。</para>
<para>如果 port 尝试通过使用符号连接或硬连接将联机手册安装为多个名字,
就必须使用 <varname>MLINKS</varname> 变量来予以明示。
由 port 创建的连接, 将由 <filename>bsd.port.mk</filename>
删除和重建, 以确认它们指向了正确的文件。 任何在 MLINKS 中列出的文件都不应在
<filename>pkg-plist</filename> 中再出现。</para>
<para>要指定是否在安装时对联机手册进行压缩,
可以使用 <varname>MANCOMPRESSED</varname> 变量。
这一变量可以取三种值, <literal>yes</literal>、 <literal>no</literal> 和
<literal>maybe</literal> 之一。 <literal>yes</literal>
表示联机手册已经以压缩的形式安装, <literal>no</literal> 表示还没有,
而 <literal>maybe</literal> 则表示所安装的软件会尊重
<varname>NO_MANCOMPRESS</varname> 的设置值, 因此
<filename>bsd.port.mk</filename> 不需要特别做什么事情。</para>
<para>如果设置了 <varname>USE_IMAKE</varname> 而未定义
<varname>NO_INSTALL_MANPAGES</varname>,
<varname>MANCOMPRESSED</varname> 会自动设为
<literal>yes</literal>, 反之则是
<literal>no</literal>。 除非默认值不合适,
否则就不需要在 port 中明确地加以改变。</para>
<para>如果 port 将联机手册放到了
<varname>PREFIX</varname> 之外的其它目录, 则应使用
<varname>MANPREFIX</varname> 来加以设置。 此外,
如果只有某些部分的联机手册会安装到不标准的位置, 例如某些 <literal>perl</literal>
模块的 port, 还可以使用
<varname>MAN<replaceable>sect</replaceable>PREFIX</varname> (此处
<replaceable>sect</replaceable> 是 <literal>1-9</literal>、
<literal>L</literal> 或 <literal>N</literal> 之一) 来指定。</para>
<para>如果您的联机手册需要装入专用于某一语言专用的子目录,
需要将 <varname>MANLANG</varname> 设为那种语言的名字。
此变量的默认值是 <literal>""</literal> (也就是只有英语)。</para>
<para>下面是一个综合的例子。</para>
<programlisting>MAN1= foo.1
MAN3= bar.3
MAN4= baz.4
MLINKS= foo.1 alt-name.8
MANLANG= "" ja
MAN3PREFIX= ${PREFIX}/share/foobar
MANCOMPRESSED= yes</programlisting>
<para>这表示 port 会安装六个文件;</para>
<programlisting>${MANPREFIX}/man/man1/foo.1.gz
${MANPREFIX}/man/ja/man1/foo.1.gz
${PREFIX}/share/foobar/man/man3/bar.3.gz
${PREFIX}/share/foobar/man/ja/man3/bar.3.gz
${MANPREFIX}/man/man4/baz.4.gz
${MANPREFIX}/man/ja/man4/baz.4.gz</programlisting>
<para>此外, <filename>${MANPREFIX}/man/man8/alt-name.8.gz</filename>
可能会通过您的 port 安装, 也可能不会。 无论如何,
都会创建一个符号连接, 把 foo(1) 和
alt-name(8) 联机手册连起来。</para>
<para>假如只有部分联机手册是翻译过的,
则可以使用一些根据 <varname>MANLANG</varname> 内容动态生成的变量:</para>
<programlisting>MANLANG= "" de ja
MAN1= foo.1
MAN1_EN= bar.1
MAN3_DE= baz.3</programlisting>
<para>这相当于下列文件:</para>
<programlisting>${MANPREFIX}/man/man1/foo.1.gz
${MANPREFIX}/man/de/man1/foo.1.gz
${MANPREFIX}/man/ja/man1/foo.1.gz
${MANPREFIX}/man/man1/bar.1.gz
${MANPREFIX}/man/de/man3/baz.3.gz</programlisting>
</sect1>
<sect1 xml:id="makefile-info">
<title>Info 文件</title>
<para>如果软件包需要安装 GNU info 文件,
则需要在 <varname>INFO</varname> 变量中一一列出 (不需要指定
<literal>.info</literal> 后缀)。 系统假定这些文件均会安装到
<filename>PREFIX/INFO_PATH</filename> 目录中。
如果软件包有需要, 也可以通过修改 <varname>INFO_PATH</varname> 来指定不同的位置。
不过, 并不推荐这样做。 所有列出的项目均是相对于
<filename>PREFIX/INFO_PATH</filename> 的文件路径。
例如, <package>lang/gcc34</package> 表示将
info 文件安装到
<filename>PREFIX/INFO_PATH/gcc34</filename>,
因此 <varname>INFO</varname> 应写成类似这样:
<programlisting>INFO= gcc34/cpp gcc34/cppinternals gcc34/g77 ...
</programlisting>
这样安装/卸载代码就会自动地在注册包之前将它们加入到临时的
<filename>pkg-plist</filename> 中了。</para>
</sect1>
<sect1 xml:id="makefile-options">
<title>Makefile 选项</title>
<para>某些大型应用程序可以在联编时使用一系列配置选项,
用以在系统中已经安装了某些库或应用程序时增加一些功能。
例如, 选择某种自然 (人类的) 语言, GUI 或命令行界面,
由于并不是所有的用户都希望使用这些库或者应用程序, port
系统提供了一组方便的机制, 来让 port 的作者控制联编时的配置。
支持这些特性可以让用户体验更好, 并达到事半功倍的效果。</para>
<sect2>
<title>开关 (Knobs)</title>
<sect3>
<title><varname>WITH_<replaceable>*</replaceable></varname> 和
<varname>WITHOUT_<replaceable>*</replaceable></varname></title>
<para>这些变量是为系统管理员准备的。 许多这样的变量被标准化并置于
<link xlink:href="http://www.freebsd.org/cgi/cvsweb.cgi/ports/KNOBS?rev=HEAD&content-type=text/x-cvsweb-markup"><filename>ports/KNOBS</filename></link>
文件。</para>
<para>在创建一个 port 的时候,不要使用某个应用程序专有的 knob
名称,比如对于 Avahi 这个 port,应该用
<varname>WITHOUT_MDNS</varname> 而不是
<varname>WITHOUT_AVAHI_MDNS</varname>。</para>
<note>
<para>您不应假定每一个
<varname>WITH_<replaceable>*</replaceable></varname>
都会有对应的
<varname>WITHOUT_<replaceable>*</replaceable></varname>
变量, 反之亦然。 一般而言, 会使用默认值。</para>
</note>
<note>
<para>除非另有说明, 这些变量都是测试是否定义,
而不是它们设置了 <literal>YES</literal> 或
<literal>NO</literal>。</para>
</note>
<table frame="none">
<title>常见的 <varname>WITH_<replaceable>*</replaceable></varname>
和 <varname>WITHOUT_<replaceable>*</replaceable></varname>
变量</title>
<tgroup cols="2">
<thead>
<row>
<entry>变量</entry>
<entry>意义</entry>
</row>
</thead>
<tbody>
<row xml:id="knobs-without-nls">
<entry><varname>WITHOUT_NLS</varname></entry>
<entry>表示不需要国际化支持, 这可以节省编译所消耗的时间。
默认情况下, 会启用国际化支持。</entry>
</row>
<row>
<entry><varname>WITH_OPENSSL_BASE</varname></entry>
<entry>使用基本系统中的 OpenSSL 版本。</entry>
</row>
<row>
<entry><varname>WITH_OPENSSL_PORT</varname></entry>
<entry>从 <package>security/openssl</package>
安装 OpenSSL,即使基本系统中的版本是最新的。</entry>
</row>
<row>
<entry><varname>WITHOUT_X11</varname></entry>
<entry>如果 port 能够在是否包含
X 支持的情况下分别联编, 则一般情况应该默认以包含
X 支持的配置来联编。 如果定义了这一变量,
则应联编不包含 X 支持的版本。</entry>
</row>
</tbody>
</tgroup>
</table>
</sect3>
<sect3>
<title>开关 (knob) 的命名</title>
<para>我们建议 port 的开发人员使用相似的开关, 以便最终用户使用,
并减少开关名称的总数。 最为常用的开关名字可以在
<link xlink:href="http://www.freebsd.org/cgi/cvsweb.cgi/ports/KNOBS?rev=HEAD&content-type=text/x-cvsweb-markup">KNOBS</link>
文件中找到。</para>
<para>开关的名字应反映其功能。 如果 port 的 <varname>PORTNAME</varname>
包括 lib- 前缀, 则开关名中应删去 lib- 前缀。</para>
</sect3>
</sect2>
<sect2>
<title><varname>OPTIONS</varname> (菜单式可选项)</title>
<sect3>
<title>背景</title>
<para><varname>OPTIONS</varname> 将为正在安装 port
的用户提供一个包含可用选项的对话框,
并将用户的选择保存到 <filename>/var/db/ports/portname/options</filename> 中。
下次重新联编 port 时, 这些选项将被再次使用。
这样一来, 就不需要劳神去记忆您之前联编 port 时的那几十个
<varname>WITH_<replaceable>*</replaceable></varname> 和
<varname>WITHOUT_<replaceable>*</replaceable></varname>选项了!</para>
<para>当用户运行 <command>make config</command> (或首次运行
<command>make build</command>) 时, 框架会首先检查
<filename>/var/db/ports/portname/options</filename>。
如果这个文件不存在, 则它会使用
<varname>OPTIONS</varname> 的值来生成一个可以启用或禁用各个选项的对话框。
随后, 用户的选择将保存到
<filename>options</filename> 文件中,
并被用于联编 port。</para>
<para>如果新版本的 port 新增了
<varname>OPTIONS</varname>, 则系统会再次给出对话框,
并根据先前的 <varname>OPTIONS</varname>
配置预设先前存在的配置。</para>
<para>使用 <command>make showconfig</command> 可以查看保存的配置。
此外, <command>make rmconfig</command> 可以删除已经保存的配置。</para>
</sect3>
<sect3>
<title>语法</title>
<para><varname>OPTIONS</varname> 变量的语法是:
<programlisting>OPTIONS= OPTION "说明性文字" 默认值 ...
</programlisting>
默认值必须是 <literal>ON</literal> 和
<literal>OFF</literal> 之一。 这种三元组可以使用多次。</para>
<para>定义 <varname>OPTIONS</varname> 变量的值,
必须在引入 <filename>bsd.port.options.mk</filename> 之前进行。
而 <varname>WITH_*</varname> 和 <varname>WITHOUT_*</varname>
变量则只能在引入了
<filename>bsd.port.options.mk</filename> 之后才可以进行检测。 使用
<filename>bsd.port.pre.mk</filename> 也可以达到同样的目的,
在系统开始提供 <filename>bsd.port.options.mk</filename> 之前的许多
port 都在使用这种用法。 不过, 请注意
<filename>bsd.port.pre.mk</filename> 会要求某些变量已经进行过定义, 如
<varname>USE_*</varname> 等。</para>
<example xml:id="ports-options-simple-use">
<title>简单的 <varname>OPTIONS</varname> 用法</title>
<para><programlisting>OPTIONS= FOO "启用 foo 选项" On \
BAR "支持 bar 功能" Off
.include <bsd.port.options.mk>
.if defined(WITHOUT_FOO)
CONFIGURE_ARGS+= --without-foo
.else
CONFIGURE_ARGS+= --with-foo
.endif
.if defined(WITH_BAR)
RUN_DEPENDS+= bar:${PORTSDIR}/bar/bar
.endif
.include <bsd.port.mk></programlisting></para>
</example>
<example xml:id="ports-options-old-style-use">
<title>Old style use of <varname>OPTIONS</varname></title>
<para><programlisting>OPTIONS= FOO "Enable option foo" On
.include <bsd.port.pre.mk>
.if defined(WITHOUT_FOO)
CONFIGURE_ARGS+= --without-foo
.else
CONFIGURE_ARGS+= --with-foo
.endif
.include <bsd.port.post.mk></programlisting></para>
</example>
</sect3>
</sect2>
<sect2>
<title>自动激活的特性</title>
<para>在使用 GNU configure 脚本时, 一定要小心有些特性会由其自动检测而激活。
您应通过明确地指定相应的
<literal>--without-xxx</literal> 或 <literal>--disable-xxx</literal>
参数到 <varname>CONFIGURE_ARGS</varname> 来禁用不希望的特性。</para>
<example>
<title>处理选项时的错误做法</title>
<programlisting>.if defined(WITH_FOO)
LIB_DEPENDS+= foo.0:${PORTSDIR}/devel/foo
CONFIGURE_ARGS+= --enable-foo
.endif</programlisting>
</example>
<para>在前面的例子中, 假设系统中已经安装了 libfoo 库。 用户可能并不希望应用程序使用 libfoo,
因此他在 <literal>make config</literal> 对话框中关掉了这个选项。
但是, 应用程序的 configure 脚本检测到了系统中存在这个库,
并将其加入到了最终可执行文件支持的功能中。 现在, 如果用户决定从系统中卸载 libfoo 时,
ports 系统就无法保护这个应用程序免遭破坏了 (因为没有记录 libfoo 的依赖关系)。</para>
<example>
<title>处理选项时的正确做法</title>
<programlisting>.if defined(WITH_FOO)
LIB_DEPENDS+= foo.0:${PORTSDIR}/devel/foo
CONFIGURE_ARGS+= --enable-foo
.else
CONFIGURE_ARGS+= --disable-foo
.endif</programlisting>
</example>
<para>在第二个例子中, libfoo 库被明确禁用。 即使系统中已经安装了这个库,
configure 脚本也不会启用相应的功能了。</para>
</sect2>
</sect1>
<sect1 xml:id="makefile-wrkdir">
<title>指定工作临时目录</title>
<para>每个 port 都会被解压缩到一个工作临时目录中, 这个目录必须是可写的。
ports 系统默认情况下会将
<varname>DISTFILES</varname> 解压缩到一个叫做
<literal>${DISTNAME}</literal> 的目录中。 换言之, 如果设了:</para>
<programlisting>PORTNAME= foo
PORTVERSION= 1.0</programlisting>
<para>则 port 的源码包文件的顶级目录将是
<filename>foo-1.0</filename>。</para>
<para>如果这不是所希望的情形, 您可以修改一系列变量的设置。</para>
<sect2>
<title><varname>WRKSRC</varname> (开始联编操作的目录名)</title>
<para>这个变量给出了在应用程序的源代码包解压缩之后所生成的目录的名字。
如果我们之前的例子解压缩生成一个叫做 <filename>foo</filename> (而不是
<filename>foo-1.0</filename>) 的目录, 您应:</para>
<programlisting>WRKSRC= ${WRKDIR}/foo</programlisting>
<para>或者, 也可能是</para>
<programlisting>WRKSRC= ${WRKDIR}/${PORTNAME}</programlisting>
</sect2>
<sect2>
<title><varname>NO_WRKSUBDIR</varname> (不需要临时的联编目录)</title>
<para>如果 port 完全不需要写入到某个子目录中,
您应设置 <varname>NO_WRKSUBDIR</varname> 以明示这一点。</para>
<programlisting>NO_WRKSUBDIR= yes</programlisting>
</sect2>
</sect1>
<sect1 xml:id="conflicts">
<title>处理冲突</title>
<para>针对不同的 package 或 port 之间的冲突情形,
系统提供了不同的变量来协助开发人员进行表达: <varname>CONFLICTS</varname>、
<varname>CONFLICTS_INSTALL</varname> 和
<varname>CONFLICTS_BUILD</varname>。</para>
<note>
<para>这些用于描述冲突的变量会自动地设置
<varname>IGNORE</varname>, 后者的完整介绍,
可以在 <xref linkend="dads-noinstall"/> 找到。</para>
</note>
<para>在删去相互冲突的 port 时, 建议将 <varname>CONFLICTS</varname>
保留几个月, 以便让那些不经常更新系统的用户能够看到。</para>
<sect2>
<title><varname>CONFLICTS_INSTALL</varname></title>
<para>如果您的软件包不能与某些软件包同时安装
(例如由于安装同样的文件到相同的位置、 运行时不兼容等等),
则应把其它软件包的名字列在
<varname>CONFLICTS_INSTALL</varname> 变量中。 此处可以使用 shell
通配符, 如 <literal>*</literal> 和 <literal>?</literal>。
列出其它软件包的名字时需要遵循它们在
<filename>/var/db/pkg</filename> 中出现的样子。 请确保
<varname>CONFLICTS_INSTALL</varname> 不会匹配到您正制作的这个预编译包的名字,
否则, 使用
<varname>FORCE_PKG_REGISTER</varname> 来强制安装就没有办法进行了。
对于 CONFLICTS_INSTALL 的检查是在联编过程之后、
安装开始之前进行的。</para>
</sect2>
<sect2>
<title><varname>CONFLICTS_BUILD</varname></title>
<para>如果您的软件包在系统中存在某些其它软件包时不能完成联编,
则应把其它软件包的名字列在
<varname>CONFLICTS_BUILD</varname> 变量中。 此处可以使用 shell
通配符, 如 <literal>*</literal> 和 <literal>?</literal>。
列出其它软件包的名字时需要遵循它们在
<filename>/var/db/pkg</filename> 中出现的样子。
对于 CONFLICTS_BUILD 的检查是在联编过程开始之前进行的。
联编时的冲突不会在编译好的包中予以记录。</para>
</sect2>
<sect2>
<title><varname>CONFLICTS</varname></title>
<para>如果您的 port 在某些其它 port 已经存在的情况下既不能联编,
也不能安装, 则应把其它软件包的名字列在
<varname>CONFLICTS</varname> 变量中。 此处可以使用 shell
通配符, 如 <literal>*</literal> 和 <literal>?</literal>。
列出其它软件包的名字时需要遵循它们在
<filename>/var/db/pkg</filename> 中出现的样子。 请确保
<varname>CONFLICTS</varname> 不会匹配到您正制作的这个预编译包的名字,
否则, 使用
<varname>FORCE_PKG_REGISTER</varname> 来强制安装就没有办法进行了。
对于 CONFLICTS 的检查是在联编过程之后、
安装开始之前进行的。</para>
</sect2>
</sect1>
<sect1 xml:id="install">
<title>安装文件</title>
<sect2 xml:id="install-macros">
<title>INSTALL_* 宏</title>
<para>一定要使用由 <filename>bsd.port.mk</filename>
提供的宏, 以确保在您自己的
<buildtarget>*-install</buildtarget> target 中能够以正确的属主和权限模式安装文件。</para>
<itemizedlist>
<listitem>
<para><varname>INSTALL_PROGRAM</varname> 是安装可执行二进制文件的命令。</para>
</listitem>
<listitem>
<para><varname>INSTALL_SCRIPT</varname> 是安装可执行脚本文件的命令。</para>
</listitem>
<listitem>
<para><varname>INSTALL_LIB</varname> 是安装动态连接库的命令。</para>
</listitem>
<listitem>
<para><varname>INSTALL_KLD</varname> 是用于安装可加载式内核模块的命令。
在某些平台上, 当对内核模块进行 strip 之后会导致一些问题,
因此您应使用这个宏而不是 <varname>INSTALL_PROGRAM</varname> 来安装内核模块。</para>
</listitem>
<listitem>
<para><varname>INSTALL_DATA</varname> 是安装可共享数据的命令。</para>
</listitem>
<listitem>
<para><varname>INSTALL_MAN</varname> 是安装联机手册和其他文档的命令
(注意它并不会执行压缩操作)。</para>
</listitem>
</itemizedlist>
<para>这些宏展开后基本上都是包含适当参数的 <command>install</command> 命令。</para>
</sect2>
<sect2 xml:id="install-strip">
<title>对可执行文件和动态连接库做脱模 (strip) 操作</title>
<para>除非不得不进行, 否则不要手工对可执行文件作脱模操作。
所有文件在安装时都应脱模, 但 <varname>INSTALL_PROGRAM</varname>
宏会在安装的同时对其进行脱模 (参见下一节的内容)。
<varname>INSTALL_LIB</varname> 宏</para>
<para>如果您需要对某一文件进行脱模, 但不希望使用
<varname>INSTALL_PROGRAM</varname> 及 <varname>INSTALL_LIB</varname> 宏,
则应使用 <varname>${STRIP_CMD}</varname> 来处理程序。
一般而言这应该在
<buildtarget>post-install</buildtarget> target 中进行。 例如:</para>
<programlisting>post-install:
${STRIP_CMD} ${PREFIX}/bin/xdl</programlisting>
<para>可以使用 &man.file.1; 命令来检查所安装的可执行文件是否进行过脱模。
如果它没有给出 <literal>not stripped</literal> 的提示,
则表示已经做过脱模了。 另外,
&man.strip.1; 不会对已经脱模过的文件重新脱模, 它会直接退出的。</para>
</sect2>
<sect2 xml:id="install-copytree">
<title>安装一个目录下的全部文件</title>
<para>有时, 会有需要安装大量的文件, 并保持其层次结构, 例如,
将整个目录结构从 <varname>WRKSRC</varname> 复制到 <varname>PREFIX</varname> 的目标目录。</para>
<para>针对这种情况, 系统提供了两个宏。 使用这些宏,
而不是直接使用 <command>cp</command> 的优势是它们能够确保目标文件的属主和权限正确。 第一个宏,
<varname>COPYTREE_BIN</varname> 将所有安装的文件视为可执行文件, 因而适合安装文件到
<filename>PREFIX/bin</filename>。
第二个宏, <varname>COPYTREE_SHARE</varname>, 则不会设置可执行权限,
因此适合于将文件安装到 <filename>PREFIX/share</filename>
下。</para>
<programlisting>post-install:
${MKDIR} ${EXAMPLESDIR}
(cd ${WRKSRC}/examples/ && ${COPYTREE_SHARE} \* ${EXAMPLESDIR})</programlisting>
<para>这个例子将原作者提供的整个
<filename>examples</filename> 目录复制到您 port 指定的安装示范文件的位置。</para>
<programlisting>post-install:
${MKDIR} ${DATADIR}/summer
(cd ${WRKSRC}/temperatures/ && ${COPYTREE_SHARE} "June July August" ${DATADIR}/summer/)</programlisting>
<para>这个例子将把夏季的三个月的数据, 复制到
<filename>DATADIR</filename> 中的
<filename>summer</filename> 子目录。</para>
<para>经由设置 <varname>COPYTREE_*</varname> 宏的第三个参数,
您还可以为 <command>find</command> 指定额外的参数。
例如, 如果希望安装除了 Makefile 之外的其他所有文件,
可以使用下述命令。</para>
<programlisting>post-install:
${MKDIR} ${EXAMPLESDIR}
(cd ${WRKSRC}/examples/ && \
${COPYTREE_SHARE} \* ${EXAMPLESDIR} "! -name Makefile")</programlisting>
<para>需要注意的是, 这些宏并不能自动将所安装的文件加到
<filename>pkg-plist</filename> 中, 您还是需要自行列出它们。</para>
</sect2>
<sect2 xml:id="install-documentation">
<title>安装附加的文档</title>
<para>如果您的软件包含了标准的联机手册和 info 手册以外的文档,
而且您认为它们对用户会有用, 请把这些文档安装到
<filename>PREFIX/share/doc</filename> 下。
和前面类似, 这也可以在
<buildtarget>post-install</buildtarget> target 中完成。</para>
<para>为您的 port 建立一个新的目录。
这个目录的名字应该反映它是属于哪个 port 的。 通常建议使用
<varname>PORTNAME</varname>。 不过, 如果您认为不同版本的 port
可能会同时安装, 也可以用完整的
<varname>PKGNAME</varname>。</para>
<para>另外, 应该让是否安装取决于变量
<varname>NOPORTDOCS</varname> 的设置, 这样用户就能够在
<filename>/etc/make.conf</filename> 中禁止安装它。 例如:</para>
<programlisting>post-install:
.if !defined(NOPORTDOCS)
${MKDIR} ${DOCSDIR}
${INSTALL_MAN} ${WRKSRC}/docs/xvdocs.ps ${DOCSDIR}
.endif</programlisting>
<para>这里是一些便于使用的变量, 以及它们在
<filename>Makefile</filename> 中默认的展开方式:</para>
<itemizedlist>
<listitem>
<para><varname>DATADIR</varname> 会展开成
<filename>PREFIX/share/PORTNAME</filename>。</para>
</listitem>
<listitem>
<para><varname>DATADIR_REL</varname> 会展开成
<filename>share/PORTNAME</filename>。</para>
</listitem>
<listitem>
<para><varname>DOCSDIR</varname> 会展开成
<filename>PREFIX/share/doc/PORTNAME</filename>。</para>
</listitem>
<listitem>
<para><varname>DOCSDIR_REL</varname> 会展开成
<filename>share/doc/PORTNAME</filename>。</para>
</listitem>
<listitem>
<para><varname>EXAMPLESDIR</varname> 会展开成
<filename>PREFIX/share/exampl