5 编写新配方
配方(.bb 文件)是 Yocto Project 的基本组成部分。构建系统中的每个
软件组件都需要配方定义。本节介绍如何创建、编写和测试新配方。
备注
配方常用变量和命名问题见参考手册的 “配方”。
5 概述
下图展示基本流程,其余各节说明具体步骤。
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} 引用配方文件名指定的版本,这样升级通常只需改名。
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 解包代码
默认值结合 UNPACKDIR 与 BP,已经指向构建系统的解包目录,
通常无需显式设置。解包成功后,可检查 ${S} 是否为源码根目录。
备注
过去 Git 配方需显式设 S 为 ${WORKDIR}/git,现在不再需要,
见 应移除 S = ${WORKDIR}/git 和 S = ${UNPACKDIR}/git。
5 修补代码
SRC_URI 中以 .patch、.diff 或其压缩后缀结尾的文件,会由
do_patch 自动作为补丁应用。
默认应能以 "-p1" 应用,即去掉一层路径。需要更多层时,在 URI 条目设置 "striplevel";需要在补丁未指定的子目录应用时,设置 "patchdir"。
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 = ""
应用提供的自定义 CMake 工具链文件应安装到
${D}${datadir}/cmake/Modules。
5 启用系统服务
服务通常开机启动并在后台运行,安装它需要额外配方定义。
如果服务初始化脚本或服务文件未安装,应通过 do_install:append 安装;
若已有 do_install,则在该函数末尾添加,而非另写追加函数。
安装应完成通常由 make install 处理的工作,确保输出布局与目标系统一致。
构建系统支持两种服务启动方式:
SysVinit: 管理系统基本初始化。init 是内核启动的第一个程序,负责 其他程序的启动、运行和关闭。
配方需继承 update-rc.d,以便安全安装到目标,并设置 INITSCRIPT_PACKAGES、INITSCRIPT_NAME 和 INITSCRIPT_PARAMS。
systemd: 用于替代 SysVinit,增强服务管理,见 https://systemd.io/。
配方需继承 systemd,详情见 OpenEmbedded-Core (OE-Core) 的
systemd.bbclass。
5 打包
打包结合构建系统自动流程与人工处理:
拆分文件: do_package 将输出拆成逻辑组件。即使只有一个 可执行文件,也可能需要拆出符号、文档等,任务负责正确拆分并打包。
运行 QA: insane 在包生成流程加入检查,排除常见 运行时问题。见该类及参考手册 “质量检查错误和警告消息”。
人工检查: 查看
${WORKDIR}/packages-split,确认 文件如预期分配。发现问题后调整 PACKAGES、FILES、do_install(:append)等。拆为多个包: 示例见 “将应用拆成多个软件包”。
安装后脚本: 示例见 “安装后脚本”。
标明包架构: 根据输出和配置,应标明是否机器专用,或完全不依赖机器架构。
默认包适用于与目标同架构的机器。若包与具体机器相关,例如配置脚本使用 MACHINE 或仅为某机器应用补丁,添加:
PACKAGE_ARCH = "${MACHINE_ARCH}"
若只打包脚本或配置等完全架构无关内容,继承 allarch:
inherit allarch
初始构建阶段这未必关键,但正确架构能确保配置变化时恰当重建,并在目标 安装正确软件包,多机器分别构建时尤其重要。
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。
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 将应用拆成多个软件包
例如,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。
如果不能用该类,应确保:
配置和编译任务不执行操作。通常不定义即可,默认实现仅在
${S}有 Makefile 时才实际工作。如果可能存在 Makefile,或继承的类覆盖了任务,可设置
[noexec]:do_configure[noexec] = "1" do_compile[noexec] = "1"
与 Deleting a Task 不同,这保留了从获取、解包、打补丁到安装的依赖链。
do_install 正确安装二进制。
下面的 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"
缩进: 使用空格而非制表符。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。