Yocto Best Practises

Introduction

This tutorial will show some practises that have been found useful when setting up a build environment and maintaining itl

Version notes

This tutorial targets the following Yocto Project versions:

Yocto Project Tutorial applies Maintenance
nanbield (4.3) :test_works: :test_works: development
mickledore (4.2) :test_works: :test_works: current stable
langdale (4.1) :test_works: :test_works: stable
kirkstone (4.0) :test_works: :test_works: LTS
honister (3.4) :test_works: :test_fails: EOL
hardknott (3.3) :test_workaround: 1 :test_fails: EOL
gatesgarth (3.2) :test_workaround: 2 :test_fails: EOL
dunfell (3.1) :test_workaround: 3 :test_works: LTS
zeus (3.0) :test_fails: 4 :test_fails: EOL
warrior (2.7) :test_fails: 4 :test_fails: EOL
thud (2.6) :test_fails: 4 :test_fails: EOL
sumo (2.5) :test_fails: 4 :test_fails: EOL
rocko (2.4) :test_fails: 4 :test_fails: EOL

1. Support for the :-override syntax was added to hardknott in the 3.3.3 patch release.
2. Support for the :-override syntax was added to gatesgarth master only, no patch release.
3. Support for the :-override syntax was added to dunfell in the 3.1.11 patch release.
4. See the EOL versions appendix.

Preliminary notes

This post aims to collect best practices around setting up, architecting and maintaining a Yocto Project based build. They are by no means mandatory to follow or hard rules, but strategies that have proven successful in a number of real life projects.

Note: code/directory listings do not necessarily appear in alphabetical order or as listed by the shell, but arranged for best understanding.

Base: Work directory

Typically, a work directory for a build consists of multiple layers and at least one build directory. As a general rule, active nesting should be avoided.

A very simple example looks like that:

my-directory
./poky
./poky/meta
./poky/meta-poky
./poky/meta-yocto-bsp
./poky/…
./build

Effectively, it is the result of executing the bare minimum setup:

mkdir my-directory
cd my-directory
git clone git://git.yoctoproject.org/poky.git
source poky/oe-init-build-env

A more real life example therefore might look like this:

my-directory
./poky
./poky/meta
./poky/meta-poky
./poky/meta-yocto-bsp
./poky/…
./meta-openembedded/meta-oe
./meta-openembedded/meta-python
./meta-openembedded/…
./meta-raspberrypi
./meta-mender/meta-mender-core
./meta-mender/meta-mender-demo
./meta-mender/meta-mender-raspberrypi
./meta-mender/…
./build

Adding layers to the build should be done using the bitbake-layers tool:

bitbake-layers add-layer ${path to layer}

In the above example, while being in the build directory, meta-oe would be added like this:

bitbake-layer add-layer ../meta-openembedded/meta-oe

all following code snippets will expect a similar hierarchy, and unless noted otherwise, assume that build is the current working directory

Using bitbake-layers for such operations is recommended for some reasons:

  • the path is checked
  • dependencies as defined in the LAYERDEPENDS variable of a layer are verified

Avoid stacking up things in local.conf

When starting a project build it is very tempting to get going quickly, and making small incremental changes through the local.conf file.

Create a layer early (…and create a layer often)

You can create and add your myproject layer like this:

bitbake-layers create-layer ../meta-myproject
bitbake-layers add-layer ../meta-myproject

The layer should also be put under revision control early on, preferably by using git.

Build a custom image

As tempting as it is to throw IMAGE_INSTALL and CORE_IMAGE_EXTRA_INSTALL operations into local.conf, it makes revision control unnecessarily hard. Especially when trying to trim down a bloated demo image from a vendor, creating a custom image is definitely the better choice. You can start

out like this:

mkdir -p ../meta-myproject/recipes-myproject/images
cp ../poky/meta/recipes-core/images/core-image-base.bb ../meta-myproject/recipes-myproject/images/myproject-image.bb

Now you have a custom image that you can build with bitbake myproject-image, and start adding your application and packages.

Create a custom distribution

Images are “just” recipes, which means that they have the sometimes surprising property of not being able to affect global properties. The probably

most prominent example is the init manager choice:

INIT_MANAGER += "systemd"

will have the desired effect in local.conf, but not in myproject-image.bb.

Remember: as it is possible to build recipes directly without targetting an image, any information that is inside an image would not be available

when building the recipe directly, resulting in different build output depending on how the recipe build was initiated. To avoid this inconsistency, recipes can never affect other recipes - their infomation is local - only configuration information is global.

The correct place to put settings that have operating system wide effects is the distro configuration. For getting started, the easiest way is basing off poky.

mkdir -p ../meta-myproject/conf/distro
cat << EOF >> ../meta-myproject/conf/distro/mydistro.conf
require conf/distro/poky.conf
DISTROOVERRIDES = "poky"
EOF

To enable it, you can either prefix the call to bitbake:

DISTRO=mydistro bitbake myproject-image

or change the DISTRO setting in your local.conf, replacing poky:

DISTRO ?= "mydistro"

This gives you a custom distribution that behaves just like poky. You can start adding your modifications and settings in ../meta-myproject/conf/distro/mydistro.conf. As your project evolves, you will probably gradually change things more and more, so its a good idea to check back into the file after some time and see if the original require is still needed. Also, if you make nontrivial changes it is advisable to change the reported DISTRO_NAME.

Append, don’t modify

Layers that you do not own and maintain should be treated as immutable. Do not modify anything in there.

Why is that? When it comes to version control of your build (see below), you should rely on defined states of the layers upstream. Those defined states, usually in form of git hashes, enable reproducibility of your build. Any modification in an upstream layer therefore means that you either lose reproducility, or have to maintain your own fork. Neither is desirable.

The correct approach to change the behaviour of a layer outside of your control is to use an append. Those are denoted by the .bbappend file suffix.

This mechanism is commonly used in vanilla poky and meta-openembedded already. To see all the .bbappends that are currently active in your build, use the bitbake-layers tool:

bitbake-layers show-appends

The name of your bbappend has to match the original recipe, including the version. If you want it to be more flexible, then you can use % as a wildcard in the version part.

Thus you can change most parts of recipe behaviour, such as variable assignments, adding patches, or injecting additional operations adjacent to tasks, using the same syntax and mechanisms as in the recipe.

1 Like