利用Bitbake、Poky、Yocto、OpenEmbedded编译生成车规AGL Linux
作为车规级的嵌入式Linux,AGL Linux使用新的构建方式,不再使用GNU Make这一套,以前老的构建方式学习曲线比较陡峭,组件删减也不方便。
那么Bitbake、Poky、Yocto、OpenEmbedded分别是干啥的呢:
Bitbake:是一个通用任务执行引擎,允许shell和python任务在复杂的任务间依赖约束条件下高效并行运行,有点类似GNU Make。
OpenEmbedded:是一个采用MIT许可证的软件架构或标准,目标在于为嵌入式系统构建Linux发行版。
Poky:参考嵌入式操作系统实际上,是一个可行的构建示例,它使用包含的构建系统(BitBake构建引擎和OpenEmbedded-Core构建系统元数据)构建一个小型嵌入式操作系统。
Yocto:是一个开源协作项目,可帮助开发人员为嵌入式产品创建定制的基于Linux的系统,而不管硬件架构如何。 该项目提供了一套灵活的工具和一个空间,全世界的嵌入式开发人员可以共享可用于为嵌入式设备创建量身定制的Linux映像的技术,软件堆栈,配置和最佳实践。下图就是yocto开发流程:
从上面可以看出,其中最核心的就是bitbake,弄懂它其他就很容易明白了,下面以一个例子来看看bitbake使用。
Bitbake例子
Bitbake根据提供的构建任务的元数据执行任务。元数据存储在配方(.bb)和相关配方“append”(.bbappend)文件,配置(.conf)和底层包含(.inc)文件以及类(.bbclass)文件中。元数据为Bitbake提供了有关运行哪些任务以及这些任务之间的依赖说明。
Bitbake包含一个程序库用于从各种地方(如本地文件,源码管理系统或网站)获取源代码。
每个要构建的单元(例如一个软件)的指令被称为“配方”文件,并且包含关于该单元的所有信息(依赖性,源文件位置,校验和,描述等扥)。
Bitbake包含客户端/服务器抽象,可以通过命令行使用或者通过XML-RPC作为服务提供,并具有多个不同的用户界面。
CentOS7下安装和使用bitbake进行项目构建
先安装python3.x:
$ sudo yum install epel-release
$ sudo yum install https://centos7.iuscommunity.org/ius-release.rpm
$ wgethttp://mirror.centos.org/centos/7/os/x86_64/Packages/openssl-libs-1.0.2k-12.el7.x86_64.rpm
$ wgethttp://mirror.centos.org/centos/7/os/x86_64/Packages/openssl-1.0.2k-12.el7.x86_64.rpm
$ sudo yum localinstall openssl-1.0.2k-12.el7.x86_64.rpm openssl-libs-1.0.2k-12.el7.x86_64.rpm
$ sudo yum install python36u
$ sudo ln -s /bin/python3.6 /bin/python3
$ sudo yum install python36u-pip
$ sudo ln -s /bin/pip3.6 /bin/pip3
安装bitbake:
$ cd /data/
$ git clone git://git.openembedded.org/bitbake
$ vi ~/.bash_profile
PATH=$PATH:$HOME/.local/bin:$HOME/bin:/data/bitbake/bin
$ source ~/.bash_profile
$ bitbake -h
建立bitbake项目,编译前目录结构如下(其中bitbake.conf、base.bbclass直接从bitbake安装目录拷贝过来):
[coadmin@vm5 bitbake_test]$ tree
.
├── build
│ └── conf
│ ├── bblayers.conf
│ └── bitbake.conf
├── meta-printhello
│ ├── classes
│ │ ├── base.bbclass
│ │ └── global.bbclass
│ ├── conf
│ │ └── layer.conf
│ └── recipes
│ ├── printhello.bb
│ └── test.bb
└── meta-two
├── classes
│ └── two.bbclass
├── conf
│ └── layer.conf
└── recipes
├── two.bb
└── two.bbappend
各文件内容:
[coadmin@vm5 bitbake_test]$ cat build/conf/bblayers.conf
BBPATH := “${TOPDIR}”
BBFILES ?= “”
BBLAYERS = ” \
${TOPDIR}/../meta-printhello \
${TOPDIR}/../meta-two \
”
[coadmin@vm5 bitbake_test]$ cat meta-printhello/classes/global.bbclass
addtask build
python global_do_build () {
bb.plain(“********************”);
bb.plain(“* global_do_build *”);
bb.plain(“********************”);
}
EXPORT_FUNCTIONS do_build
[coadmin@vm5 bitbake_test]$ cat meta-printhello/conf/layer.conf
BBPATH .= “:${LAYERDIR}”
BBFILES += “${LAYERDIR}/recipes/*.bb”
BBFILE_COLLECTIONS += “printhello”
BBFILE_PATTERN_printhello := “^${LAYERDIR_RE}/”
BBFILE_PRIORITY_printhello = “5”
[coadmin@vm5 bitbake_test]$ cat meta-printhello/recipes/printhello.bb
DESCRIPTION = “Prints Hello World”
PN = ‘printhello’
PV = ‘1’
PR = “r1”
inherit global
python do_fetch() {
bb.plain(“********************”);
bb.plain(“* *”);
bb.plain(“* FETCH RESOURCE *”);
bb.plain(“* *”);
bb.plain(“********************”);
}
def printdate1():
import time
print(time.strftime(‘%Y%m%d’, time.gmtime()))
python do_printdate() {
bb.plain(“********************”);
bb.plain(“* PRINT DATE TIME *”);
printdate1();
bb.plain(“********************”);
}
addtask printdate after do_fetch before do_build
addtask fetch
[coadmin@vm5 bitbake_test]$ cat meta-printhello/recipes/test.bb
DESCRIPTION = “TEST”
PN = ‘test’
PV = ‘1’
PR = “r0”
python do_build() {
bb.plain(“********************”);
bb.plain(“* *”);
bb.plain(“* test build *”);
bb.plain(“* *”);
bb.plain(“********************”);
}
addtask build
[coadmin@vm5 bitbake_test]$ cat meta-two/classes/two.bbclass
inherit global
two_do_configure () {
echo “running configbuild_do_configure.”
}
addtask do_configure before do_build
EXPORT_FUNCTIONS do_configure
[coadmin@vm5 bitbake_test]$ cat meta-two/conf/layer.conf
BBPATH .= “:${LAYERDIR}”
BBFILES += “${LAYERDIR}/recipes*/*.bb \
${LAYERDIR}/recipes*/*.bbappend”
BBFILE_COLLECTIONS += “two”
BBFILE_PATTERN_two = “^${LAYERDIR}/”
BBFILE_PRIORITY_two = “5”
LAYERVERSION_two = “1”
[coadmin@vm5 bitbake_test]$ cat meta-two/recipes/two.bb
DESCRIPTION = “I am the two”
PN = “two”
PR = “r1”
inherit two
再看build之后的目录结构(整个tmp目录为生成的工作目录,重构前可删除):
[coadmin@vm5 bitbake_test]$ cd build/
[coadmin@vm5 build]$ bitbake printhello
[coadmin@vm5 build]$ bitbake two
所有的目标都执行:
[coadmin@vm5 build]$ bitbake world
…
[coadmin@vm5 build]$ tree ..
..
├── build
│ ├── bitbake-cookerdaemon.log
│ ├── conf
│ │ ├── bblayers.conf
│ │ └── bitbake.conf
│ └── tmp
│ ├── cache
│ │ ├── bb_codeparser.dat
│ │ ├── bb_persist_data.sqlite3
│ │ └── local_file_checksum_cache.dat
│ ├── stamps
│ │ ├── printhello-1-r1.do_fetch
│ │ ├── printhello-1-r1.do_printdate
│ │ ├── two-1.0-r1.do_configure
│ │ └── two-1.0-r1.do_patch
│ └── work
│ ├── printhello-1-r1
│ │ └── temp
│ │ ├── log.do_build -> log.do_build.4270
│ │ ├── log.do_build.4270
│ │ ├── log.do_fetch -> log.do_fetch.4263
│ │ ├── log.do_fetch.4263
│ │ ├── log.do_printdate -> log.do_printdate.4267
│ │ ├── log.do_printdate.4267
│ │ ├── log.task_order
│ │ ├── run.do_build -> run.do_build.4270
│ │ ├── run.do_build.4270
│ │ ├── run.do_fetch -> run.do_fetch.4263
│ │ ├── run.do_fetch.4263
│ │ ├── run.do_printdate -> run.do_printdate.4267
│ │ ├── run.do_printdate.4267
│ │ └── run.global_do_build.4270
│ ├── test-1-r0
│ │ └── temp
│ │ ├── log.do_build -> log.do_build.4268
│ │ ├── log.do_build.4268
│ │ ├── log.task_order
│ │ ├── run.do_build -> run.do_build.4268
│ │ └── run.do_build.4268
│ └── two-1.0-r1
│ └── temp
│ ├── log.do_build -> log.do_build.4269
│ ├── log.do_build.4269
│ ├── log.do_configure -> log.do_configure.4265
│ ├── log.do_configure.4265
│ ├── log.do_patch -> log.do_patch.4264
│ ├── log.do_patch.4264
│ ├── log.task_order
│ ├── run.do_build -> run.do_build.4269
│ ├── run.do_build.4269
│ ├── run.do_configure -> run.do_configure.4265
│ ├── run.do_configure.4265
│ ├── run.do_patch -> run.do_patch.4264
│ ├── run.do_patch.4264
│ └── run.global_do_build.4269
├── meta-printhello
│ ├── classes
│ │ ├── base.bbclass
│ │ └── global.bbclass
│ ├── conf
│ │ └── layer.conf
│ └── recipes
│ ├── printhello.bb
│ └── test.bb
└── meta-two
├── classes
│ └── two.bbclass
├── conf
│ └── layer.conf
└── recipes
├── two.bb
└── two.bbappend
最后,看看怎么编译AGL Linux
1. 准备环境,以CentOS为例:
# sudo yum install gawk make wget tar bzip2 gzip python unzip perl patch diffutils diffstat git cpp gcc gcc-c++ glibc-devel texinfo chrpath socat SDL-devel xterm curl
# export AGL_TOP=$HOME/workspace_agl
# mkdir -p $AGL_TOP
# mkdir -p ~/bin
# export PATH=~/bin:$PATH
# curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
# chmod a+x ~/bin/repo
2. 下载源码:
# cd $AGL_TOP
# repo init -b eel -m eel_5.1.0.xml -u https://gerrit.automotivelinux.org/gerrit/AGL/AGL-repo
# repo sync
3. 编译(以目标为qemu为例),会生成各种可启动的img:
# cd $AGL_TOP
# source meta-agl/scripts/aglsetup.sh -f -m qemux86-64 agl-demo agl-devel
# bitbake agl-demo-platform
4. 运行(以VmWare Workstation为例):
# cd tmp/deploy/images/qemux86-64
# xz -d agl-demo-platform-qemux86-64.vmdk.xz
这样会得到一个vmdk格式的可启动硬盘镜像,在VmWare WorkStation建立一个虚机,选择该镜像作为挂载的硬盘,上电开机即可。