# 5.17.安装文件

> **重要**
>
> `install` 阶段对最终用户非常重要，因为它将文件添加到他们的系统中。Port **Makefile** 中的 `*-install` 目标运行的所有附加命令应回显到屏幕上。*不要* 使用 `@` 或 `.SILENT` 来隐藏这些命令。

## 5.17.1. `INSTALL_*` 宏

使用 **bsd.port.mk** 提供的宏来确保在 Port 的 `*-install` 目标中正确设置文件的权限。通过相应的条目（例如 `@(owner,group,)`、`@owner<span> </span>owner` 和 `@group<span> </span>group`）在 **pkg-plist** 中直接设置所有权。这些操作符在被重写之前有效，或者直到 **pkg-plist** 的结尾为止，因此请记得在不再需要时重置它们。默认的所有权是 `root:wheel`。有关更多信息，请参见 [Base Keywords](https://docs.freebsd.org/en/books/porters-handbook/plist/#plist-keywords-base)。

* `INSTALL_PROGRAM` 是安装二进制可执行文件的命令。
* `INSTALL_SCRIPT` 是安装可执行脚本的命令。
* `INSTALL_LIB` 是安装共享库（但不是静态库）的命令。
* `INSTALL_KLD` 是安装内核可加载模块的命令。有些架构不喜欢将模块去除符号表，因此请使用此命令，而不是 `INSTALL_PROGRAM`。
* `INSTALL_DATA` 是安装可共享数据（包括静态库）的命令。
* `INSTALL_MAN` 是安装手册页和其他文档的命令（它不进行压缩）。

这些变量会被设置为 [install(1)](https://man.freebsd.org/cgi/man.cgi?query=install\&sektion=1\&format=html) 命令，并根据不同情况使用相应的标志。

> **重要**
>
> 不要使用 `INSTALL_LIB` 来安装静态库，因为去除符号表会使它们变得无用。请改用 `INSTALL_DATA`。

## 5.17.2. 去除二进制文件和共享库的符号表

已安装的二进制文件应该去除符号表。除非绝对必要，否则不要手动去除符号表。`INSTALL_PROGRAM` 宏会同时安装并去除二进制文件的符号表。`INSTALL_LIB` 宏对共享库也执行相同的操作。

当必须去除符号表，但不希望使用 `INSTALL_PROGRAM` 或 `INSTALL_LIB` 宏时，可以使用 `${STRIP_CMD}` 来去除程序或共享库的符号表。这通常是在 `post-install` 目标中执行。例如：

```makefile
post-install:
	${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/xdl
```

当需要去除多个文件的符号表时：

```makefile
post-install:
.for l in geometry media body track world
	${STRIP_CMD} ${STAGEDIR}${PREFIX}/lib/lib${PORTNAME}-${l}.so.0
.endfor
```

使用 [file(1)](https://man.freebsd.org/cgi/man.cgi?query=file\&sektion=1\&format=html) 命令可以确定文件是否已去除符号表。二进制文件会被 [file(1)](https://man.freebsd.org/cgi/man.cgi?query=file\&sektion=1\&format=html) 报告为 `stripped` 或 `not stripped`。此外，[strip(1)](https://man.freebsd.org/cgi/man.cgi?query=strip\&sektion=1\&format=html) 将检测到已去除符号表的程序，并平稳退出。

> **重要**
>
> 当定义了 `WITH_DEBUG` 时，elf 文件 *不得* 被去除符号表。
>
> 框架提供的变量（如 `STRIP_CMD`、`INSTALL_PROGRAM`、`INSTALL_LIB` 等）和 [`USES`](https://docs.freebsd.org/en/books/porters-handbook/uses/#uses) 会自动处理这个问题。
>
> 如果某些软件在其 `LDFLAGS` 中添加了 `-s`，则在这种情况下，要么在设置了 `WITH_DEBUG` 时删除 `-s`，要么无条件地删除它，并在 `post-install` 中使用 `STRIP_CMD`。

## 5.17.3. 安装整个文件树

有时，需要安装大量文件，并保持其层次结构不变。例如，将整个目录树从 `WRKSRC` 复制到 `PREFIX` 下的目标目录。请注意，`PREFIX`、`EXAMPLESDIR`、`DATADIR` 以及其他路径变量必须始终以 `STAGEDIR` 为前缀，以遵循 staging 机制（参见 [Staging](https://docs.freebsd.org/en/books/porters-handbook/special/#staging)）。

针对这种情况，有两个宏。使用这些宏的优点是，它们能确保目标文件的正确所有权和权限。第一个宏，`COPYTREE_BIN`，会将所有安装的文件设置为可执行，因此适用于安装到 **PREFIX/bin** 目录下。第二个宏，`COPYTREE_SHARE`，不会为文件设置可执行权限，因此适合安装到 **PREFIX/share** 目标目录下。

```makefile
post-install:
	${MKDIR} ${STAGEDIR}${EXAMPLESDIR}
	(cd ${WRKSRC}/examples && ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR})
```

此示例会将供应商分发文件中的 **examples** 目录的内容安装到 Port 的适当示例位置。

```makefile
post-install:
	${MKDIR} ${STAGEDIR}${DATADIR}/summer
	(cd ${WRKSRC}/temperatures && ${COPYTREE_SHARE} "June July August" ${STAGEDIR}${DATADIR}/summer)
```

这个示例会将夏季月份的数据安装到 **DATADIR** 下的 **summer** 子目录。

可以通过 `COPYTREE_*` 宏的第三个参数传递额外的 `find` 参数。例如，要安装来自第一个示例的所有文件，但排除 Makefile，可以使用以下命令：

```makefile
post-install:
	${MKDIR} ${STAGEDIR}${EXAMPLESDIR}
	(cd ${WRKSRC}/examples && \
	${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR} "! -name Makefile")
```

这些宏不会将已安装的文件自动添加到 **pkg-plist** 中。必须手动添加它们。对于可选文档（`PORTDOCS`，参见 [安装附加文档](https://docs.freebsd.org/en/books/porters-handbook/makefiles/#install-documentation)）和示例（`PORTEXAMPLES`），必须在 **pkg-plist** 中的条目前加上 `%%PORTDOCS%%` 或 `%%PORTEXAMPLES%%` 前缀。

## 5.17.4. 安装附加文档

如果软件有除标准 man 页面和 info 页面之外的其他文档，并且对用户有用，可以将其安装到 `DOCSDIR` 下。这可以像前面的示例一样，在 `post-install` 目标中进行。

为 Port 创建一个新目录。该目录名为 `DOCSDIR`，通常等于 `PORTNAME`。但是，如果用户可能希望同时安装不同版本的 Port，则可以使用整个 `PKGNAME`。

由于只有在 **pkg-plist** 中列出的文件才会被安装，因此始终将文档安装到 `STAGEDIR` 是安全的（参见 [Staging](https://docs.freebsd.org/en/books/porters-handbook/special/#staging)）。因此，`.if` 块仅在安装的文件足够大，可能导致显著的 I/O 开销时才需要。

```makefile
post-install:
	${MKDIR} ${STAGEDIR}${DOCSDIR}
	${INSTALL_DATA} ${WRKSRC}/docs/xvdocs.ps ${STAGEDIR}${DOCSDIR}
```

另一方面，如果 Port 中有 DOCS 选项，则应在 `post-install-DOCS-on` 目标中安装文档。这些目标在 [附加构建目标](https://docs.freebsd.org/en/books/porters-handbook/makefiles/#options-targets) 中有所描述。

以下是一些有用的变量及其在 **Makefile** 中使用时的默认展开方式：

* `DATADIR` 展开为 **PREFIX/share/PORTNAME**。
* `DATADIR_REL` 展开为 **share/PORTNAME**。
* `DOCSDIR` 展开为 **PREFIX/share/doc/PORTNAME**。
* `DOCSDIR_REL` 展开为 **share/doc/PORTNAME**。
* `EXAMPLESDIR` 展开为 **PREFIX/share/examples/PORTNAME**。
* `EXAMPLESDIR_REL` 展开为 **share/examples/PORTNAME**。

> **注意**
>
> `DOCS` 选项仅控制安装到 `DOCSDIR` 中的附加文档。它不适用于标准的 man 页面和 info 页面。安装到 `EXAMPLESDIR` 中的文件由 `EXAMPLES` 选项控制。

这些变量会被导出到 `PLIST_SUB` 中。如果可能，它们的值会作为相对 **PREFIX** 的路径出现在那里。也就是说，**share/doc/PORTNAME** 将默认替换 `%%DOCSDIR%%`，以此类推。（有关 **pkg-plist** 替换的更多信息，请参见 [这里](https://docs.freebsd.org/en/books/porters-handbook/plist/#plist-sub)。）

所有有条件安装的文档文件和目录都会在 **pkg-plist** 中以 `%%PORTDOCS%%` 前缀列出，例如：

```sh
%%PORTDOCS%%%%DOCSDIR%%/AUTHORS
%%PORTDOCS%%%%DOCSDIR%%/CONTACT
```

作为列出文档文件的替代方法，Port 可以将变量 `PORTDOCS` 设置为文件名和 shell glob 模式的列表，以将其添加到最终的 packing list 中。这些文件名将相对于 `DOCSDIR`。因此，使用 `PORTDOCS` 并使用非默认位置存放文档的 Port 必须相应地设置 `DOCSDIR`。如果目录在 `PORTDOCS` 中列出或通过该变量的 glob 模式匹配，则将整个包含的文件和目录的子树注册到最终的 packing list 中。如果未设置 `DOCS` 选项，则列在 `PORTDOCS` 中的文件和目录将不会被安装或添加到 Port 的 packing list 中。安装文档到 `PORTDOCS` 如上所示，仍然由 Port 自身决定。典型的 `PORTDOCS` 使用示例如下：

```makefile
PORTDOCS=	README.* ChangeLog docs/*
```

> **注意**
>
> 安装到 `DATADIR` 和 `EXAMPLESDIR` 下的文件对应的 `PORTDOCS` 变量分别是 `PORTDATA` 和 `PORTEXAMPLES`。
>
> **pkg-message** 中的内容会在安装时显示。有关使用 **pkg-message** 的详细信息，请参见 [pkg-message 部分](https://docs.freebsd.org/en/books/porters-handbook/pkg-files/#porting-message)。**pkg-message** 不需要添加到 **pkg-plist** 中。

## 5.17.5. `PREFIX` 下的子目录

尽量让 Port 将文件放置在 `PREFIX` 的正确子目录下。某些 Port 会将所有内容都放在一个以 Port 名称命名的子目录中，这是不正确的。此外，许多 Port 会将除了二进制文件、头文件和手册页面之外的所有文件都放在 **lib** 的子目录下，但这与 BSD 的理念并不兼容。许多文件必须被移动到以下这些目录中的一个：**etc**（设置/配置文件）、**libexec**（内部启动的可执行文件）、**sbin**（供超级用户/管理员使用的可执行文件）、**info**（用于 info 浏览器的文档）或 **share**（与架构无关的文件）。有关详细信息，请参见 [hier(7)](https://man.freebsd.org/cgi/man.cgi?query=hier\&sektion=7\&format=html)；适用于 **/usr** 的规则也适用于 **/usr/local**。唯一的例外是处理 USENET "news" 的 Port，它们可以将文件放置在 **PREFIX/news** 目录中。


---

# 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-5-zhang-pei-zhi-makefile/5.17.-an-zhuang-wen-jian.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.
