Yocto is incredibly flexible. It enables building an embedded Linux distribution with virtually any combination of packages in the root filesystem (rootfs). This flexibility also makes Yocto quite powerful. Understanding how to include custom files in the rootfs can simplify the process of creating custom Linux images. This blog outlines a collection of recipes that guided me through the sea of misinformation to the process of correctly including a large custom application.
As previously recommended, you can install Yocto on your system with at least 8GB of RAM and 120GB of hard drive space, then prepare yourself and expand your Yocto knowledge with the Mega-Manual and these steps. Once you have Yocto set up, follow these steps to customize your Linux image with your own executables and shared libraries.
Including a basic executable in a custom Yocto image can be done quite easily by following the steps below. All of the steps listed here were tested using Yocto Sumo version running in Centos 7. This example is based on an example that can be found in the Yocto Mega-Manual here.
Create the necessary directory structure for your meta-layer.
Add a layer.conf file to configure your meta-layer.
Edit your layer.conf file to define paths to your recipes, and your layer version.
# We have recipes-* directories, add to BBFILES
BBFILES += “${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend \
${LAYERDIR}/recipes-*/*.bb \
${LAYERDIR}/recipes-*/*.bbappend ”
BBFILE_COLLECTIONS += “test”
BBFILE_PATTERN_test = “^${LAYERDIR}/”
BBFILE_PRIORITY_test = “1”
LAYERVERSION_test = “1”
$ mkdir -p recipes-test/helloworld && cd recipes-test/helloworld
$ touch helloworld.bb
Use your newly created recipe to build the helloworld executable:
This is an example of a basic recipe. The do_compile function tells Yocto how to build the executable. The variables CC, CFLAGS, and LDFLAGS are set by Yocto to defaults for the machine that is specified. The do_install function lays out what directories and files are to be added to the rootfs. The S variable is the location where files are used at build time, and where output binaries will be placed. The SRC_URI variable is used to define the location of source files before build time.
FILESEXTRAPATHS_prepend := “${THISDIR}/src:”
SRC_URI = “file://helloworld.c”
S = “${WORKDIR}”
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} ${WORKDIR}/helloworld.c -o helloworld
}
do_install() {
# create the /usr/bin folder in the rootfs with default permissions
install -d ${D}${bindir}
# install the application into the /usr/bin folder with default permissions
install ${WORKDIR}/hello ${D}${bindir}
}
$ mkdir src && cd src
$ touch helloworld.c && vi helloworld.c
$ cd recipes-images
$ touch core-image-minimal.bbappend && vi core-image-minimal.bbappend
The IMAGE_INSTALL variable defines which recipes will be used to make up the rootfs of the final image. The helloworld recipe that was created will be appended to the primary list of packages.
Sourcing the oe-init-build-env script will setup a new build directory and place default configuration files in the build/conf directory.
The default layer configuration file must be edited to add the newly created meta-layer to the resources for this build.
The helloworld executable will now be included in the rootfs of the image.
There are many instances where you may wish to include a shared header file in the rootfs of an image. If a library is being written that will be utilized by more than one application it can easily be included alongside other standard Linux headers using the following steps.
$ vi helloworld.h
int helloworld(void)
{
printf(“Hello, World!\n”);
return 0;
}
FILESEXTRAPATHS_prepend := “${THISDIR}/src:”
SRC_URI = “file://helloworld.c \
helloworld.h”
S = “${WORKDIR}”
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} ${WORKDIR}/helloworld.c -o helloworld
}
do_install() {
# create the /usr/bin folder in the rootfs give it default permissions
install -d ${D}${bindir}
# add the /usr/include folder to the sysroot for this recipe, to be
# added to the final rootfs
install -d ${D}${includedir}
# install the application into the /usr/bin folder
install ${S}/hello ${D}${bindir}
# install the header file in /usr/include with default permissions
install ${S}hello.h ${D}${includedir}
}
The header file should now be included in the /usr/include directory with the rest of the Linux headers.
It is frequently helpful to include prebuilt libraries in the rootfs of an image. These prebuilt libraries take the form of .so files located in /usr/lib, they allow for other applications to easily link against them. The helloworld application that we have been using can be repurposed to be included as a library, using the process laid out in the Yocto Project Wiki.
FILESEXTRAPATHS_prepend := “${THISDIR}/src:”
SRC_URI = “file://helloworld.c”
S = “${WORKDIR}”
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -shared -fPIC -Wl,-soname,libhelloworld.so.${PV} \
${WORKDIR}/helloworld.c -o libhelloworld.so.${PV}
}
do_install() {
# add the /usr/lib folder to the sysroot for this recipe, to be
# added to the final rootfs
install -d ${D}${libdir}
# install the prebuilt library in /usr/lib with default permissions
oe_soinstall ${S}/libhelloworld.so.${PV} ${D}${libdir}
}
The recipe examples here should allow any user to include custom applications in multiple ways. These instructions will allow you to improve your Yocto recipes by giving you the tools to include custom applications and libraries easily.