# 10.7.Poudriere

对于 Port 贡献者来说，poudriere 是最重要且最有用的测试和构建工具之一。它的主要功能包括：

* 批量构建整个 Ports 树、Ports 树的特定子集或单个 Port 及其依赖项
* 自动打包构建结果
* 每个 Port 的构建日志文件生成
* 提供签名的 [pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 仓库
* 在向 FreeBSD bug 跟踪器提交补丁或向 Ports 树提交时测试 Port 构建
* 测试不同选项下 Port 构建的成功与否

由于 poudriere 在干净的 [jail(8)](https://man.freebsd.org/cgi/man.cgi?query=jail\&sektion=8\&format=html) 环境中执行构建并使用 [zfs(8)](https://man.freebsd.org/cgi/man.cgi?query=zfs\&sektion=8\&format=html) 功能，因此相比传统的主机系统测试，它具有以下几个优势：

* 不污染主机环境：没有残留文件，没有意外删除，没有现有配置文件的更改。
* 验证 **pkg-plist** 是否有缺失或多余的条目
* Ports 提交者有时会要求在提交补丁时附上 poudriere 日志，以评估补丁是否准备好集成到 Ports 树中

poudriere 的设置和使用也非常简单，它没有依赖关系，并且可以在任何受支持的 FreeBSD 版本上运行。本节将展示如何作为 Ports 贡献者的一部分安装、配置和使用 poudriere。

本节中的示例显示的是 FreeBSD 中的默认文件布局，请根据需要替换任何本地更改。Ports 树（由 `${PORTSDIR}` 表示）位于 **/usr/ports**。默认情况下，`${LOCALBASE}` 和 `${PREFIX}` 都是 **/usr/local**。

## 10.7.1. 安装 poudriere

可以在 Ports 树中的 [ports-mgmt/poudriere](https://cgit.freebsd.org/ports/tree/ports-mgmt/poudriere/) 中找到 poudriere。可以通过 [pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 或从 Ports 安装：

```sh
# pkg install poudriere
```

或者

```sh
# make -C /usr/ports/ports-mgmt/poudriere install clean
```

还有一个开发中的版本，最终将成为下个发布版。可在 [ports-mgmt/poudriere-devel](https://cgit.freebsd.org/ports/tree/ports-mgmt/poudriere-devel/) 中找到它。这个开发版本用于官方的 FreeBSD 包构建，因此经过了充分的测试，并且通常包含更新的有趣功能。Ports 提交者会希望使用开发版本，因为它是用于生产环境的，并且拥有所有新的功能，能够确保一切完全正确。贡献者不一定需要这些功能，因为最重要的修复会回溯到发布版本。使用开发版本构建官方包的主要原因是它更快，在高端 32 CPU 服务器和 128GB 内存的情况下，完整构建时间将从 18 小时缩短到 17 小时。而这些优化在桌面机器上构建 Ports 时影响不大。

## 10.7.2. 设置 poudriere

该程序安装了默认的配置文件 **/usr/local/etc/poudriere.conf**。每个参数在配置文件中都有文档说明。

以下是个最简配置文件示例：

```sh
ZPOOL=zroot
BASEFS=/usr/local/poudriere
DISTFILES_CACHE=/usr/ports/distfiles
RESOLV_CONF=/etc/resolv.conf
```

`ZPOOL`\
poudriere 要使用的 ZFS 存储池的名称。必须在 `zpool status` 的输出中列出。

`BASEFS`\
poudriere 文件系统的根挂载点。此条目将导致 poudriere 将 `tank/poudriere` 挂载到 `/poudriere`。

`DISTFILES_CACHE`\
定义存储 distfiles 的位置。在这个示例中，poudriere 和主机共享 distfiles 存储目录。这可以避免重新下载已存在于系统中的 tarballs。如果该目录尚未存在，请创建它，以便 poudriere 可以找到它。

`RESOLV_CONF`\
在 jail 内部使用主机的 **/etc/resolv.conf** 进行 DNS 解析。这是为了使 jail 在下载 distfiles 时能够解析 URL。当使用代理时不需要此项。请参考默认配置文件中的代理配置。

## 10.7.3. 创建 poudriere Jail

创建 poudriere 将用于构建的基础 jail：

```sh
# poudriere jail -c -j 131Ramd64 -v 13.1-RELEASE -a amd64
```

从 **poudriere.conf** 中指定的 FTP 服务器获取 `13.1-RELEASE` 版本的 `amd64`，创建 ZFS 文件系统 `tank/poudriere/jails/131Ramd64`，将其挂载到 **/poudriere/jails/131Ramd64**，并将 `13.1-RELEASE` 的 tarballs 提取到该文件系统中。

```sh
# poudriere jail -c -j 12i386 -v stable/12 -a i386 -m git+https
```

创建 `tank/poudriere/jails/12i386`，将其挂载到 **/poudriere/jails/12i386**，然后从 **poudriere.conf** 中指定的 `GIT_HOST` 或默认的 `git.freebsd.org` 获取 `FreeBSD-12-STABLE` 的 Git 分支的最新代码，检出到 **/poudriere/jails/12i386/usr/src**，然后完成 `buildworld` 并将其安装到 **/poudriere/jails/12i386** 中。

> **注意**
>
> 尽管可以在较旧的版本上构建较新的 FreeBSD 版本，但大多数情况下它无法运行。例如，如果需要 `stable/13` jail，则主机也必须运行 `stable/13`，仅运行 `13.1-RELEASE` 不够。

> **注意**
>
> 要为 `14.0-CURRENT` 创建 poudriere jail：
>
> ```sh
> # poudriere jail -c -j 14amd64 -v main -a amd64 -m git+https
> ```
>
> 要运行 `14.0-CURRENT` 的 poudriere jail，主机必须运行 `14.0-CURRENT`。通常情况下，较新的内核可以构建并运行较旧的 jails。例如，`14.0-CURRENT` 内核可以构建并运行 `12.4-STABLE`，前提是已编译 `COMPAT_FREEBSD12` 内核选项（默认启用，`14.0-CURRENT` 的 GENERIC 内核配置中已启用）。

可以使用 `poudriere jail -l` 显示当前 poudriere 知道的 jail 列表：

```sh
# poudriere jail -l
JAILNAME             VERSION              ARCH    METHOD
131Ramd64            13.1-RELEASE         amd64   ftp
12i386               12.4-STABLE          i386    git+https
```

## 10.7.4. 保持 poudriere Jail 更新

管理更新非常简单。使用以下命令：

```
# poudriere jail -u -j JAILNAME
```

该命令会将指定的 jail 更新到最新版本。对于 FreeBSD 版本更新，可以使用 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html) 更新到最新的补丁级别。对于从源代码构建的 FreeBSD 版本，可以更新到分支中的最新 git 修订版。

> **注意**
>
> 对于采用 `git+*` 方法的 jail，添加 `-J NumberOfParallelBuildJobs` 有助于通过增加并行编译作业数来加速构建。例如，如果构建机器有 6 个 CPU，使用：
>
> ```
> # poudriere jail -u -J 6 -j JAILNAME
> ```

## 10.7.5. 为 poudriere 设置 Ports Trees

在 poudriere 中使用 Ports Trees 有多种方法。最直接的方法是让 poudriere 为自己创建一个默认的 ports tree，使用 [Git](https://docs.freebsd.org/en/books/handbook/mirrors/#git)：

```
# poudriere ports -c -m git+https -B main
```

这些命令会创建 `tank/poudriere/ports/default`，将其挂载到 **/poudriere/ports/default**，并使用 Git 填充它。之后，它会被包括在已知的 ports trees 列表中：

```
# poudriere ports -l
PORTSTREE METHOD    TIMESTAMP           PATH
default   git+https 2020-07-20 04:23:56 /poudriere/ports/default
```

> **注意**
>
> 请注意，“default”ports tree 是特殊的。除非明确指定，否则后续解释的所有构建命令将隐式使用此 ports tree。如果要使用其他树，可以在命令中添加 `-p treename`。

处理本地修改的最佳方法是使用 [Git](https://docs.freebsd.org/en/books/handbook/mirrors/#git)。与创建 jail 类似，可以使用不同的方法来创建 ports tree。为了测试本地修改和开发 Port，最好通过 git 检出树（如上所述）来添加额外的 ports tree。

## 10.7.6. 使用手动管理的 Ports Trees 与 poudriere

根据工作流的不同，使用手动维护的 ports trees 可能非常有用。例如，如果有一个位于 **/work/ports** 的本地 ports tree，可以将 poudriere 指向该位置：

```sh
# poudriere ports -c -m null -M /work/ports -p development
```

这将会在已知树的表格中列出：

```sh
# poudriere ports -l
PORTSTREE   METHOD    TIMESTAMP           PATH
development null      2020-07-20 05:06:33 /work/ports
```

> **注意**
>
> `METHOD` 列中的破折号或 `null` 表示 poudriere 永远不会更新或更改该 ports tree。用户完全负责维护此树，包括所有用于测试新 Port 和提交补丁的本地修改。

## 10.7.7. 保持 poudriere Ports 更新

更新 ports 与上述 jail 更新一样简单：

```sh
# poudriere ports -u -p PORTSTREE
```

该命令会将指定的 *PORTSTREE*（由 `poudriere -l` 输出的树）更新到官方服务器上最新的修订版。

> **注意**
>
> 没有指定更新方式的 ports，参见 [使用手动管理的 Ports 与 poudriere](https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere-ports-tree-manual)，无法像这样更新，必须由维护者手动更新。

## 10.7.8. 测试 Ports

在设置好 jails 和 ports trees 后，可以测试贡献者对 ports tree 的修改。

例如，本地修改位于 **/work/ports/www/firefox** 的 [www/firefox](https://cgit.freebsd.org/ports/tree/www/firefox/) Port，可以在之前创建的 13.1-RELEASE jail 中进行测试：

```sh
# poudriere testport -j 131Ramd64 -p development -o www/firefox
```

这将构建 Firefox 的所有依赖项。如果某个依赖项已构建并且仍是最新的，则会安装预构建的包。如果依赖项没有最新的包，poudriere 将在 jail 中使用默认选项构建该依赖项。然后会构建 Firefox 本身。

每个 Port 的完整构建过程会记录在 **/poudriere/data/logs/bulk/131Ri386-development/build-time/logs** 中。

目录名 `131Ri386-development` 是根据 `-j` 和 `-p` 参数生成的。为了方便起见，还维护了一个符号链接 **/poudriere/data/logs/bulk/131Ri386-development/latest**，该链接指向最新的 *build-time* 目录。该目录中还有一个 **index.html**，可以通过网页浏览器观察构建过程。

默认情况下，poudriere 会清理 jails 并将日志文件保留在上述目录中。为了便于调查，可以通过在 `testport` 命令中添加 `-i` 来保持 jail 在构建后继续运行：

```sh
# poudriere testport -j 131Ramd64 -p development -i -o www/firefox
```

构建完成后，无论是否成功，都会提供一个 shell 进入 jail。可以使用该 shell 进一步调查。poudriere 可以通过 `-I` 选项在构建完成后保持 jail 运行。构建完成后，poudriere 会显示退出 jail 的命令。此时可以使用 [jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html) 进入 jail：

```sh
# poudriere testport -j 131Ramd64 -p development -I -o www/firefox
[...]
====>> Installing local Pkg repository to /usr/local/etc/pkg/repos
====>> Leaving jail 131Ramd64-development-n running, mounted at /poudriere/data/.m/131Ramd64-development/ref for interactive run testing
====>> To enter jail: jexec 131Ramd64-development-n env -i TERM=$TERM /usr/bin/login -fp root
====>> To stop jail: poudriere jail -k -j 131Ramd64 -p development
# jexec 131Ramd64-development-n env -i TERM=$TERM /usr/bin/login -fp root
# [在 jail 中做一些操作]
# exit
# poudriere jail -k -j 131Ramd64 -p development
====>> Umounting file systems
```

FreeBSD ports 构建基础设施的一个重要组成部分是能够根据个人喜好调整 Port 的选项。也可以使用 poudriere 测试这些选项。添加 `-c` 选项：

```sh
# poudriere testport -j 131Ramd64 -c -o www/firefox
```

这会在构建 Port 之前显示 Port 配置对话框。`-o` 后面的 Port 将使用指定的选项，所有依赖项将使用默认选项。可以使用集合测试带有非默认选项的依赖 Port，参见 [使用集合](https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere-sets)。

> **技巧**
>
> 当在构建过程中根据所选选项修改 **pkg-plist** 时，建议分别进行一个测试运行，选中所有选项 *和* 不选任何选项。

## 10.7.9. 使用 Sets

对于所有涉及构建的操作，可以使用 `-z <setname>` 指定一个所谓的 *set*。set 表示一个完全独立的构建。例如，这允许在依赖 Port 使用非标准选项时，使用 `testport`。

为了使用 sets，poudriere 需要一个类似于 `PORT_DBDIR` 的现有目录结构，默认情况下为 **/var/db/ports**，该目录位于其配置目录中。然后，使用 [nullfs(5)](https://man.freebsd.org/cgi/man.cgi?query=nullfs\&sektion=5\&format=html) 将该目录挂载到构建 jails 中的 Port 及其依赖项的位置。通常，可以通过递归复制现有的 `PORT_DBDIR` 到 **/usr/local/etc/poudriere.d/jailname-portname-setname-options** 来获得合适的起始点。详情请见 [poudriere(8)](https://man.freebsd.org/cgi/man.cgi?query=poudriere\&sektion=8\&format=html)。例如，要在名为 `devset` 的特定 set 中测试 [www/firefox](https://cgit.freebsd.org/ports/tree/www/firefox/)，只需在 `testport` 命令中添加 `-z devset` 参数：

```sh
# poudriere testport -j 131Ramd64 -p development -z devset -o www/firefox
```

poudriere 将按以下顺序查找这些目录的存在：

* **/usr/local/etc/poudriere.d/131Ramd64-development-devset-options**
* **/usr/local/etc/poudriere.d/131Ramd64-devset-options**
* **/usr/local/etc/poudriere.d/131Ramd64-development-options**
* **/usr/local/etc/poudriere.d/devset-options**
* **/usr/local/etc/poudriere.d/development-options**
* **/usr/local/etc/poudriere.d/131Ramd64-options**
* **/usr/local/etc/poudriere.d/options**

从这个列表中，poudriere 会 [nullfs(5)](https://man.freebsd.org/cgi/man.cgi?query=nullfs\&sektion=5\&format=html) 挂载 *第一个存在* 的目录树到构建 jail 中的 **/var/db/ports** 目录。因此，所有自定义选项将用于此运行中的所有 Port。

提供 set 的目录结构后，可以更改特定 Port 的选项。例如：

```sh
# poudriere options -c www/firefox -z devset
```

这将显示 [www/firefox](https://cgit.freebsd.org/ports/tree/www/firefox/) 的配置对话框，并可以编辑选项。所选选项将保存到 `devset` set 中。

> **注意**
>
> poudriere 在选项配置方面非常灵活。poudriere 可以针对特定的 jails、ports trees 以及多个 Port 通过一个命令进行设置。有关详情，请参见 [poudriere(8)](https://man.freebsd.org/cgi/man.cgi?query=poudriere\&sektion=8\&format=html)。

## 10.7.10. 提供自定义 **make.conf** 文件

与使用 sets 类似，poudriere 还可以使用提供的自定义 **make.conf** 文件。如果提供了该文件，则无需特殊的命令行参数。相反，poudriere 会根据命令行中的名称模式查找现有文件。例如：

```sh
# poudriere testport -j 131Ramd64 -p development -z devset -o www/firefox
```

这将导致 poudriere 按以下顺序查找这些文件的存在：

* **/usr/local/etc/poudriere.d/make.conf**
* **/usr/local/etc/poudriere.d/devset-make.conf**
* **/usr/local/etc/poudriere.d/development-make.conf**
* **/usr/local/etc/poudriere.d/131Ramd64-make.conf**
* **/usr/local/etc/poudriere.d/131Ramd64-development-make.conf**
* **/usr/local/etc/poudriere.d/131Ramd64-devset-make.conf**
* **/usr/local/etc/poudriere.d/131Ramd64-development-devset-make.conf**

与 sets 不同，所有找到的文件将 *按顺序* 追加到构建 jails 中的一个 **make.conf** 文件中。因此，可以在 **/usr/local/etc/poudriere.d/make.conf** 中设置影响所有构建的通用 make 变量。特定 jails 或 sets 的特殊变量可以在专用的 **make.conf** 文件中进行设置，例如 **/usr/local/etc/poudriere.d/131Ramd64-development-devset-make.conf**。

**示例 1. 使用 make.conf 更改默认 Perl 版本**

例如，要使用名为 `perl5-20` 的 set 构建一个非默认的 Perl 版本 `5.20`，可以创建一个 **perl5-20-make.conf** 文件，并添加以下内容：

```sh
DEFAULT_VERSIONS+= perl=5.20
```

注意使用 `+=`，这样即使该变量在默认 **make.conf** 中已设置，其内容也不会被覆盖。

## 10.7.11. 清理不再需要的 distfiles

poudriere 提供了一种内建机制，用于删除不再被任何 Port 使用的过时 distfiles。使用以下命令：

```sh
# poudriere distclean -p portstree
```

该命令将扫描 **poudriere.conf** 中的 `DISTFILES_CACHE` 文件夹，并与通过 `-p <portstree>` 参数指定的 Ports 树进行对比，提示删除那些不再使用的 distfiles。如果希望跳过提示，直接无条件删除所有未使用的文件，可以添加 `-y` 参数：

```sh
# poudriere distclean -p portstree -y
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://porters-handbook.bsdcn.org/di-10-zhang-ce-shi-port/10.7.poudriere.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
