# 4.1 Port 工作原理

好的，看来这并不像想象中那样简单，Port 需要一些修改才能正常工作。在本节中，我们将逐步解释如何修改它，以使其适应 Ports 的使用模式。

***

首先，当用户在 Port 目录下输入 `make` 时，会发生以下事件。阅读 **bsd.port.mk** 的相关内容能帮助更好地理解这一过程，建议在另一个窗口打开 **bsd.port.mk** 进行参考。

但别担心，并没有多少人完全理解 **bsd.port.mk** 的工作原理… *:-)*

1. **fetch** 目标运行。`fetch` 目标负责确保 tarball 文件存在于本地的 `DISTDIR` 中。如果 `fetch` 在 `DISTDIR` 找不到所需的文件，它会查找 **Makefile** 中定义的 `MASTER_SITES` URL，或者我们将 distfiles 放入的 FTP 镜像站点作为备份。它将尝试使用 `FETCH` 下载指定的分发文件，假设请求的站点可以直接访问互联网。如果下载成功，文件将保存在 `DISTDIR` 中供将来使用，然后继续进行下一步。
2. **extract** 目标运行。该目标会在 `DISTDIR` 中查找 Port 的分发文件（通常是压缩的 tarball 文件），并将其解压到指定的临时子目录 `WRKDIR` 中（默认是 **work**）。
3. **patch** 目标运行。首先，应用在 `PATCHFILES` 中定义的补丁。其次，如果在 `PATCHDIR` 中找到任何名为 **patch-**\* 的补丁文件（默认位于 **files** 子目录中），则按字母顺序在此时应用它们。
4. **configure** 目标运行。这个目标可以执行许多不同的操作。
   1. 如果存在，则运行 **scripts/configure**。
   2. 如果设置了 `HAS_CONFIGURE` 或 `GNU_CONFIGURE`，则运行 **WRKSRC/configure**。
5. **build** 目标运行。该目标负责进入 Port 的私有工作目录（`WRKSRC`）并进行编译。
6. **stage** 目标运行。这个目标将最终构建好的文件放入一个临时目录（`STAGEDIR`，请参见 [Staging](https://docs.freebsd.org/en/books/porters-handbook/special/#staging)）。该目录的层级结构与将要安装该包的系统相似。
7. **package** 目标运行。这个目标会根据 `stage` 目标期间创建的临时目录中的文件以及 Port 的 **pkg-plist** 来创建一个包。
8. **install** 目标运行。这个目标将通过 `package` 目标创建的包安装到主机系统中。

以上是默认的操作步骤。此外，可以定义 `pre-something` 或 `post-something` 目标，或在 **scripts** 子目录中放置相应的脚本，它们将在默认操作前后执行。

例如，如果 **Makefile** 中定义了 `post-extract` 目标，并且 **scripts** 子目录中有一个 **pre-build** 文件，那么 `post-extract` 目标将在常规的解压操作之后被调用，而 **pre-build** 文件则会在默认的构建规则之前执行。建议在操作较简单时使用 **Makefile** 目标，因为这样更容易让其他人理解 Port 需要什么样的非默认操作。

默认的操作由 **bsd.port.mk** 中的 `do-something` 目标完成。例如，解压 Port 的命令位于 `do-extract` 目标中。如果默认目标没有正确执行某个操作，可以重新定义 **Makefile** 中的 `do-something` 目标。

> **注意**
>
> “主要”目标（例如 `extract`、`configure` 等）只不过是确保所有前置阶段都已完成，然后调用真正的目标或脚本，它们不应该被修改。要修复解压问题，就修改 `do-extract`，但绝不能改变 `extract` 的操作！另外，`post-deinstall` 目标无效，Port 基础设施不会运行它。

了解了用户输入 `make install` 时的操作流程之后，接下来我们将逐步介绍如何创建完美的 Port。
