> For the complete documentation index, see [llms.txt](https://handbook.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://handbook.bsdcn.org/di-4-zhang-an-zhuang-ying-yong-cheng-xu-ruan-jian-bao-he-ports/4.5.-shi-yong-ports.md).

# 4.5.使用 Ports

Ports 是一组 `Makefile`、补丁和说明文件。每一组文件用于在 FreeBSD 上编译并安装某个单独的应用程序，称为 *Port*。

默认情况下，Ports 本身存储在 **/usr/ports** 的子目录中。

> **警告**
>
> 在安装和使用 Ports 之前，请注意：通常不建议将 Ports 与通过 pkg 提供的二进制软件包混用。pkg 默认跟踪的是 Ports 树的季度分支（quarterly branch），而不是 HEAD。某个 Port 在 HEAD 中的依赖项，可能与其在季度分支中的不同，这可能会导致 pkg 安装的依赖项与 Ports 安装的依赖项之间产生冲突。如果必须同时使用 Ports 和 pkg，请确保它们使用的是同一分支的 Ports 树。

Ports 包含以软件类别划分的目录，每个类别目录中包含若干应用程序的子目录。每个应用程序的子目录包含一组告诉 FreeBSD 如何编译和安装该程序的文件，这组文件称为 *Ports 框架*。每份 Ports 框架包含以下文件和目录：

* **Makefile**：包含指定如何编译应用程序以及将组件安装到何处的指令。
* **distinfo**：包含构建该 Port 所需下载文件的名称和校验和。
* **files/**：此目录包含为了使程序能在 FreeBSD 上编译和安装所需的补丁；此外也可能包含构建该 Port 所用的其他文件。
* **pkg-descr**：提供程序的详细说明。
* **pkg-plist**：列出该 Port 安装的所有文件；它也告诉 Ports 系统在卸载时要删除哪些文件。

一些 Port 还包含 `pkg-message` 或其他用于处理特殊情况的文件。有关这些文件以及 Ports 的更多信息，请参考 [FreeBSD Porter’s Handbook](https://docs.freebsd.org/en/books/porters-handbook/)。

Port 本身并不包含实际的源代码（也叫 `distfile`）。在构建 Port 时的 extract 阶段会自动将下载的源代码保存到 **/usr/ports/distfiles**。

## 4.5.1. 安装 Ports

在通过 Port 编译应用程序之前，必须先安装 Ports。如果在安装 FreeBSD 时未选择安装 Ports，可以使用以下方法来安装：

**操作步骤：Git 方法**

如果你需要对 Ports 树有更多控制，或需要保留本地修改，或你正在使用 FreeBSD-CURRENT，可以使用 Git 获取 Ports。详细的 Git 使用说明请参考 [Git 入门指南](https://docs.freebsd.org/en/articles/committers-guide/#git-primer)。

我们在 Git 命令中加入 `--depth 1` 参数，以便在克隆时不获取提交历史，这样能节省时间，对于大多数用户而言已经足够。如果你对 Ports 树有自己的修改，或出于其他原因需要保留历史记录，则可以省略下面命令中的 `--depth 1` 参数。

1. 在使用 Git 克隆 Ports 树之前，必须先安装 Git。如果系统上已经有 Ports 树，可以通过以下方式安装 Git：

   ```sh
   # cd /usr/ports/devel/git
   # make install clean
   ```

   如果系统上没有 Ports 树，或是使用 pkg 来管理软件包，也可以用 pkg 安装 Git：

   ```sh
   # pkg install git
   ```
2. 克隆 Ports 树的 HEAD 分支：

   ```sh
   # git clone --depth 1 https://git.FreeBSD.org/ports.git /usr/ports
   ```
3. 或者，克隆某个季度分支的 Ports 树：

   ```sh
   # git clone --depth 1 https://git.FreeBSD.org/ports.git -b 2023Q1 /usr/ports
   ```
4. 在完成首次 Git 克隆之后，如需更新 **/usr/ports**：

   ```sh
   # git -C /usr/ports pull
   ```
5. 如需将 **/usr/ports** 切换到其他季度分支：

   ```sh
   # git -C /usr/ports fetch --depth 1 origin 2023Q2:2023Q2
   # git -C /usr/ports switch 2023Q2
   ```

## 4.5.2. 安装 Ports

本节介绍如何使用 Ports 安装或移除软件的基本方法。关于可用的 `make` 目标和环境变量的详细说明，请参阅 [ports(7)](https://man.freebsd.org/cgi/man.cgi?query=ports\&sektion=7\&format=html)。

> **警告**
>
> 在编译任何 Port 之前，请务必按照上一节中的说明更新 Ports。由于安装任何第三方软件都可能带来安全漏洞，建议事先在 <https://vuxml.freebsd.org/> 上查看该 Port 是否存在已知安全问题。或者，可以在安装新 Port 之前运行 `pkg audit -F`。此命令也可以配置为在每日安全检查中自动执行安全审计并更新漏洞数据库。详见 [pkg-audit(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg-audit\&sektion=8\&format=html) 和 [periodic(8)](https://man.freebsd.org/cgi/man.cgi?query=periodic\&sektion=8\&format=html)。

使用 Ports 编译程序需要系统具备正常的网络连接，并且需要超级用户权限。

要编译并安装 Port，先切换到目标 Port 的目录下，然后输入 `make install`。屏幕上会显示安装进度信息：

```sh
# cd /usr/ports/sysutils/lsof
# make install
>> lsof_4.88D.freebsd.tar.gz doesn't seem to exist in /usr/ports/distfiles/.
>> Attempting to fetch from ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/.
===>  Extracting for lsof-4.88
...
[解压输出略]
...
>> Checksum OK for lsof_4.88D.freebsd.tar.gz.
===>  Patching for lsof-4.88.d,8
===>  Applying FreeBSD patches for lsof-4.88.d,8
===>  Configuring for lsof-4.88.d,8
...
[配置输出略]
...
===>  Building for lsof-4.88.d,8
...
[编译输出略]
...

===>  Installing for lsof-4.88.d,8
...
[安装输出略]
...
===>   Generating temporary packing list
===>   Compressing manual pages for lsof-4.88.d,8
===>   Registering installation for lsof-4.88.d,8
===>  SECURITY NOTE:
      This port has installed the following binaries which execute with
      increased privileges.
/usr/local/sbin/lsof
#
```

由于 `lsof` 是一款以提升权限运行的程序，因此在安装过程中会显示安全警告。安装完成后，系统会返回 shell 提示符。

某些 shell 会缓存 `PATH` 环境变量所列目录中的可执行命令列表，以加快命令查找速度。使用 `tcsh` 的用户应在安装新命令后输入 `rehash`，以便无需指定完整路径即可调用该命令；使用 `sh` 的用户应使用 `hash -r`。更多信息请参考各自的 shell 文档。

在安装过程中，系统会创建用于临时编译的子目录。删除该目录可以节省磁盘空间，并减少将来升级该 Port 时可能出现的问题：

```sh
# make clean
===>  Cleaning for lsof-88.d,8
#
```

> **注意**
>
> 若想省去这一步，可在编译时直接使用命令 `make install clean`。

### 4.5.2.1. 自定义 Ports 安装

某些 Ports 提供构建选项，可用于启用或禁用应用程序组件、设置安全选项，或做其他定制。例如 [www/firefox](https://cgit.freebsd.org/ports/tree/www/firefox/) 和 [security/gpgme](https://cgit.freebsd.org/ports/tree/security/gpgme/)。如果该 Port 依赖其他也提供可配置选项的 Port，安装过程中可能会多次暂停并提示用户从菜单中选择选项。为避免多次交互并一次性完成所有配置，可在该 Port 的目录中运行 `make config-recursive`。然后再执行 `make install [clean]` 以编译并安装该 Port。

> **技巧**
>
> 使用 `config-recursive` 时，待配置的 Port 列表由 `all-depends-list` 目标收集。建议重复运行 `make config-recursive`，直到所有依赖 Port 的选项都已定义，且 Port 选项界面不再出现，这样才能确保所有依赖项选项均已配置完毕。

要在 Port 构建后重新进入构建选项菜单，以便添加、删除或更改选项，有几种方法。一种是进入该 Port 所在目录并输入 `make config`。另一种是使用 `make showconfig` 查看当前选项。还可以执行 `make rmconfig` 删除所有已选项，然后重新配置。这些方法及其他选项在 [ports(7)](https://man.freebsd.org/cgi/man.cgi?query=ports\&sektion=7\&format=html) 中有详细说明。

Ports 系统使用 [fetch(1)](https://man.freebsd.org/cgi/man.cgi?query=fetch\&sektion=1\&format=html) 下载源代码文件，它支持多种环境变量。如果 FreeBSD 系统处于防火墙或 FTP/HTTP 代理之后，可能需要设置 `FTP_PASSIVE_MODE`、`FTP_PROXY` 和 `FTP_PASSWORD` 等变量。完整变量列表请参考 [fetch(3)](https://man.freebsd.org/cgi/man.cgi?query=fetch\&sektion=3\&format=html)。

对于无法始终连接网络的用户，可以在 **/usr/ports** 中运行 `make fetch` 来下载所有 distfiles，也可以在某个类别目录（如 **/usr/ports/net**）或具体 Port 目录中运行该命令。但请注意，如果该 Port 有依赖项，则在分类或 Port 目录中运行此命令 *不会* 下载其他分类中依赖项的 distfiles。如需同时下载所有依赖的 distfiles，请使用 `make fetch-recursive`。

在少数情况下，如组织有本地 distfiles 存储库，可使用 `MASTER_SITES` 变量来覆盖 `Makefile` 中指定的下载路径。使用时，请指定替代位置：

```sh
# cd /usr/ports/directory
# make MASTER_SITE_OVERRIDE= \
ftp://ftp.organization.org/pub/FreeBSD/ports/distfiles/ fetch
```

`WRKDIRPREFIX` 和 `PREFIX` 变量可用来覆盖默认的构建目录和安装目录。例如：

```sh
# make WRKDIRPREFIX=/usr/home/example/ports install
```

将在 **/usr/home/example/ports** 下编译，并将程序安装到 **/usr/local**。

```sh
# make PREFIX=/usr/home/example/local install
```

将在 **/usr/ports** 下编译，并将程序安装到 **/usr/home/example/local**。再如：

```sh
# make WRKDIRPREFIX=../ports PREFIX=../local install
```

则会同时更改工作目录和安装目录。

这些变量也可以通过环境变量来设置。如何设置环境变量，请参考所使用 shell 的手册页。

## 4.5.3. 移除已安装的 Ports

已安装的 Ports 可以使用 `pkg delete` 卸载。该命令的使用示例可见于 [pkg-delete(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg-delete\&sektion=8\&format=html) 手册页。

另外，也可以在该 Port 的目录中运行 `make deinstall`：

```sh
# cd /usr/ports/sysutils/lsof
# make deinstall
===>  Deinstalling for sysutils/lsof
===>   Deinstalling
Deinstallation has been requested for the following 1 packages:

	lsof-4.88.d,8

The deinstallation will free 229 kB
[1/1] Deleting lsof-4.88.d,8... done
```

建议在卸载 Port 时阅读相关提示信息。如果该 Port 有其他依赖它的程序，将显示相关信息，但卸载过程仍将继续。在这种情况下，更好的做法可能是重新安装该程序，以避免依赖损坏。

## 4.5.4. 升级 Ports

随着时间推移，Ports 中的软件可能会有更新版本。本节说明如何判断哪些软件可以升级，以及如何执行升级操作。

要判断已安装的 Ports 是否有新版本可用，首先确保使用 [Git 方法](#id-4.5.1.-an-zhuang-ports) 中说明的命令更新 Ports 树。然后执行以下命令列出已过期的 Ports：

```sh
# pkg version -l "<"
```

> **重要**
>
> 在尝试升级之前，请阅读 **/usr/ports/UPDATING** 文件，从文件顶部阅读到离上一次升级 Ports 或系统安装时间最近的日期。该文件说明了用户在升级 Port 时可能遇到的各种问题和所需的额外步骤，例如文件格式变化、配置文件路径变更，或与旧版本不兼容等问题。请记录与需升级的 Port 匹配的所有说明，并在执行升级时遵循这些指引。

### 4.5.4.1. 升级和管理 Ports 的工具

Ports 提供了若干实用工具用于执行实际升级操作。每种工具都有其优点和局限。

以前大多数系统使用的是 Portmaster 或 Portupgrade。Synth 是一款较新的可选工具。

> **注意**
>
> 应选择哪个工具由系统管理员根据具体情况决定。建议在使用任何这些工具之前先备份数据。

### 4.5.4.2. 使用 Portmaster 升级 Ports

[ports-mgmt/portmaster](https://cgit.freebsd.org/ports/tree/ports-mgmt/portmaster/) 是非常小巧的工具，用于升级已安装的 Ports。它的设计目标是仅使用 FreeBSD 基本系统中已有的工具，而不依赖其他 Ports 或数据库。要通过 Ports 安装该工具：

```sh
# cd /usr/ports/ports-mgmt/portmaster
# make install clean
```

Portmaster 将 Ports 分为以下四类：

* 根 Port（Root Port）：没有依赖，也不会被其他 Ports 依赖。
* 树干 Port（Trunk Port）：没有依赖，但会被其他 Ports 依赖。
* 分支 Port（Branch Port）：有依赖，且会被其他 Ports 依赖。
* 叶子 Port（Leaf Port）：有依赖，但不会被其他 Ports 依赖。

要列出这些类别并检查可用更新：

```sh
# portmaster -L
===>>> Root ports (No dependencies, not depended on)
===>>> ispell-3.2.06_18
===>>> screen-4.0.3
        ===>>> New version available: screen-4.0.3_1
===>>> tcpflow-0.21_1
===>>> 7 root ports
...
===>>> Branch ports (Have dependencies, are depended on)
===>>> apache22-2.2.3
        ===>>> New version available: apache22-2.2.8
...
===>>> Leaf ports (Have dependencies, not depended on)
===>>> automake-1.9.6_2
===>>> bash-3.1.17
        ===>>> New version available: bash-3.2.33
...
===>>> 32 leaf ports

===>>> 137 total installed ports
        ===>>> 83 have new versions available
```

要升级所有已过期的 Ports：

```sh
# portmaster -a
```

> **注意**
>
> 默认情况下，Portmaster 会在删除旧版本 Port 之前创建备份软件包。如果新版本安装成功，它会删除该备份。使用 `-b` 可以指示 Portmaster 不自动删除备份。加上 `-i` 可启用交互模式，升级每个 Port 前询问用户确认。Portmaster 提供了许多其他选项，详情请阅读 [portmaster(8)](https://man.freebsd.org/cgi/man.cgi?query=portmaster\&sektion=8\&format=html) 手册页。

如果升级过程中出现错误，可加上 `-f` 强制升级并重建所有 Ports：

```sh
# portmaster -af
```

Portmaster 也可以用于安装新的 Ports，会在构建和安装新 Port 之前先升级所有依赖项。要使用此功能，请指定 Port 在 Ports 中的位置：

```sh
# portmaster shells/bash
```

更多关于 [ports-mgmt/portmaster](https://cgit.freebsd.org/ports/tree/ports-mgmt/portmaster/) 的信息请参考其 `pkg-descr`。

### 4.5.4.3. 使用 Portupgrade 升级 Ports

> **警告**
>
> Portupgrade 已弃用，并将于不久后移除。

[ports-mgmt/portupgrade](https://cgit.freebsd.org/ports/tree/ports-mgmt/portupgrade/) 是另一款用于升级 Ports 的工具。它会安装一组应用程序，用于管理 Ports。但该工具依赖 Ruby。要安装该 Port：

```sh
# cd /usr/ports/ports-mgmt/portupgrade
# make install clean
```

在使用此工具执行升级前，建议使用 `pkgdb -F` 扫描已安装的 Ports 列表，并修复其报告的所有不一致问题。

要升级系统上所有过期的 Ports，使用 `portupgrade -a`。或者，加上 `-i` 逐一确认每个升级：

```sh
# portupgrade -ai
```

若只想升级指定的程序，而不是所有可用 Ports，使用 `portupgrade pkgname`。必须加上 `-R` 以先升级该程序所依赖的所有 Ports：

```sh
# portupgrade -R firefox
```

加上 `-P` 后，Portupgrade 会在 `PKG_PATH` 中列出的本地目录中查找已有的软件包文件，若找不到，则尝试从远程站点获取。如果本地和远程都无法获取，则会使用 Ports 构建。若希望完全不使用 Ports，请使用 `-PP`，该选项在找不到软件包时会直接中止：

```sh
# portupgrade -PP gnome3
```

若只想获取 Port 的 distfiles 或包文件（若加了 `-P`），不构建或安装，可使用 `-F`。更多选项请参考 `portupgrade` 的手册页。

更多关于 [ports-mgmt/portupgrade](https://cgit.freebsd.org/ports/tree/ports-mgmt/portupgrade/) 的信息可见其 `pkg-descr`。

## 4.5.5. Ports 与磁盘空间

随着时间推移，使用 Ports 会逐渐占用磁盘空间。在构建并安装某 Port 之后，在该 Port 的目录中运行 `make clean` 可以清理临时的 `work` 目录。如果使用 Portmaster 安装 Port，它默认会自动移除该目录，除非指定了 `-K` 选项。而如果已安装 Portupgrade，以下命令可以移除本地 Ports 副本中所有的 `work` 目录：

```sh
# portsclean -C
```

此外，过时的源代码分发文件（distfiles）会在 **/usr/ports/distfiles** 中逐渐堆积。若要删除所有不再被任何 Port 引用的 distfile，可使用 Portupgrade 提供的命令：

```sh
# portsclean -D
```

Portupgrade 也可以移除系统中所有当前安装的 Ports 不再引用的 distfile：

```sh
# portsclean -DD
```

若使用 Portmaster，可执行以下命令：

```sh
# portmaster --clean-distfiles
```

默认情况下，该命令是交互式的，会逐个询问用户是否删除某个 distfile。

除了以上命令之外，[ports-mgmt/pkg\_cutleaves](https://cgit.freebsd.org/ports/tree/ports-mgmt/pkg_cutleaves/) 可用于自动化清理那些不再需要的已安装 Ports。
