How to create custom images using Yocto Project

Introduction

The Yocto Projectยฎ (YP) is an open source collaboration project that helps developers create custom Linux-based systems regardless of the hardware architecture.

The project provides a flexible set of tools and a space where embedded developers worldwide can share technologies, software stacks, configurations, and best practices that can be used to create tailored Linux images for embedded and IOT devices, or anywhere a customized Linux OS is needed.

This is a high-level tutorial and the intention is not to cover the Yocto Project in depth. If you are interested in detailed information, we recommend that you read the Yocto Project Mega-Manual.

This tutorial will provide overview of what target images are within the Yocto project. We will try to cover some common use-cases and best practices by looking at the available example images and we will also go trough how to create custom images.

yocto-project-transp

Prerequisites

To follow this tutorial, you will need:

Images

The end goal for anyone using the Yocto Project should be to create a Linux distributions that is customized to match your product(s) requirements. The Yocto Project provides Poky, which is a reference embedded Linux distribution and a starting point to kick start your journey and should not be considered something that is complete.

Images are a central concept within the Yocto Project and essential to the definition of a Linux distribution. By creating a custom image you would have taken the first step to create a custom Linux distribution that is modeled for your product(s) requirements.

There are plenty reference images that one can inspect and derive your work from. To retrieve a list of available image recipes provided by poky we can run:

find ../sources/poky/meta -name *-image-*.bb

These images are considered reference images, and the intention is not modify them and instead derive custom images from them. The most common approach is to derive a custom image from the core-image-base.bb, and this would have been a bottom-up approach as the core-image-base.bb only defines the essentials to get a hardware device to boot with.

Scope

For the sake of this tutorial lets set a couple of requirements that we want to accomplish on our target device.

We want to create two custom images:

  • one for development (unsecure!)
  • one for production

On the production image we would like to have the following packages installed:

  • inotify-tools

inotify-tools is a C library and a set of command-line programs for Linux providing a simple interface to inotify. These programs can be used to monitor and act upon filesystem events

Additionally the development image should have the following features/packages:

  • SSH server running on the device
  • gdb, gdbserver and strace installed on the device
  • evtest, i2c-tools, ethtool, fbset, memtester
    • these are all applications useful during testing debugging various parts of the system
  • our development image will only have a root user and no password required for login (unsecure!)

Building images

When using the Yocto Project, you will spend most of your time building images. The easiest way of starting a build is running,

bitbake core-image-base

NOTE! If this is the first time you are building an image in your Yocto Project environment, this can take several hours to complete depending on the resources available on your machine.

Once the image build has completed you will see a message similar to below (number of tasks might differ),

NOTE: Tasks Summary: Attempted 4923 tasks of which 5 didn't need to be rerun and all succeeded.

We should now have build artifacts available in tmp/deploy/images/*/, and we can check by running:

ls -alh tmp/deploy/images/*/

Depending on which device you are targeting in your Yocto Project environment, the content of this directory can vary a lot but usually this contains:

  • Bootloader binaries (u-boot.bin etcโ€ฆ)
  • Linux kernel binaries (kernel image and dtb)
  • Root filesystem images (core-image-base-*.ext4)
  • Disk images for device provisionsing (*.sdcard, *.img, *.sdimg)

In this tutorial we are mostly interested in the root filesystem images, so lets take a closer look at this. Earlier we ran the command bitbake core-image-base to build our image, but right now it is unclear what that image actually included.

We can inspect what packages where installed in the final rootfs image by running:

