0%

浅谈Yocto recipe之间共享文件的方法

在编写Yocto recipe的时候,可能会用到其他recipe的文件。这里简单总结一下recipe之间共享文件的几种方法,以及使用过程中遇到的问题。

方法一:DEPENDS

这种方法是最常用的,也是最推荐使用的。假设foo.bb安装了${D}${includedir}/foo.h

1
2
3
4
5
# foo.bb
do_install() {
install -d ${D}${includedir}
isntall ${S}/foo.h ${D}${includedir}
}

bar.bb需要用到foo.h,那么只需要在bar.bb加上DEPENDS += 'foo',那么bar.bb就能使用foo.h了。

1
2
# bar.bb
DEPENDS += 'foo'

简简单单一句DEPENDS,yocto在背后做了很多工作。下面来追以下yocto是如何实现的。

准备共享文件do_populate_sysroot

首先,foo.bb要准备好共享给其他recipe的文件,这一步由foo.bbdo_populate_sysroot任务完成。这个任务会复制文件到SYSROOT_DESTDIR目录(即${WORKDIR}/sysroot-destdir)。简单看一下这个任务的实现,首先会调用函数sysroot_stage_all,这个函数会完成实际的复制动作;然后调用sysroot_strip,会对ELF文件做strip;SYSROOT_PREPROCESS_FUNCS注册了一些预处理的函数,如果想对共享的文件做一些个性化处理,可以向这个变量注册函数;最后是BB_MULTI_PROVIDER_ALLOWED(不知道有啥用)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def do_populate_sysroot(d):
# SYSROOT 'version' 2
bb.build.exec_func("sysroot_stage_all", d)
bb.build.exec_func("sysroot_strip", d)
for f in (d.getVar('SYSROOT_PREPROCESS_FUNCS') or '').split():
bb.build.exec_func(f, d)
pn = d.getVar("PN")
multiprov = d.getVar("BB_MULTI_PROVIDER_ALLOWED").split()
provdir = d.expand("${SYSROOT_DESTDIR}${base_prefix}/sysroot-providers/")
bb.utils.mkdirhier(provdir)
for p in d.getVar("PROVIDES").split():
if p in multiprov:
continue
p = p.replace("/", "_")
with open(provdir + p, "w") as f:
f.write(pn)

关键是函数sysroot_stage_all,这个函数会将${D}下面的文件复制到${SYSROOT_DESTDIR}。当然,不是所有的文件都会复制,只会复制${SYSROOT_DIRS}变量列出的目录,还会删除${SYSROOT_DIRS_IGNORE}变量列出的目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
sysroot_stage_dir() {
src="$1"
dest="$2"
# if the src doesn't exist don't do anything
if [ ! -d "$src" ]; then
return
fi

mkdir -p "$dest"
rdest=$(realpath --relative-to="$src" "$dest")
(
cd $src
find . -print0 | cpio --null -pdlu $rdest
)
}

sysroot_stage_dirs() {
from="$1"
to="$2"

for dir in ${SYSROOT_DIRS}; do
sysroot_stage_dir "$from$dir" "$to$dir"
done

# Remove directories we do not care about
for dir in ${SYSROOT_DIRS_IGNORE}; do
rm -rf "$to$dir"
done
}

sysroot_stage_all() {
sysroot_stage_dirs ${D} ${SYSROOT_DESTDIR}
}

下面是SYSROOT_DIRSSYSROOT_DIRS_IGNORE变量的默认值。所以如果没有把文件安装到SYSROOT_DIRS包含的目录,或者把文件安装到了SYSROOT_DIRS_IGNORE列出的目录,那么其他的recipe就拿不到文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
SYSROOT_DIRS = " \
${includedir} \
${libdir} \
${base_libdir} \
${nonarch_base_libdir} \
${datadir} \
/sysroot-only \
"

SYSROOT_DIRS_IGNORE = " \
${mandir} \
${docdir} \
${infodir} \
${datadir}/X11/locale \
${datadir}/applications \
${datadir}/bash-completion \
${datadir}/fonts \
${datadir}/gtk-doc/html \
${datadir}/installed-tests \
${datadir}/locale \
${datadir}/pixmaps \
${datadir}/terminfo \
${libdir}/${BPN}/ptest \
"

获取共享文件do_prepare_recipe_sysroot

do_prepare_recipe_sysroot任务会根据DEPENDS变量,将文件安装到STAGING_DIR_HOST目录或STAGING_DIR_NATIVE目录。

Installs the files into the individual recipe specific sysroots (i.e. recipe-sysroot and recipe-sysroot-native under ${WORKDIR} based upon the dependencies specified by DEPENDS). See the “staging” class for more information.

这个任务的实现比较复杂,看不太懂。

kernel.bbclass

需要注意的是,kernel.bbclassSYSROOT_DIRS变量清空了。这意味其他recipes拿不到kernel recipe安装到${D}下面的文件(被坑了一把,一度怀疑人生)。

1
2
# We don't need to stage anything, not the modules/firmware since those would clash with linux-firmware
SYSROOT_DIRS = ""

在yocto 4.0.6之前,是重写了sysroot_stage_all函数。

1
2
3
4
# We don't need to stage anything, not the modules/firmware since those would clash with linux-firmware
sysroot_stage_all () {
:
}

方法二:work-shared

work-shared目录在${TMPDIR}/work-sahred,一般情况下是build/tmp/work-sahred。recipe可以将文件安装到此目录下,其他recipe就可以在此路径下拿到文件。从Yocto文档看,只用于gcc及其变种,但这个限制不是强制的。

4.2.24 build/tmp/work-shared/

For efficiency, the OpenEmbedded build system creates and uses this directory to hold recipes that share a work directory with other recipes. In practice, this is only used for gcc and its variants (e.g. gcc-cross, libgcc, gcc-runtime, and so forth).

方法三:DEPLOY_DIR_IMAGE

与work-shared目录类似,也是recipe安装文件到DEPLAY_DIR_IMAGE目录,其他recipe通过此目录读取。

DEPLOY_DIR_IMAGE

Instead, it’s only useful when a recipe needs to “read” a file already deployed by a dependency.

总结

通过DEPENDS可以很方便的拿到其他recipe安装到${D}目录的文件。但要注意SYSROOT_DIRSSYSROOT_DIRS_IGNORE变量,一个控制什么目录会共享,一个控制什么目录不会共享。通过检查${SYSROOT_DESTDIR}可以确认共享给其他recipe的文件。

此外,还能通过work-sharedDEPLOY_DIR_IMAGE目录拿到其他recipe安装的文件。

参考