Skip to content

Setting Up Yocto Projects with kas

Kas makes the setup of a Yocto build environment super simple and super fast. We call kas with a project configuration file: kas-container build ./eu-terminal-distro.yml. Kas starts a Docker container, clones the layer repositories, initialises the Yocto configuration files (local.conf and bblayers.conf), and starts building the embedded Linux system. Most Linux BSP providers don’t make a kas configuration file available. I’ll show how to convert a repo manifest file into a kas configuration file in this post.


In the post Qt Embedded Systems – Part 1: Building a Linux Image with Yocto, I go through the steps how to build the Linux image for a Raspberry Pi 3.

  • We build a Docker container image and run a Linux shell in the container.
  • We download the layer repositories as given in a repo manifest file.
  • We edit the configuration files bblayers.conf and local.conf.
  • We build the target Linux image in the container.

Each of the four steps consists of a couple of smaller steps that we must execute manually. Setting up a Yocto build environment this way and reaching the point where we can build the target Linux image with BitBake is a tedious process.

Given a project configuration file, kas reduces this process to a single command:

$ kas-container build project-configuration.yml

The project configuration file, which is written in YAML, is very similar to the repo manifest file. It also contains all the information needed for generating the configuration files bblayers.conf and local.conf – in contrast to the manifest file. The kas-container command performs all the four steps from the traditional setup in one go. Super simple and super fast!

My target devices are the Toradex Verdin iMX8M SoMs (System on Module). The kas configuration files work for both the Verdin iMX8M Mini and Verdin iMX8M Plus. Like most other SoM and SoC vendors, Toradex don’t provide kas configuration files. However, they provide repo manifest files. I’ll describe how to create the kas configuration file from the repo manifest files.

Traditional Setup with Repo Manifests

We set up the Yocto build environment in the traditional way with the repo tool. We follow the description in the section Repo and Git to install the repo tool and git on our build computer. The section First-time configuration tells us how to prepare the Yocto build environment. We do not run the commands yet, because we must figure out which version of the Toradex Linux BSP to build.

$ cd /public/Projects/traditional
$ repo init -u -b dunfell-5.x.y -m tdxref/default.xml
$ repo sync
$ . export

All the Toradex BSPs based on Yocto 3.1 LTS Dunfell have the major version 5. Toradex increment the major version when they move to a new Yocto version. They increment the minor version 5.x quarterly and the patch version 5.x.y for bug and security fixes (see also Toradex Embedded Linux Support Strategy). At the time of this writing, the last released BSP version is 5.6.0 (see also the list of available tags). The repo-init call produces the following directory structure.

# result of "repo init"
        pinned.xml              # Included by default.xml
        pinned-nxp.xml          # Included by default.xml
        pinned-tdx.xml          # Included by default.xml

Passing the branch dunfell-5.x.y to the repo-init call means that we are building the latest revision or nearly the latest revision of this branch. The manifest file defines how close the selected version is to the head of the branch. There are three manifest files for fine-tuning.

  • tdxref/next.xml points to the latest revision (HEAD) of the branch of each layer: dunfell-5.x.y for the Toradex layers and dunfell for the other layers. As this is the bleeding edge, builds may fail.
  • tdxref/integration.xml points to a certain revision (commit SHA) in the branches dunfell-5.x.y and dunfell. This revision is close to the HEAD and contains the latest fixes or improvements. The manifest is the candidate to become the next default.xml.
  • tdxref/default.xml points to a certain revision in the branches dunfell-5.x.y and dunfell. This revision is typically older than the ones in integration.xml and next.xml. The manifest defines a stable build that went through more testing than the builds defined by the other manifests. This is the manifest we should use, well, by default.

Working on the development branch dunfell-5.x.y with the default manifest is the right choice while developing a product. When we release our product, we best use a quarterly release (e.g., 5.6.0). We pass a tag (e.g., refs/tags/5.6.0) instead of the branch to the repo-init call (see available tags).

repo init -u -b refs/tags/5.6.0 -m tdxref/default.xml

The SoM revision also decides which BSP versions can be used. 1st-generation Verdin SoMs (e.g., iMX8M Mini V1.0B) only support BSP 5.1.0 and older. 2nd-generation Verdin SoMs (e.g., iMX8M Mini V1.1A and newer, iMX8M Plus V1.0B and newer) support BSP 5.2.0 and newer. The table at the end of the page Verdin Product Family Specification Update gives the details.

