Yocto Project Concepts
Explanations for Yocto Project concepts that go beyond the surface of "how-to" information and reference (or look-up) material. Concepts such as components, workflow, toolchains, cache.
Last update: 2022-05-07
Table of Content
Operator#
- Hard Assignment
=
-
Occur immediately as the statement is parsed.
VARIABLE = "value" VARIABLE = 'value with " in it'
- Soft (Default) Assignment
?=
-
Define a variable if it is undefined when the statement is parsed.
If the variable is defined, the soft assignment is lost.
If multiple soft assignments get parsed, the first one is used.
VARIABLE ?= "default value"
VARIABLE = "set value" VARIABLE ?= "default value" # this does nothing
- Weak (Default) Assignment
??=
-
Delay the assignment at the end of parsing process rather than immediately.
If multiple weak assignments get parsed, the last one is used.
VARIABLE ?= "value 1" # this wins VARIABLE ?= "value 2"
VARIABLE ??= "value 1" VARIABLE ??= "value 2" # this wins
VARIABLE ?= "value 1" # this wins VARIABLE ??= "value 2"
VARIABLE ??= "value 1" VARIABLE ?= "value 2" # this wins
VARIABLE ??= "value 1" VARIABLE = "value 3" # this always wins VARIABLE ?= "value 2"
- Variable expansion
${}
-
Refer to the value of a variable.
- The
=
operator does not immediately expand the variable reference. The expansion is deferred until the variable is used. - The
:=
immediately expand the variable reference when is parsed.
A = "${B} hello" # A = linux world hello B = "${C} world" # B = world C = "linux"
A := "${B} hello" # A = ${B} hello B := "${C} world" # B = ${C} world C = "linux"
A = "11" B = "${A}" # B = 22 when B is used A = "22" C = "${A}" # C = 22 when C is used
A = "11" B := "${A}" # B = 11 immediately A = "22" C = "${A}" # C = 22 when C is used
- The
- Appending
+=
,.=
,_append=
-
Append values with or without a space.
+=
automatically adds a space, but.=
and_append=
do not do that.A = "hello" A += "my" # A = hello my A .= "sunny" # A = hello mysunny A_append = "world" # A = hello mysunnyworld
- Prepending
=+
,=.
,_prepend=
-
Prepend values with or without a space.
=+
automatically adds a space, but=.
and_prepend=
do not do that.A = "hello" A =+ "my" # A = my hello A =. "sunny" # A = sunnymy hello A_prepend = "world" # A = worldsunnymy hello
- Removal
_remove=
-
Remove all occurrences of a value in a list.
A = "123 456 123 789 123" A_remove = "123" # A = " 456 789 ", note the spaces
- Overriding syntax
_append=
,_prepend=
,_remove=
-
Provide guaranteed operations, as compared to
+=
,=+
.1st parsedA += "world" # does not work, A is undefined
2nd parsedA = "hello" # A = hello when A is used
However, using overriding syntax give a good result:
1st parsedA_append = " world" # keep this action when A is used
2nd parsedA = "hello" # A = hello world when A is used
- Check the value
-
After all configurations are parsed:
bitbake <target/recipe> -e | grep ^VARIABLE=
Layer#
A layer is a logical collection of related recipes. Layer name should start with meta-
to follow Yocto’s naming convention. Each layer has a priority, which is used by bitbake to decide which layer takes precedence if there are recipe files with the same name in multiple layers. A higher numeric value represents a higher priority.
Despite most of the customization can be done with the
local.conf
configuration file, it is not possible to: * Store recipes for your own software projects * Create your own images * Consolidate patches/modifications to other people’s recipes * Add a new custom kernel * Add a new machine
Depending on the type of layer, add the content:
-
If the layer is adding support for a machine, add the machine configuration in
conf/machine/
-
If the layer is adding distro policy, add the distro configuration in
conf/distro/
-
If the layer introduces new recipes, put the recipes you need in
recipes-*
subdirectories of the layer directory. Recipes are divided into categories {»but hard to decide»}
-
Create new layer
There are two ways to create your own layer.
-
Manually
mkdir meta-my-layer cp meta-my-layer
Copy
poky/meta-skeleton/conf/layer.conf
to new layer:mkdir conf && cd conf cp ../../poky/meta-skeleton/conf/layer.conf .
-
Using script
bitbake-layers create-layer meta-my-layer
The tool automates layer creation by setting up a subdirectory with a
layer.conf
configuration file, a recipes-example subdirectory that contains anexample.bb
recipe, a licensing file, and a README. Default priority of the layer is6
.meta-my-layer/ ├── conf │ └── layer.conf ├── COPYING.MIT ├── README └── recipes-example └── example └── example_0.1.bb
To check layer compatibility, run
yocto-check-layer
:yocto-check-layer meta-my-layer
-
-
Layer configs
Note the
BBFILE_PRIORITY_
meta-my-layer/conf/layer.conf# We have a conf and classes directory, add to BBPATH BBPATH .= ":${LAYERDIR}" # We have recipes-* directories, add to BBFILES BBFILES += "${LAYERDIR}/recipes-*/*/*.bb ${LAYERDIR}/recipes-*/*/*.bbappend" BBFILE_COLLECTIONS += "my-layer" BBFILE_PATTERN_my-layer = "^${LAYERDIR}/" BBFILE_PRIORITY_my-layer = "6" # This should only be incremented on significant changes that will # cause compatibility issues with other layers LAYERVERSION_my-layer = "1" LAYERDEPENDS_my-layer = "core" LAYERSERIES_COMPAT_my-layer = "dunfell"
Image#
Image is a top level recipe. It inherits the image.bbclass
.
Create new Image#
You often need to create your own Image recipe in order to add new packages or functionality. There are 2 ways:
-
Create an image from scratch
The simplest way is to inherit the
core-image
bbclass, as it provides a set of image features that can be used very easily.-
Create an image directory
mkdir -p recipes-examples/images
-
Create the image recipe
nano recipes-examples/images/lwl-image.bb
lwl-image.bbSUMMARY = "A small boot image for LWL learners" LICENSE = "MIT" # start from core-image inherit core-image # core files for basic console boot IMAGE_INSTALL = "packagegroup-core-boot" # add our needed applications #IMAGE_INSTALL += "userprog"
-
-
Extend an existing recipe (preferable)
When an image mostly fits our needs, and we need to do minor adjustments on it, it is very convenient to reuse its code.
This makes code maintenance easier and highlights the functional differences.
nano recipes-examples/images/lwl-image-reuse.bb
lwl-image.bb# select base image require recipes-core/images/core-image-minimal.bb # append our needed packages, use overriding syntax #IMAGE_INSTALL_append = " userprog"
Package group#
A package group is a set of packages that can be included on any image.
Using a package group name in IMAGE_INSTALL
variable install all the packages defined by the package group into the root file system of your target image.
There are many package groups. There are present in subdirectories named packagegroups
. They are recipe files(.bb) and starts with packagegroup-
.
For example, packagegroup-core-boot
: Provides the minimum set of packages necessary to create a bootable image with console.
ls poky/meta/recipes-core/packagegroups/
nativesdk-packagegroup-sdk-host.bb packagegroup-core-standalone-sdk-target.bb
packagegroup-base.bb packagegroup-core-tools-debug.bb
packagegroup-core-boot.bb packagegroup-core-tools-profile.bb
packagegroup-core-buildessential.bb packagegroup-core-tools-testapps.bb
packagegroup-core-eclipse-debug.bb packagegroup-cross-canadian.bb
packagegroup-core-nfs.bb packagegroup-go-cross-canadian.bb
packagegroup-core-sdk.bb packagegroup-go-sdk-target.bb
packagegroup-core-ssh-dropbear.bb packagegroup-self-hosted.bb
packagegroup-core-ssh-openssh.bb
Image features#
Another method for customizing your image is to enable or disable high-level image features by using the IMAGE_FEATURES
and EXTRA_IMAGE_FEATURES
variables.
Image features is the map of features to package groups.
IMAGE_FEATURES
/EXTRA_IMAGE_FEATURES
is made to enable special features for your image, such as empty password for root, debug image, special packages, x11, splash, ssh-server.
Best practice is to:
- Use
IMAGE_FEATURES
from a recipe - Use
EXTRA_IMAGE_FEATURES
fromlocal.conf
For example:
FEATURE_PACKAGES_x11 = "packagegroup-core-x11"
FEATURE_PACKAGES_x11-base = "packagegroup-core-x11-base"
FEATURE_PACKAGES_x11-sato = "packagegroup-core-x11-sato"
FEATURE_PACKAGES_tools-debug = "packagegroup-core-tools-debug"
FEATURE_PACKAGES_eclipse-debug = "packagegroup-core-eclipse-debug"
FEATURE_PACKAGES_tools-profile = "packagegroup-core-tools-profile"
FEATURE_PACKAGES_tools-testapps = "packagegroup-core-tools-testapps"
FEATURE_PACKAGES_tools-sdk = "packagegroup-core-sdk packagegroup-core-standalone-sdk-target"
FEATURE_PACKAGES_nfs-server = "packagegroup-core-nfs-server"
FEATURE_PACKAGES_nfs-client = "packagegroup-core-nfs-client"
FEATURE_PACKAGES_ssh-server-dropbear = "packagegroup-core-ssh-dropbear"
FEATURE_PACKAGES_ssh-server-openssh = "packagegroup-core-ssh-openssh"
FEATURE_PACKAGES_hwcodecs = "${MACHINE_HWCODECS}"
IMAGE_FEATURES += "splash package-management x11-base x11-sato ssh-server-dropbear hwcodecs"
inherit core-image
EXTRA_IMAGE_FEATURES ?= "debug-tweaks"
Some Features
debug-tweaks
- Debug tweaks enables password-less login for the root user.
You must remove the
debug-tweaks
feature from production image. read-only-rootfs
- Read-only RootFS helps to reduce wear on flash memory, and to eliminate system file corruption.
splash
- Boot splash screen
tools-debug
- Installs debugging tools such as strace and gdb.
tools-sdk
- Installs a full SDK that runs on the device.
Image options#
Some other options also control the image content.
IMAGE_FSTYPES
-
Determines the root filesystem image type.
If more than one format is specified, one image per format will be generated.
Image formats instructions are delivered in Poky:
meta/classes/image_types.bbclass
If you have a particular layout on your storage (for example bootloader location on an SD card), you may want to create your own image type.
This is done through a class that inherits from
image_types
. It has to define a function namedIMAGE_CMD_<type>
.Example:
sdcard_image-rpi.bbclass
inmeta-raspberrypi
IMAGE_NAME
-
The name of the output image files minus the extension.
For example:
IMAGE_NAME = "${IMAGE_BASENAME}-${MACHINE}-${DATETIME}"
IMAGE_MANIFEST
- This file lists all the installed packages that make up the image.
IMAGE_LINGUAS
-
Specifies the list of locales to install into the image during the root filesystem construction process.
For example:
IMAGE_LINGUAS = "en-us"
Recipe#
Recipes are fundamental components in the Yocto Project environment. A Yocto/OpenEmbedded recipe is a text file with file extension .bb
.
Each software component built by the OpenEmbedded build system requires a recipe to define the component. A recipe contains information about single piece of software.
Information such as:
- Location from which to download the unaltered source
- Any patches to be applied to that source (if needed)
- Special configuration options to apply
- How to compile the source files and
- How to package the compiled output
Poky includes several classes that abstract the process for the most common development tools as projects based on Autotools, CMake, and QMake.
File Format: <base_name>_<version>.bb
Use lower-cased characters and do not include the reserved suffixes -native, -cross, -initial, or -dev.
List all recipes:
bitbake-layers show-recipes
Find a recipe in added layers:
bitbake-layers show-recipes <recipe>
Bitbake#
Yocto/OpenEmbedded’s build tool bitbake
parses a recipe and generates list of tasks that it can execute to perform the build steps:
do_fetch
: Fetches the source codedo_unpack
: Unpacks the source code into a working directorydo_patch
: Locates patch files and applies them to the source codedo_configure
: Configures the source by enabling and disabling any build-time and configuration options for the software being builtdo_compile
: Compiles the source in the compilation directorydo_install
: Copies files from the compilation directory to a holding areado_package
: Analyzes the content of the holding area and splits it into subsets based on available packages and filesdo_package_write_rpm
: Creates the actual RPM packages and places them in the Package Feed area
Generally, the only tasks that the user needs to specify in a recipe are
do_configure
,
do_compile
and
do_install
ones.
The remaining tasks are automatically defined by the YP build system
The above task list is in the correct dependency order. They are executed from top to bottom.
You can use the -c argument to execute the specific task of a recipe.
bitbake -c compile <recipe>
- Stage 1: Fetching Code (do_fetch)
-
Fetching is controlled mainly through the
SRC_URI
variable. Bitbake supports fetching source code from git, svn, https, ftp, etc.URI scheme syntax:
scheme://url;param1;param2
Example:
SRC_URI = "https://busybox.net/downloads/busybox-${PV}.tar.bz2"
- Stage 2: Unpacking (do_unpack)
-
All local files found in
SRC_URI
are copied into the recipe’s working directory, in$BUILDDIR/tmp/work/
.When extracting a tarball, BitBake expects to find the extracted files in a directory named
<application>-<version>
. This is controlled by theS
variable. - Stage 3: Patching Code (do_patch)
-
Sometimes it is necessary to patch code after it has been fetched.
Any files mentioned in
SRC_URI
whose names end in.patch
or.diff
or compressed versions of these suffixes (e.g.diff.gz
) are treated as patches.The
do_patch
task automatically applies these patches.The build system should be able to apply patches with the
-p1
option (i.e. one directory level in the path will be stripped off).If your patch needs to have more directory levels stripped off, specify the number of levels using the
striplevel
option in theSRC_URI
entry for the patch. - Stage 4: Configuration (do_configure)
-
Most software provides some means of setting build-time configuration options before compilation.
Typically, setting these options is accomplished by running a configured script with options, or by modifying a build configuration file.
-
Autotools: If your source files have a
configure.ac
file, then your software is built using Autotools. -
CMake: If your source files have a
CMakeLists.txt
file, then your software is built using CMake -
If your source files do not have a configure.ac or CMakeLists.txt file, you normally need to provide a
do_configure
task in your recipe unless there is nothing to configure.
-
- Stage 5: Compilation (do_compile)
-
do_compile
task happens after source is fetched, unpacked, and configured.No package output
Check the
do_compile
in Bitbake file to make sure the compiling commands are correct, including the value of expanded variables.Check the targets and dependencies in Makefile also.
- Stage 6: Installation (do_install)
-
After compilation completes, BitBake executes the
do_install
task.During do_install, the task copies the built files along with their hierarchy to locations that would mirror their locations on the target device.
install
keywordinstall
not only copies files but also changes its ownership and permissions and optionally removes debugging symbols from executables.It combines
cp
withchown
,chmod
andstrip
.Not installed package
When a required package is not installed,
do_rootfs
will fail with errors.The most happened error is Could not invoke dnf or No match for argument:
, which is caused by do_install
was not run, or bydo_compile
did not produce any output.Check the
do_compile
anddo_install
in Bitbake file to make sure it call tooe-runmake
if Makefile is used.Check the targets and dependencies in Makefile also.
- Stage 7: Packaging (do_package)
-
The
do_package
task splits the files produced by the recipe into logical components.Even software that produces a single binary might still have debug symbols, documentation, and other logical components that should be split out.
The
do_package
task ensures that files are split up and packaged correctly.
Bitbake Variables
S
: Contains the unpacked source files for a given recipe
D
: The destination directory (root directory of where the files are installed, before creating the image)
WORKDIR
: The location where the OpenEmbedded build system builds a recipe (i.e. does the work to create the package).
PN
: The name of the recipe used to build the package
PV
: The version of the recipe used to build the package
PR
: The revision of the recipe used to build the package.
Example 1: Add userprog
recipe
-
Create
userprog
recipe by adding its folder and bb file:recipes-example └── userprog ├── files │ ├── COPYING.MIT │ └── userprog.c └── userprog_0.1.bb
-
Add code to
userprog.c
:#include <stdio.h> int main() { printf("Hello World from "); printf("meta-my-layer/recipes-example/userprog \n"); return 0; }
-
Declare the recipe in
userprog_0.1.bb
:Run
md5hash files/COPYING.MIT
to get MD5 Checksumuserprog_0.1.bbSUMMARY = "Example userprog recipe which prints out a message" DESCRIPTION = "Example userprog in the meta-my-layer/recipe-example/userprog" LICENSE = "MIT" LIC_FILES_CHKSUM = "file://COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" # source file SRC_URI = "file://userprog.c" SRC_URI += "file://COPYING.MIT" # after fetching, set the source dir in build S = "${WORKDIR}" # call cross-compiler do_compile() { ${CC} userprog.c ${LDFLAGS} -o userprog } # create directoty and install binary with permission 0755 do_install() { install -d ${D}${bindir} install -m 0755 userprog ${D}${bindir} } # python do_before_build() { bb.plain("* USERPROG: before build *"); } addtask before_build before do_build python do_before_compile() { bb.plain("* USERPROG: before compile *"); } addtask before_compile before do_compile
-
Build the recipe
bitbake userprog
Recipe
sysroot
will be built if it is not ready yet. This contains needed headers and libraries for generating binaries that run on the target architecture. -
Add the recipe to rootfs
Use either:
orCORE_IMAGE_EXTRA_INSTALL_append = " userprog"
IMAGE_INSTALL_append = " userprog"
Example 2: Use Makefile in userprog2
recipe
This example use Makefile
to compile the source code.
oe-runmake
oe-runmake
will look for Makefile and can automatically call to primary target all
or clean
.
If Makefile has install
target, Bitbake have to call oe-runmake install
in do_install()
function.
Directory tree:
userprog2
├── files
│ ├── COPYING.MIT
│ ├── Makefile
│ └── userprog2.c
└── userprog2_0.1.bb
Source code of the program, which uses USE_SYSCALL
variable passed from make:
#include <stdio.h>
int main()
{
#ifdef USE_SYSCALL
write(1, "USE_SYSCALL\n", 12);
write(1, "Hello World from\n", 17);
write(1, "meta-my-layer/recipes-example/userprog2\n", 40);
#else
printf("Hello World from\n");
printf("meta-my-layer/recipes-example/userprog2\n");
#endif
return 0;
}
Makefile which declares all
, install
, and clean
.
# compiler flags:
# -g adds debugging information to the executable file
# -Wall turns on most, but not all, compiler warnings
CFLAGS = -g -Wall -DUSE_SYSCALL
# the name to use for both the target source file, and the output file:
TARGET = userprog2
all: $(TARGET)
$(TARGET): $(TARGET).c
${CC} $(CFLAGS) -o $(TARGET) $(TARGET).c $(LDFLAGS)
install:
install -d $(DESTDIR)
install -m 0755 $(TARGET) $(DESTDIR)
clean:
rm -f $(TARGET)
The bitbake file have to send DESTDIR
variable to make:
SUMMARY = "Example userprog2 recipe which prints out a message"
DESCRIPTION = "Example userprog2 in the meta-my-layer/recipe-example/userprog2"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
# source file
SRC_URI = "file://userprog2.c \
file://Makefile \
file://COPYING.MIT \
"
# after fetching, set the source dir in build
S = "${WORKDIR}"
do_install() {
oe_runmake install 'DESTDIR=${D}${bindir}'
}
Remote Recipe#
Yocto supports the ability to pull code from online git repositories as part of the build process.
The SRC_URI
should point to a repository, with extra paramters if needed:
-
branch
: Themaster
branch by default, or set a branch inbranch
parameter -
protocol
: Set the protocol tohttps
,file
, orssh
. -
tag
: Select a tag revision on a branch
The SRCREV
is used to set the revision, it can be ${AUTOREV}
to pull the latest source revision, or the SHA1 Hash of a revision.
If the tag
parameter is used, SRCREV
is not needed anymore.
SRC_URI="git://git@github.com/group_name/repo_name.git;protocol=ssh"
SRCREV = "${AUTOREV}"
S = ${WORKDIR}/git
Working with Git source code
The git source code is downloaded into ${WORKDIR}/git
directory, in which, you can easily update your code and commit back to the upstream.
However, there are somethings to mind:
-
Run
bitbake -c clean
will wipe the git directory, you will loose editted code -
Run
bitbake -c compile -f
to force re-compile the source code with changes. -
Use patch files if you can not commit to the upstream
Example 3: Use git and apply patch
userprog3
├── files
│ └── 001-increase-version.patch
└── userprog3_0.1.bb
diff --git a/userprog3.c b/userprog3.c
index 08aee7b..d593622 100755
--- a/userprog3.c
+++ b/userprog3.c
@@ -2,13 +2,14 @@
int main()
{
+ printf("Patch applied!!!\n");
#ifdef USE_SYSCALL
write(1, "USE_SYSCALL\n", 12);
write(1, "Hello World from\n", 17);
- write(1, "meta-my-layer/recipes-example/userprog2\n", 40);
+ write(1, "meta-my-layer/recipes-example/userprog3\n", 40);
#else
printf("Hello World from\n");
- printf("meta-my-layer/recipes-example/userprog2\n");
+ printf("meta-my-layer/recipes-example/userprog3\n");
#endif
return 0;
}
SUMMARY = "Example userprog3 recipe which prints out a message"
DESCRIPTION = "Example userprog3 in the meta-my-layer/recipe-example/userprog3"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
# source file
SRC_URI = "git:///${HOME}/local-userprog-repo;protocol=file \
file://001-increase-version.patch \
"
SRCREV = "${AUTOREV}"
# after fetching, set the source dir in build
S = "${WORKDIR}/git"
do_install() {
oe_runmake install 'DESTDIR=${D}${bindir}'
}
Package files#
The do_package
task splits the files produced by the recipe during do_install
into logical components which are normal binary, debug binary, documents_, etc. These different files will be used in different image types.
Variables controls splitting:
PACKAGES
-
List all the packages to be produced.
The default value:
${PN}
${PN}-dbg
${PN}-staticdev
${PN}-dev
${PN}-doc
${PN}-locale
${PACKAGE_BEFORE_PN}
FILES
-
Specify which files to include in each package by using an override to specify the package.
To use the FILES variable, provide a package name override that identifies the resulting package.
E.g.:
FILES_${PN}
specifies the files to go into the main package.
FILES_${PN} += "${bindir}/mydir1 ${bindir}/mydir2/myfile"
use
${sysconfdir}
rather than/etc
, or${bindir}
rather than/usr/bin