第 8 章。高级 pkg-plist 实践

8.1. 根据 Make 变量更改 pkg-plist

某些端口,特别是p5-端口,需要根据其配置选项(或perl的版本,对于p5-端口而言)更改其pkg-plist。为了简化操作,pkg-plist中的所有%%OSREL%%%%PERL_VER%%%%PERL_VERSION%%实例将被适当地替换。%%OSREL%%的值是操作系统的数字修订版(例如,4.9)。%%PERL_VERSION%%%%PERL_VER%%perl的完整版本号(例如,5.8.9)。与端口文档文件相关的其他%%VARS%%相关部分中描述。

要进行其他替换,请使用VAR=VALUE对列表设置PLIST_SUB,并将pkg-plist中的%%VAR%%实例替换为VALUE

例如,如果端口在特定于版本的子目录中安装了许多文件,请使用占位符表示版本,这样pkg-plist就不必在每次更新端口时重新生成。例如,设置

OCTAVE_VERSION=	${PORTREVISION}
PLIST_SUB=	OCTAVE_VERSION=${OCTAVE_VERSION}

Makefile中,并在pkg-plist中出现版本的任何地方使用%%OCTAVE_VERSION%%。当端口升级时,就不需要编辑pkg-plist中的几十(或者在某些情况下是几百)行。

如果文件根据端口中设置的选项有条件地安装,则处理它的常用方法是在需要启用选项时在pkg-plist行之前添加%%OPT%%,或者在禁用选项时添加%%NO_OPT%%,并将OPTIONS_SUB=yes添加到Makefile中。有关更多信息,请参阅OPTIONS_SUB

例如,如果某些文件仅在启用X11选项时安装,并且Makefile具有

OPTIONS_DEFINE=	X11
OPTIONS_SUB=	yes

pkg-plist中,在仅在启用选项时安装的行之前添加%%X11%%,如下所示

%%X11%%bin/foo-gui

此替换将在pre-installdo-install目标之间执行,方法是读取PLIST并写入TMPPLIST(默认值:WRKDIR/.PLIST.mktmp)。因此,如果端口动态构建PLIST,则在pre-install中或之前执行此操作。此外,如果端口需要编辑结果文件,则在post-install中将其编辑到名为TMPPLIST的文件中。

修改端口打包列表的另一种方法是基于设置变量PLIST_FILESPLIST_DIRS。每个变量的值被视为一个路径名列表,这些路径名将与PLIST内容一起写入TMPPLIST。虽然PLIST_FILESPLIST_DIRS中列出的名称会受到上面描述的%%VAR%%替换的影响,但最好直接使用${VAR}。除了这一点之外,来自PLIST_FILES的名称将保持不变地出现在最终的打包列表中,而来自PLIST_DIRS的名称将被@dir前缀。为了生效,PLIST_FILESPLIST_DIRS必须在写入TMPPLIST之前设置,即在pre-install或更早之前设置。

有时,使用OPTIONS_SUB还不够。在这些情况下,在Makefile中向PLIST_SUB添加一个特定的TAG,其特殊值为@comment,这将使包工具忽略该行。例如,如果某些文件仅在X11选项开启且架构为i386时安装

.include <bsd.port.pre.mk>

.if ${PORT_OPTIONS:MX11} && ${ARCH} == "i386"
PLIST_SUB+=	X11I386=""
.else
PLIST_SUB+=	X11I386="@comment "
.endif

8.2. 空目录

8.2.1. 清理空目录

在卸载时,端口必须删除它创建的空目录。大多数这些目录由pkg(8)自动删除,但对于在${PREFIX}之外创建的目录或空目录,需要做一些额外的工作。这通常是通过为这些目录添加@dir行来完成的。在删除父目录之前必须删除子目录。

[...]
@dir /var/games/oneko/saved-games
@dir /var/games/oneko

8.2.2. 创建空目录

在端口安装过程中创建的空目录需要特别注意。它们必须在创建包时存在。如果它们不是由端口代码创建的,则在Makefile中创建它们

post-install:
	${MKDIR} ${STAGEDIR}${PREFIX}/some/directory

将目录添加到pkg-plist中,就像其他任何目录一样。例如

@dir some/directory

8.3. 配置文件

如果端口将配置文件安装到PREFIX/etc(或其他位置),请不要将它们列在pkg-plist中。这将导致pkg delete删除用户精心编辑的文件,并且重新安装会将它们删除。

相反,请安装带有filename.sample扩展名的示例文件。@sample宏会自动执行此操作,请参阅使用关键字扩展包列表,了解它的确切作用。对于每个示例文件,请在pkg-plist中添加一行

@sample etc/orbit.conf.sample

如果出于某种非常好的理由,不希望默认安装一个有效的配置文件,则只在pkg-plist中列出示例文件名,不要在后面添加@sample和空格,并添加一条消息,指出用户必须复制并编辑该文件才能使用该软件。