The repo-sync command clones the git repositories into the layers/ directory and checks out the branch and revision – as given in the manifest files. It produces the following directory structure.

# result of "repo sync"
    meta-toradex-tegra        # Not needed for iMX8M

If the manifest says that the repository meta-toradex-distro shall be checked out at the revision cbde028, then we can verify this as follows:

$ cd layers/meta-toradex-distro
$ git show --oneline
cbde028 (HEAD, repo/dunfell-5.x.y, m/dunfell-5.x.y) Bump version number to 5.7.0

Sourcing the export script creates the Yocto configuration files bblayers.conf and local.conf.

# result of ". export"

The XML manifest files are the starting point for creating the kas configuration file. The directory trees under build/conf/ and layers/ are the golden reference. When kas creates the same directory trees with the same contents, the kas configuration file is correct.

Now, it’s time to run the commands from the beginning of this section – with the right branch or tag.

Installing kas

We start the installation with cloning the kas repository in any directory (e.g., /public/Projects), where we have read and write access as a normal user.

$ cd /public/Projects
$ git clone

Kas is written in Python3. We must install Python3, Pip3 and some Python packages.

$ sudo apt install python3 python3-pip
$ sudo pip3 install distro jsonschema kconfiglib PyYAML

If the command

/public/Projects/kas/run-kas --help

doesn’t show any Python error messages but just the usage of run-kas, our Python installation is OK.

The kas documentation lists four options how to use kas.

  • As seen above, we can run the script /public/Projects/kas/run-kas from the kas repository. If we add /public/Projects/kas to $PATH, we can simply type run-kas on the command line.
  • We can install kas as a system Python script by running sudo pip3 install . in the directory /public/Projects/kas. Then we can type kas at the Linux prompt.
  • We can run kas in a container locally with the script /public/Projects/kas/kas-container. If we add /public/Projects/kas to $PATH, we can simply type kas-container on the command line.
  • We can run kas in a container remotely for CI. This is out of scope for this post.

Yocto builds fail, when we run them on too old or on too new host Linux versions. Hence, the first two options will run into problems, as soon as we build target Linux systems with different Yocto versions, e.g., for the next product version or for multipe products. We avoid these problems by running the Yocto builds in a container based on a suitable Linux version. So, we choose the third option with kas-container.

When we run kas-container for the first time, it will build a Docker container image. It will run kas commands like shell, checkout and build in the Docker container. All the kas commands take a project configuration file as an argument. So, we need to create one first.

Converting Repo Manifests into Kas Configurations

We create an empty project configuration file in YAML format, say, /public/Projects/terminal-distro/terminal-distro.yml, and add the following properties.

    version: 11
distro: tdx-xwayland
build_system: oe
machine: verdin-imx8mp
target: tdx-reference-multimedia-image

header.version is the version of the configuration format. 11 is the latest version at the time of writing. The Toradex Linux image tdx-reference-multimedia-image is based on Toradex’s reference Linux distribution tdx-xwayland and is built with OpenEmbedded oe. The target image provides Wayland, Weston, Qt 5, V4L and GStreamer.

The target machine is verdin-imx8mp for the Verdin iMX8M Plus SoM. The documentation lists 13 NXP SoMs including 2 Verdin, 5 Apalis and 6 Colibri machines for different variants of the iMX6, iMX6ULL, iMX7, iMX8, iMX8X, iMX8M Mini and iMX8M Plus. We must only change the machine to verdin-imx8mm, apalis-imx6 or colibri-imx7 and the build produces the Linux image for the respective SoM.

Kas adds the distro and machine to the local configuration file build/conf/local.conf.

MACHINE ??= "verdin-imx8mp"
DISTRO ??= "tdx-xwayland"

When we set up a Yocto build environment without kas, we would assign the distro and machine to the environment variables DISTRO and MACHINE, respectively, and pass the image name tdx-reference-multimedia-image to the bitbake call.

The manifest file tdxref/default.xml defines which revision from which repository (remote) the repo tool shall fetch for a given Yocto layer (project). The entries for the meta-qt5 layer look as follows.

<remote alias="repo" fetch="" name="githq"/>

<project name="meta-qt5.git" path="layers/meta-qt5" remote="githq" revision="5ef3a0ffd3324937252790266e2b2e64d33ef34f" upstream="dunfell"/>

