--- title: 第 7 章 高级 pkg-plist 用法 prev: books/porters-handbook/special next: books/porters-handbook/pkg-files --- [[plist]] = 高级 [.filename]#pkg-plist# 用法 :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :sectnumoffset: 7 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[plist-sub]] == 根据 make 变量对 [.filename]#pkg-plist# 进行修改 某些 port, 特别是 `p5-` port, 会需要根据配置选项 (或对于 `p5-` port 而言, `perl` 的版本) 来修改它们的 [.filename]#pkg-plist#。 为简化这一工作, 在 [.filename]#pkg-plist# 中的 `%%OSREL%%`、 `%%PERL_VER%%`, 以及 `%%PERL_VERSION%%` 将自动进行相应的替换。 其中, `%%OSREL%%` 的值是操作系统以数值表示的版本 (例如 `4.9`)。 `%%PERL_VERSION%%` 和 `%%PERL_VER%%` 是 `perl` 的完整版本号 (例如 `5.8.9`)。许多其它与 port 文档文件有关的 `%%变量%%` 在 <> 中进行了介绍。 如果您还需要进行其它的替换, 可以通过将 `PLIST_SUB` 变量设置为一组 `变量=值` 对来实现。 其中, `%%VAR%%` 表示在 [.filename]#pkg-plist# 中将被 _值_ 替换的那些文字。 举例来说, 如果 port 需要把很多文件放到和版本有关的目录中, 可以在 [.filename]#Makefile# 中按照类似下面的例子: [.programlisting] .... OCTAVE_VERSION= 2.0.13 PLIST_SUB= OCTAVE_VERSION=${OCTAVE_VERSION} .... 并在 [.filename]#pkg-plist# 中将具体的版本替换为 `%%OCTAVE_VERSION%%`。 这样, 在升级 port 时, 就不需要再到 [.filename]#pkg-plist# 中修改那几十 (或者, 有时甚至是上百) 行的内容了。 如果您的 port 需要根据一定的配置来有条件地安装一些文件, 通常的做法是在 [.filename]#pkg-plist# 中列出这些文件时, 在对应行的开头加上 `%%TAG%%`, 并将 `TAG` 写到 [.filename]#Makefile# 中的 `PLIST_SUB` 变量中, 根据需要替换掉, 或替换为 `@comment`, 后者表示让打包工具忽略这行: [.programlisting] .... .if defined(WITH_X11) PLIST_SUB+= X11="" .else PLIST_SUB+= X11="@comment " .endif .... 与之对应, 在 [.filename]#pkg-plist# 中: [.programlisting] .... %%X11%%bin/foo-gui .... 这一替换过程 (以及加入 <> 的过程), 会在 `pre-install` 和 `do-install` 两个 target 之间, 通过读取 [.filename]#PLIST# 并写入 [.filename]#TMPPLIST# (默认情况下, 是: [.filename]#WRKDIR/.PLIST.mktmp#) 来完成。 因此, 如果您的 port 动态生成 [.filename]#PLIST#, 就需要在 `pre-install` 之前完成。 另外, 如果您的 port 需要编辑所生成的文件, 则需要在 `post-install` 中操作名为 [.filename]#TMPPLIST# 的那个文件。 另一种可行的修改装箱单的方法, 则是根据 `PLIST_FILES` 和 `PLIST_DIRS` 这两个变量的设置来进行。 它们的值会作为目录名连同 [.filename]#PLIST# 的内容一起写入 [.filename]#TMPPLIST#。 在 `PLIST_FILES` 和 `PLIST_DIRS` 中列出的名字, 会经历前面所介绍的 `%%变量%%` 替换过程。 除此之外, 在 `PLIST_FILES` 中列出的文件, 会不加任何修改第出现在最终的装箱单中, 而 `@dirrm` 将作为前缀加到 `PLIST_DIRS` 所列的名字之前。 为了达到目的, `PLIST_FILES` 和 `PLIST_DIRS` 必须在写 [.filename]#TMPPLIST# 之前, 也就是在 `pre-install` 或更早的阶段进行设置。 [[plist-cleaning]] == 空目录 [[plist-dir-cleaning]] === 清理空目录 一定要让 port 在卸载时进行清理空目录。 通常, 可以通过为所有由 port 创建的目录增加对应的 `@dirrm` 行来实现。 在删除父目录之前, 需要首先删除它的子目录。 [.programlisting] .... : lib/X11/oneko/pixmaps/cat.xpm lib/X11/oneko/sounds/cat.au : @dirrm lib/X11/oneko/pixmaps @dirrm lib/X11/oneko/sounds @dirrm lib/X11/oneko .... 然而, 有时 `@dirrm` 会由于其它 port 使用了同一个目录而发生错误。 利用 `@dirrmtry` 可以只删除那些空目录, 而避免给出警告。 [.programlisting] .... @dirrmtry share/doc/gimp .... 按照上面的写法, 将不会显示任何错误信息, 而且,即使在 [.filename]#${PREFIX}/shared/doc/gimp# 由于其它 port 在其中安装了一些别的文件的时候, 也不会导致 man:pkg_delete[1] 异常退出。 [[plist-dir-empty]] === 如何建立空目录 在 port 安装过程中创建的空目录需要特别留意。 安装 package 时并不会自动创建这些目录, 这是因为 package 只保存文件。 要确保安装 package 时会自动创建这些空目录, 需要在 [.filename]#pkg-plist# 中加入与 `@dirrm` 对应的行: [.programlisting] .... @exec mkdir -p %D/shared/foo/templates .... [[plist-config]] == 配置文件 如果 port 需要把一些文件放到 [.filename]#PREFIX/etc#, _不要_ 简单地安装它们, 并将其列入 [.filename]#pkg-plist#, 因为这样会导致 man:pkg_delete[1] 删除用户精心编辑的文件, 而新安装时则又会把这些文件覆盖。 因此, 您应把配置文件的例子按其它的后缀来安装 (例如 [.filename]#filename.sample# 就是一个不错的选择) 并显示一条 <> 告诉用户如何复制并编辑这个配置文件, 以便让软件能够正确工作。 因此, 应按其它的后缀来安装配置文件的例子 ([.filename]#filename.sample# 就是一个不错的选择)。 如果实际的配置文件不存在, 则将其复制为实际文件的名字。 卸载时, 如果发现用户没有修改配置文件, 则将其删除。 您需要在 port 的 [.filename]#Makefile#, 以及 [.filename]#pkg-plist# (对于从 package 安装的情形) 进行处理。 示例的 [.filename]#Makefile# 部分: [.programlisting] .... post-install: @if [ ! -f ${PREFIX}/etc/orbit.conf ]; then \ ${CP} -p ${PREFIX}/etc/orbit.conf.sample ${PREFIX}/etc/orbit.conf ; \ fi .... 示例的 [.filename]#pkg-plist# 部分: [.programlisting] .... @unexec if cmp -s %D/etc/orbit.conf.sample %D/etc/orbit.conf; then rm -f %D/etc/orbit.conf; fi etc/orbit.conf.sample @exec if [ ! -f %D/etc/orbit.conf ] ; then cp -p %D/%F %B/orbit.conf; fi .... 另外, 还应显示一条 <> 指出用户应在何处复制并编辑这个文件, 以便让软件能开始正常工作。 [[plist-dynamic]] == 动态装箱单与静态装箱单的对比 _静态装箱单_ 是指在 Ports Collection 中以 [.filename]#pkg-plist# 文件 (可能包含变量替换), 或以 `PLIST_FILES` 和 `PLIST_DIRS` 的形式嵌入到 [.filename]#Makefile# 出现的装箱单。 即使它是由工具或 Makefile 中的某个 target 在经由 committer 加入到 Ports Collection _之前_ 自动生成的也是如此, 因为可以在不下载或编译源代码包的前提下对其进行检视。 _动态装箱单_ 是指在 port 编译并安装时生成的装箱单。 在下载并编译您所移植的应用程序的源代码之前, 或在执行了 `make clean` 之后, 就无法查看其内容了。 尽管使用动态装箱单并不被禁止, 但监护人应尽可能使用静态装箱单, 因为它能够让用户使用 man:grep[1]来发现所需的 ports, 例如, 它是否会安装某个特定文件。 动态列表主要应用于复杂的, 其装箱单随所选功能会发生巨变 (因而使得维护静态装箱单不再可行), 或那些随版本而改变装箱单内容的 port (例如, 使用 Javadoc 来生成文档的那些 ports)。 我们鼓励那些选择使用动态装箱单的监护人提供一个能够生成 [.filename]#pkg-plist# 的 target, 以便于用户检视其内容。 [[plist-autoplist]] == 装箱单 (package list) 的自动化制作 首先, 请确认已经基本上完成了 port 的工作, 仅缺 [.filename]#pkg-plist#。 接下来, 建立一个用于安装您的 port 的临时目录, 并在其中安装它所依赖的所有其他软件包: [source,shell] .... # mkdir /var/tmp/`make -V PORTNAME` # mtree -U -f `make -V MTREE_FILE` -d -e -p /var/tmp/`make -V PORTNAME` # make depends PREFIX=/var/tmp/`make -V PORTNAME` .... 将目录结构保存到一新文件中。 [source,shell] .... # (cd /var/tmp/`make -V PORTNAME` && find -d * -type d) | sort > OLD-DIRS .... 建立一空白 [.filename]#pkg-plist# 文件: [source,shell] .... # :>pkg-plist .... 如果您的 port 遵循 `PREFIX` (应该如此) 则接下来应安装该 port 并创建装箱单。 [source,shell] .... # make install PREFIX=/var/tmp/`make -V PORTNAME` # (cd /var/tmp/`make -V PORTNAME` && find -d * \! -type d) | sort > pkg-plist .... 此外还应把新建立的目录加入装箱单。 [source,shell] .... # (cd /var/tmp/`make -V PORTNAME` && find -d * -type d) | sort | comm -13 OLD-DIRS - | sort -r | sed -e 's#^#@dirrm #' >> pkg-plist .... 最后需要手工整理 packing list; 这一过程不是 _完全_ 自动的。 联机手册应列入 port 的 [.filename]#Makefile# 中的 `MAN__n__`, 而不是装箱单。 用户配置文件应被删除, 或以 [.filename]#filename.sample# 这样的名字来安装。 [.filename]#info/dir# 文件, 也不应列入, 同时应按照 <> 的说明来增加一些 [.filename]#install-info# 行。 所有由 port 安装的库, 应按照 <> 小节中介绍的方法处理。 另外, 也可以使用 [.filename]#/usr/ports/Tools/scripts/# 中的 `plist` 脚本来自动创建 package list。 [.filename]#plist# 脚本是一个 Ruby 脚本, 它能够将前面介绍的手工操作自动化。 开始的步骤和上面的前三行一样, 也就是 `mkdir`, `mtree` 并 `make depends`。 然后联编和安装 port: [source,shell] .... # make install PREFIX=/var/tmp/`make -V PORTNAME` .... 然后让 `plist` 生成 [.filename]#pkg-plist# 文件: [source,shell] .... # /usr/ports/Tools/scripts/plist -Md -m `make -V MTREE_FILE` /var/tmp/`make -V PORTNAME` > pkg-plist .... 与前面类似, 如此生成的装箱单也需要手工进行一些清理工作。 另一个可以用来创建最初的 [.filename]#pkg-plist# 的工具是 package:ports-mgmt/genplist[]。 和其他自动化工具类似, 您应对它生成的 [.filename]#pkg-plist# 应手工检查并根据需要进行修改。