当端口将它的配置安装在${PREFIX}/etc的子目录中时,使用ETCDIR,它默认为${PREFIX}/etc/${PORTNAME},如果端口使用其他目录存在约定,则可以在端口的Makefile中覆盖它。%%ETCDIR%%宏将在pkg-plist中代替它。

示例配置文件应始终具有.sample后缀。如果由于某些历史原因,无法使用标准后缀,或者示例文件来自其他目录,请使用此构造

@sample etc/orbit.conf-dist etc/orbit.conf

@sample %%EXAMPLESDIR%%/orbit.conf etc/orbit.conf

格式为@sample sample-file actual-config-file

8.4. 动态与静态包列表

静态包列表是指在 Ports Collection 中提供的包列表,可以是pkg-plist(有或没有变量替换),也可以通过PLIST_FILESPLIST_DIRS 嵌入到 Makefile 中。即使内容是通过 Makefile 中的工具或目标在提交者将它包含到 Ports Collection 之前自动生成的(例如,使用make makeplist),这仍然被认为是静态列表,因为可以在不下载或编译 distfile 的情况下检查它。

动态包列表是指在编译端口时根据安装的文件和目录生成的包列表。在下载和编译移植的应用程序的源代码之前,或者在运行make clean之后,无法检查它。

虽然不禁止使用动态包列表,但维护者应尽可能使用静态包列表,因为它使用户能够grep(1) 可用端口,以发现例如哪个端口安装了某个文件。动态列表应该主要用于复杂的端口,其中包列表根据端口的可选功能发生很大变化(因此维护静态包列表不可行),或者根据使用的依赖软件的版本改变包列表的端口。例如,使用 Javadoc 生成文档的端口。

8.5. 自动化包列表创建

首先,确保端口几乎完成,只缺少pkg-plist。运行make makeplist将显示pkg-plist的示例。makeplist的输出必须仔细检查其正确性,因为它尝试自动猜测一些内容,并且可能会出错。

用户配置文件应安装为filename.sample,如配置文件中所述。info/dir 不应列出,并且必须添加适当的 install-info 行,如信息文件部分中所述。端口安装的任何库都必须如共享库部分中所述列出。

8.5.1. 使用正则表达式扩展PLIST_SUB

要替换的字符串有时需要非常具体,以避免不必要的替换。这是使用较短值时常见的问题。

为了解决这个问题,对于每个PLACEHOLDER=value,可以设置一个PLACEHOLDER_regex=regex,其中regex部分更精确地匹配value

示例 1. 使用正则表达式使用 PLIST_SUB

Perl 端口可以在特定树中安装依赖于体系结构的文件。在 FreeBSD 上,为了简化移植,这棵树被称为mach。例如,安装文件路径包含mach的端口,可能将路径字符串的那部分替换为错误的值。考虑这个Makefile

PORTNAME=	Machine-Build
DISTVERSION=	1
CATEGORIES=	devel perl5
MASTER_SITES=	CPAN
PKGNAMEPREFIX=	p5-

MAINTAINER=	[email protected]
COMMENT=	Building machine
WWW=		https://search.cpan.org/dist/Machine-Build

USES=		perl5
USE_PERL5=	configure

PLIST_SUB=	PERL_ARCH=mach

端口安装的文件是

/usr/local/bin/machine-build
/usr/local/lib/perl5/site_perl/man/man1/machine-build.1.gz
/usr/local/lib/perl5/site_perl/man/man3/Machine::Build.3.gz
/usr/local/lib/perl5/site_perl/Machine/Build.pm
/usr/local/lib/perl5/site_perl/mach/5.20/Machine/Build/Build.so

运行make makeplist会错误地生成

bin/%%PERL_ARCH%%ine-build
%%PERL5_MAN1%%/%%PERL_ARCH%%ine-build.1.gz
%%PERL5_MAN3%%/Machine::Build.3.gz
%%SITE_PERL%%/Machine/Build.pm
%%SITE_PERL%%/%%PERL_ARCH%%/%%PERL_VER%%/Machine/Build/Build.so

Makefile中的PLIST_SUB行更改为

PLIST_SUB=	PERL_ARCH=mach \
		PERL_ARCH_regex=\bmach\b

现在make makeplist会正确地生成

bin/machine-build
%%PERL5_MAN1%%/machine-build.1.gz
%%PERL5_MAN3%%/Machine::Build.3.gz
%%SITE_PERL%%/Machine/Build.pm
%%SITE_PERL%%/%%PERL_ARCH%%/%%PERL_VER%%/Machine/Build/Build.so

8.6. 使用关键字扩展包列表

所有关键字也可以在括号中接受可选参数。参数是所有者、组和模式。此参数用于引用的文件或目录。要更改配置文件的所有者、组和模式,请使用