cat tmp/deploy/images/*/core-image-base-*.manifest

Above command will list all the packages and their respective versions that where installed in our root filesystem image (core-image-base-*.ext4). What is installed in the image is typically defined in the target image recipe. In our case it is core-image-base.bb and we can inspect the image recipe by running the following:

cat ../sources/poky/meta/recipes-core/images/core-image-base.bb

It might come as a surprise to new users that the actual content of core-image-base.bb is quite sparse:

SUMMARY = "A console-only image that fully supports the target device \
hardware."

IMAGE_FEATURES += "splash"

LICENSE = "MIT"

inherit core-image

They key takeaway from above output is the following line:

inherit core-image

Which tells us that the definition of what actually gets installed is defined in the core-image.bbclass. The core-image.bblcass is common code for generating core reference images, and majority of image recipes will be derived from this bbclass.

I would recommend you to inspect the content of core-image.bbclass:

cat ../sources/poky/meta/classes/core-image.bbclass

The content of core-image.bbclass introduces some new concepts and I will try to covered some of them here,

IMAGE_FEATURES

The core-image.bbclass implements the mapping of strings in the global variable IMAGE_FEATURES to predefined packagegroups.

What IMAGE_FEATURES allows us to do is to change the content of the core-image-base.bb image without editing the file, and we can simply add pre-defined features to the global variable IMAGE_FEATURES. This also allows us to create definitions of IMAGE_FEATURES that will be applicable to all images that are derived from the core-image.bbclass.

The following example will make sure that an SSH server is installed and it will use the dropbear application to satisfy this (alternative is ssh-server-openssh):

echo 'IMAGE_FEATURES += "ssh-server-dropbear"' >> conf/local.conf

List of available IMAGE_FEATURES and a small description can be found in the Yocto Mega Manual - Image features section.

packagegroups

The name is self-explanatory and it is very common to group packages that together serve a common purpose in to something called packagegroup. One example would be the packagegroup-core-boot which is used in core-image.bbclass. The description of the packagegroup-core-boot is:

The minimal set of packages required to boot the system

Which means that by including packagegroup-core-boot in your image you will get the essentials installed to boot the system. You can inspect the definition of packagegroup-core-boot.bb:

cat ../sources/poky/meta/recipes-core/packagegroups/packagegroup-core-boot.bb

The packagegroup-core-boot contains some advanced logic that I will not try to cover in this tutorial and it is instead left as an exerciser to the reader to explore this further.

IMAGE_INSTALL

Image recipes set IMAGE_INSTALL to specify the packages to install into an image through image.bbclass. Additionally, โ€œhelperโ€ classes such as the core-image.bbclass class exist that can take lists used with IMAGE_FEATURES and turn them into auto-generated entries in IMAGE_INSTALL in addition to its default contents.

Working with custom images

Create a custom layer

To be able to proceed with this tutorial we first must create a custom layer that will contain any custom modifications or additions. Please note that the tutorials on this site will re-use this layer and if you have already created this structure by following another tutorial you can skip this step.

Create a new layer called meta-stargazer using the bitbake-layers helper application:

bitbake-layers create-layer ../sources/meta-stargazer

This will create a basic structure in meta-stargazer directory:

../sources/meta-stargazer/
โ”œโ”€โ”€ conf
โ”‚   โ””โ”€โ”€ layer.conf
โ”œโ”€โ”€ COPYING.MIT
โ”œโ”€โ”€ README
โ””โ”€โ”€ recipes-example
    โ””โ”€โ”€ example
        โ””โ”€โ”€ example_0.1.bb

3 directories, 4 files

Include the layer in our Yocto Project environment:

bitbake-layers add-layer ../sources/meta-stargazer

Creating custom images

Create location where we will put our custom image recipe:

mkdir -p ../sources/meta-stargazer/recipes-core/images/

There is no hard requirement on the actual location of image recipe but it is a fairly common practice to put it in recipes-core/images. Another common practice is to use the meta layer suffix as the parent directory, and in our case that would be recipes-stargazer/images/.

Create the โ€œproductionโ€ image per our defined requirements:

cat <<- 'EOF' > ../sources/meta-stargazer/recipes-core/images/stargazer-image.bb
SUMMARY = "Stargazer production image"

IMAGE_FEATURES += "splash"

LICENSE = "MIT"

inherit core-image

CORE_IMAGE_EXTRA_INSTALL = "inotify-tools"

EOF

Create the โ€œdevelopmentโ€ image per our defined requirements:

cat <<- 'EOF' > ../sources/meta-stargazer/recipes-core/images/stargazer-dev-image.bb
SUMMARY = "Stargazer development image"

inherit core-image
require stargazer-image.bb

IMAGE_FEATURES += "ssh-server-dropbear tools-debug debug-tweaks"

CORE_IMAGE_EXTRA_INSTALL += "ethtool evtest fbset i2c-tools memtester"

EOF

The resulting structure and content of meta-stargazer should look like this:

tree ../sources/meta-stargazer/
../sources/meta-stargazer/
โ”œโ”€โ”€ conf
โ”‚   โ””โ”€โ”€ layer.conf
โ”œโ”€โ”€ COPYING.MIT
โ”œโ”€โ”€ README
โ”œโ”€โ”€ recipes-core
โ”‚   โ””โ”€โ”€ images
โ”‚       โ”œโ”€โ”€ stargazer-dev-image.bb
โ”‚       โ””โ”€โ”€ stargazer-image.bb
โ””โ”€โ”€ recipes-example
    โ””โ”€โ”€ example
        โ””โ”€โ”€ example_0.1.bb

There is one additional optimization we can make to the current layout, and that is grouping our โ€œtest applicationsโ€ in a packagegroup. This makes it easier to create additional variants of images, and reusing packagegroups across them.

Create directory for packagegroups:

mkdir -p ../sources/meta-stargazer/recipes-core/packagegroups/

It is a common practice to put the packagegroups directory along side the images directory in your layer structure.

Create stargazer-packagegroup-testapps.bb:

cat <<- 'EOF' > ../sources/meta-stargazer/recipes-core/packagegroups/stargazer-packagegroup-testapps.bb
DESCRIPTION = "Stargazer test application packagegroup"
SUMMARY = "Stargazer packagegroup - tools/testapps"

PACKAGE_ARCH = "${MACHINE_ARCH}"

inherit packagegroup

RDEPENDS_${PN} = " \
    ethtool \
    evtest \
    fbset \
    i2c-tools \
    memtester \
"
EOF

Update the stargazer-dev-image.bb recipe to utilize the newly created stargazer-packagegroup-testapps:

cat <<- EOF > ../sources/meta-stargazer/recipes-core/images/stargazer-dev-image.bb
SUMMARY = "Stargazer development image"

inherit core-image
require stargazer-image.bb

IMAGE_FEATURES += "ssh-server-dropbear tools-debug debug-tweaks"

CORE_IMAGE_EXTRA_INSTALL += "stargazer-packagegroup-testapps"

EOF

The resulting structure and content of meta-stargazer should look like this:

tree ../sources/meta-stargazer/
../sources/meta-stargazer/
โ”œโ”€โ”€ conf
โ”‚   โ””โ”€โ”€ layer.conf
โ”œโ”€โ”€ COPYING.MIT
โ”œโ”€โ”€ README
โ”œโ”€โ”€ recipes-core
โ”‚   โ”œโ”€โ”€ images
โ”‚   โ”‚   โ”œโ”€โ”€ stargazer-dev-image.bb
โ”‚   โ”‚   โ””โ”€โ”€ stargazer-image.bb
โ”‚   โ””โ”€โ”€ packagegroups
โ”‚       โ””โ”€โ”€ stargazer-packagegroup-testapps.bb
โ””โ”€โ”€ recipes-example
    โ””โ”€โ”€ example
        โ””โ”€โ”€ example_0.1.bb

Now we should be able to run the following to build our production image:

bitbake stargazer-image

and we should be able to run the following to build our development image:

bitbake stargazer-dev-image

Conclusion

In this tutorial we have covered the basics of how to customize and create images using the Yocto Project in the hopes that it will help you get started quickly without need of digging to deep in Yocto Project internals. The layer that we created (meta-stargazer) an populated during this tutorial can be used a starting point for your own custom application/distribution layer.

Additionally you can find a very detailed explanation of the whole image building process in the Yocto Mega Manual - Bitbake section.

For further reading visit:


Yocto Project and all related marks and logos are trademarks of The Linux Foundation. This website is not, in any way, endorsed by the Yocto Project or The Linux Foundation.


If this tutorial was useful to you, please press like, or leave a thank you note to the contributor who put valuable time into this and made it available to you. It will be much appreciated!

1 Like