第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 六个概念的关系图

说了这么多,它们之间到底怎么配合?我画了一张图,你看完就明白了:

Yocto核心概念关系图 BitBake 引擎 Recipe(.bb 配方文件) Layer(功能模块层) Machine配置 Image镜像 Package(.ipk/.deb/.rpm) 构建流程说明 1. BitBake 解析 Layer 和 Machine 配置,确定构建目标 2. 根据 Recipe 执行 fetch、patch、compile、install 等任务 3. 生成 Package,最终组装成完整的 Image 镜像

从这张图你能看到:BitBake是核心调度器,它读取Layer里的Recipe,结合Machine配置,编译出Package,最后组装成Image。整个流程清晰、模块化、可复用。

3.8 本章小结

六个概念,我帮你总结成一句话:

一句话总结:BitBake根据Machine配置,执行Layer里的Recipe,生成Package,最终打包成Image。

记住这个链条,Yocto的构建逻辑你就掌握了八成。后面的章节,我们会在这个基础上,加入MQTT通信和OTA升级的逻辑。到时候你会发现,理解了这些核心概念,后面的东西都是水到渠成。