Build your own operating system images. Rudolf Streif details how to use the Yocto Project to create your own customized Linux distribution. He provides an overview of the Linux distribution blueprints available with the build system and how to customize them. He then demonstrates how to create a Linux distribution entirely from scratch using the build system tools.
In the preceding chapters, we laid the foundation for using the Yocto Project tools to build custom Linux distributions. Now it is time that we put that knowledge to work.
Chapter 2, “The Yocto Project,” outlined the prerequisites for the build system and how to set up your build host, configure a build environment, and launch a build that creates a system ready to run in the QEMU emulator. In this chapter, we reuse that build environment. If you have not yet prepared your build system, we recommend that you go back to Chapter 2 and follow the steps. Performing a build using Poky’s default settings validates your setup. It also downloads the majority of the source code packages and establishes a shared state cache, both of which speed up build time for the examples presented in this chapter.
In Chapter 3, “OpenEmbedded Build System,” and Chapter 4, “BitBake Build Engine,” we explained the OpenEmbedded build system and the BitBake syntax. This and following chapters show examples or snippets of BitBake recipes utilizing that syntax. While the syntax is mostly straightforward and resembles typical scripting languages, there are some constructs that are particular to BitBake. Referring to Chapter 4, you find syntax examples and explanations.
When experimenting with the Yocto Project, you eventually encounter build failures. They can occur for various reasons, and troubleshooting can be challenging. You may want to refer to Chapter 5, “Troubleshooting,” for the debugging tools to help you track down build failures.
Chapter 6, “Linux System Architecture,” outlined the building blocks of a Linux distribution. While bootloader and the Linux kernel are indispensable for a working Linux OS stack, user space makes up its majority. In this chapter, we focus on customizing Linux OS stacks with user space libraries and applications from recipes provided by the Yocto Project and other compatible layers from the OpenEmbedded project.
The OpenEmbedded Core (OE Core) and other Yocto Project layers include several example images. These images offer root filesystem configurations for typical Linux OS stacks. They range from very basic images that just boot a device to a command-line prompt to images that include the X Window System (X11) server and a graphical user interface. These reference images are called the core images because the names of their respective recipes begin with core-image. You can easily locate the recipes for the core images with the find command from within the installation directory of your build system (see Listing 7-1).
You can look at the core images as Linux distribution blueprints from which you can derive your own distribution by extending them. All core image recipes inherit the core-image class, which itself inherits from image class. All images set the IMAGE_INSTALL variable to specify what packages are to be installed into the root filesystem. IMAGE_INSTALL is a list of packages and package groups. Package groups are collections of packages. Defining package groups alleviates the need to potentially list hundreds of single packages in the IMAGE_INSTALL variable. We explain package groups in a coming section of this chapter. Image recipes either explicitly set Image_INSTALL or extend its default value provided by the core-image class, which installs the two package groups packagegroup-core-boot and packagegroup-base-extended. The default creates a working root filesystem that boots to the console.
core-image-minimal: This is the most basic image allowing a device to boot to a Linux command-line login. Login and command-line interpreter are provided by BusyBox.
core-image-minimal-initramfs: This image is essentially the same as core-image-minimal but with a Linux kernel that includes a RAM-based initial root filesystem (initramfs).
core-image-minimal-mtdutils: Based on core-image-minimal, this image also includes user space tools to interact with the memory technology device (MTD) subsystem in the Linux kernel to perform operations on flash memory devices.
core-image-minimal-dev: Based on core-image-minimal, this image also includes all the development packages (header files, etc.) for all the packages installed in the root filesystem. If deployed on the target together with a native target toolchain, it allows software development on the target. Together with a cross-toolchain, it can be used for software development on the development host.
core-image-rt: Based on core-image-minimal, this image target builds the Yocto Project real-time kernel and includes a test suite and tools for real-time applications.
core-image-rt-sdk: In addition to core-image-rt, this image includes the system development kit (SDK) consisting of the development packages for all packages installed; development tools such as compilers, assemblers, and linkers; as well as performance test tools and Linux kernel development packages. This image allows for software development on the target.
core-image-base: Essentially a core-image-minimal, this image also includes middle-ware and application packages to support a variety of hardware such as WiFi, Bluetooth, sound, and serial ports. The target device must include the necessary hardware components, and the Linux kernel must provide the device drivers for them.
core-image-full-cmdline: This minimal image adds typical Linux command-line tools—bash, acl, attr, grep, sed, tar, and many more—to the root filesystem.
core-image-lsb: This image contains packages required for conformance with the Linux Standard Base (LSB) specification.
core-image-lsb-dev: This image is the same as the core-image-lsb but also includes the development packages for all packages installed in the root filesystem.
core-image-lsb-sdk: In addition to core-image-lsb-dev, this image includes development tools such as compilers, assemblers, and linkers as well as performance test tools and Linux kernel development packages.
core-image-x11: This basic graphical image includes the X11 server and an X11 terminal application.
core-image-sato: This image provides X11 support that includes the OpenedHand Sato user experience for mobile devices. Besides the Sato screen manager, the image also provides several applications using the Sato theme, such as a terminal, editor, file manager, and several games.
core-image-sato-dev: This image is the same as core-image-sato but also includes the development packages for all packages installed in the root filesystem.
core-image-sato-sdk: In addition to core-image-sato-dev, this image includes development tools such as compilers, assemblers, and linkers as well as performance test tools and Linux kernel development packages.
core-image-directfb: An image that uses DirectFB for graphics and input device management, DirectFB may include graphics acceleration and a windowing system. Because of its much smaller footprint compared to X11, DirectFB is the preferred choice for lower-end embedded systems that need graphics support but not the entire functionality of X11.
core-image-clutter: This is an X11-based image that also includes the Clutter toolkit. Clutter is based on OpenGL and provides functionality for animated graphical user interfaces.
core-image-weston: This image uses Weston instead of X11. Weston is a compositor that uses the Wayland protocol and implementation to exchange data with its clients. This image also includes a Wayland-capable terminal program.
qt4e-demo-image: This image launches a demo application for the embedded Qt toolkit after completing the boot process. Qt for embedded Linux provides a development framework of graphical applications that directly write to the framebuffer instead of using the X11.
core-image-multilib-example: This image is an example of the support of multiple libraries, typically 32-bit support on an otherwise 64-bit system. The image is based on a core image and adds the desired multilib packages to IMAGE_INSTALL.
The following three images are not reference images for embedded Linux systems. We include them in this discussion for completeness purposes.
core-image-testmaster, core-image-testmaster-initramfs: These images are references for testing other images on actual hardware devices or in QEMU. They are deployed to a separate partition to boot into and then use scripts to deploy the image under test. This approach is useful for automated testing.
build-appliance-image: This recipe creates the Yocto Project Build Appliance virtual machine images that include everything needed for the Yocto Project build system. The images can be launched using VMware Player or VMware Workstation.
Studying the reference image recipes is a good way to learn how these images are built and what packages comprise them. The core images are also a good starting point for your own Linux OS stack. You can easily extend them by adding packages and package groups to IMAGE_INSTALL. Images can only be extended, not shrunk. To build an image with less functionality, you have to start from a smaller core image and add only the packages you need. There is no simple way to remove packages. The majority of them are added through package groups, and you would need to split up the package group if you do not want to install a package included with it. Of course, if you are removing a package, you also have to remove any other packages that depend on it.
There are several ways you can add packages and package groups to be included with your root filesystem. The following sections explain them and also provide information on why you would want to use one method over another.
IMAGE_INSTALL_append = " <package> <package group>"
As we have seen, image recipes set the IMAGE_INSTALL variable adding packages and package groups. To extend an image, you have to append your packages and packages group to the variable. You may wonder why we use the explicit _append operator instead of the += or .+ operators. Using the _append operator unconditionally appends the specified value to the IMAGE_INSTALL variable after all recipes and configuration files have been processed. Image recipes commonly explicitly set the IMAGE_INSTALL variable using the = or ?= operators, which may happen after BitBake processed the settings in conf/local.conf.
IMAGE_INSTALL_append = " strace sudo sqlite3"
installs the strace and sudo tools as well as SQLite in the root filesystem. When using the _append operator, you always have to remember to add a space in front of the first package or package group, as this operator does not automatically include a space.
IMAGE_INSTALL_append_pn-<image> = " <package> <package group>"
installs the strace tool only into the root filesystem of core-image-minimal. All other images are unaffected.
CORE_IMAGE_EXTRA_INSTALL = "strace sudo sqlite3"
adds these packages to all images that inherit from core-image. Images that inherit directly from image are not affected. Using CORE_IMAGE_EXTRA_INSTALL is a safer and easier method for core images than appending directly to IMAGE_INSTALL.
The round-trip time for launching QEMU is much quicker than deploying an image to actual hardware.
Frequently, hardware is not yet available when software development begins.
Yocto Project board support packages (BSP) make it simple to switch from QEMU to hardware and back.
which tells the script to locate the latest kernel and root filesystem image builds for the provided QEMU machine and otherwise launch QEMU with default parameters. The parameter values match the QEMU machine types in conf/local.conf.
When building a product, you find yourself frequently modifying your images, adding new packages, and removing extraneous packages to trim the footprint. A tool that enables you to easily verify and compare image builds with each other can simplify that otherwise tedious task.
to the conf/local.conf file of your build environment. Please note that INHERIT (uppercase) is a variable to which you have to add the buildhistory class. It is different from the inherit (lowercase) directive used by recipes and classes to inherit functionality from classes. Every time you do a build, buildhistory creates a commit to the Git repository with the changes.
Simply searching the file installed-package-names.txt gives you information on whether or not a package has been installed.
Adding packages and package groups to CORE_IMAGE_EXTRA_INSTALL and IMAGE_INSTALL and in conf/local.conf may be straightforward and quick, but doing so makes a project hard to maintain and complicates reuse. A better way is to extend a predefined image through a recipe. Listing 7-2 shows a simple recipe that extends core-image-base.
DESCRIPTION = "A console image with hardware support for our IoT device"
IMAGE_INSTALL += "sqlite3 mtd-utils coreutils"
The example includes the recipe for core-image-base and adds packages to IMAGE_INSTALL and an image feature to IMAGE_FEATURES. We explain what image features are and how to utilize them to customize image in the next section.
Unlike classes, you need to provide the path relative to the layer for BitBake to find the recipe file to include, and you need to add the .bb file extension.
While you can use either include or require to include the recipe you are extending, we recommend the use of require, since it causes BitBake to exit with an explicit error message if it cannot locate the included recipe file.
Remember to use the += operator to add to IMAGE_INSTALL. Do not use = or := because they overwrite the content of the variable defined by the included recipe.
For BitBake to actually be able to use this recipe as a build target, you have to add it to a layer that is included into your build environment via the conf/bblayers.conf file. It is not recommended that you add your recipes to the core Yocto Project layers, such as meta, meta-yocto, and others, because it makes it hard to maintain your build environment if you upgrade to a newer version of the Yocto Project. Instead, you should create a layer in which to put your recipes.
Creating a layer for one recipe may seem like a lot of overhead, but hardly any project ever stays small. What may start with one recipe eventually grows into a sophisticated project with recipes for images, packages, and package groups. In Chapter 3, we introduced the yocto-layer, which makes creating layers a breeze.
Image features provide certain functionality that you can add to your target images. This can be additional packages to be installed, modification of configuration files, and more.
For example, the dev-pkgs image feature adds the development packages, which typically include headers and other files required for development, for all packages installed in the root filesystem. Using this image feature is a convenient way to enable a target image for development without having to explicitly specify the development packages in the IMAGE_INSTALL variable. For deployment, you can then simply remove the dev-pkgs image feature.
Installation of image features is controlled by the two variables IMAGE_FEATURES and EXTRA_IMAGE_FEATURES. The former is used in image recipes to define the required set of image features. The latter is typically used in the conf/local.conf file to define additional image features that, of course, then affect all images built with that build environment. The content of EXTRA_IMAGE_FEATURES is simply added to IMAGE_FEATURES by the meta/conf/bitbake.conf configuration file.
debug-tweaks: Prepares an image for development purposes. In particular, it sets empty root passwords for console and Secure Shell (SSH) login.
package-management: Installs the package management system according to the package management class defined by PACKAGE_CLASSES for the root filesystem.
read-only-rootfs: Creates a read-only root filesystem. This image feature works only if System V Init (SysVinit) system is used rather than sytemd.
splash: Enables showing a splash screen instead of the boot messages during boot. By default, the splash screen is provided by the psplash package, which can be customized. You can also define an alternative splash screen package by setting the SPLASH variable to a different package name.
dbg-pkgs: Installs the debug packages containing symbols for all packages installed in the root filesystem.
dev-pgks: Installs the development packages containing headers and other development files for all packages installed in the root filesystem.
doc-pkgs: Installs the documentation packages for all packages installed in the root filesystem.
staticdev-pkgs: Installs the static development packages such as static library files ending in *.a for all packages installed in the root filesystem.
ptest-pkgs: Installs the package test (ptest) packages for all packages installed in the root filesystem.
eclipse-debug: Installs remote debugging tools for integration with the Eclipse IDE, namely the GDB debugging server, the Eclipse Target Communication Framework (TCF) agent, and the OpenSSH SFTP server.
hwcodecs: Installs the hardware decoders and encoders for audio, images, and video if the hardware platform provides them.
nfs-server: Installs Network File System (NFS) server, utilities, and client.
qt4-pkgs: Installs the Qt4 framework and demo applications.
ssh-server-dropbear: Installs the lightweight SSH server Dropbear, which is popular for embedded systems. This image feature is incompatible with ssh-server-openssh. Either one of the two, but not both, can be used.
ssh-server-openssh: Installs the OpenSSH server. This image feature is incompatible with ssh-server-dropbear. Either one of the two, but not both, can be used.
tools-debug: Installs debugging tools, namely the GDB debugger, the GDB remote debugging server, the system call tracing tool strace, and the memory tracing tool mtrace for the GLIBC library if it is the target library.
tools-profile: Installs common profiling tools such as oprofile, powertop, latencytop, lttng-ust, and valgrind.
tools-sdk: Installs software development tools such as the GCC compiler, Make, autoconf, automake, libtool, and many more.
tools-testapps: Installs test applications such as tests for X11 and middleware packages like the telephony manager oFono and the connection manager ConnMan.
x11: Installs the X11 server.
x11-base: Installs the X11 server with windowing system.
x11-sato: Installs the OpenedHand Sato user experience for mobile devices.
It matters what classes define the image features when creating your own image recipes and choosing the image class to inherit. The class image inherits populate_sdk_base and thus all image features defined by those two classes are available to images that inherit image. Image features defined by core-image are available only to images that inherit that class, which in turn inherits image and with it also populate_sdk_base.
We have touched on package groups a couple of times during this discussion of creating custom Linux distribution images. Package groups are bundles of packages that are referenced by a name. Using that name in the IMAGE_INSTALL variable installs all the packages defined by the package group into the root filesystem of your target image.
The Yocto Project and OE Core layers define a common set of package groups that you can readily use for your images. You can also create your own package groups containing packages from any layer, including your own. We first describe the package groups defined by the Yocto Project and OE Core layers and then look into the details on how package groups are defined.
Package groups are defined by recipes. Conventionally, the recipe files begin with packagegroup- and are placed inside packagegroup subdirectories of the respective recipe categories. For instance, you can find package group recipes related to the Qt development framework in the subdirectory meta/recipes-qt/packagegroups.
from the installation directory of the Yocto Project build system gives you a list of all the package group recipes for the predefined package groups of the Yocto Project build system.
packagegroup-core-ssh-dropbear: Provides packages for the Dropbear SSH server popular for embedded systems because of its smaller footprint compared to the OpenSSH server. This package group conflicts with packagegroup-core-ssh-openssh. You can include only one of the two in your image. The ssh-server-dropbear image feature installs this package group.
packagegroup-core-ssh-openssh: Provides packages for the standard OpenSSH server. This package group conflicts with packagegroup-core-ssh-dropbear. You can include only one of the two in your image. The ssh-server-openssh image feature installs this package group.
packagegroup-core-buildessential: Provides the essential development tools, namely the GNU Autotools utilities autoconf, automake, and libtool; the GNU binary tool set binutils which includes the linker ld, assembler as, and other tools; the compiler collection cpp; gcc; g++; the GNU internationalization and localization tool gettext; make; libstc++ with development packages; and pkgconfig.
packagegroup-core-tools-debug: Provides the essential debugging tools, namely the GDB debugger, the GDB remote debugging server, the system call tracing tool strace, and, for the GLIBC target library, the memory tracing tool mtrace.
packagegroup-core-sdk: This package group combines the packagegroup-core-buildessential package group with additional tools for development such as GNU Core Utilities coreutils with shell, file, and text manipulation utilities; dynamic linker ldd; and others. Together with packagegroup-core-standalonesdk-target, this package group forms the tools-sdk image feature.
packagegroup-core-standalone-sdk-target: Provides the GCC and standard C++ libraries. Together with packagegroup-core-sdk, this package group forms the tools-sdk image feature.
packagegroup-core-eclipse-debug: Provides the GDB debugging server, the Eclipse TCF agent, and the OpenSSH SFTP server for integration with the Eclipse IDE for remote deployment and debugging. The image feature eclipse-debug installs this package group.
packagegroup-core-tools-testapps: Provides test applications such as tests for X11 and middleware packages like the telephony manager oFono and the connection manager ConnMan. The tools-testapps image feature installs this package group.
packagegroup-self-hosted: Provides all necessary packages for a self-hosted build system. The build-appliance image target uses this package group.
packagegroup-core-boot: Provides the minimum set of packages necessary to create a bootable image with console. All core-image targets install this package group. The core-image-minimal installs just this package group and the postinstallation scripts.
packagegroup-core-nfs: Provides NFS server, utilities, and client. The nfs-server image feature installs this package group.
packagegroup-base: This recipe provides multiple package groups that depend on each other as well as on machine and distribution configuration. The purpose of these package groups is to add hardware, networking protocol, USB, filesystem, and other support to the images dependent on the machine and distribution configuration. The two top-level package groups are packagegroup-base and packagegroup-base-extended. The former adds hardware support for Bluetooth, WiFi, 3G, and NFC only if both the machine configuration and the distribution configuration require them. The latter also adds configuration for those technologies if the distribution configuration requires them. However, the machine configuration does not support them directly but provides support for PCI, PCMCIA, or USB host. This package group allows you to create an image with support for devices that can physically be added to the target device; for example, via USB hotplug. Most commonly, images providing hardware support use packagegroup-base-extended rather than packagegroup-base for dynamic hardware support; for example, core-image-base.
packagegroup-cross-canadian: Provides SDK packages for creating a toolchain using the Canadian Cross technique, which is building a toolchain on system A that executes on system B to create binaries for system C. A use case for this package group is to build a toolchain with the Yocto Project on your build host that runs on your image target but produces output for a third system with a different architecture than your image target.
packagegroup-core-tools-profile: Provides common profiling tools such as oProfile, PowerTOP, LatencyTOP, LTTng-UST, and Valgrind. The tools-profile image feature uses this package group.
packagegroup-core-device-devel: Provides distcc support for an image. Distcc allows distribution of compilation across several machines on a network. The distcc must be installed, configured, and running on your build host. On the target you must define the cross-compiler variable to use distcc instead of the local compiler (e.g., export CC="distcc").
packagegroup-qt-toolchain-target: Provides the package to build applications for the X11-based version of the Qt development toolkit on the target system.
packagegroup-qte-toolchain-target: Provides the package to build applications for the embedded version of the Qt development toolkit on the target system.
packagegroup-core-qt: Provides all necessary packages for a target system using the X11-based version of the Qt development toolkit.
packagegroup-core-qt4e: Provides all necessary packages for a target system using the embedded Qt toolkit. The qt4e-demo-image installs this package group.
packagegroup-core-x11-xserver: Provides the X.Org X11 server only.
packagegroup-core-x11: Provides packagegroup-core-x11-xserver plus basic utilities such as xhost, xauth, xset, xrandr, and initialization on startup. The x11 image feature installs this package group.
packagegroup-core-x11-base: Provides packagegroup-core-x11 plus middleware and application clients for a working X11 environment that includes the Matchbox Window Manager, Matchbox Terminal, and a fonts package. The x11-base image feature installs this package group.
packagegroup-core-x11-sato: Provides the OpenedHand Sato user experience for mobile devices, which includes the Matchbox Window Manager, Matchbox Desktop, and a variety of applications. The x11-sato image feature installs this package group. To utilize this package group for your target image, you also have to install packagegroup-core-x11-base.
packagegroup-core-clutter-core: Provides packages for the Clutter graphical toolkit. To use the toolkit for your target image, you also have to install packagegroup-core-x11-base.
packagegroup-core-directfb: Provides packages for the DirectFB support without X11. Among others, the package group includes the directfb package and the directfb-example package, and it adds touchscreen support if provided by the machine configuration.
packagegroup-core-lsb: Provides all packages required for LSB support.
packagegroup-core-full-cmdline: Provides packages for a more traditional Linux system by installing the full command-line utilities rather than the more compact BusyBox variant.
When explaining the different package groups, we used the terms provide and install somewhat liberally, since the package group recipes actually do not provide or install any packages. They only create dependencies that cause the build system to process the respective package recipes, as we see in the next section.
Several of the package groups are used by image features, which raises the question whether to use an image feature or to use the package group the image feature uses.
Package groups are defined by recipes that inherit the packagegroup class. Package group recipes are different from typical package recipes, as they do not build anything or create any output. Package group recipes only create dependencies that trigger the build system to process the recipes of the packages the package groups reference.
Listing 7-3 shows a typical package group recipe.
SUMMARY = "Custom package group for our IoT devices"
Names of package group recipes, although not enforced or required by the build system, should adhere to the convention packagegroup-<name>.bb. You also would want to place them in the subdirectory packagegroup of the recipe category the package groups are integrating. If package groups span recipes and possibly package groups from multiple categories, it is good practice to place them into the recipes-core category.
The basic structure of package group recipes is rather simple. As should any recipe (and we go into the details of writing recipes in Chapter 8, “Software Package Recipes”), a package group recipe should provide a SUMMARY of what the recipe does. The DESCRIPTION, which can provide a longer, more detailed explanation, is optional, but it is good practice to add it. Any recipe also needs to provide a LICENSE for the recipe itself. All package group recipes must inherit the packagegroup class.
The names of the actual package groups are defined by the PACKAGES variable. This variable contains a space-delimited list of the package group names. In the case of Listing 7-3, these are packagegroup-databases, packagegroup-python, and packagegroup-servers. By convention, package group names begin with packagegroup-. Although the build system does not require it, it is good practice if you adhere to it for your own package group names.
For each package group, the recipe must define its dependencies in a conditional RDEPENDS_<package-group-name> variable. These variables list the required dependencies, which can be packages or package groups.
The RRECOMMENDS_<package-group-name> definitions are optional. As we saw in Chapter 3, recommendations are weak dependencies that cause a package to be included only if it already has been built.
You can reference package groups from other variables, such as IMAGE_INSTALL, which of course causes these package groups to be installed in a target image. You can also use them to create dependencies for other package groups for a hierarchy. You must avoid circular dependencies of package groups. That may sound simple and straightforward but can easily happen by mistake in rather complex environments. BitBake, however, aborts with an error message in the case of a circular package group dependency.
Doing so allows testing the package groups before referencing them by image builds, which simplifies debugging.