4. Recipe 编写入门:从零到一构建你的第一个软件包
Recipe,说白了就是 Yocto 世界的「菜谱」。你告诉它去哪找源码、怎么编译、装到哪个目录,它就能帮你把原材料变成嵌入式系统里能跑的二进制。这一章,我带你手写一个最简单的 Recipe,把整个流程跑通。
本章核心目标:理解 Recipe 的骨架结构,掌握 LICENSE、SRC_URI、do_compile、do_install 四个关键要素,并能将 Recipe 成功加入镜像。
4.1 Recipe 文件结构:一个 .bb 文件就够了
一个标准的 Recipe 就是一个 .bb 文件,放在 meta-<layer>/recipes-<category>/<recipe-name>/ 目录下。文件名格式是 <包名>_<版本号>.bb。
举个例子:
meta-myapp/
└── recipes-example/
└── hello/
├── hello_1.0.bb
└── files/
└── hello.c
我个人习惯把源码文件放在 files/ 子目录里,这样结构清晰。Recipe 文件本身由多个变量和任务函数组成,最基本的骨架长这样:
SUMMARY = "A simple hello world program"
DESCRIPTION = "This recipe builds a hello world binary for embedded Linux"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://hello.c"
S = "${WORKDIR}"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} hello.c -o hello
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
嗯,这里要注意:S 默认指向 ${WORKDIR}/${BP},如果你的源码直接放在 files/ 下,需要显式设成 ${WORKDIR},否则 BitBake 找不到源码文件。
4.2 LICENSE 和 LIC_FILES_CHKSUM:合规的「身份证」
Yocto 对许可证管理非常严格。每个 Recipe 必须声明 LICENSE 和 LIC_FILES_CHKSUM,缺一不可。
| 变量 | 作用 | 示例 |
|---|---|---|
LICENSE |
声明软件包的许可证类型 | LICENSE = "MIT" |
LIC_FILES_CHKSUM |
指定许可证文件的路径和 MD5 校验值 | file://LICENSE;md5=xxx |
我曾经踩过一个坑:有一次我改了源码里的版权声明,但忘了更新 LIC_FILES_CHKSUM 的 MD5 值。BitBake 直接报错退出,提示「License checksum mismatch」。后来我养成了习惯——每次修改许可证相关文件后,先跑 bitbake -c fetch <recipe> 看报错信息里的正确 MD5,再填进去。
对于本地源码(file:// 协议),LIC_FILES_CHKSUM 的路径是相对于 Recipe 所在目录的。Yocto 内置了通用许可证文件,路径是 ${COMMON_LICENSE_DIR},比如 MIT、GPL-2.0 这些标准许可证可以直接引用。
4.3 SRC_URI:告诉 BitBake 去哪找源码
SRC_URI 是 Recipe 的「眼睛」,它决定了 BitBake 从哪里获取源码。支持多种协议:
- 本地文件:
file://hello.c—— 源码放在 Recipe 同级的files/目录 - Git 仓库:
git://github.com/user/repo.git;branch=main - HTTP 下载:
https://example.com/source.tar.gz - SVN:
svn://example.com/repo
你想想看,嵌入式项目里最常见的场景是什么?就是拿一个开源库,打个补丁,再编译。这时候 SRC_URI 可以写多个源:
SRC_URI = "git://github.com/libfoo/libfoo.git;branch=master \
file://0001-fix-cross-compile.patch \
file://config.patch"
BitBake 会按顺序依次获取:先 clone Git 仓库,再打补丁。补丁文件同样放在 files/ 目录下。
小技巧:如果源码在 Git 仓库里,记得设置 SRCREV 指定 commit ID。我习惯用 ${AUTOREV} 在开发阶段自动取最新提交,但发布版本一定要锁定具体 commit,否则构建不可重复。
4.4 do_compile 和 do_install:自定义编译与安装
Yocto 的构建流程是分阶段的,do_compile 和 do_install 是其中两个关键任务。默认情况下,如果源码是 autotools 或 cmake 项目,BitBake 会自动调用 ./configure && make && make install。但如果你写的是裸机程序或简单 C 项目,就得自己动手了。
do_compile:编译阶段
在这个任务里,你可以调用交叉编译器。Yocto 已经帮你设好了环境变量:
${CC}—— 交叉编译器(如 arm-poky-linux-gnueabi-gcc)${CFLAGS}—— 编译选项${LDFLAGS}—— 链接选项${WORKDIR}—— 工作目录,源码解压到这里
举个例子,编译一个多文件项目:
do_compile() {
${CC} ${CFLAGS} main.c utils.c -o myapp ${LDFLAGS}
}
do_install:安装阶段
这个任务负责把编译好的文件复制到目标目录。注意,这里的「安装」不是装到你的开发机上,而是装到 ${D} 目录(即 ${WORKDIR}/image),最终会被打包进 rootfs。
常用的安装命令:
do_install() {
# 创建目标目录
install -d ${D}${bindir} # /usr/bin
install -d ${D}${sbindir} # /usr/sbin
install -d ${D}${sysconfdir} # /etc
# 安装二进制
install -m 0755 myapp ${D}${bindir}
# 安装配置文件
install -m 0644 myapp.conf ${D}${sysconfdir}
}
注意:千万不要用 cp 命令直接复制文件!install 命令会自动处理权限、目录创建和文件属性。我见过有人用 cp 导致文件权限不对,最终 rootfs 里程序无法执行。
4.5 将 Recipe 添加到镜像
Recipe 写好了,怎么让它出现在最终的文件系统里?有两种方式:
方式一:在 local.conf 中直接添加
最直接的方法,在 build/conf/local.conf 里加上:
IMAGE_INSTALL:append = " hello"
注意 hello 前面有个空格,这是 Yocto 的语法要求。然后重新构建镜像:
bitbake core-image-minimal
方式二:通过自定义镜像 Recipe
如果你维护多个镜像,更推荐创建一个自定义镜像 Recipe。在 meta-myapp/recipes-core/images/my-image.bb 里:
require recipes-core/images/core-image-minimal.bb
IMAGE_INSTALL:append = " hello libfoo"
我个人更倾向第二种方式。为什么呢?因为 local.conf 是本地配置文件,换台机器就得重新配。而镜像 Recipe 可以随 layer 一起分发,团队里所有人都能用。
4.6 本章知识体系总览
下面这张图总结了 Recipe 从编写到生效的完整流程:
4.7 实战:完整跑通一个 Recipe
光说不练假把式。我们从头到尾跑一遍:
- 创建 layer 和目录结构
bitbake-layers create-layer meta-myapp cd meta-myapp mkdir -p recipes-example/hello/files - 编写源码文件
recipes-example/hello/files/hello.c#include <stdio.h> int main() { printf("Hello from Yocto!\n"); return 0; } - 编写 Recipe
recipes-example/hello/hello_1.0.bb(内容见 4.1 节) - 添加 layer 到构建环境
bitbake-layers add-layer meta-myapp - 加入镜像并构建
echo 'IMAGE_INSTALL:append = " hello"' >> build/conf/local.conf bitbake core-image-minimal - 验证 —— 启动 QEMU 或刷到板子上,运行
hello命令
调试小技巧:如果构建失败,先单独编译 Recipe 看日志:bitbake -c compile hello -v。我经常用 -v 参数查看详细输出,比翻 log 文件快多了。
Recipe 编写其实没那么玄乎。你只要记住:LICENSE 是合规底线,SRC_URI 是源码入口,do_compile 和 do_install 是核心逻辑,IMAGE_INSTALL 是最终归宿。把这四个点吃透,大部分 Recipe 你都能手到擒来。