常见问题

Yocto

Yocto是什么?

Yocto Project是Linux基金会旗下的开源项目,可帮助开发人员跨硬件架构统一定制嵌入式Linux系统。Yocto提供了一个灵活的工具集合和开发环境,使得所有系统开发者可以轻松地共享技术、软件栈,配置并生成定制化镜像。

Yocto是如何通过配置定制Linux镜像的?

首先,我们需要一些配置文件(configuration files),它们多以conf作为后缀。这些文件中包含了全局变量、用户定义变量的定义以及硬件配置信息。它们告诉构建引擎BitBake需要将什么数据作为系统镜像的一部分。BitBake知道要将哪些数据放入镜像后,即前往指定的网址下载相应的数据,并且按照要求剪裁后合入最终镜像。后面这些具体工作的配置则一般由元数据(metadata)定义。

配方(Recipes)则是元数据最常见的一种形式,大多以bb作为文件后缀。一个配方会包含一系列用于构建软件包的设置和任务指引,具体包含:

  1. 源代码的获取地址

  2. 具体需要应用的补丁

  3. 文件校验码

  4. 编译选项

  5. 软件库之间、配方之间的依赖关系等。

这些软件包最终会被用来合入最终的系统镜像。此外,Yocto最大特点之一便是它的开发模型——配方层模型(Layer Model)。配方一般都存在于配方层里。配方层相当于储存配方集合的仓库。通过将相关的配方集合在一起,我们可以将元数据模块化,隔离不相干的信息,如硬件相关的构建配置。同时,配方层是有层级的,后加入的层级可以覆盖以前加入的层级的配方内容。

Poky与Yocto的关系是什么?

Poky是Yocto的参考发行版,是建立在OpenEmbedded构建系统(OpenEmbedded Core层 & BitBake构建引擎)基础上的集成层。除了OpenEmbedded构建系统以外,还额外包含了一组元数据用于构建使用者自己的发行版。此外,Poky还包含了部分Yocto组件,用于协助构建流程。

注意,Poky不包含额外的二进制文件,它只是一个能将Linux源代码构建为发行版的可行例子。

为什么我们主要修改yocto-meta-openeuler目录?它属于Yocto或者Poky的一部分吗?

yocto-meta-openeuler目录则是我们自己的openEuler Embedded在定制内核的时候所必须的相关配置和工具的集合。它包含了属于本项目独特的配方层meta-openeuler等。我们在演进操作系统版本的时候,由于考虑到系统稳定性的问题,构建系统的底座尽量少做改动,所以基础配方层“meta-oe”和“meta”以及相关工具虽然也会更新,但是周期较长,大约半年到一年的时间。而我们可以通过修改独有的配方文件对基础软件包做一些小的升级改进,可以及时的修补漏洞,提升性能等。此外,我们也可以往yocto-meta-openeuler目录添加一些额外的层以及工具,用于增加功能。

openEuler Embedded构建于Poky之上,而Poky则是Yocto组件和OpenEmbedded Core的集成层。在构建镜像的时候,属于OpenEmbedded Core的基础配方层“meta-oe”和“meta”提供了很多软件包的基础配置,openEuler Embedded独有的配方文件则在之后加入,并使用之前的配置。如果遇到需要更新的情况,则独有的配方层里的内容可以覆盖基础配方层里的内容。如果只是需要进行微调,不一定需要使用bb文件,也可以使用bbappend文件。

综上,从开源项目的角度来说,openEuler Embedded是独立开源项目,但是使用了Poky项目里的相关技术作为底座。从开发模型来说,yocto-meta-openeuler目录包含的配方层在Yocto的层级模型里高于OpenEmbedded Core,因此openEuler Embedded的独有配方层依赖于这些基础配方层。如果独有配方层中有重复的配置,则可以覆盖基础配方层中的配置,比如定义更新或者更旧的软件包版本。

基于Yocto的项目的工作流程是什么样的?

  1. 开发者指定系统架构,策略,补丁和配置细节。

  2. 构建系统从配方文件指定的地方拉取源代码。

  3. 代码下载到本地后,解压到本地工作区并进行相应的修改和操作(打补丁,执行配置和编译等)。

  4. 构建系统将编译后的软件安装到临时的待命区(staging area),并且按照自定义的格式(deb,rpm,ipk)进行打包。

  5. 整个构建过程会进行不同的质量保证和健全性测试。

  6. 当二进制文件生成后,构建系统会生成一个二进制数据包用于创建最终的根文件系统。

  7. 构建系统在生成根文件系统的同时,也会生成一个定制化的可扩展的SDK用于基于生成的定制化Linux的应用程序开发。

Oebuild

Oebuild和Yocto之间的关系

Oebuild是openEuler Embedded项目旗下的开源项目之一,作为基于docker的工具,简化了构建openEuler Embedded定制化的镜像的流程。Oebuild将基于Yocto的开发流程中需要手动配置但是较为机械化的步骤整合了起来,实现了一键配置构建镜像所需的顶层配置文件,一键生成镜像等操作。

在构建的时候,开发者可以通过Oebuild提供的相关命令快捷的生成顶层配置文件(conf文件)。之后,Oebuild会调用BitBake引擎,完成Yocto的工作流程,并最终输出镜像文件。

Oebuild为什么使用容器进行构建?

在构建的时候,由于需要进行交叉编译,我们会需要用到特定的编译工具链,但是主机环境很可能已经存在一些编译工具链。如果直接进行构建,则可能出现编译路径冲突的问题,或者影响主机环境中其他的一些依赖于已有编译工具链的进程运行。为了创建纯净的编译环境,我们使用容器将构建环境与主机环境进行隔离,所以不论主机已有的编译环境如何,用户都可以不受影响的编译生成指定的目标镜像、软件包或者SDK,同时也不会影响主机环境。

Oebuild构建时如何避免受到上游社区的影响?

openEuler Embedded在构建时有许多软件包依赖上游社区,我们无法控制软件包的更新时间节点,抑或是更新内容。有时,不兼容更新确实会导致OS镜像构建失败。Oebuild引入“基线”功能,解决了这一问题。在构建镜像的时候,我们可以依照“基线”中定义的版本拉取相应的软件包和补丁,即使上游社区的软件包更新,也不会改变我们生成的镜像中的软件包版本。

“基线”的现实载体是manifest.yaml,里面的字段commit会指定某软件包相应的版本。在上游仓库中拉取软件包源码的时候,我们会根据相应的commit ID拉取相应的源码,保证构建的稳定性。

为什么我运行了oebuild update以后,重新构建相同特性和架构的镜像却会失败?

首先,oebuild目前的容器镜像是以目录为单位的,而目录又是以特性和架构为参考。如果构建相同特性和架构的镜像,构建目录会与以前的目录相同,所以使用的构建容器也与以前相同。那么,上次构建遗留下来的临时文件,也会存在于之前的容器中。

由于oebuild在运行时依赖于自己生成的某些缓存文件,而oebuild update命令并不会更新相关的缓存文件,而是只更新仓库和容器。所以此时再进行构建,则部分旧缓存会影响构建进程导致误报。比较好的解决办法是,在重新构建时运行命令oebuild bitbake [the image name] -c cleanall。这条命令会在执行bitbake之前执行-c指定的命令,将所有缓存清除后再进行构建,这样就可以避免此问题。