The corresponding entry in the kas configuration file looks like this.

        url: ""
        refspec: "5ef3a0ffd3324937252790266e2b2e64d33ef34f"
        path: "layers/meta-qt5"

The repository ID (here: meta-qt5) in the project configuration is the without the file extension. The url is the concatenation of remote.fetch and The refspec is the project.revision. The path is the project.path.

The above entry tells kas to clone the repository repository url into the path relative to the current working directory and check out the revision refspec. We can check this by running kas in a work directory of our choice (e.g., /public/Projects/terminal-distro/) .

$ cd /public/Projects/terminal-distro
$ kas-container checkout ./terminal-distro.yml 
2021-09-17 10:20:07 - INFO     - kas 2.5 started
2021-09-17 10:20:07 - INFO     - /work$ git clone -q /work/layers/meta-qt5
2021-09-17 10:20:10 - INFO     - Repository meta-qt5 cloned
2021-09-17 10:20:10 - INFO     - /work/layers/meta-qt5$ git checkout -q b4d24d70aca75791902df5cd59a4f4a54aa4a125 -B dunfell
2021-09-17 10:20:10 - ERROR    - Did not find any init-build-env script

We ignore the error at the end for the time being, as it will eventually disappear. A quick check in layers/meta-qt5/ shows that kas checked out the correct revision of the meta-qt5 repository.

$ cd layers/meta-qt5/
$ git show --oneline
5ef3a0f (HEAD, origin/jansa/dunfell, origin/dunfell-next, origin/dunfell) qtdeclarative: Fix build with gcc-11
$ cd -            # back to working directory

Next, we convert the layers from the manifest file base/pinned.xml, which is included first by default.xml.

<remote alias="repo" fetch="" name="oe"/>
<remote alias="repo" fetch="" name="yocto"/>

<project name="bitbake.git" path="layers/openembedded-core/bitbake" remote="oe" revision="0784db7dd0fef6f0621ad8d74372f44e87fef950" upstream="1.46"/>
<project name="meta-openembedded.git" path="layers/meta-openembedded" remote="oe" revision="8ff12bfffcf0840d5518788a53d88d708ad3aae0" upstream="dunfell"/>
<project name="meta-yocto" path="layers/meta-yocto" remote="yocto" revision="7e0063a8546250c4c5b9454cfa89fff451a280ee" upstream="dunfell"/>
<project name="openembedded-core.git" path="layers/openembedded-core" remote="oe" revision="add860e1a69f848097bbc511137a62d5746e5019" upstream="dunfell"/>

We add the four kas entries before meta-qt5.

        url: ""
        refspec: "0784db7dd0fef6f0621ad8d74372f44e87fef950"
        path: "layers/openembedded-core/bitbake"
        url: ""
        refspec: "8ff12bfffcf0840d5518788a53d88d708ad3aae0"
        path: "layers/meta-openembedded"
        url: ""
        refspec: "7e0063a8546250c4c5b9454cfa89fff451a280ee"
        path: "layers/meta-yocto"
        url: ""
        refspec: "add860e1a69f848097bbc511137a62d5746e5019"
        path: "layers/openembedded-core"

Kas successfully clones the repositories bitbake, meta-yocto and meta-openembedded but fails when cloning the repository openembedded-core.

2022-06-24 13:24:42 - INFO     - /work/layers/openembedded-core$ git remote set-url origin
2022-06-24 13:24:42 - ERROR    - Command "/work/layers/openembedded-core$ git remote set-url origin" failed
--- Error summary ---
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

Cloning bitbake creates a directory tree at layers/openembedded-core/bitbake. Kas clones the repository openembedded-core into this existing directory tree – and fails. If we change the order in the project configuration (openembedded-core first and bitbake second), the checkout works fine. We remove the layers/ directory before we run kas-container checkout again to confirm our fix.

When checking out several repositories for the first time, we may encounter an error like this.

2022-06-24 13:28:14 - ERROR    - fatal: unable to access '': Could not resolve host:
2022-06-24 13:28:14 - ERROR    - Command "/work$ git clone -q /work/layers/meta-qt5" failed

If the URL is correct, the error disappears by running kas again. Sometimes we need several runs.

When kas-container checkout runs without errors, it writes the configuration file bblayers.conf and local.conf to the directory build/conf/. A comparison with their “traditional” counterparts shows that they are not quite right. Let us first fix bblayers.conf and then local.conf.