@sample(games,games,640) etc/config.sample

参数是可选的。如果只需要更改组和模式,请使用

@sample(,games,660) etc/config.sample

如果关键字用于可选条目,则必须在助手之后添加它

%%FOO%%@sample etc/orbit.conf.sample

这是因为选项 plist 助手用于注释掉该行,因此它们需要放在首位。有关更多信息,请参阅OPTIONS_SUB

8.6.1. @desktop-file-utils

将在安装和卸载后运行update-desktop-database -q永远不要直接使用,在Makefile中添加USES=desktop-file-utils

8.6.2. @fc directory

为作为参数传递的目录添加一个@dir条目,并在安装和卸载后对该目录运行fc-cache -fs

8.6.3. @fontsdir directory

为作为参数传递的目录添加一个@dir条目,并在安装和卸载后对该目录运行mkfontscalemkfontdir。此外,在卸载时,如果 fonts.scalefonts.dir 缓存文件为空,它会删除它们。

8.6.4. @info file

将作为参数传递的文件添加到 plist 中,并在安装和卸载时更新信息文档索引。此外,它会在卸载时删除空索引。这永远不应该手动使用,而应该通过INFO使用。有关更多信息,请参阅信息文件

8.6.5. @kld 目录

在安装和卸载时,对目录运行kldxref。此外,在卸载时,如果目录为空,它将删除该目录。

8.6.6. @rmtry 文件

将在卸载时删除文件,如果文件不存在,则不会报错。

8.6.7. @sample 文件 [文件]

这用于处理配置文件的安装,通过打包示例文件。 “实际”的非示例文件是第二个文件名(如果存在),或者第一个文件名不带.sample扩展名。

这将执行三项操作。首先,将作为参数传递的第一个文件(示例文件)添加到plist中。然后,在安装时,如果未找到实际文件,则将示例文件复制到实际文件。最后,在卸载时,如果实际文件未被修改,则删除它。有关详细信息,请参见配置文件

8.6.8. @shared-mime-info 目录

在安装和卸载时,对目录运行update-mime-database

8.6.9. @shell 文件

将作为参数传递的文件添加到plist。

在安装时,将文件的完整路径添加到/etc/shells,同时确保它不会被添加两次。在卸载时,从/etc/shells中删除它。

8.6.10. @terminfo

不要单独使用。如果端口安装了*.terminfo文件,请在其Makefile中添加USES=terminfo

在安装和卸载时,如果存在tic,则从${PREFIX}/shared/misc中的*.terminfo文件刷新${PREFIX}/shared/misc/terminfo.db

8.6.11. 基本关键字

有一些关键字是硬编码的,在pkg-create(8)中有所描述。为了完整起见,它们也在此处进行说明。

8.6.11.1. @ [文件]

空关键字是一个占位符,用于在需要更改文件的所有者、组或模式时使用。例如,要将文件的组设置为games并添加setgid位,请添加

@(,games,2755) sbin/daemon

8.6.11.2. @preexec 命令, @postexec 命令, @preunexec 命令, @postunexec 命令

在软件包安装或卸载过程中执行命令

@preexec 命令

pre-install脚本中执行命令

@postexec 命令

post-install脚本中执行命令

@preunexec 命令

pre-deinstall脚本中执行命令

@postunexec 命令

post-deinstall脚本中执行命令

如果命令在其中包含任何这些序列,它们将在内联展开。对于以下示例,假设@cwd设置为/usr/local,并且最后一个提取的文件为bin/emacs

%F

展开到最后一个提取的文件名(如指定)。在示例中为bin/emacs

%D

展开到当前目录前缀,由@cwd设置。在示例中为/usr/local

%B

展开到完全限定文件名的基本名称,即当前目录前缀加上最后一个文件规范,减去尾随文件名。在示例中,这将是/usr/local/bin

%f

展开到完全限定名称的文件名部分,或%B的反义词。在示例中为emacs

这些关键字旨在帮助你设置软件包,使其尽可能地易于使用。它们不能被滥用以启动服务、停止服务或运行任何会修改当前正在运行的系统的其他命令。

8.6.11.3. @mode 模式

将所有后续提取文件的默认权限设置为模式。格式与chmod(1)中使用的相同。不带参数使用以将其重置为默认权限(打包时文件的模式)。

这必须是一个数字模式,例如6444755600。它不能是相对模式,例如u+s

8.6.11.4. @owner 用户

将所有后续文件的默认所有权设置为用户。不带参数使用以重置为默认所有权(root)。

8.6.11.5. @group

将所有后续文件的默认组所有权设置为。不带参数使用以重置为默认组所有权(wheel)。

8.6.11.6. @comment 字符串

打包时会忽略此行。

8.6.11.7. @dir 目录

