4.4.打补丁

在进行移植准备时,可以使用 man:diff[1] 记录已添加或已更改的文件,以便稍后交给 man:patch[1] 进行处理。 在典型文件上执行此操作涉及在进行任何更改之前使用 .orig 后缀保存原始文件的副本。

% cp file file.orig

在所有更改都完成后,返回到移植目录。 使用 make makepatch 生成位于 files 目录中的更新补丁文件。

提示: 使用 BINARY_ALIAS 在构建过程中替代硬编码命令,避免对构建文件进行补丁。 有关更多信息,请参阅交叉引用:makefiles[binary-alias,使用 BINARY_ALIAS 重命名命令,而不是对构建进行补丁]。

4.4.1. 补丁的通用规则

补丁文件存储在 PATCHDIR 中,通常是 files/,它们将会自动应用。 所有补丁都必须相对于 WRKSRC。 通常情况下,WRKSRCWRKDIR 的子目录,而 WRKDIR 是解压 distfile 的目录。 使用 make -V WRKSRC 查看实际路径。 补丁名称必须遵循以下规则:

  • 避免多个补丁修改同一文件。例如,同时有 patch-foobar.cpatch-foobar.c2${WRKSRC}/foobar.c 进行更改会导致补丁变得脆弱且难以调试。

  • 在为补丁文件创建名称时,将每个下划线 (_) 替换为两个下划线 (__),每个斜杠 (/) 替换为一个下划线 (_)。例如,要对名为 src/freeglut_joystick.c 的文件进行补丁,将相应的补丁命名为 patch-src_freeglut__joystick.c。不要使用 patch-aapatch-ab 这样的命名。在补丁名称中始终使用路径和文件名。使用 make makepatch 会自动生成正确的名称。

  • 如果更改是相关的并且补丁的命名合适,一个补丁可以修改多个文件。例如,patch-add-missing-stdlib.h

  • 仅使用字符 [-+.\_a-zA-Z0-9] 来命名补丁。特别地,不要使用 :: 作为路径分隔符, 请使用 _ 代替。

在补丁中,尽量减少非功能性空白更改的数量。 在开源世界中,项目常常共享大量的代码库,但遵循不同的样式和缩进规则。 当从一个项目中解压一个工作正常的功能来修复另一个项目中的类似区域时,请小心:生成的补丁可能充满了非功能性的更改。 这不仅会增加 Port 库的大小,还会使问题的根本原因变得难以查找,以及确切做了哪些更改。

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

4.4.2. 手动生成补丁

注意

通常情况下,不需要手动创建补丁。 如本节前面所述,自动生成补丁是首选方法。 然而,偶尔可能需要手动进行补丁。

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

在文件被修改后,会使用 man:diff[1] 记录原始版本和修改后版本之间的差异。 -u 参数使 man:diff[1] 生成“unified”diff,这是首选形式。

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

在生成新添加文件的补丁时,使用 -N 参数告诉 man:diff[1] 将不存在的原始文件视为存在但为空:

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

使用 man:diff[1] 的递归 (-r) 选项生成补丁是可以的,但请检查生成的补丁,确保其中没有不必要的内容。 特别地,当 Port 使用 Imake 或 GNU configure 时,两个备份文件之间的差异,比如 Makefile,是不必要的,需要删除。 如果需要编辑 configure.in 并运行 autoconf 重新生成 configure,请不要取 configure 的差异(它通常会增长到几千行!)。 而是定义 USES=autoreconf 并取 configure.in 的差异。

4.4.3. 简单的自动替换

可以直接从 Port 的 Makefile 使用 man:sed[1] 的就地模式进行简单的替换。 这在更改使用变量的值时非常有用:

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

重要

只使用 man:sed[1] 来替换变量内容。 必须使用补丁文件来替换静态内容,而不是使用 man:sed[1]。

经常情况下,正在移植的软件在源文件中使用 CR/LF 约定。 这可能会导致后续的补丁、编译器警告或脚本执行问题(例如 /bin/sh^M not found)。 要快速将所有文件从 CR/LF 转换为 LF,将以下条目添加到 Port 的 Makefile 中:

USES=	dos2unix

可以提供要转换的特定文件列表:

USES=	dos2unix
DOS2UNIX_FILES=	util.c util.h

使用 DOS2UNIX_REGEX 可以在子目录中跨组转换一组文件。 它的参数是与 man:find[1] 兼容的正则表达式。 有关格式的更多信息,请参阅 man:re_format[7]。 这个选项对于转换特定扩展名的所有文件非常有用。 例如,将所有源代码文件转换为 LF,而保留二进制文件不变:

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

类似的选项是 DOS2UNIX_GLOB,它对列表中的每个元素运行 find 命令。

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

转换的基本目录可以设置。 当有多个 distfiles,其中几个包含需要进行换行符转换的文件时,这非常有用。

USES=	dos2unix
DOS2UNIX_WRKSRC=	${WRKDIR}

4.4.4. 有条件地进行补丁

某些 Port 可能需要仅在特定的 FreeBSD 版本或特定选项启用或禁用时才应用的补丁。 有条件的补丁通过将补丁文件的完整路径放入 EXTRA_PATCHES 来指定。 有条件的补丁文件名通常以 extra- 开头,尽管这不是必需的。 然而,它们的文件名 不能patch- 开头。 如果是这样,它们将被框架无条件地应用,这对于有条件的补丁是不希望的。

为特定 FreeBSD 版本应用补丁的示例

.include <bsd.port.options.mk>

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

.include <bsd.port.mk>

可选地应用补丁的示例

当 crossref:makefiles[makefile-options,option] 需要一个补丁时,可以使用 opt_EXTRA_PATCHESopt_EXTRA_PATCHES_OFF 来使补丁与 opt 选项相关联。 有关更多信息,请参阅 crossref:makefiles[options-variables,Generic Variables Replacement, OPT_VARIABLE and OPT_VARIABLE_OFF]。

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

使用目录进行 EXTRA_PATCHES 的示例

有时,某个功能需要许多补丁,此时可以将 EXTRA_PATCHES 指向一个目录,框架将自动应用其中所有文件名为 patch-* 的补丁。

${PATCHDIR} 中创建一个子目录,并将补丁文件移到其中。 例如:

% 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

然后将下面内容添加到 Makefile 中:

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

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

最后更新于

FreeBSD 中文社区