# 4.4.打补丁

在准备 Port 时，可以使用 [diff(1)](https://man.freebsd.org/cgi/man.cgi?query=diff\&sektion=1\&format=html) 记录已添加或修改的文件，稍后将这些差异提供给 [patch(1)](https://man.freebsd.org/cgi/man.cgi?query=patch\&sektion=1\&format=html)。对典型文件进行此操作时，需要在进行任何更改之前保存原始文件的副本，并使用 **.orig** 后缀。

```sh
% cp file file.orig
```

在所有更改完成后，`cd` 回到 Port 目录。使用 `make makepatch` 生成更新的补丁文件，并将其存储在 **files** 目录中。

> **技巧**
>
> 使用 `BINARY_ALIAS` 在构建过程中替代硬编码命令，避免修改构建文件。有关更多信息，请参阅 [使用 `BINARY_ALIAS` 重命名命令，而不是修改构建](https://docs.freebsd.org/en/books/porters-handbook/makefiles/#binary-alias)。

## 4.4.1. 补丁的一般规则

补丁文件存储在 `PATCHDIR` 中，通常是 **files/**，从这里它们会被自动应用。所有补丁必须相对于 `WRKSRC`。通常 `WRKSRC` 是 `WRKDIR` 的子目录，即提取 distfile 的目录。使用 `make -V WRKSRC` 查看实际路径。补丁名称需要遵循以下规则：

* 避免多个补丁修改同一个文件。例如，**patch-foobar.c** 和 **patch-foobar.c2** 都修改 **${WRKSRC}/foobar.c**，这样会使补丁变得脆弱且难以调试。
* 在创建补丁文件名时，将每个下划线 (`_`) 替换为两个下划线 (`__`)，将每个斜杠 (`/`) 替换为一个下划线 (`_`)。例如，要修补名为 **src/freeglut\_joystick.c** 的文件，对应的补丁应命名为 **patch-src\_freeglut\_\_joystick.c**。不要使用像 **patch-aa** 或 **patch-ab** 这样的命名方式。始终在补丁名称中使用路径和文件名。使用 `make makepatch` 可以自动生成正确的名称。
* 如果更改相关，补丁可以修改多个文件，并且补丁名称应适当命名。例如，**patch-add-missing-stdlib.h**。
* 补丁名称只能使用字符 `[-+._a-zA-Z0-9]`。特别地，*不要使用 `::` 作为路径分隔符*，应使用 `_` 代替。

尽量减少补丁中的非功能性空白字符更改。在开源世界中，项目常常共享大量代码基础，但遵循不同的样式和缩进规则。当从一个项目中提取出一个工作功能并修复另一个项目中的类似问题时，请小心：结果补丁可能充满了非功能性更改。这不仅增加了 Ports 仓库的大小，还使得很难确定到底是哪个问题导致了错误，或者具体修改了哪些内容。

如果必须删除文件，请在 `post-extract` 目标中进行，而不是作为补丁的一部分。

## 4.4.2. 手动生成补丁

> **注意**
>
> 通常不需要手动创建补丁。自动生成补丁，如本节前述，通常是首选方法。然而，有时可能需要手动补丁。

补丁保存为名为 **patch-\*** 的文件，其中 \* 表示被修补的文件路径名，例如 **patch-Imakefile** 或 **patch-src-config.h**。不以 **patch-** 开头的补丁文件名将不会被自动应用。

修改文件后，使用 [diff(1)](https://man.freebsd.org/cgi/man.cgi?query=diff\&sektion=1\&format=html) 记录原始文件和修改后文件之间的差异。`-u` 选项使 [diff(1)](https://man.freebsd.org/cgi/man.cgi?query=diff\&sektion=1\&format=html) 生成“统一”格式的差异，这是首选格式。

```sh
% diff -u file.orig file > patch-pathname-file
```

当为新添加的文件生成补丁时，使用 `-N` 告诉 [diff(1)](https://man.freebsd.org/cgi/man.cgi?query=diff\&sektion=1\&format=html) 将不存在的原始文件视为空文件：

```sh
% diff -u -N newfile.orig newfile > patch-pathname-newfile
```

使用 [diff(1)](https://man.freebsd.org/cgi/man.cgi?query=diff\&sektion=1\&format=html) 的递归选项 (`-r`) 生成补丁是可以的，但请检查生成的补丁，确保其中没有不必要的内容。特别是，当两个备份文件、使用 `Imake` 或 GNU `configure` 的 **Makefile** 之间的差异是没有必要的，并且应该删除。如果需要编辑 **configure.in** 并运行 `autoconf` 以重新生成 `configure`，不要对 `configure` 进行差异比较（它通常会变得很大，达到几千行！）。相反，定义 `USES=autoreconf`，并对 **configure.in** 进行差异比较。

## 4.4.3. 简单自动替换

可以通过 Port 的 **Makefile** 直接使用 [sed(1)](https://man.freebsd.org/cgi/man.cgi?query=sed\&sektion=1\&format=html) 的原地模式进行简单的替换。这对于需要使用变量值的更改非常有用：

```makefile
post-patch:
	@${REINPLACE_CMD} -e 's|/usr/local|${PREFIX}|g' ${WRKSRC}/Makefile
```

> **重要**
>
> 仅使用 [sed(1)](https://man.freebsd.org/cgi/man.cgi?query=sed\&sektion=1\&format=html) 来替换变量内容。必须使用补丁文件而不是 [sed(1)](https://man.freebsd.org/cgi/man.cgi?query=sed\&sektion=1\&format=html) 来替换静态内容。

在移植软件时，源文件中常常使用 CR/LF 换行符约定。这可能会导致进一步打补丁时出现问题、编译警告或脚本执行错误（例如 `/bin/sh^M not found`）。要快速将所有文件从 CR/LF 转换为仅 LF，可以在 Port 的 **Makefile** 中添加以下内容：

```makefile
USES=	dos2unix
```

可以指定要转换的特定文件列表：

```makefile
USES=	dos2unix
DOS2UNIX_FILES=	util.c util.h
```

使用 `DOS2UNIX_REGEX` 可以跨子目录转换一组文件。其参数是一个与 [find(1)](https://man.freebsd.org/cgi/man.cgi?query=find\&sektion=1\&format=html) 兼容的正则表达式。有关格式的更多信息，请参阅 [re\_format(7)](https://man.freebsd.org/cgi/man.cgi?query=re_format\&sektion=7\&format=html)。此选项对于转换所有具有特定扩展名的文件非常有用。例如，转换所有源代码文件，而保持二进制文件不变：

```makefile
USES=	dos2unix
DOS2UNIX_REGEX=	.*\.([ch]|cpp)
```

类似的选项是 `DOS2UNIX_GLOB`，它会对其中列出的每个元素运行 `find`。

```makefile
USES=	dos2unix
DOS2UNIX_GLOB=	*.c *.cpp *.h
```

可以设置转换的基础目录。这在存在多个 distfile 且其中几个文件需要进行行结束符转换时非常有用。

```makefile
USES=	dos2unix
DOS2UNIX_WRKSRC=	${WRKDIR}
```

## 4.4.4. 条件性打补丁

有些 Ports 需要在特定的 FreeBSD 版本或启用/禁用某个选项时才应用补丁。条件性补丁通过将补丁文件的完整路径添加到 `EXTRA_PATCHES` 中来指定。条件性补丁的文件名通常以 **extra-** 开头，虽然这不是必须的。然而，它们的文件名 *不能* 以 **patch-** 开头。如果以 **patch-** 开头，框架将无条件地应用它们，这对条件性补丁来说是不可取的。

**示例 1. 针对特定 FreeBSD 版本应用补丁**

```makefile
.include <bsd.port.options.mk>

# 在此之前打上 iconv const 限定符
.if ${OPSYS} == FreeBSD && ${OSVERSION} < 1100069
EXTRA_PATCHES=	${PATCHDIR}/extra-patch-fbsd10
.endif

.include <bsd.port.mk>
```

**示例 2. 可选地应用补丁**

当一个 [选项](https://docs.freebsd.org/en/books/porters-handbook/makefiles/#makefile-options) 需要补丁时，可以使用 `opt_EXTRA_PATCHES` 和 `opt_EXTRA_PATCHES_OFF` 使补丁依赖于 `opt` 选项。有关更多信息，请参阅 [通用变量替换](https://docs.freebsd.org/en/books/porters-handbook/makefiles/#options-variables)。

```makefile
OPTIONS_DEFINE=	  FOO BAR
FOO_EXTRA_PATCHES=  ${PATCHDIR}/extra-patch-foo
BAR_EXTRA_PATCHES_OFF=	${PATCHDIR}/extra-patch-bar.c \
		${PATCHDIR}/extra-patch-bar.h
```

**示例 3. 使用 `EXTRA_PATCHES` 与目录**

有时，某个功能需要多个补丁，在这种情况下，可以将 `EXTRA_PATCHES` 指向一个目录，它将自动应用该目录中所有名为 **patch-\*** 的文件。

在 **${PATCHDIR}** 中创建一个子目录，并将补丁文件移入其中。例如：

```sh
% ls -l files/foo-patches
-rw-r--r--  1 root  wheel    350 Jan 16 01:27 patch-Makefile.in
-rw-r--r--  1 root  wheel   3084 Jan 18 15:37 patch-configure.ac
```

然后将以下内容添加到 **Makefile** 中：

```makefile
OPTIONS_DEFINE=	FOO
FOO_EXTRA_PATCHES=	${PATCHDIR}/foo-patches
```

框架将会使用该目录中所有名为 **patch-\*** 的文件。


---

# 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-4-zhang-fu-za-de-port/4.4.-da-bu-ding.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.
