第3章:Yocto核心概念:Layer、Recipe、BitBake、Machine配置、Image与Package
好,咱们进入正题。Yocto项目刚上手时,最让人头晕的就是那一堆术语——Layer、Recipe、BitBake、Machine……说实话,我当年第一次看文档,翻了半小时还在想“这到底是个啥”。
其实没那么复杂。Yocto本质上是一套自动化构建系统。你告诉它“我要给哪个硬件做系统、装哪些软件”,它就去下载源码、打补丁、编译、打包,最后生成一个完整的嵌入式Linux镜像。
这一章,我把六个核心概念拆开揉碎了讲。你理解透它们,Yocto的大门就算真正推开了。
3.1 BitBake:Yocto的引擎
BitBake是Yocto的构建引擎,说白了就是一个任务调度器。它负责解析配置文件、执行任务、处理依赖关系。
我个人习惯把BitBake类比成Make的升级版。Make处理的是文件依赖,BitBake处理的是任务依赖。比如你要编译一个软件包,得先下载源码、再解压、再打补丁、再配置、再编译……这一串任务,BitBake帮你按顺序搞定。
核心要点:BitBake不直接编译代码,它调度Recipe(配方)里定义的任务。每个Recipe就是一个“怎么做”的说明书。
BitBake的命令很简单,常用的就几个:
bitbake core-image-minimal # 构建一个最小镜像
bitbake -c clean linux-yocto # 清理某个包的构建产物
bitbake -s | grep openssl # 搜索某个包的版本
bitbake -g core-image-base # 生成依赖关系图
我在项目中遇到过一个问题:构建到一半报错,说某个包依赖不满足。当时我直接bitbake -c cleanall把整个包缓存清了重来——其实没必要,用-c clean只清那个包就行。嗯,这里要注意,cleanall会连下载的源码一起删掉,下次构建又要重新下载,很浪费时间。
3.2 Recipe:构建的“菜谱”
Recipe是Yocto里最基础的单位。每个Recipe描述了一个软件包从哪里下载、如何编译、安装到哪个目录。
Recipe文件以.bb结尾,放在Layer的recipes-xxx目录下。看一个最简单的例子:
# hello_1.0.bb
SUMMARY = "A simple hello world program"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade..."
SRC_URI = "file://hello.c"
S = "${WORKDIR}"
do_compile() {
${CC} ${CFLAGS} hello.c -o hello
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
你看,Recipe里定义了源码位置、编译命令、安装路径。BitBake拿到这个Recipe,就会自动执行do_fetch、do_unpack、do_compile、do_install等一系列任务。
小技巧:Recipe文件名有讲究。hello_1.0.bb里的1.0是版本号。Yocto会自动解析这个版本号,用于版本比较和依赖管理。如果你写hello_1.0.1.bb,它也能正确识别。
3.3 Layer:模块化的“积木盒”
Layer是Yocto实现模块化的关键。每个Layer包含一组相关的Recipe、配置文件和补丁。你可以把Layer想象成一个功能模块:
- meta:Yocto核心层,提供基础Recipe和配置
- meta-poky:Poky参考发行版配置
- meta-yocto-bsp:官方支持的板级支持包
- meta-raspberrypi:树莓派的BSP层(社区维护)
- meta-qt5:Qt5图形框架层
为什么要用Layer?我举个例子。你在做一个项目,用了A公司的SoC和B公司的Wi-Fi模块。A公司提供meta-company-a层,B公司提供meta-company-b层。你把它们加到bblayers.conf里,就能同时支持两家硬件。互不干扰,各管各的。
Layer的优先级通过BBFILE_PRIORITY控制。数值越大,优先级越高。当两个Layer提供同名的Recipe时,高优先级的会覆盖低优先级的。
避坑指南:我曾经在项目中遇到一个诡异的问题——明明改了Recipe,构建出来的东西却没变。查了半天,发现是另一个Layer里有个同名的.bbappend文件,把我们的修改覆盖了。所以,一定要搞清楚Layer的优先级和.bbappend的覆盖规则。
3.4 Machine配置:告诉Yocto你的硬件长什么样
Machine配置定义了目标硬件的特性。它告诉Yocto:CPU是什么架构、内存多大、用什么bootloader、串口是哪个……
Machine配置文件放在conf/machine/目录下,以.conf结尾。看一个树莓派3的配置片段:
# conf/machine/raspberrypi3.conf
include conf/machine/raspberrypi-armv7a.conf
MACHINE = "raspberrypi3"
SERIAL_CONSOLES = "115200;ttyAMA0 115200;ttyS0"
KERNEL_IMAGETYPE = "zImage"
MACHINE_FEATURES = "apm usbhost keyboard vfat ext2 ext3"
MACHINE_EXTRA_RRECOMMENDS = "kernel-modules"
你想想看,如果没有Machine配置,Yocto怎么知道该编译arm架构的内核还是x86架构的?怎么知道用哪个设备树?所以,Machine配置是构建的起点。
构建时,通过MACHINE变量指定目标:
MACHINE=raspberrypi3 bitbake core-image-minimal
3.5 Image:你要的最终系统
Image就是最终生成的系统镜像。它是一组Recipe的集合,定义了系统里要装哪些包。
Yocto自带了一些参考Image:
| Image名称 | 包含内容 | 适用场景 |
|---|---|---|
| core-image-minimal | 仅基础系统 | 最小化验证 |
| core-image-base | 基础系统+常用工具 | 通用嵌入式系统 |
| core-image-sato | 带X11桌面环境 | 带显示的系统 |
| core-image-full-cmdline | 完整命令行工具 | 类似服务器环境 |
你也可以自定义Image。创建一个.bb文件,继承core-image类,然后往IMAGE_INSTALL里加包就行:
# my-custom-image.bb
inherit core-image
IMAGE_INSTALL += " \
openssh \
python3 \
mosquitto \
i2c-tools \
"
构建时直接:
bitbake my-custom-image
嗯,这里要注意,Image的Recipe通常放在recipes-core/images/目录下。这是Yocto的惯例,虽然不是强制的,但建议遵守。
3.6 Package:软件包的“最小单元”
Package是Yocto里软件分发的最小单位。一个Recipe可以生成多个Package。比如openssh这个Recipe,会生成openssh、openssh-sshd、openssh-sftp-server等多个Package。
这样做的好处是按需裁剪。你的设备可能只需要SSH客户端,不需要服务端。那就在Image里只装openssh,不装openssh-sshd,省空间。
Package的打包格式由PACKAGE_CLASSES决定:
- package_rpm:RPM格式,适合大型系统
- package_deb:Deb格式,Debian系习惯
- package_ipk:IPK格式,轻量级,嵌入式常用
我个人习惯用IPK。它体积小、依赖处理简单,很适合资源受限的嵌入式设备。
3.7 六个概念的关系图
说了这么多,它们之间到底怎么配合?我画了一张图,你看完就明白了:
从这张图你能看到:BitBake是核心调度器,它读取Layer里的Recipe,结合Machine配置,编译出Package,最后组装成Image。整个流程清晰、模块化、可复用。
3.8 本章小结
六个概念,我帮你总结成一句话:
一句话总结:BitBake根据Machine配置,执行Layer里的Recipe,生成Package,最终打包成Image。
记住这个链条,Yocto的构建逻辑你就掌握了八成。后面的章节,我们会在这个基础上,加入MQTT通信和OTA升级的逻辑。到时候你会发现,理解了这些核心概念,后面的东西都是水到渠成。