注意:由于 task 和 recipe 是 BitBake 的基础概念。个人觉得翻译成任务和配方不免有误解之处,因此文中基本不对这两个词做翻译。类似的还有 configure。
- 序言
1.1 关于本教程
如果你阅读本教程,说明你已经知道 BitBake 是一种类似 make 的构建工具,主要用于 OpenEmbedded 和 Yocto 工程构建 Linux 发行版本。你可能也已经意识到 BitBake 的学习曲线有点陡,本文可让这个曲线变平缓一些。
本文不会告诉你 BitBake 的一切,但是会尝试解释使用 BitBake 时用到的一些基本功能。理解这些基础可帮助你开始写自己的 BitBake recipe。
1.2 本教程的目标
本教程展示如何创建一个最小工程,并一步步扩展,说明 BitBake 如何运作。
1.3 致谢
感谢 Tritech 给我时间准备本文档。同时感谢大家在问题跟踪站点报告的问题与打印错误。
1.4 反馈
如果你发现 bug、不清楚的章节、打印错误或其他建议, 请使用 issue tracker https://bitbucket.org/a4z/bitbakeguide/issues,不需要注册。
同时也可以使用本文底部的 Disqus 评论功能。
- BitBake
2.1 什么是 BitBake
以下内容有助于理解 BitBake:
基本上,BitBake是一个Python程序,它由用户创建的配置驱动,可以为用户指定的目标执行用户创建的任务,即所谓的配方(recipes)。
2.1.1 Config、tasks 与 recipes
通过一种 BitBake 领域特定语言写 Config、tasks 与 recipes,这种语言包含变量与可执行的 shell、python 代码。所以理论上,BitBake 可以执行代码,你也可以用 BitBake 做除构建软件之外的事情,但是并不推荐这么做。
BitBake 是一种构建软件的工具,因此有一些特殊的功能,比如可以定义依赖关系。BitBake 可以解决依赖关系,并将其任务以正确顺序运行。此外,构建软件包通常包含相同或相似的任务。比如常见的任务:下载源代码包,解压源代码,跑 configure,跑 make,或简单的输出 log。Bitbake 提供一种机制,可通过一种可配置的方式,抽象、封装和重用这个功能。
- 配置 BitBake
BitBake 可以从这里下载:https://github.com/openembedded/bitbake。选择一个版本的分支,并下载 zip。解压 zip 包,可找到一个 bitbake-$version 目录。
注意:本文使用的 Bitbake 版本是 bitbake-1.22,因此适合本教程的 bitbake 版本应该大于或等于1.22。
注意:译者使用 bitbake-1.27.0,因此文中样例为 1.27.0 版本 bitbake 样例。
提示:如果使用 Yocto,则不需要安装 BitBake,Yocto 源代码本身捆绑了 BitBake。Yocto 要求你 source 一个脚本,这个脚本和我们这里做的一样,安装 BitBake 到我们的环境中。
3.1 安装 BitBake
安装过程很简单:
- 添加 bitbake-$version/bin 目录到 PATH
- 添加 bitbake-$version/lib 目录到 PYTHONPATH
即执行:
export PATH=/path/to/bbtutor/bitbake/bin:$PATH
export PYTHONPATH=/path/to/bbtutor/bitbake/lib:$PYTHONPATH
这基本和 yocto init 脚本一致。yocto init 脚本同时也创建 build 目录,我们将在一会儿创建。
首先检测是不是一切正常、bitbake 是否安装成功。通过执行以下 bitbake 命令:
bitbake --version
运行结果应该类似:
BitBake Build Tool Core version 1.27.0
3.2 BitBake 文档
最实际的版本带有源代码。
在终端中,cd 到 bitbake-$version/doc 目录并执行以下命令,生成 doc/bitbake-user-manual/bitbake-user-manual.html。
make html DOC=bitbake-user-manual
这个文档可与本教程并行阅读,在读完本教程后也需要阅读该文档。
yocto 工程文档 也有一个 bitbake 章节。
- 创建工程
4.1 Bitbake 工程布局
通过 BitBake 工程通过 layers 目录与一个 build 目录组织,layer 目录包含配置文件和 meta data。
4.1.1 Layer 目录
Layer 目录包含配置、任务和目标描述。常用 meta-'something' 命名 Layer 目录。
4.1.2 Build 目录
Build 目录是 bitbake 命令被执行的地方。在这里,BitBake 期望能找到其初始配置文件,并将其生成的所有文件放在这个目录。
为了让 BitBake 运行时出现有任何错误,我们需要创建一个 build 目录和一个 layer 目录,并在此存放一些需要的配置文件。
4.2 最小工程
最小的配置看起来像这样:
bbTutorial/
├── build
│ ├── bitbake.lock
│ └── conf
│ └── bblayers.conf
└── meta-tutorial
├── classes
│ └── base.bbclass
└── conf
├── bitbake.conf
└── layer.conf
需要创建这 4 个文件:
- bblayers.conf
- base.bbclass
- bitbake.conf
- layer.conf
4.2.1 需要的配置文件
首先描述需要的文件,然后简要说明其内容。
build/conf/bblayers.conf
,BitBake 在其工作目录(即 build 目录)期望找到的第一个文件。现在我们以以下内容创建一个 bblayers.conf:
BBPATH := "${TOPDIR}"
BBFILES ?= ""
BBLAYERS = "/path/to/meta-tutorial"
meta-tutorial/conf/layer.conf
,每个 layer 需要一个 conf/layer.conf 文件。现在我们以以下内容创建它:
BBPATH .= ":${LAYERDIR}"
BBFILES += ""
meta-tutorial/classes/base.bbclass
meta-tutorial/conf/bitbake.conf
现在,这些文件可以从 BitBake 安装目录中获取。这些文件位于文件夹 bitbake-$version/conf 和 bitbake-$version/classes中。只需将它们复制到 tutorial 项目中。
4.2.2 创建文件的一些注意事项
build/conf/bblayers.conf
- 添加当前目录到 BBPATH,TOPDIR 被 BitBake 设置为当前工作目录。
- 初始设置 BBFILES 变量为空,Recipes 在后面会添加。
- 添加我们 meta-tutorial 的路径到 BBLAYERS 变量。当 BitBake 开始执行时,它会搜索所有给定的 layer 目录,以便获得其他配置。
meta-tutorial/conf/layer.conf
- LAYERDIR 是 BitBake 传给其所加载 Layer 的变量。我们添加该路径到 BBPATH 变量。
- BBFILES 告诉 BitBake recipes 在哪,现在我们没有添加任何东西,但是一会儿我们会改变它。
注意事项。“.=” 和“+=” 以不添加空格、添加空格的方式,将追加值附给一个变量。
conf/bitbake.conf
conf/bibake.conf 包含 一系列我们讨论的变量。
classes/base.bbclass
一个 *.bbclass 文件包含共享功能。我们的 base.bbclass 包含一些我们一会儿使用的 log 函数,以及一个 buld 任务。
并不是很有用,但是 BitBake 有需求,因为如果没有任何具体业务时,BitBake 默认需求的。我们随后将改变此功能。
4.2.3 BitBake 搜索路径
对于 BitBake 来讲,有许多 BBPATH 非法和文件路径。这说明如果我们告诉 BitBake 探索一些路径时,它会搜索 BBPATH。
我们添加 TOPDIR 和 LAYERDIR 到 BBPATH,放在 classes/base.bbclass 或 conf/bitbake.conf 中的任意一个。
当然,我们会添加 meta-tutorial 目录。
编译目录不应含有通用文件。只有像 local.conf 对实际编译是有效的,后面我们会用到 local.conf。
第一次运行
创建上述四个配置文件后,在终端 cd 到 build 目录,这是我们的工作目录。我们一直在 build 目录运行 bitbake 命令,以便 bitbake 可以找到相应的 conf/bblayers.conf 文件。
现在,在 build 目录,不带任何参数运行 bitbake 命令:
bitbake
如果先前的步骤正确,则控制台会输出:
Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.
这没什么用,但是一个好的开始。
这里介绍一个很有用的命令标志:输出一些 debug 信息。
执行 bitbake -vDD
,然后查看其输出,它告诉我们大量关于 BitBake 如何动作的信息。
DEBUG: Found bblayers.conf (~/bbTutorial/build/conf/bblayers.conf)
DEBUG: LOAD ~/bbTutorial/build/conf/bblayers.conf
DEBUG: Adding layer ~/bbTutorial/meta-tutorial
DEBUG: LOAD ~/bbTutorial/meta-tutorial/conf/layer.conf
DEBUG: LOAD ~/bbTutorial/meta-tutorial/conf/bitbake.conf
DEBUG: BB configuration INHERITs:0: inheriting ~/bbTutorial/meta-tutorial/classes/base.bbclass
DEBUG: BB ~/bbTutorial/meta-tutorial/classes/base.bbclass: handle(data, include)
DEBUG: LOAD ~/bbTutorial/meta-tutorial/classes/base.bbclass
DEBUG: Clearing SRCREV cache due to cache policy of: clear
DEBUG: Using cache in '~/bbTutorial/build/tmp/cache/local_file_checksum_cache.dat'
DEBUG: Using cache in '~/bbTutorial/build/tmp/cache/bb_codeparser.dat'
你在注意到 BitBake 创建了一个 bitbake.log 文件和一个 tmp 目录?
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ ls
bitbake.lock conf tmp
提示,所有的样例代码都可从 https://bitbucket.org/a4z/bitbakeguide 获取。本样例在 ch04。
- 第一个 recipe
BitBake 需要 recipes 定义要做些什么,现在这里什么都没有。
我们可以通过 bitbake -s
确认运行时什么也没做:
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ bitbake -s
ERROR: no recipe files to build, check your BBPATH and BBFILES?
Summary: There was 1 ERROR message shown, returning a non-zero exit code.
NOTE: Not using a cache. Set CACHE = <directory> to enable.
Recipe Name Latest Version Preferred Version
=========== ============== =================
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$
这告诉我们两个信息:
- 没有定义任何 cache;
- BitBake 真的没事可做,只显示了一个空的 recipe 列表
5.1 cache 位置
BitBake 缓存 meta data 在一个目录,即 cache 目录。这会帮助加速后面执行的命令。
我们可通过简单添加一个变量到 bitbake.conf 文件,解决 cache 找不到的问题。因此,我们编辑 meta-tutorial/conf/bitbake.conf 文件,并在底部添加:
CACHE = "${TMPDIR}/cache/default"
添加后运行 bitbake -s
的结果:
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ bitbake -s
ERROR: no recipe files to build, check your BBPATH and BBFILES?
Summary: There was 1 ERROR message shown, returning a non-zero exit code.
注意:在实现项目中,比如 Yocto,这些变量已经设置好,我们不用关心。通常 cache 路径由不同的变量组成,在名称中包含实际的构建配置,如 debug 或 release。
下一步是添加一个 recipe,需要两个步骤:
- 使 bitbake 可以找到 recipes
- 写第一个 recipe
5.2 添加一个 recipe 到 tutorial layer
BitBake 需要知道一个 layer 提供哪些 recipes,可通过编辑 meta-tutorial/conf/layer.conf 文件,使用通配符告诉 BitBake 加载所有的 recipe:
BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb"
现在可以使用先前在 build/conf/bblayers.conf 定义的变量。recipe 文件的扩展名是 .bb,如果我们通过通配符的方式,只用一行就可以告诉 BitBake 加载所有 recipes。
通常 recipes 有自己的目录,并以 groups 的形式收集在一起,也就是说把有关联的 recipes 放在同一个目录。
注意:通常使用 recipes-'group' 命令这些目录,这里 group 名表示一个 category 或一些程序。
现在 BitBake 已经知道从哪找 recipe,我们可以开始添加第一个 recipe 了。
按通常的做法,我们创建目录 meta-tutorial/recipes-tutorial/first,并在此创建第一个 recipe。 Recipe 文件也有通用的命名方法:{recipe}_{version}.bb
5.3 创建第一个 recipe 和 task
我们的第一个 recipe 只打印一些 log 信息。将它放在 tutorial group,版本为 0.1。所以我们的第一个 recipe 是:
meta-tutorial/recipes-tutorial/first/first_0.1.bb
DESCRIPTION = "I am the first recipe"
PR = "r1"
do_build () {
echo "first: some shell script running as build"
}
- task do_build 覆盖 base.bbclass 中的全局 build task。
- PR 是内部修订数据,在每次修订后应被更新。
- 设置 description 可解释该 recipe 的用途。
如果上面都做对了,可以通过 bitbake -s
列出可用的 recipes。
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ bitbake -s
Parsing recipes: 100% |################################################################################| Time: 00:00:00
Parsing of 1 .bb files complete (0 cached, 1 parsed). 1 targets, 0 skipped, 0 masked, 0 errors.
Recipe Name Latest Version Preferred Version
=========== ============== =================
first :0.1-r1
然后就可以执行 bitbake first
编译 first 组件。
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ bitbake first
Parsing recipes: 100% |################################################################################| Time: 00:00:00
Parsing of 1 .bb files complete (0 cached, 1 parsed). 1 targets, 0 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
NOTE: Preparing RunQueue
NOTE: Executing RunQueue Tasks
NOTE: Tasks Summary: Attempted 1 tasks of which 0 didn't need to be rerun and all succeeded.
现在检查 tmp/work/first-0.1-r1/temp 目录,里面有一些有趣的文件:
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ ls -al tmp/work/first-0.1-r1/temp/
total 20
drwxrwxr-x 2 sunyongfeng sunyongfeng 4096 10月 20 11:19 .
drwxrwxr-x 3 sunyongfeng sunyongfeng 4096 10月 20 11:19 ..
lrwxrwxrwx 1 sunyongfeng sunyongfeng 18 10月 20 11:19 log.do_build -> log.do_build.17314
-rw-rw-r-- 1 sunyongfeng sunyongfeng 123 10月 20 11:19 log.do_build.17314
-rw-rw-r-- 1 sunyongfeng sunyongfeng 37 10月 20 11:19 log.task_order
lrwxrwxrwx 1 sunyongfeng sunyongfeng 18 10月 20 11:19 run.do_build -> run.do_build.17314
-rwxrwxr-x 1 sunyongfeng sunyongfeng 909 10月 20 11:19 run.do_build.17314
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ cat tmp/work/first-0.1-r1/temp/log.do_build.17314
DEBUG: Executing shell function do_build
first: some shell script running as build
DEBUG: Shell function do_build finished
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ cat tmp/work/first-0.1-r1/temp/log.task_order
do_build (17314): log.do_build.17314
sunyongfeng@openswitch-OptiPlex-380:~/workshop/test/tutorial/build$ cat tmp/work/first-0.1-r1/temp/run.do_build
#!/bin/sh
# Emit a useful diagnostic if something fails:
bb_exit_handler() {
ret=$?
case $ret in
0) ;;
*) case $BASH_VERSION in
"") echo "WARNING: exit code $ret from a shell command.";;
*) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from
"$BASH_COMMAND"";;
esac
exit $ret
esac
}
trap 'bb_exit_handler' 0
set -e
export HOME="/home/sunyongfeng"
export SHELL="/bin/bash"
export LOGNAME="sunyongfeng"
export USER="sunyongfeng"
export PATH="/home/sunyongfeng/ops-build.test/yocto/poky/scripts:/home/sunyongfeng/ops-build.test/yocto/poky/bitbake/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
export TERM="linux"
do_build(