yocto(二)——bitbake工作流程

本文参考yocto官方手册,如有理解不当之处,欢迎留言指出。

项目概述和概念手册:https://docs.yoctoproject.org/overview-manual/index.html

项目参考手册:https://docs.yoctoproject.org/ref-manual/index.html

yocto项目的厨师——bitbake

​ bitbake是OpenEmbedded构建系统的引擎,通过解析一系列配置文件(主要为recipes,即bb/bbappend文件)来创建任务列表,并根据依赖关系依次执行。通过bitbake -c listtasks xxx(模块名或映像名)命令可以查看编译一个模块或整个映像所需任务名称。编译一个模块/映像的主要执行过程如下(内核除外):
yocto(二)——bitbake工作流程
​ 下面按四个阶段来讲解bitbake的执行过程及涉及的变量,其中第四阶段(上图标黄的两任务)不是编译每个模块都有的。

源码获取及处理

​ 这个阶段包含三个任务,分别为do_fetch、do_unpack、do_patch。

原文件来源

​ yocto编译一个模块所需的源代码或开源组件从哪里获取?
yocto(二)——bitbake工作流程

​ 如上图所示,源码可以从上游开源项目(如busybox-1.28.3.tar.bz2)、本地项目(本地源代码)、软件配置管理(如git)中获取。

do_fetch任务:根据配置文件中SRC_URI变量所指定的方式获取源代码。

do_unpack任务:如果源文件需要解压,则该任务会将源码包解压到指定目录下。
yocto(二)——bitbake工作流程

do_patch任务:如果对开源项目进行了修改,则这个任务可以为解压后的源码打补丁,比如对linux开源内核源码进行了修改。
yocto(二)——bitbake工作流程

这个大阶段涉及的配置变量有以下:

SRC_URI

​ 源文件列表变量,位于配方(recipe)文件中。每个recipe必须有一个指向源的SRC_URI变量。

SRC_URI = "git://github.com/openbmc/linux;protocol=git;branch=${KBRANCH}" #表示从github上下载linux内核源码
SRC_URI += "file://defconfig" #表示从本地目录获取内核默认配置,目录的路径由FILESPATH变量指定

​ 以下列举常用的获取方式,更多的获取方式请参阅SRC_URI

file://-从本地机器 获取文件,通常是元数据附带的文件(例如补丁、内核配置、uboot配置文件),路径是相对于FILESPATH变量的。构建系统默认在配方同目录下名为“${BP}”"${BPN}""files"的目录下查找指定文件,如需增加额外路径,便通过FILESPATHFILESEXTRAPATHS指定。

​ 注意:如果源码时本地文件,确保每个文件都使用file://写出,而不是使用指定文件夹方式下载整个文件夹,因为yocto需要单独检查每个文件是否被修改。

bzr://-从 Bazaar 版本控制存储库中获取文件。

git://-从 Git 版本控制存储库中获取文件。

​ 如果是从本地仓库下载,则先配置好本地Git仓库。yocto依赖与git的版本控制,简单来说就是yocto每次编译时候,会去检查软件包的源文件是否修改,只有修改过才会让软件包重新编译,那么对于git仓库就是对比上一次与当次版本差异。所以,对于源码在本地的git仓库的软件包,需要在每次编译前进行以下操作:

