This tutorial goes through the process, step-by-step, of adding your own software into a Linux distribution generated by Yocto. It's initial goal was to document my own learning process, but, perhaps it can also be useful for other beginners in Yocto. This tutorial is not meant to be a complete Yocto reference. The idea is to be that initial little-push, so you can start figuring out the Yocto workflow.
We use a docker container with Yocto and VNC installed. Check out the container manual to see it's features and how to install it. The first step once the docker image is install is to start its VNC. All the Linux image creation will be done using VNC.
This layer depends on:
-
URI: git://git.yoctoproject.org/poky
- branch: master
- revision: HEAD
-
URI: git://git.openembedded.org/meta-openembedded
- layers: meta-oe
- branch: master
- revision: HEAD
In this tutorial we are assuming that the target board is a Raspbery Pi3, but it can easily be adapted for the other boards of the family.
Assuming you are already in the VNC, for actual deployment on a RPI3 processor, we need to change the MACHINE
variable in ~/rpi/build/conf/local.conf
to raspberrypi3
or raspberrypi3-64
.
In this tutorial we are assuming the former. If you have a different board, this is the place to specify it.
# This sets the default machine to be qemux86-64 if no other machine is selected:
MACHINE ??= "raspberrypi3"
Next, let's build the main parts of the Linux image: kernel, rootfs, etc. Later we build our custom recipes on top of this build. This step takes a long time ...
$ cd ~/rpi
$ source /opt/yocto/dunfell/src/poky/oe-init-build-env
$ cd ~/rpi/build
$ bitbake core-image-minimal -c populate_sdk
$ bitbake core-image-minimal
I am not sure if it is mandatory to include SDK (i.e. populate_sdk
) in the image. This needs some additional testing in the future.
In addition, there is other default Yocto images besides core-image-minimal
. Check the reference images here.
Otherwise, if you want to just implement these examples and skip the step-by-step process, then run:
$ cd ~/rpi
$ source /opt/yocto/dunfell/src/poky/oe-init-build-env
$ cd ~/rpi/build
$ git clone -b dunfell https://github.com/amamory-embedded/learning-yocto.git meta-learning
$ bitbake-layers add-layer meta-learning
$ bitbake core-image-minimal -c populate_sdk
$ bitbake core-image-minimal
The last command bitbake core-image-minimal
will create the packages and the image.
Although this tutorial is independent of board, we are using RPi3 due to its availability, pleanty of documentation, and easy access. If you want to have a more complete RPI3 configuration than the one provided by the standard core-image-minimal
image, please check out the meta-myrpi
. Note that this step is optional, but without it you will end up with a very simple RPi3 configuration, with basically only keyboard and HDMI.
Everywhere you read about Yocto recommends that you create your own layer to deploy your software in the Linux image. So this section goes through this process of creating the layer where your recipes will be added.
$ bitbake-layers show-layers
$ bitbake-layers create-layer meta-learning
$ bitbake-layers add-layer meta-learning
$ bitbake-layers show-layers
NOTE: Starting bitbake server...
layer path priority
==========================================================================
meta /opt/yocto/dunfell/src/poky/meta 5
meta-poky /opt/yocto/dunfell/src/poky/meta-poky 5
meta-yocto-bsp /opt/yocto/dunfell/src/poky/meta-yocto-bsp 5
meta-learning /home/build/rpi/build/meta-learning 7
meta-raspberrypi /opt/yocto/dunfell/src/meta-raspberrypi 9
meta-oe /opt/yocto/dunfell/src/meta-openembedded/meta-oe 6
meta-networking /opt/yocto/dunfell/src/meta-openembedded/meta-networking 5
meta-python /opt/yocto/dunfell/src/meta-openembedded/meta-python
$ tree meta-learning/
meta-learning/
├── conf
│  └── layer.conf
├── COPYING.MIT
├── README
└── recipes-example
└── example
└── example_0.1.bb
Next, we start configuring the recipe as described below.
$ ~/rpi/build
$ cat meta-learning/recipes-example/example/example_0.1.bb
SUMMARY = "bitbake-layers recipe"
DESCRIPTION = "Recipe created by bitbake-layers"
LICENSE = "MIT"
do_compile() {
echo "Example recipe created by bitbake-layers" >> ${WORKDIR}/example
}
do_install() {
install -d ${D}${datadir}
install -m 0644 ${WORKDIR}/example ${D}${datadir}
}
The rules do_compile
and do_install
are, in practice, only copying a file into the image. Next, the recipe is built.
$ cd ~/rpi/build
$ bitbake example
Parsing recipes: 100% |#################################################################################################################| Time: 0:00:20
Parsing of 2120 .bb files complete (0 cached, 2120 parsed). 3212 targets, 134 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
Build Configuration:
BB_VERSION = "1.46.0"
BUILD_SYS = "x86_64-linux"
NATIVELSBSTRING = "universal"
TARGET_SYS = "arm-poky-linux-gnueabi"
MACHINE = "raspberrypi3"
DISTRO = "poky"
DISTRO_VERSION = "3.1.12"
TUNE_FEATURES = "arm vfp cortexa7 neon vfpv4 thumb callconvention-hard"
TARGET_FPU = "hard"
meta
meta-poky
meta-yocto-bsp = "dunfell:cf5a00721f721d5077c73d1f4e812e5c79833fba"
meta-learning = "<unknown>:<unknown>"
meta-raspberrypi = "dunfell:934064a01903b2ba9a82be93b3f0efdb4543a0e8"
meta-oe
meta-networking
meta-python = "dunfell:69f94af4d91215e7d4e225bab54bf3bcfee42f1c"
Initialising tasks: 100% |##############################################################################################################| Time: 0:00:00
Sstate summary: Wanted 7 Found 0 Missed 7 Current 134 (0% match, 95% complete)
NOTE: Executing Tasks
NOTE: Tasks Summary: Attempted 570 tasks of which 554 didn't need to be rerun and all succeeded.
Run the following command to find out the recipe workdir:
$ bitbake -e example | grep ^WORKDIR=
WORKDIR="/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/example/0.1-r0"
$ find /mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/example/0.1-r0 -name example
...
/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/example/0.1-r0/image/usr/share/example
$ cat /mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/example/0.1-r0/image/usr/share/example
Example recipe created by bitbake-layers
The file got deployed in the /usr/share
directory.
This link has more information about making a recipe that copies a file into the image.
As mentioned before, the previous recipe only copies a file into the Linux image. No code is actually compiled. The next step is to create a recipe that compiles a code. First, let's create a proper directory structure to save the new recipe:
$ cd ~/rpi/build
$ mkdir meta-learning/recipes-example/hello
$ cd meta-learning/recipes-example/hello
$ nano hello.bb
SUMMARY = "Simple helloworld application"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://helloworld.c"
TARGET_CC_ARCH += "${LDFLAGS}"
S = "${WORKDIR}"
do_compile() {
${CC} helloworld.c -o helloworld
}
do_install() {
install -d ${D}${bindir}
install -m 0755 helloworld ${D}${bindir}
}
Check this other link to learn more about how to write do_install
.
This is the place to store the source files:
$ mkdir files
$ cd files
$ nano helloworld.c
#include <stdio.h>
int main()
{
printf("Hello World");
return 0;
}
The resulting directory tree should be like this one:
:~/rpi/build/meta-learning/recipes-example/hello$ tree
.
├── files
│  └── helloworld.c
└── hello.bb
This recipe created is an example of a recipe where the code is locally stored within the recipe itself. This is not a usual configuration and is recommended only for testing purposes of small applications. Next, return to the build dir and compile the new recipe:
$ cd ~/rpi/build
$ bitbake hello
$ bitbake -e hello | grep ^WORKDIR=
WORKDIR="/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hello/1.0-r0"
$ cd /mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hello/1.0-r0
$ ll hello*
-rwxr-xr-x 1 build build 11556 Dec 17 20:08 helloworld*
-rw-r--r-- 1 build build 80 Dec 17 20:03 helloworld.c
To install this last application into a newly built image, we need to add the following lines into meta-learning/conf/local.conf
:
# add here the name of the recipes to be included into the image
IMAGE_INSTALL_append = " example"
IMAGE_INSTALL_append = " hello"
...
Pay attention to the initial space before hello. This space is required.
The following recipe shows how to write a recipe for a project with Make. First, let's create a proper dir structure to save the new recipe:
$ cd ~/rpi/build
$ mkdir meta-learning/recipes-example/hellomake
$ cd meta-learning/recipes-example/hellomake
$ nano hellomake.bb
SUMMARY = "Simple helloworld application with Make"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://helloworld.c"
TARGET_CC_ARCH += "${LDFLAGS}"
S = "${WORKDIR}"
do_compile() {
${CC} helloworld.c -o helloworld
}
do_install() {
install -d ${D}${bindir}
install -m 0755 helloworld ${D}${bindir}
}
This is the place to store the source file and the Makefile:
$ mkdir files
$ cd files
$ nano helloworld.c
#include <stdio.h>
int main()
{
printf("Hello Make");
return 0;
}
$ nano Makefile
....
The next recipe shows how to write a recipe for a project with CMake. More info here. First, let's create a proper dir structure to save the new recipe:
$ cd ~/rpi/build
$ mkdir meta-learning/recipes-example/hellocmake
$ cd meta-learning/recipes-example/hellocmake
$ nano hellocmake.bb
SUMMARY = "Simple Hello World Cmake application"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "\
file://CMakeLists.txt \
file://hellocmake.cpp \
"
S = "${WORKDIR}"
inherit cmake
EXTRA_OECMAKE = ""
This is the place to store the source file and the CMakeLists.txt:
$ mkdir files
$ cd files
$ nano hellocmake.c
#include <stdio.h>
int main()
{
printf("Hello CMake");
return 0;
}
$ nano CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project (hellocmake)
add_executable(hellocmake hellocmake.c)
install(TARGETS hellocmake RUNTIME DESTINATION bin)
Finally, let's build the recipe:
$ cd ~/rpi/build
$ bitbake hellocmake
$ bitbake -e hellocmake | grep ^WORKDIR=
WORKDIR="/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellocmake/1.0-r0"
$ cd /mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellocmake/1.0-r0
ll build/hello*
-rwxr-xr-x 1 build build 14024 Dec 17 20:45 build/hellocmake*
This example shows how to build a library (shared or static), using cmake. Yocto requires a specific format for libraries, i.e., naming convention and also requirements from the building system, as seen next.
References:
First, create the directories as the previous examples for the recipe libhello
. The recipe file has nothing especially related to a library.
$ nano hellolib.bb
SUMMARY = "Simple Hello Library with Cmake application"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "\
file://CMakeLists.txt \
file://hellolib.c \
file://hello.h \
"
S = "${WORKDIR}"
inherit cmake
EXTRA_OECMAKE = ""
The super fancy library:
$ nano files/hellolib.c
char * hello()
{
return "My Lib";
}
$ nano files/hello.h
#ifndef _HELLOLIB_H
#define _HELLOLIB_H
char * hello();
#endif
And the cmake for building the library with Yocto. This cmake file is considerably more complex than the cmake for an application. This is because Yocto requires a specific format for libraries, i.e., name must be lib*. It also requires a library version and an install rule for the library and the headers (if the headers are available).
$ nano files/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
# the project version is required
project (hello VERSION 1.0 DESCRIPTION "My Hello Library")
# chooseee the library format: static or dynamic by
# replacing SHARED by STATIC to change the library type
add_library(${CMAKE_PROJECT_NAME} SHARED libhello.c)
#add_library(${CMAKE_PROJECT_NAME} STATIC libhello.c)
# VERSION and SOVERSION are required
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
PUBLIC_HEADER hello.h)
# replace LIBRARY by ARCHIVE depending on the library format
install(TARGETS ${CMAKE_PROJECT_NAME}
DESTINATION /usr
LIBRARY DESTINATION /usr/lib
#ARCHIVE DESTINATION /usr/lib
PUBLIC_HEADER DESTINATION /usr/include
)
# any addition file that needs to be installed
#install(FILES <filename> DESTINATION <dir>)
Note in the cmkae file that we are forcing the install to use the default paths for libraries and includes. This will make it easier to find them later in the next recipe.
Check it out for more cmake library configuration options.
This example creates an application that uses the library libhello
defined in the previous section. So, we need to define a dependency among these two recipes.
This example is also built with cmake.
First, create the directories as in the previous examples.
Name the bitbake file as hellodep.bb. Note in the end of the files includes the dependency clauses among the recipes. DEPEDENCY
is for build-time dependencies, while RDEPENDECY
is for runtime.
SUMMARY = "Simple Hello World Cmake application that requires a library"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "\
file://CMakeLists.txt \
file://hellodep.c \
"
S = "${WORKDIR}"
inherit cmake
EXTRA_OECMAKE = ""
DEPENDS += "libhello"
RDEPENDS_${PN} += "libhello"
Name the source code as hellodep.c
#include <stdio.h>
#include <hello.h>
int main()
{
printf("Hello %s\n",hello());
return 0;
}
And finally, the CMakeLists.txt file. Note that this cmake file is very simplified since libhello
is deployed in the
default /usr/lib
and /usr/include
directories. Otherwise, it's recommended to create a cmake module for the library
so that the user applications can easily find the library with the cmake command find_package
.
See the comments in the CMakeLists.txt for further pointers.
cmake_minimum_required(VERSION 3.9)
project (hellodep)
# the source code to tha app
add_executable(${PROJECT_NAME} hellodep.c)
# libraries to be linked
target_link_libraries(${PROJECT_NAME} hello)
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
)
Now, let's build the new recipe. Sometimes, if you find an error while building a recipe, it's just a matter of cleaning it before building it again.
cd ~/rpi/build
bitbake hellodep -c cleanall
bitbake hellodep
Let's search the generated files for this new recipe:
$ find /mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellodep/1.0-r0/recipe-sysroot/ -name "*hello*"
/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellodep/1.0-r0/recipe-sysroot/usr/include/hello.h
/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellodep/1.0-r0/recipe-sysroot/usr/lib/libhello.so
/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellodep/1.0-r0/recipe-sysroot/usr/lib/libhello.so.1.0
/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellodep/1.0-r0/recipe-sysroot/usr/lib/libhello.so.1
/mnt/yocto/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hellodep/1.0-r0/recipe-sysroot/sysroot-providers/libhello
We see the library, it's header file, both required to build hellodep
.
Next, make sure that the meta-learnning layer.conf file has the following lines to include the recipes to the image:
IMAGE_INSTALL_append = " libhello"
IMAGE_INSTALL_append = " hellodep"
Then build the Linux image again to include the new recipes into the image.
$ bitbake core-image-minimal
$ find /mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/ -name *hello*
...
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/lib/libhello.so.1.0
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/lib/libhello.so.1
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/bin/hellodep
We can see that both the libhello
shared library and the hellodep
application were deployed into the image. Success!
As mentioned before, it's not recommended for actual deployment to combine the Yocto recipes and the source code. So, this new recipe shows how to separate these parts by linking the source code repository into the recipe.
Follow the previous steps to create a new recipe called hellogit
.
The directory files
is not required in this configuration since it does not include source code.
This is the bitbake file called hellogit.bb
. This recipe uses autoconf and make.
Note that the variables SRCREV
and SRC_URI
define, respectively, the commit hash and the git repository URL.
DESCRIPTION = "Example Hello World application for Yocto build Using git and Autoconf."
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=0ec5801450d6b777d973eb956c021332"
SRCREV = "22f4bf448930e5d92195c36a10bdf3662c577699"
SRC_URI = "git://github.com/amamory-embedded/Hello-World-Autoconf.git"
S = "${WORKDIR}/git"
inherit autotools
PARALLEL_MAKE = ""
Now, let's test the recipe.
$ cd ~/rpi/build
$ bitbake hellogit
In the same directory of hellogit.bb
, let's create another recipe called hellogitcmake.bb
, which uses cmake instead of autoconf.
DESCRIPTION = "Example Hello World application for Yocto build Using git and Cmake."
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=3d7054b26bdd0f2f5fc6b2e53f28783d"
SRCREV = "47f73c318bb726bb2a5cf8e4d58204ba5fe3d207"
SRC_URI = "git://github.com/amamory-embedded/Hello-World-Cmake.git/;branch=main"
S = "${WORKDIR}/git"
inherit cmake
EXTRA_OECMAKE = ""
Note that this repository names the master branch as main
, instead of master
. This way, we have to specify the branch name.
Other than that, we just have to update the hashes (the license and the repository hashes) and inherit cmake.
This example (originally from here) is located in recipes-kernel/hello-mod
. It shows how to add a custom kernel module, in this case using make and git.
Once all recipes we want to include in the image were tested separately, the next step is to actually incorporate them into the image.
For this, edit meta-learning/conf/local.conf
to include the following lines:
# add here the name of the recipes to be included into the image
IMAGE_INSTALL_append = " example"
IMAGE_INSTALL_append = " hello"
#IMAGE_INSTALL_append = " hellomake"
IMAGE_INSTALL_append = " hellocmake"
IMAGE_INSTALL_append = " libhello"
IMAGE_INSTALL_append = " hellodep"
IMAGE_INSTALL_append = " hellogit"
IMAGE_INSTALL_append = " hellogitcmake"
# my kernel recipes
MACHINE_EXTRA_RDEPENDS += "kernel-module-hello"
Next, time for building the recipes together.
$ cd ~/rpi/build
$ bitbake core-image-minimal
$ find /mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/ -name *hello*
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/lib/libhello.so.1.0
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/lib/libhello.so.1
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/bin/hellodep
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/bin/hellocmake
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/bin/helloworld
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/bin/hellogitcmake
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/bin/hellogit
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/lib/modules/5.4.72-v7/extra/hello.ko
$ find /mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/ -name example
/mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/rootfs/usr/share/example
We can see all custom applications and libraries were deployed. We can also check the generated packages for all the custom recipes:
$ ll /mnt/yocto/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-minimal/1.0-r0/oe-rootfs-repo/cortexa7t2hf-neon-vfpv4/ | grep hello
-rw-r--r-- 3 build build 2144 Dec 17 20:08 hello_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 2380 Dec 17 20:45 hellocmake_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 4660 Dec 17 20:45 hellocmake-dbg_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 864 Dec 17 20:45 hellocmake-dev_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 976 Dec 17 20:45 hellocmake-src_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 3632 Dec 17 20:08 hello-dbg_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 2208 Dec 20 10:50 hellodep_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 4712 Dec 20 10:50 hellodep-dbg_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 892 Dec 20 10:50 hellodep-dev_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 1000 Dec 20 10:50 hellodep-src_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 856 Dec 17 20:08 hello-dev_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 2196 Dec 18 19:33 hellogit_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 2192 Dec 20 15:32 hellogitcmake_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 4684 Dec 20 15:32 hellogitcmake-dbg_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 892 Dec 20 15:32 hellogitcmake-dev_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 1012 Dec 20 15:32 hellogitcmake-src_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 3672 Dec 18 19:33 hellogit-dbg_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 1084 Dec 18 19:33 hellogit-dev_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 1900 Dec 20 15:34 libhello1_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 2452 Dec 20 15:34 libhello-dbg_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 1012 Dec 20 15:34 libhello-dev_1.0-r0_armhf.deb
-rw-r--r-- 3 build build 948 Dec 20 15:34 libhello-src_1.0-r0_armhf.deb
For emulating the RPI3 processor with QEMU we need to change the MACHINE
variable in ~/rpi/build/conf/local.conf
.
Comment the current assignment to this variable and add the following lines:
# This sets the default machine to be qemux86-64 if no other machine is selected:
MACHINE ??= "qemux86-64"
Rerun the full image build:
$ bitbake core-image-minimal
This process will take a while ... go for a cup of coffee or Check out more information on these links:
- https://tutorialadda.com/yocto/quick-start-your-first-yocto-project-build/
- https://blog.mbedded.ninja/programming/embedded-linux/yocto-project/adding-a-custom-app-to-a-yocto-build/
After finishing the image built process, run:
runqemu qemux86-64 slirp nographic
The boot process will start. When QEMU prompt appears, run:
root@qemux86-64:~# find /usr -name *hello*
/usr/lib/libhello.so.1.0
/usr/lib/libhello.so.1
/usr/bin/hellodep
/usr/bin/hellogitcmake
/usr/bin/hellogit
/usr/bin/hellocmake
/usr/bin/helloworld
We can check all applications generated by the custom recipes. Run poweroff
when finishing with qemu.
After building the image for qemu, we are going to see two different images in the following directory:
~/rpi/build$ ls /mnt/yocto/tmp/deploy/images/
qemux86-64 raspberrypi3
For actual deployment on a RPI3 processor, we need to change the MACHINE
variable in ~/rpi/build/conf/local.conf
back to its original, raspberrypi3
or raspberrypi3-64
.
For example:
# This sets the default machine to be qemux86-64 if no other machine is selected:
MACHINE ??= "raspberrypi3"
It's also recommended to change the output format of the image by adding the following line of the same file:
IMAGE_FSTYPES ?= "tar.bz2 ext3 rpi-sdimg"
After building the image again (this time, it's quick), you will find the following file:
~/rpi/build$ find /mnt/yocto/tmp/deploy/images/raspberrypi3/ -name *.rpi-sdimg
/mnt/yocto/tmp/deploy/images/raspberrypi3/core-image-minimal-raspberrypi3-20211221153332.rootfs.rpi-sdimg
/mnt/yocto/tmp/deploy/images/raspberrypi3/core-image-minimal-raspberrypi3.rpi-sdimg
Remember that the /mnt/yocto/tmp
is shared between the docker image and the host, so it's easy to burn the image file into an SD card.
Return to the host computer and run df
to find out the SD card device (assuming it is /dev/sdb
) and run:
$ sudo dd if=/<host mounting point>/deploy/images/raspberrypi3/core-image-minimal-raspberrypi3.rpi-sdimg of=/dev/sdb bs=4M
Check out more information on these links:
- https://george-calin.medium.com/how-to-prepare-a-helloworld-c-recipe-with-yocto-project-1f74c296a777
- https://tutorialadda.com/yocto/create-your-own-linux-image-for-the-raspberry-pi-board-using-yocto-project
- Yocto Project Mega-Manual (v3.1 dunfell);
- Books about Yocto;
- Ready, Set, Yocto!;
- https://git.yoctoproject.org/poky/tree/meta-skeleton;
- More Yocto templates.
- add kernel module:
- add a linux service;
- make a cmake module for
libhello
; - how to deploy
libhello-dev
into the image ? this would include the library headers.
Did you find a bug in this tutorial ? Do you have some extensions or updates to add ? Please send me a Pull Request.