The Yocto Project is an open-source collaboration that provides templates, tools, and methods to create custom Linux-based systems for embedded hardware, regardless of the underlying architecture. It isn't a single embedded Linux distribution — it's a build system that helps you create your own.
Building a custom embedded Linux image by hand — cross-compiling a toolchain, kernel, bootloader, and root filesystem for a specific board — is slow and error-prone. Yocto standardizes this into a reproducible, metadata-driven build process so you can target new hardware, swap components, and maintain long-term builds without starting from scratch.
MACHINE.| Component | Role |
|---|---|
| Poky | The reference distribution — a working combination of BitBake + OpenEmbedded-Core metadata used as a starting point. |
| BitBake | The task-execution/build engine that parses recipes and resolves dependencies to build packages and images. |
| OpenEmbedded-Core (OE-Core) | Core metadata (recipes/classes) shared across OpenEmbedded-based distributions, including Yocto. |
| Metadata | Recipes (.bb), append files (.bbappend), configuration (.conf), and classes (.bbclass) that describe what to build. |
| BSP Layers | Board Support Package layers containing machine-specific configuration, kernel, and bootloader recipes. |
BitBake reads recipes and their dependency graph, then executes a series of tasks (fetch, unpack, patch, configure, compile, install, package) for each recipe in the correct order, using a scheduler similar in spirit to make but distribution-aware and cross-compilation-first.
# source the build environment (creates/enters build/ dir) source oe-init-build-env # build a full image for the configured MACHINE bitbake core-image-minimal # build just one recipe/package bitbake busybox # see the full task list bitbake would run, without running them bitbake -g core-image-minimal # generates pn-depends.dot / task-depends.dot # force re-execution of a specific task bitbake -c compile -f busybox bitbake -c cleansstate busybox # wipe shared-state cache for a recipe # jump into a recipe's work directory to debug interactively bitbake -c devshell busybox
A layer is simply a structured directory of recipes and configuration that can be stacked on top of others. This layered model is what lets you cleanly add a BSP, a middleware stack, or your own product customizations without touching upstream metadata.
meta-raspberrypi, meta-ti, meta-freescale).# create a new layer skeleton with the right structure bitbake-layers create-layer meta-mylayer # add it to bblayers.conf so BitBake picks it up bitbake-layers add-layer ../meta-mylayer # list currently enabled layers and their priority bitbake-layers show-layers # see which layer/recipe provides a given package bitbake-layers show-recipes busybox bitbake-layers show-appends
A recipe (.bb file) describes how to fetch, configure, build, and package a single piece of software. Recipes can be extended non-destructively with .bbappend files in another layer, which is the standard way to customize upstream recipes.
SUMMARY = "A simple example application"
DESCRIPTION = "Demonstrates a minimal Yocto recipe for a C program"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "git://github.com/example/myapp.git;branch=main;protocol=https"
SRCREV = "abcdef1234567890abcdef1234567890abcdef12"
S = "${WORKDIR}/git"
DEPENDS = "zlib"
inherit cmake
do_install:append() {
install -d ${D}${bindir}
install -m 0755 ${B}/myapp ${D}${bindir}/myapp
}
FILES:${PN} = "${bindir}/myapp"
# placed in meta-mylayer/recipes-myapp/myapp/myapp_%.bbappend
# extends the upstream myapp recipe without editing it directly
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
SRC_URI += "file://0001-fix-custom-config.patch"
do_install:append() {
install -d ${D}${sysconfdir}/myapp
install -m 0644 ${WORKDIR}/myapp.conf ${D}${sysconfdir}/myapp/
}
Classes (.bbclass) capture shared build logic — like autotools.bbclass, cmake.bbclass, or kernel.bbclass — so recipes can inherit common behavior instead of repeating boilerplate. Every recipe is built up from a fixed sequence of tasks.
| Task | What Happens |
|---|---|
do_fetch | Downloads source from SRC_URI (git, http, local files, etc.) into the download cache. |
do_unpack | Unpacks fetched sources into the working directory (${WORKDIR}). |
do_patch | Applies any patches listed in SRC_URI. |
do_configure | Runs the project's configure step (autotools, cmake, meson, etc.). |
do_compile | Cross-compiles the source using the generated toolchain. |
do_install | Installs build output into ${D}, a staging "destination" root. |
do_package | Splits installed files into one or more binary packages (ipk/deb/rpm). |
do_rootfs | (Image-level) Assembles all selected packages into the final root filesystem. |
A typical first build — targeting the QEMU emulator so you don't even need real hardware — looks like this:
# 1. clone poky (the reference distro / build system) git clone -b kirkstone git://git.yoctoproject.org/poky cd poky # 2. set up the build environment (creates build/conf/local.conf) source oe-init-build-env # 3. (edit build/conf/local.conf to set MACHINE, e.g. qemux86-64) # 4. kick off the build - this compiles the toolchain, kernel, and rootfs bitbake core-image-minimal # 5. boot the resulting image under QEMU runqemu qemux86-64
The first build can take hours since it compiles an entire cross-toolchain and every package from source — subsequent builds are much faster thanks to shared state (sstate) caching, which reuses previously built task output whenever its inputs haven't changed.
Custom images are just recipes that inherit core-image and declare which packages to install, typically placed in your own layer under recipes-core/images/.
SUMMARY = "Custom product image with SSH, Python, and myapp"
LICENSE = "MIT"
inherit core-image
IMAGE_FEATURES += "ssh-server-openssh package-management"
IMAGE_INSTALL:append = " \
myapp \
python3 \
python3-pip \
htop \
nano \
"
IMAGE_ROOTFS_EXTRA_SPACE = "1048576"
MACHINE, DISTRO, parallelism (BB_NUMBER_THREADS), download/sstate directories.:append rather than overriding outright.systemd, wayland, bluetooth) that gate which recipes get built with which options.local.conf is per-build-tree and untracked; a proper distro layer's .conf is versioned and reusable across builds.Yocto can also generate a standalone cross-toolchain (an "extensible SDK" or classic SDK) so application developers can build software for the target without needing the full BitBake build environment.
# build a relocatable SDK matching your image bitbake core-image-minimal -c populate_sdk # or the extensible SDK (includes devtool, faster iteration) bitbake core-image-minimal -c populate_sdk_ext # install the generated SDK (produces environment-setup-* script) ./tmp/deploy/sdk/poky-glibc-x86_64-core-image-minimal-*.sh # use it to cross-compile an app against the target sysroot source /opt/poky/*/environment-setup-* $CC myapp.c -o myapp # devtool: iterate on a recipe/source without a full rebuild devtool modify myapp devtool build myapp devtool deploy-target myapp root@192.168.1.50
bitbake-layers show-recipes <name> to check what's actually available.bitbake -c devshell <recipe> to reproduce the exact cross-compile environment interactively.bitbake -c cleansstate <recipe> before rebuilding.LICENSE/LIC_FILES_CHKSUM or bad file placement will fail the build by design, not by accident.tmp/work/<arch>/<recipe>/<version>/temp/log.do_<task>; always check these before assuming a mysterious failure.| Command | What It Does |
|---|---|
source oe-init-build-env [dir] | Set up/enter a BitBake build environment. |
bitbake <target> | Build a recipe or image target. |
bitbake -c <task> <target> | Run a specific task (e.g. compile, devshell, menuconfig) for a target. |
bitbake -e <target> | grep ^VAR= | Inspect the final expanded value of a variable for a recipe. |
bitbake-layers show-layers | List all layers currently configured in the build. |
devtool modify <recipe> | Extract a recipe's source into a local workspace for iterative development. |
runqemu <machine> | Boot a built image in the QEMU emulator. |
wic list images | List available disk image (.wic) definitions for flashing to media. |
You need to customize an existing upstream recipe (add a patch and an extra config file) without editing it directly. What's the correct Yocto mechanism?
.bbappend file in your own layermeta/ directlysed on the generated package after the buildThe official docs are dense but authoritative — pairing them with a few community resources makes the learning curve much more manageable: