在自建 Yocto layer 中编译并打包本地 C 程序

原文:Yocto Project 5.0.13《Writing a New Recipe》,合并同版 Layers 与 SDK Toolchain 相关内容。原页未署个人作者;维护方为 Yocto Project。译编:未完纪。

本文范围:按照候选任务边界,完整展开“自建 layer、准备本地 C 文件、编写配方、构建、安装与拆包检查”。新配方源章已完整阅读;本文是按任务译编,不是整章逐段翻译,不展开 LZ4、GNU Hello、Meson、服务安装和预编译二进制等其他示例。补充命令及调整均在文中注明。

Yocto本地C程序从layer到取源、编译、暂存安装和拆包的流程
图:本地单文件配方的构建路径。未完纪依据 Yocto Project 5.0.13 文档原创绘制;这是技术示意图,不是运行截图。

配方负责什么

配方是扩展名为 .bb 的文件,也是 Yocto Project 环境的基本组成部分。OpenEmbedded 构建系统构建的每个软件组件,都需要一份配方来定义。写新配方通常是反复构建、发现缺失信息、再补齐信息的过程。

你可以从零开始,也可以用 devtool add 创建配方及适合开发的环境,用 recipetool create 根据源文件生成基础配方,或寻找现有的类似配方。前两者采用相同的自动生成逻辑,但 devtool add 还为修改源码和补丁准备开发环境。现有配方可以在 OpenEmbedded Layer Index 查找;使用不熟悉的配方时,要检查是否带入无关功能或遗漏必要设置。本例只有一个本地 C 文件,直接写一份小配方即可。

1. 创建并启用自己的 layer

layer 用于将不同类型的定制分开。创建之前,先检查是否已有满足需求的 layer。自建目录通常使用 meta- 前缀,并应置于独立位置,例如官方 poky 仓库旁。

译注:以下命令串起同版官方步骤。假设已经准备好 Yocto Project 5.0.13 / Scarthgap 的 Poky 和受支持的 Linux 构建环境,当前位于 Poky 根目录。在普通构建用户的 shell 中载入环境,默认会进入 poky/build:

source oe-init-build-env
bitbake-layers create-layer ../../meta-localhello
bitbake-layers add-layer ../../meta-localhello
bitbake-layers show-layers

create-layer 根据当前源码树模板创建基础 layer,默认优先级为 6,包含 conf/layer.conf、示例配方、COPYING.MIT 和 README。需要改变优先级或示例名时可分别使用 --priority、--example-recipe-name,详细语法见 bitbake-layers create-layer --help。模板假定 layer 本身使用 MIT,不能据此推定其中每份软件源码的许可。

add-layer 将 layer 加入构建目录的 conf/bblayers.conf,show-layers 可核对实际启用结果。构建系统按 BBLAYERS 顺序读取每个 layer 的配置。若你使用了自定义构建目录,上面的相对路径必须随之调整。

版本修正:5.0.13 的 layer 章节仍保留了 LAYERSERIES_COMPAT_yoctobsp = "dunfell" 的历史示意配置。本流程使用 Scarthgap 环境的工具生成配置,再核对兼容声明,避免照搬旧示例。兼容声明不是兼容性测试结果,也不应仅为了绕过错误而随意改写。

配方的位置必须能被 layer.conf 中的 BBFILES 匹配。典型写法是:

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

BBPATH 用于寻找类、配置和被 include / require 引入的文件。BBFILE_COLLECTIONS 标识 layer,BBFILE_PATTERN 定义匹配目录,BBFILE_PRIORITY 处理同名配方优先级,LAYERDEPENDS 声明其他 layer 依赖。自建类与配置文件宜使用不易冲突的名称。

2. 准备本地 C 源文件

在构建目录中创建本例目录:

mkdir -p ../../meta-localhello/recipes-examples/helloworld/files
meta-localhello/
├── conf/
│   └── layer.conf
└── recipes-examples/
    └── helloworld/
        ├── helloworld_1.0.bb
        └── files/
            └── helloworld.c

配方采用 basename_version.bb 命名,因此本例基名为 helloworld,版本为 1.0。使用小写;除非确实表示对应用途,不要把 -native、-cross、-initial 或 -dev 等保留后缀随意用作名称的一部分。

新配方章节没有提供 helloworld.c 内容。这里补入同版 SDK 章节的完整 hello.c 示例,将文件名统一为 helloworld.c,只整理缩进,保留原函数写法与输出:

#include <stdio.h>

int main()
{
    printf("Hello World!\n");
    return 0;
}

许可证边界:该片段作为官方文档示例引用。文档中的程序并不自动给读者自己的 C 源码授予 MIT 许可。下节原配方的 LICENSE 和 LIC_FILES_CHKSUM 是 MIT 示例;实际打包前,必须让这两项与所用源代码的真实许可证及声明相符。文章转载授权、文档许可和软件源码许可应分别处理。

3. 写出完整单文件配方

