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怎么组织这些配方和配置。