5 编写新配方

配方(.bb 文件)是 Yocto Project 的基本组成部分。构建系统中的每个 软件组件都需要配方定义。本节介绍如何创建、编写和测试新配方。

备注

配方常用变量和命名问题见参考手册的 “配方”。

5 概述

下图展示基本流程,其余各节说明具体步骤。

../_images/recipe-workflow.png

5 查找或自动创建基础配方

可以从头编写,也可以用以下方式快速起步:

  • devtool add:辅助创建配方及适合开发的环境。

  • recipetool create:根据源码自动生成基础配方。

  • 已有配方: 查找功能相似的配方并修改。

备注

语法见 “配方语法”。

5 使用 devtool add 创建基础配方

devtool add 使用与下文 recipetool create 相同的自动生成逻辑,还 建立便于修补源码和修改配方的环境。这些操作通常是添加新软件所必需的。

完整说明见应用开发与 eSDK 手册的 “深入了解 devtool add”。

5 使用 recipetool create 创建基础配方

只要能提取或指向源码,recipetool create 就能生成基础配方,并自动设置 构建前的信息。例如,Autotools 应用的配方会包含构建依赖、许可证要求及校验和。

先加载环境脚本 oe-init-build-env,进入 Build Directory。 查看帮助:

$ recipetool -h
NOTE: Starting bitbake server...
usage: recipetool [-d] [-q] [--color COLOR] [-h] <subcommand> ...

OpenEmbedded recipe tool

options:
  -d, --debug       Enable debug output
  -q, --quiet       Print only errors
  --color COLOR     Colorize output (where COLOR is auto, always, never)
  -h, --help        show this help message and exit

subcommands:
  newappend         Create a bbappend for the specified target in the specified layer
  create            Create a new recipe
  setvar            Set a variable within a recipe
  appendfile        Create/update a bbappend to replace a target file
  appendsrcfiles    Create/update a bbappend to add or replace source files
  appendsrcfile     Create/update a bbappend to add or replace a source file
  edit              Edit the recipe and appends for the specified target. This obeys $VISUAL if set,
                    otherwise $EDITOR, otherwise vi.
Use recipetool <subcommand> --help to get help on a specific command

recipetool create -o OUTFILE 生成基础配方,并放在包含源码的层中适当位置。 语法示例:

  • 根据源码生成配方,存放在现有源码层:

    recipetool create -o OUTFILE source
    
  • 从上游提取源码并生成配方,提取内容放在 EXTERNALSRC 指定的层:

    recipetool create -o OUTFILE -x EXTERNALSRC source
    
  • 根据源码生成配方并输出调试信息,配方放在现有源码层:

    recipetool create -d -o OUTFILE source
    

5 查找和使用相似配方

从头编写前,先检查是否已有满足或接近需求的配方。Yocto Project 和 OpenEmbedded 社区维护大量配方,集中索引见 OpenEmbedded 层索引。

以已有配方或骨架为起点通常最好:

  • 查找并修改相似配方: 适合熟悉现有配方的人,对新手未必合适。

    风险包括照搬无关部分,或没有识别必须补写的内容,根源都是对已有配方 不够熟悉。

  • 使用下面的配方骨架: 如果不想使用 recipetool,也找不到相似配方, 可用以下结构作为基础:

    DESCRIPTION = ""
    HOMEPAGE = ""
    LICENSE = ""
    SECTION = ""
    DEPENDS = ""
    LIC_FILES_CHKSUM = ""
    
    SRC_URI = ""
    

5 保存和命名配方