以下保留官方示例的完整字段和任务,保存文件名为 helloworld_1.0.bb。MIT 字段的适用条件见上文:

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

SRC_URI = "file://helloworld.c"

S = "${WORKDIR}"

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

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

SRC_URI 指定取源位置。file:// 表示本地文件,按 FILESPATH 查找;常见目录包括配方旁的 ${BP}、${BPN} 与 files。本例使用 files。没有压缩包内部的顶层源码目录,所以在 5.0.13 中将 S 设为 ${WORKDIR}。迁移其他 Yocto 版本前应重新核对该行为。

do_compile 使用构建系统提供的交叉编译器 ${CC} 和链接选项 ${LDFLAGS},不要替换成主机的普通 gcc。do_install 先建目录,再以 0755 权限安装程序。${D} 是安装暂存根目录,${bindir} 是目标可执行文件目录;这不是直接把文件写入构建主机的 /usr/bin。

译注:可选的编译任务调整。原例没有显式传入 CPPFLAGS 与 CFLAGS。为保留构建环境的预处理、优化和加固标志,可将编译任务替换为下面的版本。差异是增加这两个变量,并把链接标志放在源文件之后;该改动只经过静态审阅:

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

不要把 ${CC} 整体加成一个 shell 引号参数,因为它可能包含编译器及多个参数。若以后添加来自外部的任意值,则必须按实际用途验证和引用,避免把不可信内容拼接成 shell 命令。

4. 构建与定位工作目录

在已载入环境的构建目录执行:

bitbake helloworld

命令只接受这里所需的配方基名。构建系统为每份配方建立工作目录,保存源文件、任务日志、中间文件和打包结果。路径随架构、版本和上下文变化,应查询实际值:

bitbake -e helloworld | grep '^WORKDIR='

用输出定位工作目录,检查以下内容:

  • temp/log.do_fetch:源文件是否找到,配方引用与实际文件名是否一致。
  • temp/log.do_compile:编译器、参数及错误,是否错误地使用主机的 /usr/include 或 /usr/lib。
  • temp/log.do_install 与 image:安装位置与权限是否符合预期。
  • packages-split:文件最后进入哪个包,是否缺失、重复或被放错位置。

原文任务日志采用 log.taskname 命名,例如 log.do_configure、log.do_fetch、log.do_compile。构建没有报错并不等于软件包内容正确。

如果软件需要其他配方提供的库或头文件,应使用 DEPENDS 声明构建依赖。运行时依赖是按软件包设置的,主包写作 RDEPENDS:${PN}。部分共享库依赖会在打包阶段自动推导,仍不能省略准确的构建依赖。如果遇到找不到头文件或库,先检查依赖和 sysroot 路径;并行构建偶发失败则要检查构建规则的依赖关系,而不是把偶然成功当成解决。

5. 检查安装与拆包

do_install 把源目录、构建目录和工作目录的文件复制到 ${D},形成目标设备上的目录结构。手动安装先使用 install -d 建目录,再复制文件。若需要修改已安装文件里的路径,应复制后再修改目标副本,保证任务能够重新执行。

配方不应直接写入 sysroot。应先安装到 ${D} 的标准位置,再由 do_populate_sysroot 按目录规则填充 sysroot。构建系统通过清单跟踪这些文件,便于配方更改或移除后清理旧内容。

do_package 将产物拆成主程序、调试信息、开发文件等逻辑组件。原文列出默认的 helloworld、helloworld-dbg 和 helloworld-dev 包名;实际是否输出空包或非空包取决于规则与内容,必须检查 packages-split。即使只生成一个二进制,也可能需要单独的调试包。

insane 类参与打包 QA,发现运行时常见问题。出现文件归属问题时,检查 PACKAGES、FILES 与 do_install,不要仅关闭 QA 来掩盖问题。编译出的 C 二进制与目标架构有关,不应因为代码简单就使用 inherit allarch。

6. 在目标环境验证

配方的最后一步是确认程序可以正确运行。按现有镜像定制方式将构建出的软件包加入镜像,在目标设备或对应仿真环境中执行 helloworld。如果目标架构与构建主机不同,不能直接在主机执行该交叉编译产物。

示例源代码的预期输出是一行 Hello World!。这只是从源码确定的预期行为;本文没有运行 BitBake、生成镜像或启动目标设备。读者实际运行前仍应落实源代码许可、目标机配置和版本匹配。

来源、许可与审核范围

本文依据 Yocto Project 5.0.13 新配方章节的本地取源、命名、依赖、编译、安装、拆包、测试及单 C 文件示例,合并 layer 创建与启用和 SDK C 示例。其他构建系统与高级配方场景可沿原文继续阅读。

Yocto Project 文档按 CC BY-SA 2.0 UK: England & Wales 提供;本译编保留来源,文档译编部分沿用该许可。软件示例实际许可应另行核对。2026-10-05 的改动包括中文译编、统一源文件名、用当前环境生成 layer 配置、补充执行顺序和静态审核,并单列编译标志调整。没有执行原文或修订后的构建命令。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容