3 配方风格指南

3 配方命名约定

一般来说,大多数配方应遵循 recipes-category/recipename/recipename_version.bb 命名约定。 相关项目的配方可以共用同一个配方目录。 recipename 和 category 可以包含连字符,但 version 不允许包含连字符。

如果配方跟踪的 Git 修订并不对应已发布的软件版本,version 可以为 git (例如 recipename_git.bb),并在配方中设置 PV。

3 版本规则

版本采用 <epoch>:<version>-<revision> 格式, 对应的 BitBake 变量表示为 ${PE}:${PV}-${PR}。 我们通常遵循定义这些术语的 Debian 版本规则。

多数情况下,PV 自动根据配方文件名设置。 建议使用软件正式发布的版本,因为这些是上游期望用户使用的修订。

配方版本必须始终能够正确比较和排序,升级才能按预期工作。 对于从 1.4 升到 1.5 这样的常规版本,这很自然。 但有些版本不能直接按预期排序,例如 1.5 Release Candidate 2 可以写成 1.5rc2,但它会排在 1.5 之后,导致软件包源升级异常。

此时可以使用波浪号(~),它排在空字符串之前, 因此 1.5~rc2 位于 1.5 之前。历史上也有将 PV 设为 “先前版本 + 预发布版本”的写法,例如 PV=1.4+1.5rc2。 这种语法有效,但更推荐波浪号形式。

需要判断两个版本号的大小关系时,可以使用 opkg-utils 提供的 opkg-compare-versions 程序。最终以 BitBake 内的版本比较算法为准, 它旨在尽量与包管理器和 Debian 规则保持一致。

如果配方引用的 Git 修订不对应已发布的软件版本(例如没有版本标签), PV 应包含 +,以便 bitbake 在打包阶段自动加入软件包版本信息::

PV = "<version>+git"

这里的 <version> 应为当前源码修订之前最近发布的软件版本, git describe 可以帮助确定它。 虽然不建议发布的层这样做,但在本地开发中,此格式也适合配合 AUTOREV, 让配方自动跟进版本控制系统中的修订。

3 版本号变更

PR 表示引用同一上游源码版本的配方的不同修订。 它可用于强制设备从软件包源安装该配方的新版本。 过去需要手动设置,现在多数情况下可以由与软件包源关联的 PR 服务器自动设置和递增。

当 PV 增加时,可以且应当删除已有的 PR 值。

如果 PV 改变后并未相对于旧值增大,则需要增加 PE, 确保包管理器正确升级。如果此前未设置 PE,应将其设为 "1", 因为默认空值在不同包管理器中容易与 "0" 混淆。PE 只能取整数。

3 配方格式

3 变量格式

  • 变量赋值运算符两侧应各有一个空格,例如 FOO = "bar",而非 FOO="bar"。

  • 赋值右侧应使用双引号,例如 FOO = "bar",而非 FOO = 'bar'。

  • 变量缩进使用空格,每一级制表宽度为 4 个空格。

  • 长变量尽可能使用续行符(\)拆分为多行。

  • 将长变量拆分为多行时,所有续行均使用空格缩进,与第一行的起始引号对齐::

    FOO = "this line is \
           long \
           "
    

    不应写成::

    FOO = "this line is \
    long \
    "
    

3 Python 函数格式

  • Python 代码必须使用空格缩进,每一级制表宽度为 4 个空格。

3 Shell 函数格式

  • 同一层内的 shell 函数格式应保持一致。有些层使用制表符,有些使用空格。

3 配方元数据

3 必需变量

所有配方都应包含以下变量:

  • SUMMARY:用一行描述上游项目。

  • DESCRIPTION:对上游项目的详细描述,可以有多行。 如果无法写出合理的详细描述,可以省略,因为默认值是 SUMMARY。

  • HOMEPAGE:上游项目主页的 URL。

  • BUGTRACKER:上游项目缺陷跟踪网站的 URL(如适用)。

3 配方内容顺序

在配方和类中定义变量时,应尽可能遵循以下顺序:

某些情况下顺序有实际意义,此时应优先满足具体要求。例如:

  • PACKAGE_ARCH 必须在 inherit packagegroup 之前设置。

