Eclass 编写指南
本节简要介绍了 Eclass 的编写。
Eclass 的用途
Eclass 是一组代码,可以被多个 Ebuild 使用。在撰写本文时,所有 Eclass 都位于树中的 eclass/
目录中。粗略地说,有三种类型的 Eclass
- 提供许多 Ebuild 使用的通用函数(例如,
autotools
、bash-completion-r1
、flag-o-matic
、toolchain-funcs
) - 为许多类似的软件包提供基本的构建系统(例如,
perl-module
、vim-plugin
) - 处理一个或少量具有复杂构建系统的软件包(例如,
kernel-2
、toolchain
)
添加和更新 Eclass
在将新的 Eclass 提交到树中之前,必须将其发送邮件到 gentoo-dev 邮件列表,并附上理由和建议的实现。不要跳过此步骤——有时会建议更好的实现或不需要新 Eclass 的替代方案。
在更新任何 Eclass 之前,请将补丁发送到 gentoo-dev 邮件列表。您提出的更改可能以您未预料到的方式被破坏,或者存在执行相同目的的现有函数,或者您的函数可能最好放在其自己的 Eclass 中。如果您没有先发送邮件到 gentoo-dev,并且最终破坏了一些东西,那么您将遇到很多麻烦。
请注意,在发送补丁时,您还应该将 @MAINTAINER
标记中列出的 Eclass 的任何维护者抄送。您可能希望提前联系他们并征求他们的意见。
此规则的例外情况是更新每个软件包的 Eclass。例如,apache-2
Eclass 仅由 www-servers/apache
软件包使用,因此通常不需要将更改发送邮件进行审查。
在移除函数或更改 Eclass 的 API 时,请确保它不会破坏 Gentoo 存储库中的任何 Ebuild。或者,您可能希望对一些(官方)覆盖层(如 ::guru
、::musl
或 ::science
)进行调查,以便更好地了解它当前的使用方式,并在可能的情况下通过直接 ping 维护者来避免破坏。
如果 Eclass 有现有的维护者(通常情况如此),您**必须**在提交任何更改之前与维护者联系。
验证语法的简单方法是 bash -n foo.eclass
。
#gentoo-dev
)或非正式地确定是否存在其他类似的更改(主要是文档),这些更改应该同时推送,以避免在彼此之间几个小时或几天内不必要地重新生成缓存。这同样适用于您自己的工作——请准备所有提交并尽可能批量推送它们。移除 Eclass
不再使用的 Eclass 可以从树中移除,但开发人员必须遵守以下流程
- 确保树中没有软件包或其他 Eclass
inherit
该 Eclass。 - 向 gentoo-dev 和 gentoo-dev-announce 邮件列表发送 lastrite 消息,宣布将在 30 天内移除未使用的 Eclass。
- 在 Eclass 中添加一行注释,内容为
# @DEAD
,以便 eclass-manpages 软件包不会尝试对其进行文档化。 - 等待 30 天期限过去,然后从树中移除 Eclass。
# @DEPRECATED:
,以确保如果使用者仍在使用该 Eclass,则会收到警告。在移除 Eclass 之前至少 30 天执行此操作。Eclass 文档
Eclass 使用遵循特殊标记语法的注释块进行文档化。注释块由空行分隔,每个块记录 Eclass 的单个元素。
各种 Eclass 元素的文档标记在下面各自的部分中进行了说明。对于所有接受多行自由文本的标记,在必要时应使用 @CODE
标记创建无格式的代码块(例如示例代码),方法是在开头和结尾处放置该标记。 @SUBSECTION
标记插入小节标题;它仅在主 @DESCRIPTION
中允许。
请注意,pkgcheck
可以检查 Eclass 文档中的问题。您可以运行例如 pkgcheck scan -s eclass
来检查树中的问题,或 pkgcheck scan --commits
来检查暂存的 git 提交中的问题。
groff
在生成 Eclass 手册页时正确换行。基本的 Eclass 格式
Eclass 是一个在 Ebuild 环境中被加载的 bash
脚本。文件应为一个简单的文本文件,扩展名为 .eclass
。文件名中允许的字符为小写字母、数字 0-9、连字符、下划线和点。Eclass 本身没有版本。
Eclass 应以标准的 Ebuild 标头开头,以及解释 Eclass 的维护者和用途以及任何其他相关文档的注释。Eclass 文档块应是 Eclass 中出现的第一个文档块。下表总结了可用的文档标记
标记 | 必需? | 参数 | 描述 |
---|---|---|---|
@ECLASS |
是 | 正在记录的 Eclass 的名称 | 记录有关 Eclass 的各种信息。必须作为注释块中的第一个标记出现。 |
@MAINTAINER |
是 | 一个或多个姓名和电子邮件对 | 要联系的 Eclass 维护者列表。每个维护者一行。必须在标记后换行开始。 |
@AUTHOR |
否 | 一个或多个姓名和电子邮件对 | Eclass 作者列表。每个作者一行。必须在标记后换行开始。 |
@BUGREPORTS |
否 | 多行自由文本 | 描述如何报告与该 Eclass 相关的 bug |
@VCSURL |
否 | URI 字符串 | 指向包含 Eclass 源代码的 VCS 的 URL |
@SUPPORTED_EAPIS |
否 | 以空格分隔的 EAPI 列表 | 此 Eclass 支持的 EAPI 列表 |
@PROVIDES |
否 | 以空格分隔的 Eclass 名称列表 | 继承此 Eclass 的 Ebuild 可以使用其函数和变量的间接继承 Eclass 列表 |
@BLURB |
是 | 单行自由文本 | 包含 Eclass 的简短描述。必须与标记在同一行。 |
@DEPRECATED |
否 | 替换(或“无”) | 声明此 Eclass 不应再使用 |
@DEAD |
否 | — | 对于计划删除的 Eclass;防止 eclass-manpages 软件包记录该 Eclass。 |
@DESCRIPTION |
否 | 多行自由文本 | Eclass 的长描述 |
@EXAMPLE |
否 | 多行自由文本 | 说明此 Eclass 的各种用法的示例 |
Eclass 变量
顶级变量可以像 ebuild 一样定义。如果使用了任何 USE 标记,则必须设置 IUSE
。在 eclass 中**不能**设置 KEYWORDS
变量。
您应该记录 eclass 中每个变量的含义、用法和默认值。可用于记录 eclass 变量的标签如下
标记 | 必需? | 参数 | 描述 |
---|---|---|---|
@ECLASS_VARIABLE |
是 | 文档适用的 eclass 变量的名称 | 此标签适用于影响 eclass 默认行为的 eclass 特定变量。eclass 为开发者使用而导出的只读变量也使用此标签进行记录。必须作为注释块中的第一个标签出现。 |
@PRE_INHERIT |
否 | — | 此标签描述变量是否必须在继承 eclass 之前定义。如果在源代码时全局范围内修改了任何例如依赖项,这一点很重要。 |
@USER_VARIABLE |
否 | — | 此标签描述变量是否不适合在 ebuild 中使用,即它仅供用户通过例如 make.conf 或类似机制使用。 |
@OUTPUT_VARIABLE |
否 | — | 此标签表示应在 ebuild 中读取变量的值(该值将由 eclass 设置)。 |
@DEFAULT_UNSET |
否 | — | 指示如果开发人员未设置此变量,则默认情况下它未设置。 |
@INTERNAL |
否 | — | 指示变量是 eclass 的内部变量。 |
@REQUIRED |
否 | — | 指示开发人员必须设置此变量。 |
@DEPRECATED |
否 | 可选地,任何替换变量的名称 | 声明此变量不再应该在 ebuild 中使用。 |
@DESCRIPTION |
是 | 多行自由文本 | eclass 变量的详细描述。 |
Eclass 函数
Eclass 可以定义函数。这些函数对继承 eclass 的任何内容都可见。
您应该记录 eclass 中每个函数的目的、参数、用法和返回值。可用于文档的标准标签为
标记 | 必需? | 参数 | 描述 |
---|---|---|---|
@FUNCTION |
是 | 文档块适用的函数的名称 | 记录有关 eclass 函数的信息,例如其调用约定等。必须作为注释块中的第一个标签出现。 |
@USAGE |
否 | 函数的必需参数和可选参数列表 | eclass 函数接受的参数列表,以以下语法指定:< 必需参数> [ 可选参数] |
@RETURN |
* | 函数的返回值 |
函数返回值的描述。 *:对于返回值的函数是必需的。 |
@MAINTAINER |
否 | 多行自由文本 | eclass 函数的联系人列表。每个维护者一行。必须在标签后换行开始。 |
@INTERNAL |
否 | — | 指示函数是 eclass 的内部函数,不应从外部调用。 |
@DEPRECATED |
否 | 可选地,替换函数的名称 | 声明此函数不再应该在 ebuild 中使用。 |
@DESCRIPTION |
* | 多行自由文本 |
eclass 函数的详细描述。 *: 如果缺少 |
Eclass 函数变量
Eclass 函数可以像任何其他 bash 函数一样使用变量。特殊函数特定变量应使用以下标签进行记录
标记 | 必需? | 参数 | 描述 |
---|---|---|---|
@VARIABLE |
是 | 文档适用的函数特定变量的名称 | 此标签适用于 eclass 的特定函数使用的变量。它们仅在调用特定函数时有效。 |
@USER_VARIABLE |
否 | — | 此标签描述变量是否不适合在 ebuild 中使用,即它仅供用户通过例如 make.conf 或类似机制使用。 |
@DEFAULT_UNSET |
否 | — | 指示如果开发人员未设置此变量,则默认情况下它未设置。 |
@INTERNAL |
否 | — | 指示变量是 eclass 函数的内部变量。 |
@REQUIRED |
否 | — | 指示开发人员必须设置此变量。 |
@DEPRECATED |
否 | 可选地,任何替换变量的名称 | 声明此变量不再应该在 ebuild 中使用。 |
@DESCRIPTION |
是 | 多行自由文本 | 函数变量的详细描述。 |
简单的通用函数 eclass 示例
例如,以下 eclass 是为了说明在讨论此主题时安装 OSX 应用程序文件的更好方法而编写的。它定义了一个函数 domacosapp
。
# Copyright 1999-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: macosapp.eclass
# @MAINTAINER:
# Ciaran McCreesh <[email protected]>
# @BLURB: install macos .app files to the relevant location.
# @FUNCTION: domacosapp
# @USAGE: <app-file> [new-file]
# @DESCRIPTION:
# Install the given .app file into the appropriate location. If
# [new-file] is given, it will be used as the new (installed) name of
# the file. Otherwise <app-file> is installed as-is.
domacosapp() {
[[ -z "${1}" ]] && die "usage: domacosapp <file> [new file]"
if use ppc-macos ; then
insinto /Applications
newins "$1" "${2:-${1}}"
fi
}
导出函数
Eclass 可以为任何 ebuild 阶段函数(src_unpack
、pkg_postinst
等)提供默认实现。这可以通过简单的函数定义(不适合多个 eclass)或使用 EXPORT_FUNCTIONS
来完成。给定给 EXPORT_FUNCTIONS
的函数按正常方式实现,但其名称以 ${ECLASS}_
为前缀(“命名空间”)。
EXPORT_FUNCTIONS
中指定 ebuild 阶段函数。EXPORT_FUNCTIONS
是一个函数,而不是变量。如果多个 eclass 导出相同的函数,则最后一个(最后继承)定义的版本获胜。如果 ebuild 定义了一个导出的函数,则此函数优先于任何 eclass 版本。这可以用来覆盖 eclass 定义的默认值——例如,假设我们有 fnord.eclass
fnord_src_compile() {
do_stuff || die
}
EXPORT_FUNCTIONS src_compile
然后 ebuild 可以这样做
inherit fnord
src_compile() {
do_pre_stuff || die
fnord_src_compile
do_post_stuff || die
}
Eclass 可以继承其他 eclass 以利用其功能,并且历史上 eclass 调用 EXPORT_FUNCTIONS
然后继承另一个 eclass 的情况也存在。由于继承的 eclass 也可能执行 EXPORT_FUNCTIONS
,因此没有完全定义哪些默认值应该生效。一般的建议是,eclass 不应该在调用 EXPORT_FUNCTIONS
之后继承其他 eclass。
继承保护
常见的做法是用“继承保护”包围 eclass 的主要内容。就像从 C 中知道的头文件保护一样,继承保护有助于确保 eclass 可以被多次继承,并且其函数和变量只定义一次。只有对于可以被多次继承的 eclass,才需要继承保护。
一个典型的继承保护如下所示(对于假设的 foo.eclass
)
if [[ -z ${_FOO_ECLASS} ]]; then
_FOO_ECLASS=1
# Variables and functions go here
fi
当涉及到 EXPORT_FUNCTIONS
和继承保护时,对 EXPORT_FUNCTIONS
的调用必须放在 eclass 的最后一行外部任何继承保护,如下所示
if [[ -z ${_FOO_ECLASS} ]]; then
_FOO_ECLASS=1
foo_src_compile() {
...
}
fi
EXPORT_FUNCTIONS src_compile
这有助于确保最后继承的 eclass 可以定义默认阶段函数。考虑两个 eclass foo.eclass
和 bar.eclass
,它们通过 EXPORT_FUNCTIONS
定义相同的默认阶段函数。如果 ebuild 将两者都继承为 inherit foo bar
,则默认阶段由 bar.eclass
定义。如果 foo.eclass
随后被修改为也继承 bar
,那么如果 EXPORT_FUNCTIONS
放在继承保护内,则 ebuild 的默认函数可能会突然变成来自 foo
的函数。
EXPORT_FUNCTIONS
的调用(如果有)放在 eclass 的最后一行,在任何继承保护之外。EXPORT_FUNCTIONS
放在其他地方,甚至在 inherit
之前。不应盲目地更新它们以遵循此处推荐的模式,因为这可能会导致重大破坏。处理 eclass 的不正确使用
有时 ebuild 会错误地使用 eclass,并且 eclass 知道它被错误地使用——常见的例子是 eclass 仅适用于特定一组 EAPI,但被具有不同 EAPI 的 ebuild 访问(继承)。在这些情况下,作为最后的手段谨慎使用,eclass 允许从全局范围调用 die。例如
# Copyright 1999-2022 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: eapi-die.eclass
# @MAINTAINER:
# Gentoo Devmanual Project <[email protected]>
# @SUPPORTED_EAPIS: 7 8
# @BLURB: Calls die when used with an invalid EAPI.
case ${EAPI} in
7|8) ;;
*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac
简单的构建系统 eclass 示例
这是一个关于假设的 jmake
构建系统的简单 eclass 可能是什么样子的示例。eclass 定义了一些函数,以及 src_configure
和 src_compile
阶段函数的默认实现。
# Copyright 1999-2022 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: jmake.eclass
# @MAINTAINER:
# Gentoo Devmanual Project <[email protected]>
# @AUTHOR:
# Ciaran McCreesh <[email protected]>
# @BLURB: Demonstrate a simple build system eclass.
# @DESCRIPTION:
# Demonstrates EXPORT_FUNCTIONS and defines simple wrappers for the
# (hypothetical) jmake build system along with default src_configure and
# src_compile phase functions.
case ${EAPI} in
7|8) ;;
*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac
if [[ -z ${_JMAKE_ECLASS} ]]; then
_JMAKE_ECLASS=1
BDEPEND=">=sys-devel/jmake-2"
# @FUNCTION: jmake-configure
# @USAGE: [additional-args]
# @DESCRIPTION:
# Passes all arguments through to the appropriate "jmake configure"
# command.
jmake_configure() {
jmake configure --prefix=/usr "$@"
}
# @FUNCTION: jmake_src_configure
# @USAGE: [additional-args]
# @DESCRIPTION:
# Calls jmake_configure() to configure a jmake project. Passes all
# arguments through to the appropriate "jmake configure" command.
jmake_src_configure() {
jmake_configure "$@" || die "configure failed"
}
# @FUNCTION: jmake_build
# @USAGE: [additional-args]
# @DESCRIPTION:
# First builds all dependencies, and then passes through its arguments
# to the appropriate "jmake build" command.
jmake_build() {
jmake dep && jmake build "$@"
}
# @FUNCTION: jmake_src_compile
# @DESCRIPTION:
# Calls jmake_build() to compile a jmake project.
jmake_src_compile() {
jmake_build || die "build failed"
}
fi
EXPORT_FUNCTIONS src_configure src_compile