---
title: 第 10 章 升级一个 port
prev: books/porters-handbook/testing
next: books/porters-handbook/security
showBookMenu: true
weight: 10
params:
  path: "/books/porters-handbook/port-upgrading/"
---

[[port-upgrading]]
= 升级一个 port
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 10
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/porters-handbook/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

如果您发现某个 port 相对原作者所发布的版本已经过时， 则首先需要确认的是您的 port 是最新的。 您可以在 FreeBSD FTP 镜像的 [.filename]#ports/ports-current# 目录中找到它们。 但是， 如果您正在使用较多的 port， 则可能使用 CVSup 来保持 Ports Collection 最新更为简单， 这在 extref:{handbook}[使用手册, CVSUP-CONFIG] 中进行了介绍。 此外， 这样做也有助于保持 port 依赖关系的正确性。

下一步是检查是否已经有在等待的更新。 要完成这项工作， 可以采用下列两种方法之一。 有一个用于搜索 http://www.FreeBSD.org/cgi/query-pr-summary.cgi?query[ FreeBSD 问题报告 (PR) 数据库] (也被称作 `GNATS`)。 在下拉框中选择 `ports`， 然后输入 port 的名字。

但是， 有些时候人们会忘记将避免混淆的 port 的名字放到 Synopsis 字段中。 这种时候， 您可以试试看 <<portsmon, FreeBSD Ports 监视系统>> (也被叫做 `portsmon`)。 这个系统会尝试按照 port 的名字来进行分类。 要搜索和某个特定 port 有关的 PR， 可以使用 http://portsmon.FreeBSD.org/portoverview.py[ port概览]。

如果没有候审的 PR， 下一步是给 port 的维护者写信， 这可以通过执行 `make maintainer` 看到。 这个人可能正在进行升级工作， 或者由于某种理由暂时没有升级 (例如， 新版本有稳定性问题)； 一般您不希望重复他们的工作。 注意没有维护者的 port 的维护者会显示为 `ports@FreeBSD.org`， 这是一般性 port 问题的邮件列表， 因此发邮件给它一般没什么意义。

如果维护者要求您去完成升级， 或者没有维护者， 您就有机会通过自行完成升级来帮助 FreeBSD 了！ 请使用基本系统提供的 man:diff[1] 命令来完成相关的工作。

如果只修改一个文件， 可以直接使用 `diff` 来生成补丁， 将需要修改的文件复制成 _something.orig_， 并将改动放进 _something_， 接着生成补丁：

[source,shell]
....
% /usr/bin/diff something.orig something > something.diff
....