声明目录名称。默认情况下,软件包安装在PREFIX下创建的目录会自动删除。当PREFIX下的空目录需要创建,或者目录需要具有非默认所有者、组或模式时使用。PREFIX以外的目录需要注册。例如,/var/db/${PORTNAME}需要具有@dir条目,而${PREFIX}/shared/${PORTNAME}不需要,前提是它包含文件或使用默认所有者、组和模式。

8.6.11.8. @exec 命令, @unexec 命令 (已弃用)

在安装或卸载过程中执行命令。请改用@preexec 命令

8.6.11.9. @dirrm 目录 (已弃用)

声明在卸载时要删除的目录名称。默认情况下,软件包安装在PREFIX下创建的目录在卸载软件包时会被删除。

8.6.11.10. @dirrmtry 目录 (已弃用)

声明要删除的目录名称,与@dirrm相同,但如果无法删除该目录,不会发出警告。

8.6.12. 创建新关键字

软件包列表文件可以通过在${PORTSDIR}/Keywords目录中定义的关键字进行扩展。每个关键字的设置都存储在一个名为keyword.ucl的UCL文件中。该文件必须至少包含以下部分之一

  • attributes

  • action

  • pre-install

  • post-install

  • pre-deinstall

  • post-deinstall

  • pre-upgrade

  • post-upgrade

8.6.12.1. attributes

更改关键字使用的所有者、组或模式。包含一个关联数组,其中可能的键是ownergroupmode。值分别是用户名、组名和文件模式。例如

attributes: { owner: "games", group: "games", mode: 0555 }

8.6.12.2. action

定义对关键字参数的操作。包含一个数组,其中可能的取值是

setprefix

为下一个plist条目设置前缀。

dir

注册一个在安装时创建并在卸载时删除的目录。

dirrm

注册一个在卸载时要删除的目录。已弃用。

dirrmtry

注册一个在卸载时尝试删除的目录。已弃用。

file

注册一个文件。

setmode

为下一个plist条目设置模式。

setowner

为下一个plist条目设置所有者。

setgroup

为下一个plist条目设置组。

comment

不执行任何操作,等同于不输入action部分。

ignore_next

忽略plist中的下一个条目。

8.6.12.3. arguments

如果设置为true,则添加参数处理,将整行%@拆分为编号参数%1%2等。例如,对于以下行

@foo some.content other.content

%1%2将包含

some.content
other.content

它还会影响action条目的工作方式。当存在多个参数时,必须指定参数编号。例如

actions: [file(1)]

8.6.12.4. pre-install, post-install, pre-deinstall, post-deinstall, pre-upgrade, post-upgrade

这些关键字包含一个sh(1)脚本,在软件包安装、卸载或升级之前或之后执行。除了@preexec 命令中描述的常用@exec %foo占位符之外,还有一个新的占位符%@,它表示关键字的参数。

8.6.12.5. 自定义关键字示例

示例 2. @dirrmtryecho关键字示例

此关键字执行两项操作:它将@dirrmtry 目录行添加到打包列表中,并回显在卸载软件包时删除目录的事实。

actions: [dirrmtry]
post-deinstall: <<EOD
  echo "Directory %D/%@ removed."
EOD
示例 3. 实际示例,@sample的实现方式

此关键字执行三项操作:它将作为参数传递给@sample的第一个文件名添加到打包列表中,它将向post-install脚本指令添加内容,如果实际配置文件不存在,则将示例复制到实际配置文件,并且它将向post-deinstall脚本指令添加内容,如果实际配置文件未被修改,则删除它。

actions: [file(1)]
arguments: true
post-install: <<EOD
  case "%1" in
  /*) sample_file="%1" ;;
  *) sample_file="%D/%1" ;;
  esac
  target_file="${sample_file%.sample}"
  set -- %@
  if [ $# -eq 2 ]; then
      target_file=${2}
  fi
  case "${target_file}" in
  /*) target_file="${target_file}" ;;
  *) target_file="%D/${target_file}" ;;
  esac
  if ! [ -f "${target_file}" ]; then
    /bin/cp -p "${sample_file}" "${target_file}" && \
      /bin/chmod u+w "${target_file}"
  fi
EOD
pre-deinstall: <<EOD
  case "%1" in
  /*) sample_file="%1" ;;
  *) sample_file="%D/%1" ;;
  esac
  target_file="${sample_file%.sample}"
  set -- %@
  if [ $# -eq 2 ]; then
      set -- %@
      target_file=${2}
  fi
  case "${target_file}" in
  /*) target_file="${target_file}" ;;
  *) target_file="%D/${target_file}" ;;
  esac
  if cmp -s "${target_file}" "${sample_file}"; then
    rm -f "${target_file}"
  else
    echo "You may need to manually remove ${target_file} if it is no longer needed."
  fi
EOD

上次修改时间:2024年9月23日,由Fernando Apesteguía修改