任务应按通常的执行顺序排列。常用任务的顺序为:

自定义任务也应遵循相同原则。

特定软件包的变量通常放在一起,例如::

RDEPENDS:${PN} = "foo"
RDEPENDS:${PN}-libs = "bar"

RRECOMMENDS:${PN} = "one"
RRECOMMENDS:${PN}-libs = "two"

3 配方许可证字段

配方需要同时定义 LICENSE 和 LIC_FILES_CHKSUM:

  • LICENSE:指定软件的许可证。如果不知道所构建软件采用什么许可证, 应到源码中查找。常见的许可证信息文件有 COPYING、LICENSE 和 README,也可能位于源文件顶部。 例如,软件采用 GNU 通用公共许可证第 2 版时,应设置::

    LICENSE = "GPL-2.0-only"
    

    LICENSE 中的许可证名称可以自行指定,但不能包含空格, 因为空格用作名称之间的分隔符。 标准许可证应使用 meta/files/common-licenses/ 中的文件名, 或 meta/conf/licenses.conf 定义的 SPDXLICENSEMAP 标志名称。

    备注

    配方中的 LICENSE 适用于该配方构建的软件,而不是配方文件本身。 配方、配置文件和脚本本身的许可证也应明确标注,例如通过注释, 或通过存放这些文件的 layer 中的许可证文件说明。 许可证文件通常位于层的根目录;例外应在层的 README 或 LICENSE 中明确说明。

    例如,OpenEmbedded-Core (OE-Core) 层同时提供 GPL-2.0-only 和 MIT 许可证文件,并用 "LICENSE" 文件解释二者分别适用于层中的哪些文件。

  • LIC_FILES_CHKSUM:OpenEmbedded 构建系统用它确认许可证文本没有变化。 如果文本改变,构建会报错,让你有机会查明并修正问题。

    需要列出软件适用的全部许可证文件。配置步骤结束时, 构建流程会比较文件校验和,确认文本未变。任何差异都会导致错误, 错误消息包含当前校验和。 更多说明和示例见 跟踪许可证变化。

    要确定正确的校验和,可以先在 LIC_FILES_CHKSUM 中列出相应文件, 填入错误的 MD5 字符串,然后尝试构建,读取错误消息报告的正确 MD5 值。 更多信息见 获取代码。

    假设软件有一个 COPYING 文件,示例如下::

    LIC_FILES_CHKSUM = "file://COPYING;md5=xxx"
    

    尝试构建时,构建系统会报错并给出正确字符串。 将它替换到配方文件中,即可用于后续构建。

3 许可证更新

如果因为上游修改许可证文件而变更配方中的 LICENSE 或 LIC_FILES_CHKSUM,需要通过 License-Update: 标签简要说明原因。 通常只是很小的变化,例如::

License-Update: copyright years refreshed

有时许可证条款本身也会改变。此时应尽量提供上游作出该决定或说明理由的链接。

如果提交只是修正本项目元数据中错误的许可证信息,则不需要 License-Update:。 示例见 OE-Core 提交 6c8b76d。

3 编写配方的建议与准则

  • 尽可能使用 BBCLASSEXTEND,而不是单独创建 -native、-nativesdk 等配方,以免同时维护多个配方文件。

  • 配方任务应具备幂等性,即多次执行同一任务不应改变最终结果。 构建环境建立在这一假设之上,违反它可能导致难以排查的构建失败。

  • 在任务中修改文件时,为保证幂等性,通常最好:

    • 将文件 X 复制为 X.orig,仅在后者尚不存在时执行;

    • 再将 X.orig 复制回 X;

    • 最后修改 X。

    这样,无论重跑多少次任务,最终结果都相同,同时保留原始文件供重用。 也能避免构建中断导致文件损坏。

3 补丁上游状态

为了跟踪配方应用的补丁,并最终减少需要维护的补丁数量, OpenEmbedded 构建系统要求提供每个补丁的上游状态。

每个补丁的描述应详细说明它解决的问题,包括缺陷跟踪系统中的 URL、 相关邮件列表归档链接等。

