3. 理解Poky与BitBake:Poky参考发行版结构、BitBake任务执行引擎、元数据(Metadata)与配方(Recipe)概念

好,咱们进入第三章。这一章是Yocto的“内功心法”。

很多新手刚开始接触Yocto,第一反应就是:“这玩意儿怎么这么复杂?目录一堆,文件一堆,到底谁管谁?”

我当年也一样。第一次运行 bitbake core-image-minimal 的时候,看着屏幕上刷刷刷滚过的日志,心里完全没底。它到底在干什么?那些 .bb 文件和 .conf 文件是什么关系?

说白了,Yocto的核心就三样东西:Poky(参考发行版)、BitBake(执行引擎)、元数据(配方和配置)。搞懂这三样,你就拿到了Yocto的钥匙。

3.1 Poky:Yocto项目的“样板间”

Poky是什么?它不是Yocto本身,而是Yocto项目官方维护的一个参考发行版。你可以把它理解成装修公司的“样板间”——展示了Yocto能怎么用,也提供了最基础的构建环境。

我习惯把Poky看作一个“大杂烩”仓库。它里面包含了:

  • BitBake:任务执行引擎,后面细说。
  • OpenEmbedded-Core (OE-Core):核心元数据层,提供最基础的配方和类。
  • meta-yocto-bsp:针对特定硬件(比如QEMU模拟器)的板级支持包。
  • meta-poky:Poky发行版本身的配置和策略。

你从Yocto官网下载的 poky-*.tar.bz2,解压后就是这个结构。我刚开始做项目时,直接拿Poky当根目录,在里面加自己的层。后来项目多了,才发现这样很乱。现在我的习惯是:Poky只作为上游,不动它。自己的东西全部放在独立的层里。

看一下Poky的顶层目录结构,你就明白了:

poky/
├── bitbake/           # BitBake引擎源码
├── meta/              # OE-Core核心层
├── meta-poky/         # Poky发行版配置
├── meta-yocto-bsp/    # 参考BSP层
├── oe-init-build-env  # 环境初始化脚本
└── README.hardware    # 硬件支持说明

嗯,这里要注意:meta/ 目录就是OE-Core,它是所有层的“地基”。你写的任何配方,几乎都会继承 meta/classes/ 下的 .bbclass 文件。

3.2 BitBake:Yocto的“心脏”

BitBake是一个任务执行引擎。你可以把它想象成一个“超级Makefile解释器”。但它比Make强大得多——它懂得依赖关系、懂得缓存、懂得并行执行。

我举个例子。你执行 bitbake core-image-minimal,BitBake会做以下几件事:

  1. 解析元数据:读取所有 .bb 和 .conf 文件,构建依赖树。
  2. 计算任务:每个配方都有多个任务(fetch、extract、configure、compile、install、package等)。BitBake会算出哪些任务需要执行,哪些可以跳过(因为缓存命中)。
  3. 调度执行:根据依赖关系,并行执行任务。默认是CPU核心数+2个线程。
  4. 输出结果:生成IPK/DEB/RPM包、根文件系统、内核镜像等。

BitBake的核心概念是任务(Task)。每个任务都有一个名字,比如 do_fetch、do_compile。任务之间可以设置依赖:

# 在配方中,让do_configure依赖于do_patch
do_configure[depends] += "virtual/kernel:do_populate_sysroot"

我曾经踩过一个坑:自己写了一个配方,编译时总报头文件找不到。查了半天,发现是 do_configure 没有正确依赖 do_populate_sysroot。BitBake不会自动帮你推断所有依赖,你得显式告诉它。

小技巧: 调试BitBake任务时,我常用 bitbake -c listtasks <recipe> 查看一个配方有哪些任务。用 bitbake -g <recipe> 可以生成依赖图(dot格式),用Graphviz打开一目了然。

3.3 元数据(Metadata)与配方(Recipe)

元数据是Yocto的灵魂。它是一堆描述“怎么构建软件”的文件。元数据主要分三类:

类型 文件后缀 作用 例子
配方(Recipe) .bb 描述一个软件的构建方法 zlib_1.2.11.bb
附加配方(Append) .bbappend 扩展或覆盖已有配方 zlib_%.bbappend
配置文件 .conf 设置全局变量和策略 local.conf, bblayers.conf
类文件 .bbclass 可复用的构建逻辑 autotools.bbclass
包含文件 .inc 配方间共享的代码片段 glibc.inc

一个典型的配方长什么样?我拿 zlib 举例:

# zlib_1.2.11.bb
SUMMARY = "Zlib Compression Library"
HOMEPAGE = "http://zlib.net/"
SECTION = "libs"
LICENSE = "Zlib"
LIC_FILES_CHKSUM = "file://zlib.h;beginline=4;endline=23;md5=..."

SRC_URI = "http://zlib.net/${BP}.tar.gz"
SRC_URI[md5sum] = "1c9f62f0778697a09d36121ead88e08e"

inherit autotools

do_install_append() {
    # 我自己加的:删除静态库以节省空间
    rm -f ${D}${libdir}/libz.a
}

你看,配方就是一组变量的赋值和函数的定义。inherit autotools 这行很关键——它把 autotools.bbclass 里的几十个函数全部拉进来了,包括 do_configure、do_compile、do_install 的默认实现。

我个人的经验是:写配方时,尽量继承已有的类。除非你要构建的东西非常特殊,否则 autotools、cmake、makefile 这些类已经覆盖了90%的场景。自己手写 do_compile 很容易出问题。

注意: 配方的命名有讲究。zlib_1.2.11.bb 中的 1.2.11 是版本号。BitBake会根据版本号自动选择最新版本。如果你有多个版本,可以用 PREFERRED_VERSION 指定。我曾经因为文件名写错版本号(比如 zlib_1.2.11.bb 写成 zlib_1.2.11.bb 但内部PV设成1.2.10),导致构建用了旧版本,排查了半天。

3.4 知识结构图:Poky、BitBake、元数据的关系

下面这张图,我画出了这三者的关系。你看完应该能理解Yocto的整体架构。

Yocto核心架构:Poky、BitBake、元数据 Poky 参考发行版 BitBake 任务执行引擎 解析 → 调度 → 执行 → 输出 元数据(Metadata) 配方(.bb) / 配置(.conf) / 类(.bbclass) 读取 OE-Core (meta/) 核心配方 + 基类 BSP 层 (meta-yocto-bsp) 板级配置 + 内核 + 引导 构建输出产物 IPK/DEB/RPM包 | 根文件系统 | 内核镜像 | 设备树 产出 用户自定义层 (meta-custom) — 通过 bblayers.conf 加入

这张图你看懂了吗?从上到下:

  • Poky 是一个大容器,把 BitBake、OE-Core、BSP 层打包在一起。
  • BitBake 是引擎,负责读取元数据并执行任务。
  • 元数据 分散在 OE-Core、BSP 层和你自己的层里。BitBake 把它们全部解析,构建出完整的依赖图。
  • 最终输出你需要的镜像、包、内核等。

你想想看,如果没有Poky这个“样板间”,你从零开始搭环境得多累?Poky帮你把最基础的东西都配好了。你只需要在 bblayers.conf 里加上自己的层,在 local.conf 里改改目标机器和特性,就能开始构建。

核心要点:
  • Poky = BitBake + OE-Core + 参考BSP + 发行版配置
  • BitBake 是任务调度器,核心是“任务”和“依赖”
  • 元数据是“怎么做”的描述,配方是元数据的主体
  • 写配方时优先继承 .bbclass,不要重复造轮子