# Traditional bblayers.conf (partial)
  ${TOPDIR}/../layers/meta-openembedded/meta-oe \
  ${TOPDIR}/../layers/meta-openembedded/meta-filesystems \
  ${TOPDIR}/../layers/meta-openembedded/meta-gnome \
  ${TOPDIR}/../layers/meta-openembedded/meta-xfce \
  ${TOPDIR}/../layers/meta-openembedded/meta-initramfs \
  ${TOPDIR}/../layers/meta-openembedded/meta-networking \
  ${TOPDIR}/../layers/meta-openembedded/meta-multimedia \
  ${TOPDIR}/../layers/meta-openembedded/meta-python \

# New bblayers.conf (partial)
  /work/layers/meta-openembedded \

The new bblayers.conf file should contain eight entries for the sublayers of the layer container meta-openembedded instead of a single entry for the container itself. meta-openembedded is not a layer, because it doesn’t provide a layer configuration file conf/layer.conf. It is a layer container. We list the eight layers for meta-openembedded as follows:

        url: ""
        refspec: "8ff12bfffcf0840d5518788a53d88d708ad3aae0"
        path: "layers/meta-openembedded"

After running kas, bblayers.conf contains the eight layers as desired. Similarly, we specify meta-poky as the layer for the container meta-yocto and meta for openembedded-core. If we forget to specify the layers, the kas build will complain about a missing conf/layer.conf file.

ERROR: Unable to parse /work/layers/meta-openembedded/conf/layer.conf: [Errno 2] file /work/layers/meta-openembedded/conf/layer.conf not found

The bblayers.conf file generated by kas contains an entry for bitbake, whereas the “traditional” counterpart does not. As bitbake is neither a layer nor a layer container but just a repository providing the bitbake source code, we exclude the bitbake “layer”. This removes the bitbake entry from bblayers.conf. Another run of kas confirms this.

        url: ""
        refspec: "0784db7dd0fef6f0621ad8d74372f44e87fef950"
        path: "layers/openembedded-core/bitbake"
            bitbake: excluded

We add the remaining layers to the kas configuration based on the manifest files bsp/pinned-nxp.xml, bsp/pinned-tdx.xml and tdxref/default.xml. There shouldn’t be any further problems.

Completing the Yocto Configuration Files

From the project configuration file, kas generates the two Yocto configuration files build/conf/bblayers.conf and build/conf/local.conf. These files are not complete yet, as a comparison with their “traditional” counterparts shows. The following entry makes kas add the missing line (marked in bold face) to the beginning of bblayers.conf – before the definition of the layers.

    custom-bblayers-conf: |

We add another three lines (marked in bold face again) to the beginning of local.conf – before the definitions of the machine and the distro.

    custom-local-conf: |
        ACCEPT_FSL_EULA = "1"
        DL_DIR = "/work/downloads"
        SSTATE_DIR = "/work/sstate-cache"

The first line accepts the Freescale EULA automatically so that the build doesn’t end up waiting for user input. The second and third line move the downloads and sstate cache out of the build directory /work/build so that other developers can reuse them, say, over NFS. This saves other developers from rebuilding the whole Linux image themselves. They use most of the build from the fast build server and perform only small incremental builds on their PCs.

As we have done several times before, we test the project configuration with the command

$ kas-container checkout ./terminal-distro.yml

If the command runs without any errors and if the Yocto configuration files and the directory structure are the same as the golden reference, we can build the Linux image.

Building the Linux Image

We build the Linux image specified in the project configuration file terminal-distro.yml with the command

$ kas-container build ./terminal-distro.yml

The command runs BitBake for the image tdx-reference-multimedia-image in a Docker container:

/build$ /work/layers/openembedded-core/bitbake/bin/bitbake -c build tdx-reference-multimedia-image

The Yocto build will take a couple of hours to finish.

We can build the SDK with the command

kas-container build ./terminal-distro.yml -c populate_sdk

Kas sets up the Yocto build environment in the container for interactive use with the command

kas-container shell ./terminal-distro.yml

We can then run Yocto commands like bitbake from the Linux prompt inside the container.

Useful Resources

  • Github repository with the complete project configuration file from this post. The README explains how to build the embedded Linux image for the Toradex Verdin iMX8M Plus.
  • Kas repository on Github.
  • Siemens: Kas User Guide. Everything you need to about kas as a user and a developer.
  • Paula Santamaria: Introduction to YAML. A brief introduction into YAML. Good enough to understand the YAML in the kas project configuration files.