4、BitBake基础:BitBake是什么、任务与依赖、语法基础(变量、函数)、.bb和.bbclass文件、常用命令

好,咱们进入第四章。这一章我打算聊聊BitBake。说实话,很多刚接触Yocto的朋友,第一关就是被BitBake搞晕的。它到底是什么?为什么Yocto离不开它?别急,咱们一步步拆开看。

4.1 BitBake是什么?

BitBake,说白了就是一个任务调度引擎。你可以把它想象成一个超级智能的“包工头”。你告诉它:“我要编译一个Linux系统”,它就会自动拆解成几百个小任务,然后按顺序、按依赖关系,一个个执行下去。

我刚开始接触Yocto时,总觉得BitBake很神秘。后来我意识到,它本质上就是一个构建工具,类似Make,但比Make聪明得多。Make只能处理文件依赖,而BitBake能处理任务依赖。举个例子:

  • Make:文件A依赖文件B,B改了,A重新编译。
  • BitBake:任务A(编译)依赖任务B(配置),B完成了,A才能开始。

嗯,这里要注意:BitBake不是编译器,也不是包管理器。它是个调度器。它知道每个任务该做什么,以及任务之间的先后顺序。

核心概念:BitBake = 任务调度 + 依赖解析 + 变量展开 + 缓存管理。

4.2 任务与依赖

在BitBake的世界里,一切皆任务。每个.bb文件(配方)都定义了一组任务。最常见的任务有:

任务名 说明
do_fetch 下载源码
do_unpack 解压源码
do_patch 打补丁
do_configure 配置(如./configure)
do_compile 编译
do_install 安装到临时目录
do_package 打包成ipk/deb/rpm

任务之间怎么串联?靠依赖。BitBake有两种依赖:

  • 构建时依赖(DEPENDS):编译A之前,必须先编译好B。比如编译libfoo前,需要先有libbar的头文件和库。
  • 运行时依赖(RDEPENDS):A运行起来后,需要B在系统里。比如你的程序需要libbar.so,那运行时就得装上libbar。

我在项目中遇到过一个问题:两个库互相依赖,A依赖B,B也依赖A。BitBake会直接报错,说检测到循环依赖。解决办法?要么合并成一个包,要么拆出公共部分。嗯,这坑我踩过。

个人经验:写配方时,DEPENDS别写多了。写多了会拉长构建时间。我习惯只写直接依赖,间接依赖让BitBake自己推导。

4.3 语法基础:变量与函数

BitBake的语法,其实挺简单的。它用的是自己的元语言,不是Python也不是Shell,但长得有点像两者结合。

变量

变量是BitBake的核心。你想想看,整个构建过程就是变量在驱动。比如:

# 定义一个变量
SRC_URI = "https://example.com/foo-1.0.tar.gz"

# 变量展开
PV = "1.0"
PN = "foo"
S = "${WORKDIR}/${PN}-${PV}"

变量可以覆盖。这是BitBake最强大的地方。你可以在.bb文件里定义默认值,然后在layer的配置里覆盖它。比如:

# 在meta-custom/conf/machine/myboard.conf里
PREFERRED_VERSION_linux-yocto = "5.10%"

这样,所有引用linux-yocto的地方,都会用5.10版本。嗯,这就是分层覆盖的思想。

函数

BitBake支持两种函数:Shell函数和Python函数。

Shell函数

do_compile() {
    oe_runmake
}

Python函数

python do_display_banner() {
    bb.plain("Hello, BitBake!")
}

addtask display_banner before do_build

我个人习惯,复杂的逻辑用Python写,简单的用Shell。为什么?Python调试方便,出错时能打印详细堆栈。Shell函数一旦出错,排查起来比较头疼。

避坑指南:我曾经在Shell函数里用了局部变量,结果BitBake不认。后来发现,BitBake的Shell函数里,变量要用${}展开,不能用$()。嗯,细节决定成败。

4.4 .bb和.bbclass文件

这两个文件,是Yocto的基石。

.bb文件(配方)

.bb文件描述了一个软件包怎么构建。它包含:

  • 元数据(名字、版本、许可证)
  • 源码位置(SRC_URI)
  • 依赖关系(DEPENDS、RDEPENDS)
  • 构建步骤(do_compile、do_install等)

一个典型的.bb文件长这样:

SUMMARY = "A simple hello world program"
LICENSE = "MIT"
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}
}

.bbclass文件(类)

.bbclass是共享代码。你想想看,如果每个.bb文件都写一遍do_compile,那得多累?.bbclass就是把这些公共逻辑抽出来。

比如autotools.bbclass,它定义了autotools的构建流程:

inherit autotools

就这么一行,你的配方就自动获得了./configure、make、make install的能力。嗯,这就是继承的力量。

我在项目中经常自己写.bbclass。比如,我们团队有统一的代码风格检查,我就写了个checkstyle.bbclass,所有配方inherit一下就行。省事。

4.5 常用命令

最后,咱们聊聊BitBake的常用命令。这些命令我每天都要敲,闭着眼睛都能打出来。

命令 说明
bitbake core-image-minimal 构建一个最小系统镜像
bitbake -c clean linux-yocto 清理linux-yocto的构建产物
bitbake -c compile -f linux-yocto 强制重新编译linux-yocto
bitbake -s | grep linux 查看所有包含linux的配方及其版本
bitbake -g core-image-minimal 生成依赖图(dot格式)
bitbake -e linux-yocto | grep ^SRC_URI 查看linux-yocto的SRC_URI最终值

我个人最常用的是-e参数。为什么?因为变量覆盖太复杂了,有时候你根本不知道最终值是什么。用-e一查,一目了然。

小技巧:想查看某个配方的所有任务?试试bitbake -c listtasks linux-yocto。它会列出所有可执行的任务名。嗯,调试时很有用。

好了,这一章就到这里。BitBake的内容其实很深,但掌握了这些基础,你已经能看懂大部分配方了。下一章,咱们聊聊元数据与层,看看Yocto怎么组织这些配方和配置。