Cross-Platform Yocto Development with VSCode Dev containers

VSCode and Docker offer a clean way to build Yocto projects on Mac, Windows, and Linux

Bailey Blankenship By Bailey Blankenship on

Yocto is an ecosystem of tools for building embedded Linux distributions. Its tools are meant to be run on a Linux machine - a powerful one. If you’re a Mac or Windows user, you have a few options:

  1. Use a virtual machine (Parallels, VirtualBox, etc.)
  2. Use CROPS, which is a container-based development system maintained by the Yocto team.

A virtual machine could be a win. You’ll have some processing overhead to run your Linux distro’s UI (unless you use a server distro), but in general, it’s a fine option. However, if you’re a little Apple simp like me, you’ll be emulating an x86 machine on an ARM-based M* processor, which adds a good bit of overhead.

CROPS is great, and we’ll be taking a similar approach that’s a bit more developer-friendly.

A Clean Solution: VSCode’s Dev Containers

Dev Containers are a feature of VSCode that allows a repository owner to craft a development environment based on Docker containers. Because we’re using containers, the only requirement of our developers is that they have Docker installed. Easy. This development environment is configured in a devcontainer.json file that lives in a .devcontainer folder at the root of the project. When developers open a repository with a Dev Container in VSCode, they’re prompted to reopen the codebase inside the container-based development environment.

Let’s walk through the setup of a VSCode Dev Container-based development environment for Yocto.

1. Increase Docker Desktop’s Resource Limits

Yocto projects take up a lot of space - like 100GB - so you’ll need to double-check your Docker Dekstop configuration such that the Virtual disk limit is at least 120GB (to give some room for other stuff). You’ll also want to set the CPU and memory limits to their max so that your build takes full advantage of the machine.

On Mac or Windows:

  1. Open Docker Desktop’s dashboard
  2. Navigate to Settings > Resources
  3. Increase your CPU and Memory usage to max; ensure that the Virtual Disk has >120GB of space

2. Obtain a Yocto project

If you don’t already have a Yocto repo to work with, now is a good time to clone Poky, a Yocto template project, and open it in VSCode. In the below example, I use Poky’s kirkstone branch, which uses the kirkstone version of Yocto and has long-term support until 2026. If you already have a Yocto project, open it in VSCode.

git clone git://git.yoctoproject.org/poky -b kirkstone
cd poky
code .

3. Set up the Dockerfile

As suggested by the name, Dev Containers use containers to run development environments. To begin, we’ll add a folder called .devcontainer to the root of our project and then create a Dockerfile there.

Add this to .devcontainer/Dockerfile:

FROM ubuntu:22.04

# Install packages per Yocto's requirements
RUN apt update && export DEBIAN_FRONTEND=noninteractive \
    && apt install -y --no-install-recommends \
    sudo \
    gawk \
    wget \
    git \
    diffstat \
    unzip \
    texinfo \
    gcc \
    build-essential \
    chrpath \
    socat \
    cpio \
    python3 \
    python3-pip \
    python3-pexpect \
    xz-utils \
    debianutils \
    iputils-ping \
    python3-git \
    python3-jinja2 \
    libegl1-mesa \
    libsdl1.2-dev \
    python3-subunit \
    mesa-common-dev \
    zstd \
    liblz4-tool \
    file \
    locales \
    libacl1 \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*

# Set default locale to en_US.UTF-8
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
    echo 'LANG="en_US.UTF-8"'>/etc/default/locale && \
    dpkg-reconfigure --frontend=noninteractive locales && \
    update-locale LANG=en_US.UTF-8

# Set up a non-root user
ARG USER_NAME=yocto
ARG HOST_UID=1000
ARG HOST_GID=$HOST_UID

# Create the user and its group
RUN groupadd --gid $HOST_GID $USER_NAME && \
    useradd --gid $HOST_GID --uid $HOST_UID --shell /bin/bash --create-home $USER_NAME && \
    # Add sudo support for the user
    usermod -aG sudo $USER_NAME && \
    # Don't require a password for sudo
    echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

USER $USER_NAME

