2. Yocto核心概念解析:Poky、BitBake、Metadata、Layer、Recipe、Class、Configuration文件

说实话,刚接触Yocto那会儿,我也被这一堆术语搞得头晕。Poky是什么?BitBake又是干嘛的?Layer和Recipe到底啥关系?

别急,这一章咱们就把这些概念一个一个掰开揉碎了讲。我保证,看完之后你心里会有一张清晰的地图。

2.1 核心概念全景图

先上一张图,让你对整体有个印象。Yocto的构建系统,本质上是一个分层驱动的代码生成工厂。

Yocto核心概念关系图 Yocto 核心概念关系图 Poky(参考发行版 + 元数据集合) BitBake(任务执行引擎) Metadata(元数据)—— 构建的“配方”与“规则” Recipe (.bb) Class (.bbclass) Configuration (.conf) Append (.bbappend) Layer(层)—— 按功能组织的元数据集合 构建产物(镜像、SDK、IPK包)

这张图你看懂了吗?从上到下,就是Yocto构建的完整逻辑链。咱们接下来一个一个拆解。

2.2 Poky —— 一切的起点

Poky 是什么?说白了,它是Yocto项目官方维护的一个参考发行版,也是一个元数据集合。

我刚开始学的时候,以为Poky就是Yocto本身。其实不是。Yocto是一个开源协作项目,而Poky是它提供的一个“样板间”。

  • 它包含BitBake:任务执行引擎。
  • 它包含Metadata:一堆Recipe、Class、Configuration文件。
  • 它包含一个默认的构建环境:你 git clone 下来,就能直接跑 bitbake core-image-minimal。
我的习惯: 做BSP移植时,我通常基于Poky的某个稳定分支(比如 kirkstone 或 dunfell)开始。不要从零开始写,那是给自己找麻烦。

2.3 BitBake —— 真正的“发动机”

BitBake 是一个任务调度引擎。你可以把它理解成一个“超级Makefile解析器”。

它负责:

  1. 解析Metadata:读取所有Recipe、Class、Configuration。
  2. 构建依赖树:搞清楚哪个包先编译,哪个后编译。
  3. 调度任务:比如 do_fetch、do_patch、do_compile、do_install。
  4. 执行任务:并行或串行,全看依赖关系。

BitBake 的核心语法是 Shell 函数 + Python 函数。你可以在Recipe里写Shell脚本,也可以用Python写更复杂的逻辑。

# 一个简单的BitBake任务示例
do_display_config() {
    bbplain "当前机器类型: ${MACHINE}"
    bbplain "目标架构: ${TUNE_ARCH}"
}
addtask display_config before do_build
我曾经踩过的坑: 有一次我修改了 local.conf,但忘了重新 source oe-init-build-env,结果BitBake一直用旧配置跑了两个小时。记住:修改配置后,最好重新source环境,或者至少 bitbake -c cleanall 一下。

2.4 Metadata —— 构建的“灵魂”

Metadata 是Yocto里所有“非代码”文件的统称。它告诉BitBake:要做什么、怎么做、用什么做。

Metadata 主要包含以下几类文件:

文件类型 扩展名 作用 类比
Recipe .bb 描述一个软件包的来源、依赖、编译、安装方式 菜谱
Class .bbclass 可复用的构建逻辑,被多个Recipe继承 模板函数
Configuration .conf 定义全局变量、机器特性、发行版策略 配置文件
Append .bbappend 在不修改原Recipe的前提下,追加或覆盖内容 补丁文件

2.5 Recipe —— 最核心的“菜谱”

Recipe 是Yocto里出现频率最高的文件。每个Recipe对应一个软件包(比如 openssl、busybox、linux-yocto)。

一个典型的Recipe长这样:

# hello_1.0.bb
SUMMARY = "A simple hello world program"
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}
}

你看,Recipe里定义了:

  • 元信息:摘要、许可证。
  • 源码来源:从哪里下载或获取源码。
  • 编译方法:用什么编译器、什么参数。
  • 安装方法:把编译产物放到哪个目录。