如果不是这样的话， 则您应使用 `cvs diff` 的方法 (<<cvs-diff>>)， 或将目录整个复制到另一个目录， 并使用 man:diff[1] 比较两个目录时在目录中递归产生的输出结果 (例如， 如果您修改后的 port 目录的名字是 [.filename]#superedit# 而原始文件的目录是 [.filename]#superedit.bak#， 则应保存 `diff -ruN superedit.bak superedit` 的结果)。 一致式 (unified) 或 上下文式 (context) diff 都是可以的， 但一般来说 port committer 会更喜欢一致式 diff。 请注意这里使用的选项 `-N`， 它的目的是强制 diff 正确地处理出现新文件， 或老文件被删除的情形。 在把 diff 发给我们之前， 请再次检查输出， 以便确认每一个修改都是有意义的。 (特别注意， 在对比目录之前要用 `make clean` 清理一下)。

为了简化常用的补丁文件操作， 您可以使用 [.filename]#/usr/ports/Tools/scripts/patchtool.py#。 使用之前， 请首先阅读 [.filename]#/usr/ports/Tools/scripts/README.patchtool#。

如果 port 目前还无人维护， 而且您自己经常使用它， 请考虑自荐为它的维护者。 FreeBSD 有超过 4000 个没有维护者的 port， 而这正是最需要志愿人员的领域。 (要了解关于维护者的任务描述， 请参见 extref:{developers-handbook}[开发手册中的相关部分, POLICIES-MAINTAINER]。)

将 diff 发送给我们的最佳方式是通过 man:send-pr[1] (category 一栏写 `ports`)。 如果您正维护那个 port， 请务必在 synopsis 的开头写上 `[maintainer update]`， 并将您的 PR 的 "Class" 设置为 `maintainer-update`。 反之， 您的 PR 的 "Class" 就应该是 `change-request`。 请在信中逐个提及每一个删除或增加的文件， 因为这些都必须明确地在使用 man:cvs[1] 进行 commit 时明确地指定。 如果 diff 超过了 20K， 请考虑压缩并对其进行 uuencode； 否则， 简单地将其原样加入 PR 即可。

在您 man:send-pr[1] 之前， 请再次阅读 Problem Reports 一文中的 extref:{problem-reports}[如何撰写问题报告, pr-writing] 小节； 它给出了丰富的关于如何撰写更好的问题报告的介绍。

[IMPORTANT]
====
如果您的更新是由于安全考虑， 或修复已经 commit 的 port 中的严重问题， 请通知 {portmgr} 来申请立即重建和分发您的 port 的 package。 否则， 不愿怀疑的使用 man:pkg_add[1] 的用户， 可能会在未来数周之内继续通过使用 `pkg_add -r` 安装旧版本。
====

[NOTE]
====
再次强调， 请使用 man:diff[1] 而非 man:shar[1] 来发送现有 port 的更新！ 这可以帮助 ports committer 理解需要修改的内容。
====

现在您已经了解了所需的所有操作， 您可能会像要阅读在 crossref:keeping-up[keeping-up, 保持同步] 中关于如何保持最新的描述。

[[cvs-diff]]
== 使用 CVS 制作补丁

如果可能的话， 请提交man:cvs[1] diff； 这种情形要比直接比较 "新、旧" 目录要容易处理。 此外， 这种方法也让您更容易看出到底改了什么， 并在其他人更新了 Ports Collection 时容易合并这些改动， 在提交之前， 这可以减少维护补丁所需的工作。

[source,shell]
....
% cd ~/my_wrkdir <.>
% cvs -d R_CVSROOT co pdnsd <.> <.>
% cd ~/my_wrkdir/pdnsd
....

<.> 当然， 这可以是您指定的任意目录； 联编 port 并不局限于 [.filename]#/usr/ports/# 的子目录。

<.> R_CVSROOT 是任何一个公共的 cvs 镜像服务器， 您可以在 extref:{handbook}mirrors/[FreeBSD 使用手册, cvsup] 中挑选一个。

<.> pdnsd 是 port 的模块名字； 通常说来它和 port 的名字一样， 不过也有些例外， 特别是那些本地化类别 (package:german/selfhtml[] 对应的模块名字是 de-selfhtml)； 您可以通过 link:https://www.FreeBSD.org/cgi/cvsweb.cgi/ports[cvsweb 界面] 查询， 或者也可以指定完整路径， 例如在我们这个例子中是 [.filename]#ports/dns/pdnsd#。

在工作目录中， 您可以像往常一样进行任何更改。 如果您添加或删除了文件， 则需要告诉 `cvs` 来追踪这些改动：

[source,shell]
....
% cvs add new_file
% cvs remove deleted_file
....

请反复检查 crossref:quick-porting[porting-testing, 测试 port] 列出的事项并使用 crossref:quick-porting[porting-portlint, 用 portlint 来检查 port] 进行检查。

[source,shell]
....
% cvs status
% cvs update <.>
....

<.> 这会合并 CVS 中其他人做的改动和您的补丁； 在这个过程中， 您需要仔细观察输出。 文件名前面的那个字母会显示做了什么， 请参阅 <<table-cvs-up>> 中给出的说明。

[[table-cvs-up]]
.cvs update 文件名前字母前缀的含义
[cols="1,1", frame="none"]
|===
|U
|文件更新无误。

|P
|文件更新无误 (通常只有在使用远程代码库时才会看到)。

|M
|文件有本地修改， 并合并成功而未产生任何冲突。

|C
|文件有本地修改， 进行了合并并产生了冲突。
|===

如果您在执行 `cvs update` 时某些文件出现了 `C`， 则说明有其他人在 CVS 中做了修改， 而 man:cvs[1] 无法将这些改动与您本地的改动进行合并。 不过， 无论如何， 最好都检查一下合并的结果， 因为 `cvs` 并不知道 port 应该是什么样子， 因此它所做的合并无论是否产生了冲突， 都有可能 (并且并不罕见) 产生没有意义的结果。

最后一步是以 CVS 中的文件为基础生成 unified man:diff[1]：

[source,shell]
....
% cvs diff -uN > ../`basename ${PWD}`.diff
....

[NOTE]
====
指定 `-N` 十分重要， 因为它确保了添加或删除的文件也出现在补丁中。 补丁将包含删除的文件， 在打上补丁时， 这些文件会被清空， 所以最好在 PR 中提醒 committer 删除它们。
====

根据 <<port-upgrading>> 的指导提交您的补丁。

[[moved-and-updating-files]]
== UPDATING 和 MOVED 文件

如果在升级 port 时需要类似修改配置文件或运行特定的程序这样的特别步骤， 则应在 [.filename]#/usr/ports/UPDATING# 文件中予以说明。 这个文件中的项目格式如下：

[.programlisting]
....
YYYYMMDD:
  AFFECTS: users of port类别/port名字
  AUTHOR: 您的名字 <您的电子邮件地址>

  所需执行的特别步骤
....

如果您需要在内文中加入具体的 portmaster 或 portupgrade 的说明， 请确保所用的 shell 命令使用了正确的转义字符。

如果 port 被删除或改名，则应在 [.filename]#/usr/ports/MOVED# 中添加相应的说明项目。 这个文件中的项目格式如下：

[.programlisting]
....
原来的名字|新名字 (如果删除则应留空)|删除或改名的日期|原因
....
