注意:由於 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()