yocto默认规则的BB文件命名规范是由PN PV 和PR三个变量组成,其中PN是recipes名,PV代表版本号,PR代表子版本号。
比如meta-qt中随便找一个bb文件:
可以看到bb文件名为python3-gsocketpool_0.1.6.bb
其中python3-gsocketpool同recipe名,0.1.6是版本号名,子版本号不写默认是r1
在yocto中bb文件的名称是可以通过bitbake命令进行编译的,比如:
bitbake python3-gsocketpool
但是在yocto中编译内核的时候通常使用如下指令:
bitbake virtual/kernel
在yocto中内核bb文件如下:
正常使用bitbake linux-fsmp1也是可以的。下面分析一下virtual/kernel的由来
recipes可以覆盖或者添加到可用于指定该包的符号名字的PROVIDES变量。然后依赖于它的包可以引用符号名字。如果多个包或者同一个包的多个版本提供相同的功能性,那么这是有意义的:
PROVIDES =+ virtual/kernel
这个用在recipes中针对kernel的声明将增加符号名字virtual/kernel到名字列表。符号名字实际上做什么不重要,但是为了避免符号名字和实际其他菜谱名字的无意冲突,开发者已经采用惯例来使用前缀virtual/用于符号名字 。
kernel的版本可能随着发展需要会进行升级,这时源码中就会存在多个相同符号名字的bb文件。这时候需要指定使用的具体版本号。
在yocto中使用PREFERRED_PROVIDER变量来解决这个问题
比如:
PREFERRED_PROVIDER_virtual/kernel = "linunx-fsmp1_5.4"
这样就会指定系统使用fsmp1_5.4的内核来构建系统,通常这种配置都会在发行版的conf文件中有涉及。
本小节想详细分析一下yocto中内核的编译过程。
编译内核:
bitbake virtual/kernel
配置内核以后再编译内核:
bitbake virtual/kernel -c menuconfig
bitbake virtual/kernel -C compile
build-openstlinuxeglfs-fsmp1a/tmp-glibc/work/fsmp1a-ostl-linux-gnueabi/linux-fsmp1/5.4.31-r0路径下看到编译输出:
最终kernel的打包产物在package下面:
下面fsmp1开发板的linux-fsmp1_5.4bb文件,删减部分无关信息:
SUMMARY = "Linux STM32MP Kernel"
SECTION = "kernel"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814"
include linux-fsmp1.inc
LINUX_VERSION = "5.4"
LINUX_SUBVERSION = "31"
SRC_URI = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${LINUX_VERSION}.${LINUX_SUBVERSION}.tar.xz;name=kernel"
SRC_URI[kernel.sha256sum] = "a11083f8f809887f6a0f8d4467532385b99418f17998fe6e837807491c276eeb"
SRC_URI += " \
file://${LINUX_VERSION}/${LINUX_VERSION}.${LINUX_SUBVERSION}/0001-ARM-stm32mp1-r1-MACHINE.patch \
file://${LINUX_VERSION}/${LINUX_VERSION}.${LINUX_SUBVERSION}/0002-ARM-stm32mp1-r1-CPUFREQ.patch \
......
"
PV = "${LINUX_VERSION}.${LINUX_SUBVERSION}"
S = "${WORKDIR}/linux-${LINUX_VERSION}.${LINUX_SUBVERSION}"
# ---------------------------------
# Configure devupstream class usage
# ---------------------------------
BBCLASSEXTEND = "devupstream:target"
# SRC_URI_class-devupstream = "git://github.com/STMicroelectronics/linux.git;protocol=https;branch=v${LINUX_VERSION}-stm32mp;name=linux"
# SRCREV_class-devupstream = "b8663f5fdb5cfd6f243b72c9fac82c24b2594294"
SRCREV_FORMAT_class-devupstream = "linux"
PV_class-devupstream = "${LINUX_VERSION}+github+${SRCPV}"
# ---------------------------------
# Configure default preference to manage dynamic selection between tarball and github
# ---------------------------------
STM32MP_SOURCE_SELECTION ?= "tarball"
DEFAULT_PREFERENCE = "${@bb.utils.contains('STM32MP_SOURCE_SELECTION', 'github', '-1', '1', d)}"
# ---------------------------------
# Configure archiver use
# ---------------------------------
include ${@oe.utils.ifelse(d.getVar('ST_ARCHIVER_ENABLE') == '1', 'linux-fsmp1-archiver.inc','')}
# -------------------------------------------------------------
# Defconfig
#
KERNEL_DEFCONFIG = "stm32_fsmp1a_defconfig"
FSMP1A_USB_SERIAL = "fsmp1a"
KERNEL_CONFIG_FRAGMENTS = "${@bb.utils.contains('KERNEL_DEFCONFIG', 'stm32_fsmp1a_defconfig', '${S}/arch/arm/configs/fragment-01-multiv7_cleanup.config', '', d)}"
KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('KERNEL_DEFCONFIG', 'stm32_fsmp1a_defconfig', '${S}/arch/arm/configs/fragment-02-multiv7_addons.config', '', d)}"
.......
KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('DISTRO_FEATURES', 'systemd', '${WORKDIR}/fragments/5.4/fragment-03-systemd.config', '', d)} "
KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('COMBINED_FEATURES', 'optee', '${WORKDIR}/fragments/5.4/fragment-04-optee.config', '', d)}"
KERNEL_CONFIG_FRAGMENTS += "${WORKDIR}/fragments/5.4/fragment-05-modules.config"
KERNEL_CONFIG_FRAGMENTS += "${@oe.utils.ifelse(d.getVar('KERNEL_SIGN_ENABLE') == '1', '${WORKDIR}/fragments/5.4/fragment-06-signature.config','')} "
SRC_URI += "file://${LINUX_VERSION}/fragment-03-systemd.config;subdir=fragments"
SRC_URI += "file://${LINUX_VERSION}/fragment-04-optee.config;subdir=fragments"
......
# -------------------------------------------------------------
# Kernel Args
#
KERNEL_EXTRA_ARGS += "LOADADDR=${ST_KERNEL_LOADADDR}"
重要变量介绍:
LIC_FILES_CHKSUM:许可文件的名字和MD5校验和。
LINUX_VERSION: 正在构建的Linux内核的版本号
SRC_URI: 指定到Linux内核tar包的路径。另外,变量必须指定一个包含内核配置的defconfig文件。
SRC_URI[md5sum],SRC_URI[sha256sum]: 用于远程下载的校验和
S:内核源被解压到的目录。
COMPATIBLE_MACHINE:被内核支持的机器的名字列表。
KERNEL_FEATURES:包含内核特性配置的文件列表。
KMACHINE:Linux内核已知的硬件机器的名字。每个内核菜谱都必须设置这个变量。
从linux-fsmp1_5.4bb文件中可以看到主要干了几件事:
可以看到实际的定制细节没有在linux-fsmp1_5.4bb中体现,linux-fsmp1-archiver.inc里面并没有干什么事,重头戏都在linux-fsmp1.inc里面。下面重点关注这个文件内容:
COMPATIBLE_MACHINE = "(stm32mpcommon)"
inherit kernel
DEPENDS += "openssl-native util-linux-native libyaml-native"
B = "${WORKDIR}/build"
# Configure build dir for externalsrc class usage through devtool
EXTERNALSRC_BUILD_pn-${PN} = "${WORKDIR}/build"
# To share config fragments between layers
FILESEXTRAPATHS_prepend := "${THISDIR}:"
# -------------------------------------------------------------
# Do not deploy kernel module with specfic tarball
MODULE_TARBALL_DEPLOY = "0"
do_deploy[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}/kernel"
#---------------------------------------------------------------
# Module kernel signature
KERNEL_SIGN_ENABLE ?= "0"
EXTRA_OEMAKE += "${@oe.utils.ifelse(d.getVar('KERNEL_SIGN_ENABLE') == '1', 'INSTALL_MOD_STRIP=1','')}"
#
# Force task order for link creation
#
python () {
d.appendVarFlag("do_unpack", "postfuncs", " do_symlink_kernsrc")
}
do_symlink_kernsrc[noexec] = "1"
# ---------------------------------------------------------------------
# Defconfig
#
#If the defconfig is contained on the kernel tree (arch/${ARCH}/configs)
#you must use the following line
do_configure_prepend() {
unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS MACHINE
if [ ! -z ${KERNEL_DEFCONFIG} ];
then
bbnote "Kernel customized: configuration of linux STM by using DEFCONFIG: ${KERNEL_DEFCONFIG}"
oe_runmake ${PARALLEL_MAKE} -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" ${KERNEL_DEFCONFIG}
else
if [ ! -z ${KERNEL_EXTERNAL_DEFCONFIG} ];
then
bbnote "Kernel customized: configuration of linux STM by using external DEFCONFIG"
install -m 0644 ${WORKDIR}/${KERNEL_EXTERNAL_DEFCONFIG} ${B}/.config
oe_runmake -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" oldconfig
else
bbwarn "You must specify KERNEL_DEFCONFIG or KERNEL_EXTERNAL_DEFCONFIG"
die "NO DEFCONFIG SPECIFIED"
fi
fi
if [ ! -z "${KERNEL_CONFIG_FRAGMENTS}" ]
then
for f in ${KERNEL_CONFIG_FRAGMENTS}
do
bbnote "file = $f"
# Check if the config fragment was copied into the WORKDIR from
# the OE meta data
if [ ! -e "$f" ]
then
echo "Could not find kernel config fragment $f"
exit 1
fi
done
bbnote "${S}/scripts/kconfig/merge_config.sh -m -r -O ${B} ${B}/.config ${KERNEL_CONFIG_FRAGMENTS} 1>&2"
# Now that all the fragments are located merge them.
(${S}/scripts/kconfig/merge_config.sh -m -r -O ${B} ${B}/.config ${KERNEL_CONFIG_FRAGMENTS} 1>&2 )
fi
yes '' | oe_runmake -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" oldconfig
#oe_runmake -C ${S} O=${B} savedefconfig && cp ${B}/defconfig ${WORKDIR}/defconfig.saved
}
# ---------------------------------------------------------------------
do_install_append() {
# Install KERNEL_IMAGETYPE for kernel-imagebootfs package
install -m 0644 ${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE} ${D}/${KERNEL_IMAGEDEST}
if ${@bb.utils.contains('MACHINE_FEATURES','gpu','true','false',d)}; then
# when ACCEPT_EULA are filled
echo "blacklist etnaviv" > ${D}/${sysconfdir}/modprobe.d/blacklist.conf
fi
}
# ---------------------------------------------------------------------
# Support checking the kernel load address parameter: expecting proper value for ST kernel.
#
python do_loadaddrcheck() {
if not d.getVar('ST_KERNEL_LOADADDR'):
bb.fatal('Missing ST_KERNEL_LOADADDR value for ST kernel build: please define it in your machine.')
}
PACKAGES =+ "${KERNEL_PACKAGE_NAME}-headers ${KERNEL_PACKAGE_NAME}-imagebootfs"
FILES_${KERNEL_PACKAGE_NAME}-headers = "${exec_prefix}/src/linux*"
FILES_${KERNEL_PACKAGE_NAME}-image += "boot/ ${KERNEL_IMAGEDEST}"
FILES_${KERNEL_PACKAGE_NAME}-imagebootfs = "boot/*.dtb boot/${KERNEL_IMAGETYPE}"
FILES_${KERNEL_PACKAGE_NAME}-modules += "${sysconfdir}/modprobe.d"
重要内容解析:
COMPATIBLE_MACHINE:指定了内核支持的机器的名字是 stm32mpcommon
inherit kernel :引用了OE的kernel类,帮我们作绝大多数的编译内核工作
DEPENDS : 指定内核的依赖包
B:指定内核编译的目录
FILESEXTRAPATHS_prepend:将本菜谱的路径添加到构建系统的搜索路径
do_deploy[sstate-outputdirs]: 指定内核部署的构建输出目录,最终的路径在tmp-glibc/deploy/images/fsmp1a/kernel下
实际编译以后的构建输出如下:
EXTRA_OEMAKE += "${@oe.utils.ifelse(d.getVar('KERNEL_SIGN_ENABLE') == '1', 'INSTALL_MOD_STRIP=1','')}"
指定编译内核的配置选项,INSTALL_MOD_STRIP表示是否去掉module的符号等调试选项,减小module的占用内存大小
d.appendVarFlag("do_unpack", "postfuncs", " do_symlink_kernsrc"):
yocto中可以在任务的主函数之前和之后运行函数。这是使用任务的“prefuncs”和“postfuncs”标志来列出要运行的函数完成的。上面的意思就是在do_unpack 解压内核以后运行symlink_kernsrc。
do_symlink_kernsrc[noexec] = "1" :
强制每次编译kernel都作一次symlink_kernsrc动作,noexec表示不关注共享缓存状态,强制执行
do_configure_prepend():
do_configure_prepend函数表示在作kernel的配置之前先运行这段函数,这段函数的大致流程如下:
do_install_append():
第一部分:
install -m 0644 ${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE} ${D}/${KERNEL_IMAGEDEST}
通过bitbake virtual/kernel -e 查看环境变量可以知道这几个变量的含义分别是:
KERNEL_OUTPUT_DIR:arch/arm/boot
KERNEL_IMAGETYPE: zImage vmlinux Image uImage
KERNEL_IMAGEDEST:boot
所以该函数的意思是在执行完install流程以后将arch/arm/boot下的 zImage vmlinux Image uImage 都安装到安装目录image/boot下面。
第二部分:
如果MACHINE_FEATURES指定了gpu选项则配置blacklist etnaviv 添加到模块自动加载目录
do_loadaddrcheck():该函数检查bb文件是否指定了内核的链接地址
PACKAGES =+ "${KERNEL_PACKAGE_NAME}-headers ${KERNEL_PACKAGE_NAME}-imagebootfs":
在打包过程中创建 -headers 和-imagebootfs两个包
FILES_${KERNEL_PACKAGE_NAME}-headers = "${exec_prefix}/src/linux*"
FILES_${KERNEL_PACKAGE_NAME}-image += "boot/ ${KERNEL_IMAGEDEST}"
FILES_${KERNEL_PACKAGE_NAME}-imagebootfs = "boot/*.dtb boot/${KERNEL_IMAGETYPE}"
FILES_${KERNEL_PACKAGE_NAME}-modules += "${sysconfdir}/modprobe.d"
指定被放进特定包中的目录和文件的列表,下面是所有被打包的内容:
headers 表示将内核的所有头文件放到/usr/src/linux-*下面
分析Done