git add ./*
git commit -m "test"

​ 这样就可以确保本地git仓库源码修改后能参与yocto工程编译。

svn://-从 Subversion (svn) 版本控制存储库中获取文件。

http://-使用http。

https://-使用https。

DL_DIR

​ 用于指定开源组件包(tar、git等)下载的存放路径,位于编译目标层的conf/local.conf.sample文件中(如有需要可在此文件中修改),这个文件将被解析到build/conf/local.conf文件中,默认情况下该变量指定的目录为build/downloads/,如果多用户在一台编译机器上使用,可以指定一个公共目录,避免重复下载,比如DL_DIR ?= "/opt/downloads",同时修改chmod 777 /opt/downloads即可。

​ 通常一个组件包下载完毕后,在存放目录下会生成一个包名加".done"的文件表示该包下载完成,比如:

root:~/work/open_source/openbmc/build/downloads$ ls busybox-1.32.0.tar.bz2* -l
-rw-r--r-- 1 root root 2439463 Jun 27  2020 busybox-1.32.0.tar.bz2
-rw-r--r-- 1 root root     463 Mar  7  2021 busybox-1.32.0.tar.bz2.done

​ 一个技巧就是某个开源组件一直下载失败,那就从其他地方下载同版本组件拷贝到存放目录下,并复制一个*.done文件改为对应名字即可。

FILESPATH

​ 构建系统搜索本地文件(本地源码、补丁、配置文件等)的目录集合,位于配方(recipe)文件中。在构建过程中,bitbake查找SRC_URI变量的file://语句指定的本地文件时,会依次搜索FILESPATH变量指定的目录集合,该变量的默认值在meta/classes/base.bbclass中定义:

FILESPATH = "${@base_set_filespath(["${FILE_DIRNAME}/${BP}", \
        "${FILE_DIRNAME}/${BPN}", "${FILE_DIRNAME}/files"], d)}"

​ 一个简单的示例如下。

SRC_URI += "file://defconfig" #只是将配置文件拷贝到${WORKDIR}目录下
SRC_URI += "file://patch/0001-XXX.patch" #将补丁文件拷贝到${WORKDIR}目录下,并将补丁应用到${S}目录
SRC_URI += "file://patch/0002-XXX.diff" #将补丁文件拷贝到${WORKDIR}目录下,并将补丁应用到${S}目录
SRC_URI += "file://patch/0003-XXX.patch;apply=yes" #将补丁文件拷贝到${WORKDIR}目录下,并将补丁应用到${S}目录,这是显示指定应用补丁
SRC_URI += "file://patch/0004-XXX.patch;apply=no" #将补丁文件拷贝到${WORKDIR}目录下,但不应用补丁

​ 默认情况,构建系统会将file://指定带".diff"或".patch"的补丁文件应用到${S}目录,如果不想使用这个补丁,可以显示指定不应用。

注意:很多时候我们会在配方文件(.bb)中看见如下类似代码。

FILESPATH := "${THISDIR}/../../sources/${PN}:"

​ 这种强制更改FILESPATH变量默认值的方法是不正确的,正确做法是采用FILESEXTRAPATHS变量来扩展搜索目录,比如下面做法:

FILESEXTRAPATHS_prepend := " ${THISDIR}/../../sources/XXX:${THISDIR}/../../sources/YYY:" #同时扩展2个路径
FILESEXTRAPATHS_append := " ${THISDIR}/../EEE:" #尾部的分号必须存在

​ 额外说明一下,在.bbappend文件中只能使用FILESEXTRAPATHS变量。关于"_prepend""_append"操作符说明将在后续讲解。

THISDIR

​ bb或bbappend文件所在目录,位于配方(recipe)文件中。比如某个配方文件位于如下路径:

/work/open_source/openbmc/meta-aspeed/recipes-kernel/linux/linux-aspeed_git.bb

​ 那么在linux-aspeed_%.bbappend配方文件中的${THISDIR}变量值为也包含/work/open_source/openbmc/meta-aspeed/recipes-kernel/linux/

​ 注意,${THISDIR}变量值是配方文件所在目录,也就是说在bbappend文件中使用${THISDIR}变量,即包含bb文件所在目录,也包含bbappend文件所在目录,比如上面和下面两个目录都包含。

/work/open_source/openbmc/meta-ibm/meta-romulus/recipes-kernel/linux/linux-aspeed_%.bbappend

TMPDIR

​ 此变量是 OpenEmbedded 构建系统用于所有构建输出和中间文件(共享状态缓存除外)的基本目录,位于编译目标层的conf/local.conf.sample文件中(如有需要可在此文件中修改),这个文件将被解析到build/conf/local.conf文件中,默认情况下该变量指定的目录为build/tmp/

PACKAGE_ARCH

​ 包架构名,位于配方(recipe)文件中(一般不会在配方中自己指定)。查看示例:

~/work/bmc/build$ bitbake -e obmc-phosphor-image | grep ^PACKAGE_ARCH
PACKAGE_ARCH="4u_x201"
PACKAGE_ARCHS="all any noarch arm armv4 armv4t armv5 armv5t armv5e armv5te armv6 armv6t 4u_x201"

​ 其中obmc-phosphor-image为映像名(可使用包名),最终该映像就会生成于build\tmp\deploy\images\4u-x201目录中。

TARGET_OS

​ 指定目标的操作系统。对于基于 glibc 的系统(GNU C 库),该变量可以设置为“linux”,对于 musl libc 可以设置为“linux-musl”。对于 ARM/EABI 目标,存在“linux-gnueabi”“linux-musleabi”可能的值。查看示例:

~/work/bmc/build$ bitbake -e obmc-phosphor-image | grep ^TARGET_OS
TARGET_OS="linux-gnueabi"

PN

​ 用于构建包的配方名称或包的名称,该变量位于配方文件中(如有需要可在此文件中修改,一般会自动捕获配方名称)。例如,如果配方名为 expat_2.0.1.bb,则PN默认值为“expat”。这里需要注意一下,包名或配方名称中不能使用下划线_,在yocto下划线为版本分隔符。查看示例:

~/work/bmc/build$ bitbake -e obmc-phosphor-image | grep ^PN
PN="obmc-phosphor-image"

PV

​ 配方版本,该变量位于配方文件中(如有需要可在此文件中修改,一般会自动捕获配方版本)。例如,如果配方名为 expat_2.0.1.bb,则 PV 的默认值将为“2.0.1”。查看示例:

~/work/bmc/build$ bitbake -e obmc-phosphor-image | grep ^PV
PV="1.0"

PR

​ 配方的修订版本,该变量位于配方文件中(如有需要可在此文件中修改,一般需要手动指定)。此变量的默认值为“r0”,配方的后续修订通常具有值“r1”、“r2”等。当PV增加时,PR通常重置为“r0”。查看示例:

~/work/bmc/build$ bitbake -e obmc-phosphor-image | grep ^PR
PR="r0"

BP

​ 该变量的值基本配方名称和版本,但没有任何特殊配方名称后缀(即 -native、lib64- 等)。 BP 由组成为${BPN}-${PV},其查看示例为:

~/work/bmc/build$ bitbake -e obmc-phosphor-image | grep ^BP
BP="obmc-phosphor-image-1.0"

WORKDIR

​ OpenEmbedded 构建系统在其中构建配方的工作目录的路径名。该目录位于TMPDIR目录下,实际路径基于正在构建的配方和正在构建的系统,默认定义如下:

${TMPDIR}/work/${MULTIMACH_TARGET_SYS}/${PN}/${EXTENDPE}${PV}-${PR}
  • TMPDIR: 顶层构建输出目录
  • MULTIMACH_TARGET_SYS: 目标系统标识符,默认值为${PACKAGE_ARCH}${TARGET_VENDOR}-${TARGET_OS},示例值为4u_x201-openbmc-linux-gnueabiarmv6-openbmc-linux-gnueabi,详细请参阅https://www.yoctoproject.org/docs/2.7/ref-manual/ref-manual.html#var-MULTIMACH_TARGET_SYS
  • PN: 配方名称
  • EXTENDPE: 扩展前缀 ,如果PE未指定,EXTENDPE`则为空白,大多数食谱通常都是这种情况
  • PV: 配方版本
  • PR: 配方修改

BPN

​ 用于构建包的配方名称,它是PN变量的变种,去掉了常用的前缀和后缀,比如nativesdk-、-cross、-native,以及multilib的lib64-和lib32-。

~/work/byobmc/build$ bitbake -e obmc-phosphor-image | grep ^BPN
BPN="obmc-phosphor-image"

S

​ 解压后的配方源代码所在路径,该变量位于配方文件中(如有需要可在此文件中修改)。默认情况下,此目录为${WORKDIR}/${BPN}-${PV},如果源压缩包将代码提取到名为 ${BPN}-${PV} 以外的任何目录,或者如果源代码是从 SCM(例如 Git 或 Subversion)获取的,则必须在配方中设置 S,以便OpenEmbedded 构建系统知道在哪里可以找到解压的源代码。

​ 假设bb文件中指定源码来源于git,则在 do_fetch 期间,源码将被被克隆到 ${WORKDIR}/git目录中。由于此路径与S的默认值不同,所以必须专门设置,才能定位到源:

 SRC_URI = "git://path/to/repo.git"
 S = "${WORKDIR}/git"

总结:源码获取及处理阶段就是根据${SRC_URI}变量指定方式来获取源码存放到${WORKDIR}路径,若是压缩包则解压到S路径下,若有补丁文件则应用到S目录。

源码配置、源码编译及成果物安装

​ 源代码打好补丁后,bitbake 执行配置和编译源代码的任务。编译完成后,成果物文件将复制到保存区域(暂存)以准备打包:
yocto(二)——bitbake工作流程

​ 这个阶段主要包含四个任务,分别为do_prepare_recipe_sysrootdo_configuredo_compiledo_install

​ 是不是很好奇,突然跑出一个奇怪任务do_prepare_recipe_sysroot?虽然这个任务没有出现在上图中,但它却非常重要。

do_prepare_recipe_sysroot任务

​ 网上大多资料都简要带过这个任务,未能讲明白这个任务是做什么的,但想要了解yocto的配方文件共享机制就必须弄明白这个任务是做什么的!

do_prepare_recipe_sysrootdo_populate_sysrootstaging.bbclass类中关键任务,用于共享配方之间成果物!抛出一个问题思考一下,如果一个配方B需要使用配方A的成果物怎么办(比如头文件、动态/静态链接库、配置文件)?yocto为了解决这种问题,提供了一套配方成果物共享机制,该机制分为两阶段:

  • ​ 第一阶段在A配方构建时完成。A配方在构建时,需要在do_install 任务中将需要共享的文件安装至${D}目录,后续执行的do_populate_sysroot任务将自动拷贝${D}目录下部分子目录到${SYSROOT_DESTDIR},而${SYSROOT_DESTDIR}目录最终会放置到共享区(默认为build/tmp/sysroots-components)暂存,其他配方构建时就可以从共享区拷贝。

    ​ 那么,${D}目录下哪些子目录会被自动拷贝?自动拷贝的目录由三个变量指定,分别为SYSROOT_DIRS(目标设备需要保存的子目录)、SYSROOT_DIRS_BLACKLIST(目标设备不需要保存的子目录)、SYSROOT_DIRS_NATIVE(本机设备需要保存的目录),以SYSROOT_DIRS变量为例,其默认值为:

    SYSROOT_DIRS = " \
             ${includedir} \
             ${libdir} \
             ${base_libdir} \
             ${nonarch_base_libdir} \
             ${datadir} \
         "
    

    如果需要添加其他额外保存的目录,可以在配方文件中增加SYSROOT_DIRS += “YYY”

  • ​ 第二阶段在B配方构建时完成。B配方中添加DEPENDS += "A",便可使用A配方的成果物了。bitbake执行构建任务时会保证B配方的do_prepare_recipe_sysroot任务执行前,A配方的成果物已位于build/tmp/sysroots-components中。

    do_prepare_recipe_sysroot任务会在${WORKDIR}目录中创建两个sysroot目录并填充(所有依赖拷贝到其中),这两个目录名分别为"recipe-sysroot""recipe-sysroot-native"(本机),其中"recipe-sysroot"给目标设备使用,A配方生成的成果物就在里面,另一个"recipe-sysroot-native"是给本机设备使用的。

    ​ 不知道目标设备与本机设备的差异?简要讲解一下:假设要给arm平台编译flash固件,编译主机是x86平台,那么目标设备就是arm设备,本机设备就是x86编译主机。我们知道为arm设备编译代码需要使用交叉编译链(如arm-linux-gcc),编译链需要使用根文件系统下的各种库(或其他配方生成的头文件及库),因此便设置"recipe-sysroot"为编译器使用的文件系统。注意了,源码编译do_compile任务只是bitbake众多任务当中的一个,那其他任务也需要使用库或工具(如制作文件系统工具、压缩工具、cmake工具)怎么办?所以yocto将本机执行其他任务所需库或工具都放置于"recipe-sysroot-native"

    ​ 这里只介绍了构建依赖DEPENDS,但还有一种运行时依赖RDEPENDS,一般情况运行依赖会由构建系统自动添加,详情请参阅RDEPENDS

do_configure任务

​ 此任务用于完成编译源码前的配置,配置可以来自配方本身,也可以来自继承的类,一般情况我们都会使用autotools(配方中使用inherit autotools)、cmake类(配方中使用inherit cmake)或默认的make(不需要额外配置)。该任务运行时将当前工作目录设置为${B}(一般与${S}相同),该任务有个默认行为,即如果找到一个makefile (makefile, makefile,或GNUmakefile)并且CLEANBROKEN没有设置为“1”,则运行oe_runmake clean

​ 简单说明一下该任务怎么用:

  • ​ 如果你的软件包编译是基于autotools的,则可以使用EXTRA_OECONFPACKAGECONFIG_CONFARGS 变量添加其他配置选项,比如在配方文件中添加如下:

    EXTRA_OECONF += "--with-mib-modules="mib" \
                     --with-openssl=openssl \
                     --with-default-snmp-version="3" \
                     --with-logfile="/var/log/snmpd.log" \
                     --with-persistent-directory="/etc" \
                     --enable-privacy \
                     --enable-md5 \
                     --enable-des \
                     --prefiex=/xxx/yyy/ \
     "
    

    do_configure任务就如同手动执行./configure ${EXTRA_OECONF} ${PACKAGECONFIG_CONFARGS}一样。

  • ​ 如果你的软件包编译是基于cmake的,则可以使用EXTRA_OECMAKE变量添加其他配置选项,比如在软件包的配方文件添加如下:

    EXTRA_OECMAKE = " \
         -DBMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE=ON  -DBMCWEB_INSECURE_DISABLE_SSL=ON  \
         -DBMCWEB_ENABLE_DEBUG=ON -DBMCW   EB_ENABLE_LOGGING=ON \
    "
    

    do_configure任务就如同手动执行mkdir build/ && cd build/ && cmake ${EXTRA_OECMAKE}一样。

  • ​ 如果你的软件包编译是基于make的,则可以使用EXTRA_OEMAKE变量添加其他配置选项,比如在软件包的配方文件添加如下:

    EXTRA_OEMAKE = "INSTALL_PREFIX=${D} OTHERLDFLAGS='${LDFLAGS}' HOST_CPPFLAGS='${BUILD_CPPFLAGS}'"
    

    do_configure任务相当于只做默认行为(EXTRA_OEMAKE在执行make时才传入)。

do_compile任务

​ 编译源代码。该任务运行时将当前工作目录设置为${B}(一般与${S}相同),该任务有个默认行为,即如果找到一个makefile (makefile, makefile,或GNUmakefile),则运行oe_runmake,若未找到此类文件将不执行任何操作。

​ 如果在执行oe_runmake时需要传入额外编译选项或链接库,则可以使用在配方中以下变量:

CFLAGS += "-I${WORKDIR}/recipe-sysroot/usr/include/xxx  -DBMCW=ON" #gcc的编译选项,增加额外头文件检索路径,定义BMCW宏
CXXFLAGS = " -fPIC" #g++的编译选项,告诉编译器产生位置无关代码
LDFLAGS += "-L${WORKDIR}/recipe-sysroot/usr/lib -yyy" #编译器链接选项

​ 注意,如果源码是在${WORKDIR}${BPN}-${PV} 以外的任何目录,需要显性指定S变量值,比如从本地直接获取源码xxx.c和Makefile,则需要在配方中添加S = "${WORKDIR}",这样编译任务才能正常进行。

do_install任务

​ 文件或成果物的安装任务。该任务会将编译目录${B}中需要打包的文件(放到目标设备中去的及其他配方依赖的)复制到保存区${D}中。注意:安装文件时不要把所有者和组ID设置错误,特别是使用cp命令时会保留原始文件的UID和GID,以下是推荐的安全方法:

使用install命令
使用cp命令时加上"--no-preserve=ownership"选项
使用tar命令时加上"--no-same-owner"选项

​ 还记得上面提过的依赖吗?这个任务就是把其他配方所需依赖安装到${D}目录,然后do_populate_sysroot任务才能去${D}目中拷贝。假设A模块执行do_install的一个简单示例:

do_install() {
    oe_runmake DESTDIR=${D}${libdir} install #执行Makefile中安装任务(安装.so),传入安装目录
    install -m 0644 -d ${D}${includedir}/api #创建头文件目录
    install -m 0644 ${S}/api_common.h  ${D}${includedir}/api #安装头文件
    install -m 0644 ${S}/api_xxx.h  ${D}${includedir}/api #安装头文件
}

​ 之后B模块在配方中添加如下:

DEPENDS += " A"
CFLAGS += " -I${WORKDIR}/recipe-sysroot/usr/include/api"

​ 这样B模块就可以使用A模块编译的动态库。

该大阶段涉及但未解释的变量有以下:

B

​ 包构建的编译目录,一般情况${B}${S}相同,即为${WORKDIR}/${BPN}-${PV}

D

​ 包构建成果物的安装目录,也称为目标目录。默认情况这个目录为${WORKDIR}/image

SYSROOT_DESTDIR

​ 指向包构建工作目录下的临时目录,其默认值为${WORKDIR}/sysroot-destdir

总结:源码配置、源码编译及成果物安装阶段就是引用依赖(如果有)完成源码配置、编译及成果物安装,安装的成果物可能是目标设备使用的,也可能是其他模块所需依赖。

包拆分处理

​ 在配置、编译和安装完成后,构建系统分析结果并将包拆分处理,比如将文件stripped后放入packages-split目录:
yocto(二)——bitbake工作流程

​ 该阶段分为三个任务,分别为do_packagedo_packagedatado_populate_sysroot

do_package、do_packagedata任务

do_packagedo_packagedata任务组合起来分析在${D}目录中找到的文件,并根据可用的包和文件将它们分成子集。分析处理过程包括以下内容:去除调试符号,查看包之间的共享库依赖关系,以及查看包之间的关系。

do_packagedata任务根据分析创建包元数据放置Package Feeds(即PKGDATA_DIR指定目录)中,这样构建系统就可以从拿到包生成最终的image。

do_populate_sysroot任务

do_populate_sysroot任务在之前已经介绍过。该任务将自动拷贝${D}目录下部分子目录到${SYSROOT_DESTDIR},并将${SYSROOT_DESTDIR}目录内容暂存至共享区(默认为build/tmp/sysroots-components)。自动拷贝的子目录由三个变量指定,分别为SYSROOT_DIRS(目标设备需要保存的子目录)、 SYSROOT_DIRS_BLACKLIST(目标设备不需要保存的子目录)、SYSROOT_DIRS_NATIVE(本机设备需要保存的目录),前面已经简要介绍过它们,详情请点入进官网参看。

该大阶段会涉及到以下变量:

PACKAGE_CLASSES

​ 用于指定构建系统在打包文件时使用何种包管理器,该变量位于编译目标层的conf/local.conf.sample文件中(如有需要可在此文件中修改),这个文件将被解析到build/conf/local.conf文件中,比如设置其值为PACKAGE_CLASSES ?= "package_rpm package_tar"

PKGD

​ 在将包拆分为单独的包之前,包的目标目录。

PKGDESTWORK

do_package任务用来保存包元数据的临时工作区(即pkgdata)。

PKGDEST

​ 拆分后的包的父目录(即packages-split)。

PKGDATA_DIR

​ 一个共享的全局状态目录,其中包含打包过程中生成的打包元数据。打包过程将元数据从PKGDESTWORK复制到PKGDATA_DIR区域,在那里元数据成为全局可用的。

STAGING_DIR_HOST

​ 要运行组件的系统的系统根路径(即recipe-sysroot),也就是当前配方源码编译时的根文件系统,里面包含配方所需依赖及交叉编译器所需依赖。

STAGING_DIR_NATIVE

​ 为构建主机构建组件时使用的系统根路径(即recipe-sysroot-native)。

STAGING_DIR_TARGET

​ 当构建在系统上执行的组件并为另一台机器生成代码(例如cross-canadian recipes)时使用的 sysroot 路径,一般与STAGING_DIR_HOST一样。

FILES

​ 用于指定包(模块)安装在${D}目录中哪些成果物要打包,该变量位于配方文件中(如有需要可在此文件中修改)。简单来说就是在do_install任务中安装在${D}目录下的文件不会都打包,以rsa模块为例该变量的默认值为:

FILES_rsa="/usr/bin/* /usr/sbin/* /usr/libexec/* /usr/lib/lib*.so.*             /etc /com /var             /bin/* /sbin/*             /lib/*.so.*             /lib/udev /usr/lib/udev             /lib/udev /usr/lib/udev             /usr/share/hikrsa /usr/lib/hikrsa/*             /usr/share/pixmaps /usr/share/applications             /usr/share/idl /usr/share/omf /usr/share/sounds             /usr/lib/bonobo/servers"

​ 也就是说,只要成果物安装在上面这些目录下的都会参与打包,当然如果不放心或需要额外增加文件,可以在配方文件中显性指定:

FILES_${PN} += " \
        ${sbindir}/rsaverify \
" #${sbindir}默认为/usr/sbin/

总结:包拆分处理阶段就是根据conf配置将${D}目录中成果物打包放置于Package Feeds 区域,同时生成包元数据,最后将其他配方可能用到的文件放置于文件共享区(为其他配方提供依赖)。

image生成

​ 注意,这个阶段在一般包(模块)编译过程中不存在!!!可通过bitbake -c listtasks XXX(包名或固件名)命令查看编译任务列表。

​ 一旦软件包被拆分并存储在 Package Feeds 区域中,构建系统将使用 bitbake 生成根文件系统映像(image):
yocto(二)——bitbake工作流程

​ 该阶段涉及两个任务,分别为do_rootfsdo_image

do_rootfs任务

​ 该任务将创建目标设备的根文件系统(将需要打包至目标设备的程序、库、文件等都放置到根文件系统中),这个根文件系统最终打包到image中。do_rootfs任务会通过ROOTFS_POSTPROCESS_COMMAND来优化文件大小(如mklibs过程优化了库的大小,同时prelink优化了共享库的动态链接以减少可执行文件的启动时间),ROOTFS_POSTPROCESS_COMMAND如下:

ROOTFS_POSTPROCESS_COMMAND() {
write_package_manifest; license_create_manifest;   ssh_allow_empty_password;  ssh_allow_root_login;  postinst_enable_logging;
  rootfs_update_timestamp ;   write_image_test_data ;  set_systemd_default_target; systemd_create_users; empty_var_volatile;
remove_etc_version ;  set_user_group; sort_passwd; rootfs_reproducible;
}

​ 创建的文件系统所在位置由IMAGE_ROOTFS变量指定,查看示例:

~/work/bmc$ bitbake -e obmc-phosphor-image | grep ^IMAGE_ROOTFS
IMAGE_ROOTFS="~/work/bmc/build/tmp/work/4u_x201-openbmc-linux-gnueabi/obmc-phosphor-image/1.0-r0/rootfs"

​ 如果我们修改了某个程序,但又不想重新烧写整个固件,那就去这个目录下找到程序,再通过TFTP方式(或NFS直接挂载)下载到目标是设备调试即可。

do_image任务

do_image 任务会通过 IMAGE_PREPROCESS_COMMAND 对image进行预处理,主要是优化image大小,IMAGE_PREPROCESS_COMMAND 如下:

IMAGE_PREPROCESS_COMMAND() {
 mklibs_optimize_image;  prelink_setup; prelink_image;  reproducible_final_image_task;
}

​ 构建系统do_image根据需要动态生成支持的 do_image_* 任务,生成的任务类型取决于IMAGE_FSTYPES变量。do_image_* 任务将所有内容转换为一个image文件或一组image文件,并且可以压缩根文件系统image大小,以减小最终烧写到目标设备的image整体大小。用于根文件系统的格式取决于 IMAGE_FSTYPES变量,压缩取决于格式是否支持压缩。

image生成完成后执行最后一个任务do_image_complete,该任务将通过IMAGE_POSTPROCESS_COMMAND完成image的后续处理,默认情况IMAGE_POSTPROCESS_COMMAND为空,查看示例:

~/work/bmc$ bitbake -e obmc-phosphor-image | grep ^IMAGE_POSTPROCESS_COMMAND
IMAGE_POSTPROCESS_COMMAND=""

该大阶段涉及变量如下:

IMAGE_INSTALL

​ 该变量指明Package Feeds 区域安装的基本软件包集中,哪些包(模块)最终要打包到image,该变量一般位于编译目标层的conf/local.conf.sample文件中(如有需要可在此文件中修改),这个文件将被解析到build/conf/layer.conf文件中。注意与FILES差异,FILES是指明软件包内部哪些文件需要参与打包,而IMAGE_INSTALL是指明哪个软件包需要参与打包。

​ 一个简单示例:

IMAGE_INSTALL_append += ”rsa"

PACKAGE_EXCLUDE

​ 指定不应安装到image中的包。

IMAGE_FEATURES

​ 指定要包含在图像中的特征,大多数这些功能映射到其他安装包(未能弄明白具体作用)。

IMAGE_LINGUAS

​ 确定安装附加语言支持包的语言,该变量一般位于编译目标层的conf/local.conf.sample文件中(如有需要可在此文件中修改),这个文件将被解析到build/conf/layer.conf文件中。默认情况下为:

~/work/bmc$ bitbake -e rsa | grep ^IMAGE_LINGUAS
IMAGE_LINGUAS="en-us en-gb"

PACKAGE_INSTALL

​ 传递给包管理器以安装到映像中的包的最终列表。

DEPLOY_DIR

​ 最终image和SDK输出的目录,默认值为build/tmp/deploy/

总结:image生成生成阶段就是创建目标设备的根文件系统,并将需要打包至目标设备的程序、库、文件等都放置到根文件系统中,然后对文件和整个文件系统进行优化压缩,最终生成image。

简单示例

api.bb的配方为例,其内容如下:

SUMMARY = "This is an example"
SECTION = "Examples" 
HOMEPAGE = "http://www.xxx.com.cn/"
PR = "r1" #修订版本
#PV = "1.0" #这里不用指定配方版本,因为配方api.bb本身就不带版本,所以默认PV就是1.0

LICENSE = "MIT" #许可证类型
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" #校验许可证

FILESPATH := "${THISDIR}/../../sources/${PN}:" #本地文件搜索路径,PN默认为配方名称,即api

#此配方所依赖的其他配方
DEPENDS += "audit"
DEPENDS += "phosphor-ipmi-host"

#由于源码下载的目录是${WORKDIR},而非${WORKDIR}/${BPN}-${PV},因此需设置S,否则编译失败
S = "${WORKDIR}"
#指定需要下载的文件
SRC_URI += "\
                        file://Makefile \
                        file://make.libs \
                        file://api_common.h \
                        file://api_common.c \
                        file://api_ethernet.c \
                        file://api_ethernet.h \
                        "
#增加额外文件              
SRC_URI += " \
            file://api_systems.c \
            file://api_systems.h \
        "

#追加额外路径下的文件,还记得之前提过吗?尽量不要直接改变FILESPATH变量的值,所以下面方法虽然可用,但不是合理的,正确做法是使用FILESEXTRAPATHS变量
FILESPATH_append := "${THISDIR}/../../../meta-common/sources/host-ipmid/ipmi/:" #换成FILESEXTRAPATHS_append
SRC_URI_append = " \
            file://sharememory.c \
"

#传入参数
TARGET_CC_ARCH += "${LDFLAGS}"
EXTRA_OEMAKE = " 'RECIPE_SYSROOT=${RECIPE_SYSROOT}' "
CFLAGS_prepend = "-I${WORKDIR}/recipe-sysroot/usr/include/audit "

#这里注意没有出现do_configure、do_compile任务,故默认执行构建系统的do_configure、do_compile任务
#在本配方基于make构建包,所以do_configure任务相当于啥也没干,do_compile任务则执行oe_runmake并传入参数

#重载do_install任务,即构建系统原始的do_install任务不再执行,转而执行以下do_install任务
do_install() {
        oe_runmake DESTDIR=${D}${libdir} install #执行Makefile文件中的安装任务,并传入安装目录
        install -m 0644 -d ${D}${includedir}/api
        install -m 0644 ${S}/api_common.h  ${D}${includedir}/api
        install -m 0644 ${S}/api_common.c  ${D}${includedir}/api #不仅可以安装头文件,c文件也可以给其他模块使用,但这不是合理的方式
        install -m 0644 ${S}/api_ethernet.h ${D}${includedir}/api
    	install -m 0644 ${S}/api_systems.h ${D}${includedir}/api
}

#FILES变量默认会加上${includedir}和${libdir}目录,所以下面语句可以不要
#FILES_${PN} += "${includedir}/api"
#FILES_${PN} += "${libdir}"
上一篇:220113


下一篇:(2) JavaScript - 流程控制语句