- Installing WSL
- Setup Docker
- Using Poky Container
- Yocto build
- Beaglebone Black
- Raspberry Pi
- Adding a CustomLayer
- Using wxWidgets
- Creating an Image Recipe
- Making an SDK
Installing WSL
To install WSL on your Windows machine, open PowerShell with administrator privileges. As an example, install Ubuntu 22.04 by running the following commands:wsl --install -d Ubuntu-22.04 wsl -l -v
Once the installation is complete, launch Ubuntu from the Start menu and create your Linux username and password. For detailed instructions, refer to the WSL Setup Guide here [1]. If you encounter issues installing WSL, open "Turn Windows features on or off" from the Windows search bar and enable Virtual Machine Platform and Windows Subsystem for Linux. In some cases, you may also need to check and enable virtualization technology (Intel VT-x or AMD-V) in your BIOS settings.
It's often beneficial to move your distribution to a drive that has ample space, such as 300 GB, especially if you plan to install many tools or store large projects.
# powershell in admin mode wsl --shutdown wsl --export Ubuntu-22.04 "D:\WSL\temp\ubuntu-export.tar" wsl --unregister Ubuntu-22.04 wsl --import Ubuntu D:\WSL\Ubuntu "D:\WSL\temp\ubuntu-export.tar" # delete temp\ubuntu-export.tar
Thereafter you can install essential host packages on your WSL Ubuntu build host by running:
sudo apt-get update sudo apt-get install build-essential chrpath cpio debianutils diffstat file gawk gcc git iputils-ping libacl1 liblz4-tool locales python3 python3-git python3-jinja2 python3-pexpect python3-pip python3-subunit socat texinfo unzip wget xz-utils zstd
You also need to make sure locale en_US.UTF-8 is available on your system.
locale -a # check if it lists en_US.UTF-8 sudo nano /etc/locale.gen # uncomment en_US.UTF, write ctrl+o , and exit ctrl+x sudo locale-gen
Alternatively, you can just use poky container as discussed in the following section. If you do not plan to use Docker, you may skip ahead to the Yocto build section.
Setup Docker
The next option is to install Docker in your WSL Ubuntu distribution. Use the following commands to install Docker non-interactively with the convenience script provided by Docker:sudo apt install curl curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh
For more information, please refer to the following resources: Verify the installation:
sudo service docker start sudo docker run hello-world
Add user to docker group and run docker without sudo:
# sudo groupadd docker sudo usermod -aG docker $USER newgrp docker docker run hello-world
Allow local root for xserver:
sudo apt install x11-xserver-utils xhost +local:root grep -qxF 'xhost +local:root' ~/.profile || echo 'xhost +local:root' >> ~/.profile
Using Poky Container
Pull the Poky container from Docker hub:docker pull crops/poky:ubuntu-22.04
Or, you can use a Poky Container from GitHub conttainer registry, available as: Pull the Docker image from ghcr.io using the following command [6]:
docker pull ghcr.io/crops/poky:ubuntu-22.04
An important step is to create a workspace directory on the Linux host. This ensures that any output generated inside the container will be accessible by the user on the host system. You can create a directory named workdir in WSL:
# in WSL root directory, it must not be in Windows file system like /mnt/c/ sudo mkdir /workdir sudo chown $USER:$USER /workdir
To run the container, use the following command:
docker run --rm -it -v /workdir:/workdir \ crops/poky:ubuntu-22.04 --workdir=/workdir
Or, in case, you want to use a specific image from GitHub Container Registry such as ubuntu-22.04, use the following command:
docker run --rm -it -v /workdir:/workdir \ ghcr.io/crops/poky:ubuntu-22.04 \ --workdir=/workdir
Yocto Build
Create a directory "workdir" in WSL, if you haven't done so already.sudo mkdir /workdir sudo chown $USER:$USER /workdir
Once inside the workdir on WSL or the container, you can follow the instructions at The first step is to select a release to use; check for a stable version at the Yocto Project Releases page. Clone the Poky repository and checkout to scarthgap branch as follows:
# navigate to work dir cd /workdir git clone git://git.yoctoproject.org/poky cd poky git branch -a # check https://wiki.yoctoproject.org/wiki/Releases for stable git checkout -t origin/scarthgap -b my-scarthgap # go back to workdir cd ..
Navigate to work directory, initialize the build environment by running oe-init-build-env script:
source poky/oe-init-build-env
The script creates a "build" directory, and generates a local configuration file "local.conf" inside "build/conf". Firstly, you can build and test the default qemux86-64 machine. In your build environment console, run the BitBake command with the -k (--continue) option to continue building despite errors, which helps in identifying multiple issues at once:
bitbake -k core-image-minimal
You may need to run this command multiple times until the build completes successfully. When encountering an AppArmor error, which is a known issue particularly with Ubuntu 24.04 and later distributions, while running BitBake, you can resolve it by creating the file /etc/apparmor.d/bitbake with the following content:
abi < abi/4.0 >,
include < tunables/global >
# Profile for the bitbake executable to allow unconfined execution
profile bitbake /**/bitbake/bin/bitbake flags=(unconfined) {
userns,
}
After saving the file, apply the new profile immediately without rebooting by running this command in your terminal:
sudo apparmor_parser -r /etc/apparmor.d/bitbake
Once the build is completed, run the qemu without using graphic.
runqemu qemux86-64 slirp nographic
Enter 'root' as the username to login. No password is required.
Press:
ctrl+a x
to exit from qemu.
Beaglebone Black
The next step is to edit "local.conf" to change the target machine as Beaglebone Black. You can skip to the next section - Raspberry Pi if you want to build for Raspberry Pi. If your current console is inside a Docker container, you can open a separate WSL Ubuntu 22.04 terminal to access and edit the configuration file on the host system. Navigate to "build/conf" directory under workdir and open "local.conf" with your preferred editor, for example:# navigate to work dir cd /workdir cd build/conf nano local.conf # or if you prefer to use Visual Studio Code # code local.conf
Find and uncomment line 33 (or the relevant line) to specify the machine target to Beaglebone Black.
MACHINE ?= "beaglebone-yocto" #MACHINE ?= "genericarm64" #MACHINE ?= "genericx86"
Return to your build environment console and run the BitBake command again.
bitbake -k core-image-minimal
Once the build completes successfully, you can find the resulting image and flash it onto a micro SD card. In the windows file explorer, go to "Linux > Ubuntu-22.04 ", and open the build directory. Under "tmp/deploy/images/beagle…/", look for the biggest file with .wic extension. In my case, the path is as follow:
\\wsl.localhost\Ubuntu-22.04\workdir\build\tmp\deploy\images\beaglebone-yocto
where, username is your Linux username. Copy this file to a Windows folder like Downloads, and then use balenaEtcher to flash the image onto a micro SD card.
To connect to the BBB, you can use a serial console. Attach an FTDI cable to the J1 header on the BeagleBone Black (BBB), ensuring the GND wire is connected to pin 1, which is located near the small '.' mark. You can then use PuTTY or Tera Term to connect to the COM port, setting the baud rate to 115200.
After flashing the image, insert the microSD card into the Beaglebone Black (BBB) and prepare a 5V power supply to power it on. By default, the BBB boots from the onboard eMMC. To boot from the SD card, press and hold the boot button S2 (located near the USB port) while powering on the board.
When the login prompt beaglebone-yocto login: appears, enter root as the username. No password is required.
Raspberry Pi
The Board Support Package (BSP) layer includes the bootloader, device tree blobs, drivers, firmware, and additional software necessary to enable support for a specific hardware device or device family within Yocto. The layer name for a machine is typically prefixed with meta-. You can find available layers on the OpenEmbedded Layer Index:The GitHub repositories for BSP layers and other layers are available at:
For Raspberry Pi, after navigating to the working directory located one level above the poky directory, clone the appropriate branch of the BSP meta-raspberrypi layer and other dependency layers as follows:
# navigate to workdir cd /workdir git clone git://git.yoctoproject.org/meta-raspberrypi -b scarthgap git clone git://git.openembedded.org/meta-openembedded -b scarthgap
Next, return to the workdir directory and set up a new build environment for Raspberry Pi with the directory name build-rpi by running:
source poky/oe-init-build-env build-rpi
The script creates a "build-rpi" directory, and generates a local configuration file "local.conf" inside "build/conf". Check bitbake layers
bitbake-layers show-layers
and add all necessary layers, including meta-raspberrypi, in the correct order.
# need ../ infront because the layers are in the dir 1 levels above the build dir bitbake-layers add-layer ../meta-openembedded/meta-oe bitbake-layers add-layer ../meta-openembedded/meta-python bitbake-layers add-layer ../meta-openembedded/meta-networking bitbake-layers add-layer ../meta-openembedded/meta-multimedia bitbake-layers add-layer ../meta-raspberrypi bitbake-layers show-layers
After rechecking the BitBake layers, there should be a total of eight layers configured. List the supported machines in meta-raspberrypi:
ls ../meta-raspberrypi/conf/machine
Edit the conf/local.conf file.
nano conf/local.conf
And add the following line near the existing beaglebone-yocto configuration, adjusting it according to your hardware board:
MACHINE ?= "raspberrypi5"
You can comment out the default machine line also.
# MACHINE ??= "qemux86-64"
Additionally, append ssh-server-openssh to the EXTRA_IMAGE_FEATURES variable (typically around line 150) in the conf/local.conf file as follows:
EXTRA_IMAGE_FEATURES ?= "debug-tweaks ssh-server-openssh"
By default, commercial or restricted licenses are not allowed. To enable specific restricted licenses, such as synaptics-killswitch, add the following line to your local.conf file:
LICENSE_FLAGS_ACCEPTED = "synaptics-killswitch"
Raspberry Pi 5 has a UART debug port [2, 3] which is located between two HDMI connectors. The serial console for this debug port is always enabled. For older Raspberry Pis, if you want to enable the UART on the GPIO header, include this line in your local.conf file:
ENABLE_UART = "1"
To list the available image recipes, run:
ls ../meta-raspberrypi/recipes-core/images/
Finally, build the rpi-test-image using BitBake with the following command:
bitbake rpi-test-image
You can also use machine-agnostic images like core-image-minimal. Once the build completes successfully, locate the resulting image file named "rpi-test-image-raspberrypi5.rootfs-yyyymmddhhmmss.wic.bz2" in the following directory:
\\wsl.localhost\Ubuntu-22.04\workdir\build-rpi\tmp\deploy\images\raspberrypi5
Copy this file to a Windows folder like Downloads, extract it to obtain the wic image. Alternatively, you can extract and copy in WSL also.
cd /workdir/build-rpi/tmp/deploy/images/raspberrypi5 ls *.wic.bz2 cp rpi-test-image-raspberrypi5.rootfs-yyyymmddhhmmss.wic.bz2 rpi-img.wic.bz2 bunzip2 rpi-img.wic.bz2 ls *.wic cp rpi-img.wic /mnt/c/Users/username/Downloads/
And then flash the wic image onto a microSD card using balenaEtcher, following the same steps as before.
For the Raspberry Pi, connect a display to the first HDMI port, located near the USB-C power connector. Next, attach a keyboard and mouse to the USB ports to interact with the console.
Alternatively, you can use serial console by connecting a Raspberry Pi Debug Probe to the Raspberry Pi's UART port as shown below.
For older Raspberry Pi models, connect an FTDI cable to the GPIO header pins, as illustrated in the following figure.
After supplying power via USB-C connector, when the login prompt raspberrypi5 login: appears, enter root as the username. No password is required.
If your Raspberry Pi is connected to a network, you can find its IP address by running:
ifconfig
To connect via SSH, use PuTTY.
- Select SSH as the connection type,
- Enter the IP address or hostname "raspberrypi5.local"
- Set the port to 22.
- Then connect to your Raspberry Pi.
Adding a Custom Layer
You can add packages to your custom image by appending them to the IMAGE_INSTALL variable in the local.conf file which is located inside 'conf' under your build directory. For example, to add nano text editor to your custom image, append the following line to your local.conf file with a space infront of nano:IMAGE_INSTALL:append = " nano"
You can also enable predefined image features in your image, which usually pull in a set of packages or configuration tweaks. For example, to enable the SSH server, and development tools, append the following line:
EXTRA_IMAGE_FEATURES ?= "debug-tweaks ssh-server-openssh tools-sdk"
To add a custom layer to your Yocto build environment, you can use bitbake-layers command with create-layer as shown below.
# navigate to work dir cd /workdir # navigate to build dir or create build env if you hanen't done so source poky/oe-init-build-env build-rpi bitbake-layers create-layer ../meta-myapp
This command creates a new layer named meta-myapp in the workspace directory. You can navigate to the newly created layer directory and check the structure, rename recipe directory, and file names as needed:
cd /workdir/meta-myapp tree mv recipes-example recipes-myapp cd recipes-myapp mv example myapp cd myapp mv example_0.1.bb myapp_0.1.bb mkdir -p files cd files
Thereafter, create a C++ source file - myapp.cpp inside the 'files' directory:
#include < iostream >
int main() {
std::cout << "myapp: Hello Yocto World!" << std::endl;
return 0;
}
You can also check md5 checksum of the COPYING.MIT file in the meta-myapp directory.
md5sum ../../../COPYING.MIT
Next, edit the recipe file myapp_0.1.bb with the following content which is located in the directory one level above:
SUMMARY = "Simple myapp application"
DESCRIPTION = "myapp test"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://../../../COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
SRC_URI = "file://myapp.cpp \
file://../../../COPYING.MIT"
S = "${WORKDIR}"
do_compile() {
${CXX} ${CXXFLAGS} ${LDFLAGS} -o myapp myapp.cpp
}
do_install() {
install -d ${D}${bindir}
install -m 0755 myapp ${D}${bindir}
}
Add the application to your local.conf file:
IMAGE_INSTALL:append = " nano myapp"
Then, add the new layer to your build environment, and build the image:
cd /workdir/build-rpi bitbake-layers add-layer ../meta-myapp bitbake myapp bitbake rpi-test-image
After deploying the resulting image, you can test your application by running:
my-app
It should print out the message defined in your program.
Using wxWidgets
Since wxwidgets recipe is in the meta-oe layer (OpenEmbedded layer), make sure you have meta-oe layer added to your build environment as described in the Raspberry Pi section. Then, a new layer for wxWidgets application can be created using the similar steps used in the previous section.cd /workdir/build-rpi bitbake-layers create-layer ../meta-wxapp
In the WSL console, navigate to the created directory, and rename the directories and files accordingly.
cd /workdir/meta-wxapp tree mv recipes-example recipes-wxapp cd recipes-wxapp mv example wxapp cd wxapp mv example_0.1.bb wxapp_0.1.bb mkdir -p files cd files
Create wxapp.cpp file for the following program inside 'files' directory:
#include < wx/wx.h >
class Simple : public wxFrame
{
public:
Simple(const wxString& title);
};
Simple::Simple(const wxString& title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 150))
{
Centre();
}
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
Simple *simple = new Simple(wxT("Simple"));
simple->Show(true);
return true;
}
And update the recipe file wxapp_0.1.bb as follows:
SUMMARY = "Simple wxapp application"
DESCRIPTION = "wxapp test"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://../../../COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
SRC_URI = "file://wxapp.cpp \
file://../../../COPYING.MIT"
DEPENDS += "wxwidgets"
inherit pkgconfig
S = "${WORKDIR}"
do_compile() {
wx-config --version
${CXX} ${CXXFLAGS} ${LDFLAGS} wxapp.cpp -o wxapp `wx-config --cxxflags --libs`
}
do_install() {
install -d ${D}${bindir}
install -m 0755 wxapp ${D}${bindir}
}
Thereafter, patch for mesa and append wxwidgets and wxapp applications to IMAGE_INSTALL in local.conf:
DEPENDS:append:pn-mesa = " libxshmfence" IMAGE_INSTALL:append = " libxshmfence wxwidgets wxapp"
Finally, add the new layer to your build environment, then build the core-image-weston image, which includes the GUI environment.
# navigate back to build directory cd /workdir/build-rpi bitbake-layers add-layer ../meta-wxapp bitbake wxapp bitbake core-image-weston
Once the build is complete, flash the resulting image onto a microSD card. Connect a display to the first HDMI port and power on the Raspberry Pi 5. The figure below shows the application running after opening the terminal and entering the command wxapp.
Making an Image Recipe
Rather than making local configuration changes, you can create a reusable image recipe that can be shared and maintained more easily. Image recipes are typically in images directory and you can list them as follow:# from workdir ls ./*/meta*/recipe*/images*/*.bb
Create a file namely myapp-image.bb in meta-myapp/recipes-local/images directory.
cd /workdir cd meta-myapp mkdir -p recipes-local/images cd recipes-local/images cd images nano myapp-image.bb
Include base image and add the changes similar to the changes made in conf/local.conf. The content of the recipe can be as follows:
require recipes-core/images/core-image-minimal.bb # require recipes-graphics/images/core-image-weston.bb IMAGE_INSTALL += "wxwidgets nano myapp"
After setting up the build environment and adding the meta-myapp layer, build the image using the following command:
# The commented commands are only necessary if you haven't already # set up the environment and added the layer # Navigate to your working directory # cd /workdir # Initialize the build environment for your build directory # source poky/oe-init-build-env build-rpi # Add the meta-myapp layer to your build configuration # bitbake-layers add-layer ../meta-myapp # Build your custom image bitbake myapp-image
For myapp-image, you no longer need to append those packages to the IMAGE_INSTALL:append line in your local.conf file.
Generating an SDK
You can create SDK for an image by using bitbake with populate_sdk:bitbake -c populate_sdk myapp-image
This command generates the toolchain with development libraries and include files for the target, along with an SDK installer script in the following directory:
tmp/deploy/sdk
The name of the script depends on the machine and the settings, and in the above case it is as follow:
poky-glibc-x86_64-myapp-image-cortexa76-raspberrypi5-toolchain-5.0.11.sh
You can share this script, or run it on a host system to install the SDK. The default installation directory is /opt/poky/5.0.11. You need to setup the environment before using the SDK by running the following command:
source /opt/poky/5.0.11/environment-setup-cortexa76-poky-linux
It is recommended to open a new terminal window to set up the toolchain environment. This helps prevent any potential conflicts with your current build environment. Setting up the toolchain will define environment variables like $CC for the C compiler and $CXX for the C++ compiler. You can verify the C++ compiler variable by running:
echo $CXX
Next, create a simple C++ program named hello.cpp that prints "hello". Cross-compile it using the following command:
$CXX hello.cpp -o hello
After compilation, transfer the generated binary hello to the target machine, where it should execute correctly.
References
[1] Microsoft. Set up a WSL development environment. 2025.
url: https://learn.microsoft.com/en-us/windows/wsl/setup/environment.
[2] Raspberry Pi. Raspberry Pi 3-pin Debug Connector Specification.
url: https://rptl.io/debug-spec.
[3] Raspberry Pi. Raspberry Pi Debug Probe.
url: https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html.
[4] Docker. Install Docker Engine on Ubuntu.
url: https://docs.docker.com/engine/install/ubuntu/.
[5] Docker. GitHub repository docker/docker-install.
url: https://github.com/docker/docker-install.
[6] crops. poky-container.
url: https://github.com/crops/poky-container.










No comments:
Post a Comment
Comments are moderated and don't be surprised if your comment does not appear promptly.