还应添加 Upstream-Status: 标签,使用以下状态字符串之一:

Pending

尚未作出判断,或补丁尚未提交上游。

从长期看,每个提交到上游的补丁都会减轻 OpenEmbedded 和 Yocto 项目的维护负担。 因此,只有确实存在障碍、无法向上游提交的特殊情况才应使用此状态, 并在补丁中写明原因。

Submitted [where]

已提交上游,等待接受。可以附上提交位置,例如作者、邮件列表等。

Backport [version]

上游已接受并将在下一版本发布,或因为本项目使用固定版本, 从更高上游版本回移而来。应注明上游版本信息,例如提交 ID 或预计发布版本。

Denied

上游不接受此补丁,应在补丁中说明原因。

Inactive-Upstream [lastcommit: when (and/or) lastrelease: when]

上游已不再活跃,通常指一个多年没有活动的停止维护项目。 判断时,不仅应看最后一次发布的时间,还应看最后一次提交时间, 以及之后新建的缺陷报告和合并请求是否始终无人回应。 建议在补丁描述中加入能清楚反映上游停止活动的相关链接。

Inappropriate [reason]

补丁不适合提交上游,应在同一行的 [] 中简要说明原因。 过去存在多种不向上游提交补丁的理由,但每个未进入上游的补丁都会增加配方维护负担。 目前,只有以下理由可以使用此状态:

  • oe specific:问题仅与 OpenEmbedded 的构建方式或运行时配置方式有关, 只能通过补丁解决,但该补丁不适用于通用上游场景。

  • upstream ticket <link>:问题并非 OpenEmbedded 特有,应在上游修复, 但当前补丁不适合合入上游,而作者缺少编写适当补丁的专业知识。 因此改为通过缺陷报告处理,并附上链接。

如果之后有其他人负责将补丁提交上游,应将状态改为 Submitted [where], 并由承担提交责任的人添加一行 Signed-off-by:。

3 示例

下面是已经提交上游的补丁示例::

rpm: Adjusted the foo setting in bar

[RPM Ticket #65] -- http://rpm5.org/cvs/tktview?tn=65,5

The foo setting in bar was decreased from X to X-50% in order to
ensure we don't exhaust all system memory with foobar threads.

Upstream-Status: Submitted [rpm5-devel@rpm5.org]

Signed-off-by: Joe Developer <joe.developer@example.com>

后续可以根据情况将状态改为 Backport 或 Denied。

下面是 OpenEmbedded 特有补丁的示例::

Do not treat warnings as errors

There are additional warnings found with musl which are
treated as errors and fails the build, we have more combinations
than upstream supports to handle.

Upstream-Status: Inappropriate [oe specific]

下面是从上游提交回移的补丁示例::

include missing sys/file.h for LOCK_EX

Upstream-Status: Backport [https://github.com/systemd/systemd/commit/ac8db36cbc26694ee94beecc8dca208ec4b5fd45]

3 CVE 补丁

为了更好地管理漏洞,修复 CVE 的补丁必须包含 CVE: 标签, 列出该补丁修复的所有 CVE。多个 CVE 之间用空格分隔。

3 CVE 示例

修复 GRUB2 中 CVE-2015-8370 的补丁应包含如下头部::

grub2: Fix CVE-2015-8370

[No upstream tracking] -- https://bugzilla.redhat.com/show_bug.cgi?id=1286966

Back to 28; Grub2 Authentication

Two functions suffer from integer underflow fault; the grub_username_get() and grub_password_get()located in
grub-core/normal/auth.c and lib/crypto.c respectively. This can be exploited to obtain a Grub rescue shell.

Upstream-Status: Backport [http://git.savannah.gnu.org/cgit/grub.git/commit/?id=451d80e52d851432e109771bb8febafca7a5f1f2]
CVE: CVE-2015-8370
Signed-off-by: Joe Developer <joe.developer@example.com>

3 补丁格式

默认情况下,git format-patch 创建的补丁末尾带有 Git 版本签名。 为了避免生成或更新的补丁包含该签名,可以设置 Git 配置::

git config --global format.signature ""

备注

devtool 生成或更新的补丁不包含签名。