Kinda Quick Explanation

We set up our Dockerfile such that it adheres to the system requirements of the Yocto project.

We’ll start by using Ubuntu 22.04, which is officially supported, and then install the required packages listed in Yocto’s documentation. There are some flags in the package installation command to say that we’re non-interactive (meaning the user isn’t able to interact with the installation process with the terminal) and to auto-approve (-y flag) all of the package installs. We also clean up a bit afterward so that our container stays slim.

Then we set the default locale to en_US.UTF-8. I had to Google around for a minute on this one. But if you don’t have this set, Yocto will get upset, so you’ve gotta set that locale.

Then we set up a non-root user. By default, the Linux user in containers is root. Again, Yocto will complain. So we set up a new user named yocto and use the USER command in the Dockerfile to make that the container’s user.

4. Set up the dev container

Add this to .devcontainer/devcontainer.json:

{
    "name": "Yocto",
    "build": {
        // Use a Dockerfile to build a Yocto-compatible Linux environment
        "dockerfile": "Dockerfile"
    },

    // Mount the build directory as a Docker Volume to persist build artifacts
    // while still using a Yocto-compatible filesystem
    "mounts": [
        "source=${localWorkspaceFolderBasename}-build,target=${containerWorkspaceFolder}/build,type=volume"
    ],
    "postCreateCommand": "sudo chown yocto build && sudo chgrp yocto build"
}

Kinda Quick Explanation

In the build section, we specify that our dev container should be built from a Dockerfile in our repo at the provided path.

The mounts section indicates that we’d like to mount a Docker-managed volume (aka Named Volume) at the path /workspaces/repo-name/build (${containerWorkspaceFolder} resolves to /workspaces/repo-name).

If we didn’t have this, then by default, VSCode would use a bind mount to mount the repo’s folder, which resides on the host (aka your Mac or Windows OS), into the container. This behavior is similar to what you’d see in Linux when you mount an external hard drive. This is problematic because that mounted folder inherits the behaviors of your host’s filesystem. In the case of Mac and Windows machines, their filesystems are “Case Insensitive”, meaning that /Dog.txt and /dog.txt are treated as the same file. Why? Who’s to say? But that’s not how things work in Linux and will cause problems when building Yocto projects.

In this configuration, we use a Docker Volume just for the build folder. Docker will manage this for us, and it’ll do it in a way that’s compatible with Yocto. We chose this folder because that’s where most of Yocto’s work is done, and that’s where Yocto cares about the filesystem. Read more here. The rest of the files are still bind mounted, but Yocto doesn’t care about their filesystem as much.

In the postCreateCommand, we change the permissions of the build folder we mounted to be owned by the container’s user (yocto). By default, the owner is set to root, so our yocto user wouldn’t have permission to use it.

5. Run the dev container 🎉

In VSCode, open the command pallet (cmd-shift-p), type “Dev Containers”, and select Dev Containers: Rebuild and Reopen in Container. Then just sip ur coffee and hang tight - VSCode will tell Docker to build the container, then it will reopen itself in the container. If something fails, make sure Docker Desktop is running.

Once VSCode has reopened the codebase, open a terminal in VSCode and build the example image:

source oe-init-build-env
bitbake core-image-sato

Now sit back and relax for at least one, maybe two, extended editions of Lord of the Rings. This first build will take a few hours.

Use Dev Containers for projects with toolchains

Dev Containers with VSCode are ridiculously convenient. They improve output for your dev team by allowing them to get started immediately without fiddling with environment setup, and they help ensure repeatable builds between various systems. For most codebases that entail a tedious software toolchain, Dev Containers are the way to go.

You can modify the steps above to launch a container with a C, C++, or even a FORTRAN toolchain with relative ease. The only caveat is that projects that produce GUIs will be tricky, but they’re still workable with X11 forwarding.

Ringtail Labs can help your team

We’re experts in DevOps and embedded systems. If you need help architecting your infrastructure, improving your development team, or working with Yocto, you should schedule a meeting with us. We’re happy to help!

Any errors or omissions? Please let us know at hello@ringtaillabs.com