有了基础配方,应放入自己的层并正确命名,使 BitBake 能找到它。

  • 保存位置: 构建系统通过 conf/layer.conf 的 BBFILES 搜索 配方,典型设置如下:

    BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
                ${LAYERDIR}/recipes-*/*/*.bbappend"
    

    确保新配方位于匹配路径中。层结构详见 “理解和创建层”。

  • 命名: 遵循以下惯例:

    basename_version.bb
    

    使用小写,不要随意使用保留后缀 -native、-cross、-initial、 -dev,除非含义确实适用。以下示例包含特殊版本标识 "git":

    cups_2.4.12.bb
    gawk_5.3.2.bb
    psplash_git.bb
    

5 构建配方

新配方通常需多轮 BitBake 构建,逐步发现并补齐所需信息。

加载 oe-init-build-env 并进入 Build Directory 后,只需 向 BitBake 提供前述 basename:

$ bitbake basename

构建系统为每个配方创建 ${WORKDIR},保存提取源码、日志、 编译及打包中间文件等。

路径取决于构建上下文,最快可通过 bitbake-getvar 查询:

$ bitbake-getvar -r basename WORKDIR

例如,Source Directory 为 bitbake-builds,构建目录为 bitbake-builds/build,PACKAGE_ARCH 为 cortexa57, DISTRO 为 poky,TARGET_OS 为 linux,配方为 foo_1.3.0.bb,则工作目录为:

bitbake-builds/build/tmp/work/cortexa57-poky-linux/foo/1.3.0

备注

此例假定 PACKAGE_ARCH 使用默认 TUNE_PKGARCH。它有时 会设为 MACHINE_ARCH,详见该变量文档。

目录中有 image、packages-split、temp 等子目录,构建后可检查 它们以确认结果。

备注

各任务日志位于 temp,例如 bitbake-builds/build/tmp/work/cortexa57-poky-linux/foo/1.3.0/temp。 日志名为 log.taskname,如 log.do_configure、log.do_fetch、 log.do_compile。

构建流程详见概述与概念手册的 “Yocto Project 开发环境”。

5 获取代码

配方首先要指定如何获取源码,主要通过 SRC_URI 指向源码位置。 来源的图示见 “源码”。

do_fetch 根据 SRC_URI 各条目的前缀选择 获取器。 获取完成后,do_patch 也使用该变量应用补丁。本地文件搜索由 FILESOVERRIDES 指导。

应列出源码的每个独立来源。URL 中不要硬编码版本号,使用 ${PV} 引用配方文件名指定的版本,这样升级通常只需改名。

以下 strace 配方 从单个归档获取源码,注意 PV:

SRC_URI = "${GITHUB_BASE_URI}/download/v${PV}/strace-${PV}.tar.xz \

.tar、.tar.gz、.tar.bz2、.zip 等归档会在 do_unpack 自动解包。另见 “构建 Autotools 软件包”。

也可从 SCM 获取。Git 仓库必须指定 SRCREV,PV 定义应含 +。例如 l3afpad_git.bb:

SRC_URI = "git://github.com/stevenhoneyman/l3afpad.git;branch=master;protocol=https"

PV = "0.8.18.1.11+git"
SRCREV = "3cdccdc9505643e50f8208171d9eee5de11a42ff"

若 URL 指向版本控制系统之外的远程文件,BitBake 会用配方校验和验证,确保 文件自配方编写后未被篡改或修改。支持 SRC_URI[md5sum]、SRC_URI[sha1sum]、 SRC_URI[sha256sum]、SRC_URI[sha384sum] 和 SRC_URI[sha512sum], 通常只使用 SHA-256。

备注

MD5 过去常用,但已弃用,更新配方时应替换为 SRC_URI[sha256sum]。

如果有多个非 SCM URL,需分别指定 SHA-256:先给各 URL 命名,再在校验和 语句引用名称。以下组合 git.inc 和 git_2.24.1.bb 的内容:

SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \
           ${KERNELORG_MIRROR}/software/scm/git/git-manpages-${PV}.tar.gz;name=manpages"

SRC_URI[tarball.sha256sum] = "ad5334956301c86841eb1e5b1bb20884a6bad89a10a6762c958220c7cf64da02"
SRC_URI[manpages.sha256sum] = "9a7ae3a093bea39770eb96ca3e5b40bff7af0b9f6123f089d7821d0e5b8e1230"

上游下载页可能提供 SHA-256 及 MD5、SHA-1、GPG 等其他签名。构建系统通常 只处理 SHA-256,因此应手动验证所有提供的签名。

缺少或错误校验和都会使构建报错,消息中包含所下载文件的正确校验和。确认后 可复制到配方,再构建。

备注

上游若提供源码签名,应先手动验证,再设置配方校验和并继续。

更复杂的 rxvt-unicode 示例同时列出归档、补丁、桌面文件和图标:

SRC_URI = "http://dist.schmorp.de/rxvt-unicode/Attic/rxvt-unicode-${PV}.tar.bz2 \
           file://xwc.patch \
           file://rxvt.desktop \
           file://rxvt.png"

file:// 从本机获取文件,路径相对于 FILESPATH,依次搜索配方 或追加文件旁的 ${BP}、${BPN} 和 files 子目录。另见 “构建单个 .c 文件软件包”。

补丁通常以 .patch、.diff 或 diff.gz、patch.bz2 等压缩后缀 结尾,构建系统会自动应用,见 “修补代码”。

5 通过防火墙获取代码

需要通过代理获取代码时,参阅 “常见问题”。

5 限制并行连接数

如果防火墙或服务器限制连接数,可在 build/conf/local.conf 限制并行获取任务:

do_fetch[number_threads] = "4"

5 解包代码

do_unpack 解包源码,${S} 指向解包位置。

默认值结合 UNPACKDIR 与 BP,已经指向构建系统的解包目录, 通常无需显式设置。解包成功后,可检查 ${S} 是否为源码根目录。

备注

过去 Git 配方需显式设 S 为 ${WORKDIR}/git,现在不再需要, 见 应移除 S = ${WORKDIR}/git 和 S = ${UNPACKDIR}/git。

5 修补代码

SRC_URI 中以 .patch、.diff 或其压缩后缀结尾的文件,会由 do_patch 自动作为补丁应用。

默认应能以 "-p1" 应用,即去掉一层路径。需要更多层时,在 URI 条目设置 "striplevel";需要在补丁未指定的子目录应用时,设置 "patchdir"。

与其他 file:// 文件一样,补丁应放在配方旁以 BP、BPN 或 "files" 命名的目录。

5 许可证

配方需定义软件分发许可证相关变量,详见贡献者指南的 配方许可证字段。

5 依赖

大多数软件依赖其他软件,分为构建时依赖和必须安装到目标才能运行的运行时依赖。

DEPENDS 指定构建依赖。虽然存在细节差异,条目通常应为其他配方名, 且所有构建依赖都应显式声明。

配置脚本可能自动检测可选依赖,并在找到后启用相应功能。若希望配方通用, 例如发布给他人使用,不应简单硬性禁用,而应通过 PACKAGECONFIG 让用户轻松启停功能及对应依赖。

运行时依赖通过包专用变量 RDEPENDS 指定。此类变量必须带包名覆盖。 主包与配方同名,即 ${PN},因此用 RDEPENDS:${PN}; ${PN}-tools 则用 RDEPENDS:${PN}-tools。

部分运行时依赖在打包时自动添加。例如,"example" 提供 "libexample", "mypackage" 的二进制链接该库,就会自动依赖 "example"。详见 “自动添加运行时依赖”。

5 配置配方

多数软件允许在编译前设置构建选项,通常运行带参数的 configure 脚本,或修改 构建配置文件。

备注

自 Yocto Project 1.7 起,部分核心配方禁用二进制配置脚本,因为其路径 替换容易出错,转而使用更稳健的 pkg-config。禁用列表见 “禁用二进制配置脚本”。

配置阶段的重要工作是检查依赖,并可能启用可选功能。应把所需依赖对应的配方 写入 DEPENDS,软件文档通常会说明构建或运行时依赖。

根据构建系统,注意以下事项:

  • Autotools: 有 configure.ac 通常表示使用 Autotools。继承 autotools* 后,无需自写 do_configure, 可用 EXTRA_OECONF 或 PACKAGECONFIG_CONFARGS 传入配方专用 配置选项。

  • CMake: 有 CMakeLists.txt 表示使用 CMake。继承 cmake 后无需自写配置任务,用 EXTRA_OECMAKE 传入额外选项。

    备注

    应用提供的自定义 CMake 工具链文件,应在 do_install 安装到 ${D}${datadir}/cmake/Modules。

  • 其他: 如果没有上述文件,通常需要自行定义 do_configure, 除非根本无需配置。

    先判断是否确需配置,可能只需修改 Makefile 或其他配置文件,也可能要 带适当选项运行自定义脚本。后者可通过 ./configure --help 查找选项。

配置成功后,应检查 log.do_configure,确认所需选项启用,且 DEPENDS 没有遗漏。如果找到未声明依赖,或缺少可选功能需要的依赖, 应补入。若启用了不需要的内容,或声明的依赖仍未找到,则考虑调整配置参数。 具体选项见 ${S} 中 ./configure --help 或上游文档。

5 通过头文件与设备交互

应用需要与设备或自定义内核 API 交互时,应提供适当头文件。绝不要修改现有 meta/recipes-kernel/linux-libc-headers/linux-libc-headers.inc。这些头文件 用于构建 libc,不能混入自定义或机器专用信息,否则所有使用 libc 的应用 都会受影响。

备注

不要复制并定制上述 libc 头文件配方。

正确方式是用独立软件包提供驱动或特殊接口的额外头文件,并让应用显式依赖该 提供者。

请注意:

  • linux-libc-headers.inc 属于 libc 系统,不是直接访问内核的接口。 应通过特定 libc 调用使用 libc。

  • 直接访问设备的应用应自带头文件,或依赖驱动专用头文件包。

例如,修改头文件增加 I/O 控制或网络支持,如果只有少量程序需要,可以提供 独立版本,影响很小,但仍应遵循以上原则。

备注

如果确实需要修改 libc 以及系统其他应用的行为,应使用 .bbappend 修改 linux-kernel-headers.inc,但不能使修改依赖具体机器。

即使内核较旧、需要旧 libc ABI,配方安装的头文件也应来自标准主线内核,而非 自定义内核。

自定义内核头文件应从 STAGING_KERNEL_DIR 获取,该目录包含构建树外 模块所需头文件。配方还需:

do_configure[depends] += "virtual/kernel:do_shared_workdir"

5 编译

源码获取、解包和配置后执行 do_compile。成功则无需额外处理, 失败则需诊断。常见原因如下。

备注

如果配置路径不正确或找不到库、头文件,确保使用更稳健的 pkg-config, 参阅 “配置配方” 的说明。

  • 并行构建失败: 表现为间歇性错误,或找不到应由其他构建部分生成的文件、 目录。失败后检查时文件可能已存在,因为实际问题是执行顺序错误。

    应修复 Makefile 或生成它的脚本中缺失的依赖,或临时清空 PARALLEL_MAKE:

    PARALLEL_MAKE = ""
    

    详见 “调试并行 Make 竞态”。

  • 错误使用主机路径: 仅涉及目标或 nativesdk 配方, 即交叉编译时使用了不应使用的主机头文件、库等。

    检查 log.do_compile 中的 /usr/include、/usr/lib 等路径, 通过配置选项、补丁或两者修复。

  • 找不到必需库或头文件: 可能漏写 DEPENDS,也可能依赖存在但 搜索路径错误,且配置时未发现。应补充配置选项,必要时补齐依赖。

    有时需修补源码以使用正确路径。引用其他配方放入系统根目录的文件时, 应使用 STAGING_BINDIR、STAGING_INCDIR、 STAGING_DATADIR 等构建系统变量。

5 安装

do_install 将构建文件及目录层级复制为目标系统布局。文件从 ${S}、${B} 和 ${UNPACKDIR} 复制到 ${D}。

不同构建系统需要不同处理:

  • Autotools 和 CMake: 系统知道如何安装,无需自写安装任务,只需确认 无错误完成。若要额外安装 make install 未处理的文件,使用 do_install:append,按下文手动方式调用 install。

  • 其他使用 make install 的系统: 需定义安装函数,调用 oe_runmake install,通常还需传入目标目录。具体参数由 Makefile 决定,例如 DESTDIR=${D}、PREFIX=${D}、INSTALLROOT=${D}。

    示例见 “构建基于 Makefile 的软件包”。

  • 手动安装: 定义安装函数,先用 install -d 在 ${D} 下创建目录,再用 install 放入构建文件。

    工具说明见 https://www.gnu.org/software/coreutils/manual/html_node/install-invocation.html。

非 Autotools/CMake 情况下,需跟踪安装并排错,直到全部正确。检查默认 ${D} 即 ${WORKDIR}/image,确认文件位置。

备注

  • 有时需修改已安装文件以适应目标布局,例如将初始化脚本硬编码的 /usr/bin/ 改为 ${bindir}。应先复制,再修改目标文件,确保 do_install 可重复执行。

  • 直接调用或由类间接调用的 oe_runmake install 都会并行安装。 Makefile 目标缺少依赖时可能发生竞态。安装间歇性失败可临时禁用并行:

    PARALLEL_MAKEINST = ""
    

    详见 PARALLEL_MAKEINST。

  • 应用提供的自定义 CMake 工具链文件应安装到 ${D}${datadir}/cmake/Modules。

5 启用系统服务

服务通常开机启动并在后台运行,安装它需要额外配方定义。

如果服务初始化脚本或服务文件未安装,应通过 do_install:append 安装; 若已有 do_install,则在该函数末尾添加,而非另写追加函数。

安装应完成通常由 make install 处理的工作,确保输出布局与目标系统一致。

构建系统支持两种服务启动方式:

5 打包

打包结合构建系统自动流程与人工处理:

  • 拆分文件: do_package 将输出拆成逻辑组件。即使只有一个 可执行文件,也可能需要拆出符号、文档等,任务负责正确拆分并打包。

  • 运行 QA: insane 在包生成流程加入检查,排除常见 运行时问题。见该类及参考手册 “质量检查错误和警告消息”。

  • 人工检查: 查看 ${WORKDIR}/packages-split,确认 文件如预期分配。发现问题后调整 PACKAGES、FILES、 do_install(:append) 等。

  • 拆为多个包: 示例见 “将应用拆成多个软件包”。

  • 安装后脚本: 示例见 “安装后脚本”。

  • 标明包架构: 根据输出和配置,应标明是否机器专用,或完全不依赖机器架构。

    默认包适用于与目标同架构的机器。若包与具体机器相关,例如配置脚本使用 MACHINE 或仅为某机器应用补丁,添加:

    PACKAGE_ARCH = "${MACHINE_ARCH}"
    

    若只打包脚本或配置等完全架构无关内容,继承 allarch:

    inherit allarch
    

    初始构建阶段这未必关键,但正确架构能确保配置变化时恰当重建,并在目标 安装正确软件包,多机器分别构建时尤其重要。

5 在配方之间共享文件

应用链接公共库时,需要其他配方提供的库和头文件,通过系统根目录访问。 每个配方的工作目录有两个系统根目录:目标文件的 recipe-sysroot 和 主机文件的 recipe-sysroot-native。

备注

Yocto Project 也用 “staging” 描述填充系统根目录,例如 STAGING_DIR。

不要直接写系统根目录。应在 do_install 中安装到 ${D} 的标准位置。相关文件会登记清单,使配方修改或移除 时可清理,避免残留旧文件。

do_populate_sysroot 按 SYSROOT_DIRS 选择安装文件的 子集,自动填充。可以扩展目录列表,例如加入 /opt:

SYSROOT_DIRS += "/opt"

备注

/sysroot-only 用于不进入目标文件系统的产物,允许配方共享这些内容而 不使用 DEPLOY_DIR。

该任务及相关函数详见 staging 类。

5 使用虚拟提供者

多个配方提供相同功能时,可用 virtual/* 作为占位,在构建时选择实际提供者。

典型例子是内核。假设有 kernel-big、kernel-mid、kernel-small 三个 PN,各自通过 PROVIDES 声明提供 virtual/kernel。 kernel 类中的一种方式为:

PROVIDES += "virtual/kernel"

继承该类的配方均声明能提供 virtual/kernel。

构建镜像时,通过 PREFERRED_PROVIDER 选择实际内核。例如机器配置 x86-base.inc 使所有 x86 机器使用 linux-yocto,相关语句为:

PREFERRED_PROVIDER_virtual/kernel ??= "linux-yocto"

这样无需将具体配方名硬编码为依赖,可在 DEPENDS 使用虚拟名称:

DEPENDS = "virtual/kernel"

构建时按 PREFERRED_PROVIDER 选取。若要使用前述小内核:

PREFERRED_PROVIDER_virtual/kernel ??= "kernel-small"

备注

提供 virtual/* 但最终未被选中的配方不会构建。这通常符合预期,因为 此机制用于选择相互排斥的提供者。

虚拟提供者示例:

  • virtual/kernel:内核镜像所用配方。

  • virtual/bootloader:镜像所用引导加载程序。

  • virtual/libgbm:提供 gbm.pc。

  • virtual/egl:提供 egl.pc,可能还有 wayland-egl.pc。

  • virtual/libgl:提供 gl.pc,即 libGL。

  • virtual/libgles1:提供 glesv1_cm.pc,即 libGLESv1_CM。

  • virtual/libgles2:提供 glesv2.pc,即 libGLESv2。

备注

虚拟提供者仅适用于 PROVIDES 和 DEPENDS 构建依赖, 不适用于 RPROVIDES 和 RDEPENDS 运行时依赖。

5 正确设置预发布配方版本

配方命名可能使正式版升级出问题。例如,前文的 irssi_0.8.16-rc1.bb 在正式发布后变为 irssi_0.8.16.bb,但构建系统和包管理器会认为 0.8.16-rc1 到 0.8.16 是降级,不能正确触发升级。

推荐使用波浪号 ~:

PV = "0.8.16~rc1"

这样 0.8.16~rc1 排在 0.8.16 之前。预发布或特定 Git 提交的版本 规则详见 “版本规则”。

5 安装后脚本

脚本在目标安装软件包后立即运行,或包被纳入镜像时在镜像创建期间运行。 在配方添加 pkg_postinst:PACKAGENAME(),将 PACKAGENAME 替换为对应包名。主包通常使用 ${PN}。

结构如下:

pkg_postinst:PACKAGENAME() {
    # Commands to carry out
}

根文件系统创建时调用该函数,成功后将包标记为已安装。

备注

目标上运行的 RPM 安装后脚本应返回 0。RPM 不接受非零返回值,否则目标 安装失败。

有时脚本必须在设备运行,需要延迟到首次启动。必须显式用 pkg_postinst_ontarget(),或在 pkg_postinst() 中调用 postinst_intercept delay_to_first_boot。普通安装后脚本任何失败,包括 exit 1,都会使 do_rootfs 报错。

如果脚本在根文件系统构建期间需要非标准本机工具,应通过 PACKAGE_WRITE_DEPS 列出。否则工具可能缺失,导致延迟到首次启动, 这通常不理想,对只读根文件系统更不可行。

备注

也支持 pkg_preinst、pkg_prerm、pkg_postrm,分别在安装前、 卸载前和卸载后运行,机制相同,只是时机不同,因此不像安装后脚本那样 适合在镜像创建时执行。

5 测试

配方完成前,最后要确认软件运行正确。将输出包加入镜像,在目标上进行运行时 测试。添加包的方法见 “定制镜像”。

5 示例

以下示例归纳不同场景的配方写法:

5 构建单个 .c 文件软件包

本地 files 等目录中的单个源码文件应列入 SRC_URI,手动实现 do_compile 和 do_install。此时 S 设为 UNPACKDIR,即 BitBake 用于构建的源码目录:

SUMMARY = "Simple helloworld application"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://helloworld.c"

S = "${UNPACKDIR}"

do_compile() {
    ${CC} ${LDFLAGS} helloworld.c -o helloworld
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 helloworld ${D}${bindir}
}

默认生成 helloworld、helloworld-dbg 和 helloworld-dev。自定义 拆包见 “将应用拆成多个软件包”。

5 构建基于 Makefile 的软件包

GNU make 应用需在 SRC_URI 列出源码归档,无需另写编译任务,BitBake 默认调用 make。额外选项放在 EXTRA_OEMAKE 或 PACKAGECONFIG_CONFARGS。安装任务仍必须定义,否则默认为空。

额外编译参数,例如头文件搜索路径,可以加入 CFLAGS:

CFLAGS:prepend = "-I ${S}/include "

以下为基于 Makefile 的 lz4 示例:

SUMMARY = "Extremely Fast Compression algorithm"
DESCRIPTION = "LZ4 is a very fast lossless compression algorithm, providing compression speed at 400 MB/s per core, scalable with multi-cores CPU. It also features an extremely fast decoder, with speed in multiple GB/s per core, typically reaching RAM speed limits on multi-core systems."
HOMEPAGE = "https://github.com/lz4/lz4"

LICENSE = "BSD-2-Clause | GPL-2.0-only"
LIC_FILES_CHKSUM = "file://lib/LICENSE;md5=ebc2ea4814a64de7708f1571904b32cc \
                    file://programs/COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263 \
                    file://LICENSE;md5=d57c0d21cb917fb4e0af2454aa48b956 \
                    "

PE = "1"

SRCREV = "d44371841a2f1728a3f36839fd4b7e872d0927d3"

SRC_URI = "git://github.com/lz4/lz4.git;branch=release;protocol=https \
           file://CVE-2021-3520.patch \
           "
UPSTREAM_CHECK_GITTAGREGEX = "v(?P<pver>.*)"

CVE_STATUS[CVE-2014-4715] = "fixed-version: Fixed in r118, which is larger than the current version"

EXTRA_OEMAKE = "PREFIX=${prefix} CC='${CC}' CFLAGS='${CFLAGS}' DESTDIR=${D} LIBDIR=${libdir} INCLUDEDIR=${includedir} BUILD_STATIC=no"

do_install() {
        oe_runmake install
}

BBCLASSEXTEND = "native nativesdk"

5 构建 Autotools 软件包

Autoconf、Automake 应用需指定源码归档,并继承 autotools*, 该类定义全部必要步骤。结果自动打包;若使用 NLS 本地化,还会按语言生成包。 例如 hello_2.3.bb:

SUMMARY = "GNU Helloworld application"
SECTION = "examples"
LICENSE = "GPL-2.0-or-later"
LIC_FILES_CHKSUM = "file://COPYING;md5=751419260aa954499f7abaabaa882bbe"

SRC_URI = "${GNU_MIRROR}/hello/hello-${PV}.tar.gz"

inherit autotools gettext

LIC_FILES_CHKSUM 跟踪源码许可证变化,见 “跟踪许可证变化”。可按此方式快速编写 Autotools 配方。

5 构建 Meson 软件包

Meson 应用只需在 SRC_URI 描述源码并 继承 meson。

ipcalc 配方 是不带依赖的简单示例:

SUMMARY = "Tool to assist in network address calculations for IPv4 and IPv6."
HOMEPAGE = "https://gitlab.com/ipcalc/ipcalc"

SECTION = "net"

LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263"

SRC_URI = "git://gitlab.com/ipcalc/ipcalc.git;protocol=https;branch=master"
SRCREV = "4c4261a47f355946ee74013d4f5d0494487cc2d6"

inherit meson

有依赖的应用通常还继承 pkgconfig,因为 Meson 默认通过 pkg-config 查找依赖并获取编译参数。

5 将应用拆成多个软件包

使用 PACKAGES 和 FILES 可拆包。

例如,libxpm 默认将库和少量二进制放在一个包中,可修改为:

require xorg-lib-common.inc

SUMMARY = "Xpm: X Pixmap extension library"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://COPYING;md5=51f4270b012ecd4ab1a164f5f4ed6cf7"
DEPENDS += "libxext libsm libxt"
PE = "1"

XORG_PN = "libXpm"

PACKAGES =+ "sxpm cxpm"
FILES:cxpm = "${bindir}/cxpm"
FILES:sxpm = "${bindir}/sxpm"

这里将 sxpm 和 cxpm 分开。因为 bindir 默认属于主包,所以将 新包名放在 PACKAGES 前面,再通过 FILES:* 指定各包内容。 前面的包已收集的文件会被后面的包跳过,因此主包不再包含它们。

5 打包外部生成的二进制文件

有时只有预编译二进制而没有源码,例如公司其他部门提供的专有程序,不能使用 获取源码再编译的普通配方,只能打包后安装到镜像。一般不推荐这样做,因为 可能妨碍可复现构建并引入未来 ABI 兼容问题,但有时别无选择。

最简单的方法是继承 bin_package,并采用默认产物位置。 它为配置和编译任务设置 noexec,将 FILES:${PN} 设为 "/" 收集全部 文件,并提供从 ${S} 复制到 ${D} 的安装任务。适用于解包内容已经 按目标布局组织的情况。变量详见 FILES、PN、S 和 D。

备注

  • 二进制组件也应使用 DEPENDS,共享库尤其需要。列出库依赖可 确保其他配方链接时,系统根目录中存在必需的库。

  • DEPENDS 也有助于自动添加运行时依赖,见 “自动添加运行时依赖”。

如果不能用该类,应确保:

  • 配置和编译任务不执行操作。通常不定义即可,默认实现仅在 ${S} 有 Makefile 时才实际工作。

    如果可能存在 Makefile,或继承的类覆盖了任务,可设置 [noexec]:

    do_configure[noexec] = "1"
    do_compile[noexec] = "1"
    

    与 Deleting a Task 不同,这保留了从获取、解包、打补丁到安装的依赖链。

  • do_install 正确安装二进制。

  • 设置 FILES,通常为 FILES:${PN},使其覆盖 实际安装位置,特别是非默认位置。

下面的 wireless-regdb 片段获取二进制归档并手动安装,无需配置或编译,展示 bin-package 风格配方:

SRC_URI = "https://www.kernel.org/pub/software/network/${BPN}/${BP}.tar.xz"
SRC_URI[sha256sum] = "57f8e7721cf5a880c13ae0c202edbb21092a060d45f9e9c59bcd2a8272bfa456"

inherit bin_package allarch

do_install() {
    install -d -m0755 ${D}${nonarch_libdir}/crda
    install -d -m0755 ${D}${sysconfdir}/wireless-regdb/pubkeys
    install -m 0644 regulatory.bin ${D}${nonarch_libdir}/crda/regulatory.bin
    install -m 0644 wens.key.pub.pem ${D}${sysconfdir}/wireless-regdb/pubkeys/wens.key.pub.pem
    install -m 0644 -D regulatory.db ${D}${nonarch_base_libdir}/firmware/regulatory.db
    install -m 0644 regulatory.db.p7s ${D}${nonarch_base_libdir}/firmware/regulatory.db.p7s
}

5 遵循配方风格指南

编写时应遵循 “配方风格指南”。已有配方可能 略有偏离,但至少应保持一致。赋值 = 两侧省略空格、配方组成顺序混乱等 普遍被视为不良风格。

5 配方语法

以下概述基本语法,完整说明见 “Syntax and Operators”。

  • 变量赋值与操作: 值可以是静态文本,也可以引用其他变量,支持前后追加。 示例:

    S = "${UNPACKDIR}/postfix-${PV}"
    CFLAGS += "-DNO_ASM"
    CFLAGS:append = " --enable-important-feature"
    
  • 函数: 定义一组操作,通常覆盖默认任务或在其前后追加。标准函数使用 sh 语法,也可访问 OpenEmbedded 变量和内部方法。sed 配方示例:

    do_install () {
        autotools_do_install
        install -d ${D}${base_bindir}
        if [ ! ${D}${bindir} -ef ${D}${base_bindir} ]; then
            mv ${D}${bindir}/sed ${D}${base_bindir}/sed
            rmdir ${D}${bindir}/
        fi
    }
    

    也可添加在已有任务之间调用的新函数,或使用 Python 实现,但多数配方 不涉及这些方式。

  • 关键字: inherit 引入公共功能,include、require 从其他 文件加载内容,export 导出环境变量。例如:

    export POSTCONF = "${STAGING_BINDIR}/postconf"
    inherit autoconf
    require otherfile.inc
    
  • 注释(#): 以 # 开头的行被忽略:

    # This is a comment
    

下面归纳最常用语法,更多信息仍见 BitBake 元数据章节。

  • 续行(\): 行末使用反斜杠 \,将语句延续到下一行:

    VAR = "A really long \
           line"
    

    备注

    反斜杠后不能有任何字符,包括空格或制表符。

  • 引用变量(${VARNAME}): 用 ${VARNAME} 访问变量内容:

    SRC_URI = "${SOURCEFORGE_MIRROR}/libpng/zlib-${PV}.tar.gz"
    

    备注

    此表达式不会立即替换,而在随后需要时展开,例如函数执行时,从而使用 最适合最终上下文的值。少数确需立即展开的情况,可用 := 代替 =。

  • 为所有赋值加引号: 值均用双引号,例如:

    VAR1 = "${OTHERVAR}"
    VAR2 = "The version is ${PV}"
    
  • 条件赋值(?=): 仅在变量尚未设置时赋值,也称软赋值。常用于 配置文件,允许外部环境提供变量值。

    以下仅在 VAR1 未设置时赋为 "New value",已设置则保留:

    VAR1 ?= "New value"
    

    下例最终为 "Original value":

    VAR1 = "Original value"
    VAR1 ?= "New value"
    
  • 后追加(+=): 向已有变量追加内容。

    备注

    新旧内容之间自动插入空格。

    示例:

    SRC_URI += "file://fix-makefile.patch"
    
  • 前追加(=+): 向变量前面添加内容。

    备注

    新旧内容之间自动插入空格。

    示例:

    VAR =+ "Starts"
    
  • 后追加(:append): 不自动加空格,在全部 +=、=+ 和 = 操作之后应用。配方使用后,其他层只能通过 :remove 去除,而去除后 后续层也不能重新加回。

    以下显式加前导空格,避免与原值粘连:

    CFLAGS:append = " --enable-important-feature"
    

    配合覆盖,仅为指定目标或机器应用:

    CFLAGS:append:sh4 = " --enable-important-sh4-specific-feature"
    
  • 前追加(:prepend): 不自动加空格,同样在其他赋值操作之后应用。 以下显式添加末尾空格:

    CFLAGS:prepend = "-I${S}/myincludes "
    

    也可配合覆盖:

    CFLAGS:prepend:sh4 = "-I${S}/myincludes "
    
  • 覆盖: 根据构建条件设置值。例如,所有 MACHINE 的 KBRANCH 为 "standard/base",只有 qemuarm 使用 "standard/arm-versatile-926ejs":

    KBRANCH = "standard/base"
    KBRANCH:qemuarm = "standard/arm-versatile-926ejs"
    

    覆盖还用于区分其他替代值。例如,包专用 FILES 和 RDEPENDS 必须使用对应包名覆盖。

  • 缩进: 使用空格而非制表符。Shell 函数目前两者都可,但 Yocto Project 策略要求 Shell 函数使用制表符;部分层则要求所有缩进使用空格。

  • 用 Python 处理复杂操作: 可在赋值时执行搜索替换等处理,以 ${@python_code} 表示:

    SRC_URI = "ftp://ftp.info-zip.org/pub/infozip/src/zip${@d.getVar('PV',1).replace('.', '')}.tgz"
    
  • Shell 函数语法: 像普通 Shell 脚本一样编写操作,确保兼容通用 sh, 不依赖 Bash 或其他专用功能。sed、grep、awk 等工具也应注意 兼容性,不确定时用多个实现测试,包括 BusyBox。