关键点: Recipe的命名有讲究。hello_1.0.bb 表示包名是 hello,版本是 1.0。BitBake会根据版本号自动选择最新版本。

2.6 Class —— 可复用的“模板”

你有没有想过,如果每个Recipe都要自己写 do_compile 和 do_install,那得多累?

Class 就是来解决这个问题的。它把通用的构建逻辑抽出来,让Recipe通过 inherit 关键字继承。

常用的Class有:

  • autotools.bbclass:处理 ./configure && make && make install 流程。
  • cmake.bbclass:处理 CMake 构建。
  • kernel.bbclass:专门用于构建Linux内核。
  • deploy.bbclass:把构建产物部署到输出目录。
# 使用Class的Recipe示例
inherit autotools

SRC_URI = "https://example.com/libfoo-${PV}.tar.gz"

# 不需要写do_compile和do_install了,autotools.bbclass已经帮你搞定
我的建议: 写新Recipe时,先看看有没有现成的Class可以用。我见过太多人自己手写 do_compile,结果跟 autotools.bbclass 冲突,排查了半天。

2.7 Configuration 文件 —— 全局“开关”

Configuration 文件用来设置全局变量。它们决定了整个构建系统的行为。

常见的Configuration文件:

文件 位置 作用
local.conf build/conf/ 用户本地配置,比如并行编译数、下载目录、目标机器
bblayers.conf build/conf/ 声明哪些Layer被包含在构建中
machine.conf meta-*/conf/machine/ 定义特定硬件平台的特性(如串口、内核设备树)
distro.conf meta-*/conf/distro/ 定义发行版策略(如包管理器、C库选择)

举个例子,local.conf 里你可能会看到:

MACHINE ?= "qemuarm"
DL_DIR ?= "${TOPDIR}/downloads"
BB_NUMBER_THREADS ?= "4"
PARALLEL_MAKE ?= "-j 4"
注意: Configuration文件的解析顺序很重要。BitBake会按 layer.conf → machine.conf → distro.conf → local.conf 的顺序加载。后面的会覆盖前面的变量。

2.8 Layer —— 组织的“文件夹”

Layer 是Yocto里组织Metadata的基本单位。一个Layer就是一个有特定目录结构的文件夹。

典型的Layer结构:

meta-mylayer/
├── conf/
│   ├── layer.conf          # Layer自身的配置
│   └── machine/
│       └── myboard.conf    # 机器配置
├── recipes-example/
│   └── hello/
│       ├── hello_1.0.bb    # Recipe
│       └── files/
│           └── hello.c     # 源码文件
├── classes/
│   └── my-custom.bbclass   # 自定义Class
└── README

为什么要用Layer?

  • 模块化:BSP相关的东西放 meta-my-bsp,应用层的东西放 meta-my-app。
  • 可复用:换项目时,直接把Layer搬过去就行。
  • 优先级控制:通过 bblayers.conf 里的Layer顺序,控制谁覆盖谁。
实战经验: 我习惯把Layer分成三层:
1. 底层:meta(Poky官方)、meta-yocto-bsp(官方BSP)。
2. 中间层:meta-my-bsp(板级支持包)、meta-my-vendor(芯片厂商提供)。
3. 应用层:meta-my-product(产品特有配置和软件)。
这样分层,后期维护起来特别清爽。

2.9 小结:它们是怎么协同工作的?

咱们来捋一遍整个流程:

  1. 你执行 bitbake core-image-minimal。
  2. BitBake 读取 bblayers.conf,知道要加载哪些Layer。
  3. BitBake 解析每个Layer里的 .conf 文件,设置全局变量(比如 MACHINE、DISTRO)。
  4. BitBake 找到 core-image-minimal 这个Recipe,解析它。
  5. Recipe 通过 inherit 引入Class,获得通用的构建函数。
  6. BitBake 构建依赖树,按顺序执行 do_fetch、do_patch、do_compile、do_install、do_package 等任务。
  7. 最终生成镜像文件、IPK包或SDK。

你看,从Poky到BitBake,再到Metadata、Layer、Recipe、Class、Configuration,每个概念都有自己的位置和职责。理解了它们,你就掌握了Yocto的“语法”。