Back to Articles

Yocto Cheat Sheet: BitBake Commands, Recipes, Variables and Debugging

A practical Yocto cheat sheet covering common BitBake commands, recipes, bbappend files, variables, packages, debugging and useful build paths.

Yocto Cheat Sheet: BitBake Commands, Recipes, Variables and Debugging

I spend a lot of time working with Yocto, but I still don’t remember every BitBake command or variable I need. Some commands are used every day; others are needed just infrequently enough that I have to look them up each time.

This is my cheat sheet for the Yocto and BitBake commands I use regularly when building, debugging and modifying images.

Common BitBake commands

What I want to doCommand
Build a recipe or imagebitbake <recipe>
Build a specific taskbitbake -c <task> <recipe>
List available tasksbitbake -c listtasks <recipe>
Open a development shellbitbake -c devshell <recipe>
Clean a recipebitbake -c clean <recipe>
Remove recipe output and shared statebitbake -c cleansstate <recipe>
Inspect the BitBake environmentbitbake -e <recipe>
Inspect the BitBake environment for a specific variablebitbake-getvar -r <recipe> variable
Show configured layersbitbake-layers show-layers
Find recipesbitbake-layers show-recipes
Find .bbappend filesbitbake-layers show-appends
Show package contentsoe-pkgdata-util list-pkg-files <package>
Find which package contains a fileoe-pkgdata-util find-path <path>

The rest of this cheat sheet goes into each of these areas in more detail.

Building with BitBake

Build an image or recipe:

bitbake core-image-minimal

BitBake resolves the dependencies, executes any tasks that need to run and produces the requested target.

Multiple targets can be specified:

bitbake busybox dropbear

Run a specific task

Use -c to execute a particular task:

bitbake -c compile busybox

Common tasks include:

do_fetch
do_unpack
do_patch
do_configure
do_compile
do_install
do_package
do_deploy

The do_ prefix can normally be omitted when using -c:

bitbake -c compile busybox

rather than:

bitbake -c do_compile busybox

List the tasks for a recipe

bitbake -c listtasks busybox

This is useful when a recipe or class adds tasks that you aren’t familiar with.

Force a task to run

bitbake -c compile -f busybox

-f marks the requested task as forced.

Be careful with this during normal development. BitBake’s dependency and signature handling is there for a reason, and repeatedly forcing tasks can hide problems with the metadata.

Cleaning recipes

There are several levels of cleaning.

Clean generated output

bitbake -c clean busybox

This removes the recipe’s generated output from its work directory.

Remove output and shared state

bitbake -c cleansstate busybox

This also removes the recipe’s shared-state output so that BitBake cannot simply restore the task from the local sstate cache.

This is useful when I want to make sure a recipe is rebuilt rather than restored from previously generated shared state.

Remove downloaded source

bitbake -c cleanall busybox

cleanall goes further and removes downloaded source files as well.

I rarely need this. If I’m debugging a build problem, cleansstate is normally a better starting point.

Layers

Show configured layers

bitbake-layers show-layers

For example:

layer             path                                           priority
=========================================================================
core              /work/layers/third-party/openembedded-core/meta       5
meta-oe           /work/layers/third-party/meta-openembedded/meta-oe    5
meta-project      /work/layers/project/meta-my-project                 10

This is one of the first commands I use when checking an unfamiliar build.

It tells me which layers BitBake is actually using, rather than which layers happen to exist in the source tree.

Create a layer

bitbake-layers create-layer ../layers/project/meta-example

Add a layer

bitbake-layers add-layer ../layers/project/meta-example

This adds the layer to BBLAYERS in bblayers.conf.

Finding recipes

List the recipes BitBake knows about:

bitbake-layers show-recipes

Find a particular recipe:

bitbake-layers show-recipes | grep busybox

This also helps when several layers provide versions of the same recipe.

Show recipes from a particular layer

bitbake-layers show-recipes --layer meta-openembedded

Which recipe version will BitBake use?

The selected recipe can be seen with:

bitbake-layers show-recipes busybox

If several versions exist, the output shows which one is preferred.

You can also inspect:

bitbake-getvar -r busybox PV

to see the final version BitBake has selected.

.bb recipe files

A BitBake recipe normally has the extension:

.bb

For example:

busybox_1.36.1.bb

The filename usually contains:

<recipe-name>_<version>.bb

Inside the recipe, BitBake derives:

PN
PV

where PN is the recipe name and PV is its version.

For the example above:

PN = "busybox"
PV = "1.36.1"

.bbappend files

A .bbappend modifies an existing recipe without requiring you to edit or copy the original .bb file.

For example:

busybox_1.36.1.bbappend

applies to:

busybox_1.36.1.bb

This is particularly useful when the original recipe belongs to another layer.

See the article .bbappend files in a Yocto Project for more details.

Match multiple recipe versions

A % wildcard can be used in the filename:

busybox_%.bbappend

This allows the append to apply to different versions of the BusyBox recipe.

I use this when the change genuinely applies across versions. If a modification depends on a particular upstream version, explicitly matching that version is safer.

Show .bbappend files

bitbake-layers show-appends

Find appends relating to a particular recipe:

bitbake-layers show-appends | grep busybox

This is extremely useful when trying to work out where a recipe is being modified.

Check for unmatched .bbappend files

If an append exists but no matching recipe is available, BitBake will normally report it as an error during parsing.

A common cause is upgrading a layer containing:

foo_1.2.bb

to a version containing:

foo_1.3.bb

while another layer still contains:

foo_1.2.bbappend

Using:

foo_%.bbappend

can avoid this when the append is genuinely version-independent.

Adding files from a .bbappend

A common use for a .bbappend is to add configuration files, patches or systemd units to an existing recipe.

A typical layout is:

recipes-example/
└── example/
    ├── example_%.bbappend
    └── files/
        └── example.conf

The append can contain:

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI += "file://example.conf"

FILESEXTRAPATHS tells BitBake where else to look for files referenced by SRC_URI.

The file can then be installed with:

do_install:append() {
    install -d ${D}${sysconfdir}
    install -m 0644 ${WORKDIR}/example.conf \
        ${D}${sysconfdir}/example.conf
}

On newer Yocto releases where recipe sources are unpacked into UNPACKDIR, use the appropriate source location for the release and recipe you are working with rather than assuming ${WORKDIR}.

Inspecting variables

One of the most useful BitBake debugging commands is:

bitbake -e <recipe>

For example:

bitbake -e busybox

This prints the environment BitBake has constructed for the recipe after configuration files, classes, recipes, appends and overrides have been processed.

Find the value of a variable

bitbake-getvar -r busybox SRC_URI

Or:

bitbake-getvar -r busybox PACKAGECONFIG

See where a variable was changed

The output of bitbake -e and bitbake-getvar includes a history of the operations that produced many variables.

A useful approach is:

bitbake -e busybox | less

then search for:

/SRC_URI=

The comments above the final value show where assignments and modifications came from.

This is often far more useful than searching through layers manually.

BitBake variable assignment

BitBake has several assignment operators.

This is also covered in Lesson 6 of my training course.

Normal assignment

FOO = "bar"

The value is expanded when the variable is used.

Immediate expansion

FOO := "${BAR}"

The value is expanded when the assignment is parsed.

Set if undefined

FOO ?= "bar"

This assigns the value if FOO has not already been set.

Weak default

FOO ??= "bar"

This provides a weak default value.

Append with a space

FOO += "bar"

Prepend with a space

FOO =+ "bar"

Override-style append

FOO:append = " bar"

Notice the leading space. Override-style :append does not insert one automatically.

Override-style prepend

FOO:prepend = "bar "

Remove a value

FOO:remove = "bar"

Overrides

Overrides allow metadata to change according to the active configuration.

For example:

SRC_URI:append:my-machine = " file://my-machine.patch"

This addition is applied only when the my-machine override is active.

Machine-specific values are common:

KERNEL_DEVICETREE:my-machine = "vendor/my-board.dtb"

Other overrides can apply to distributions, architectures, classes and packages.

To see the active overrides:

bitbake -e <recipe> | grep '^OVERRIDES='

Appending and prepending tasks

Existing tasks can be extended without replacing them.

do_install:append() {
    install -d ${D}${sysconfdir}
    install -m 0644 ${UNPACKDIR}/example.conf \
        ${D}${sysconfdir}/example.conf
}

Or prepend commands:

do_configure:prepend() {
    echo "Running before configure"
}

This is generally preferable to replacing an entire task when you only need to add a small amount of behaviour.

Common recipe variables

These are some of the variables I encounter most often.

VariableMeaning
PNRecipe name
PVRecipe version
PRRecipe revision
WORKDIRRecipe work directory
UNPACKDIRDirectory used for unpacked source files on newer releases (Wrynose+)
SSource directory
BBuild directory
DDestination directory used by do_install
bindir/usr/bin
sbindir/usr/sbin
sysconfdir/etc
libdirLibrary directory
datadirShared data directory
systemd_system_unitdirsystemd system unit directory
DEPLOY_DIR_IMAGEDeployed images for the current machine

Using variables such as ${sysconfdir} and ${bindir} is preferable to hard-coding paths into recipes.

There is a longer list in the Common Variables article.

SRC_URI

SRC_URI specifies sources used by a recipe.

A Git repository might be:

SRC_URI = "git://github.com/example/project.git;protocol=https;branch=main"

A local file:

SRC_URI += "file://example.conf"

A patch:

SRC_URI += "file://0001-fix-something.patch"

Patches in SRC_URI are normally applied automatically during do_patch.

Git revisions

A Git recipe will commonly specify:

SRCREV = "0123456789abcdef..."

and:

PV = "1.0+git${SRCPV}"

Pinning SRCREV makes the source used for the build reproducible.

Dependencies

There are two dependency types that are particularly important.

Build-time dependencies

DEPENDS = "openssl zlib"

These are dependencies required while building the recipe.

Runtime dependencies

RDEPENDS:${PN} = "bash"

These are packages required on the target when the package is installed.

This distinction matters.

If a program needs a library in order to compile, that is generally a build-time dependency.

If a shell script installed by the recipe needs Bash when it runs on the target, that is a runtime dependency.

PACKAGECONFIG

PACKAGECONFIG is commonly used to enable or disable optional features in a recipe.

For example:

PACKAGECONFIG ??= "ssl"

PACKAGECONFIG[ssl] = "--enable-ssl,--disable-ssl,openssl"

The four commonly used fields are:

enable options,
disable options,
build dependencies,
runtime dependencies

Inspect the final configuration with:

bitbake -e <recipe> | grep '^PACKAGECONFIG='

A .bbappend can add a feature:

PACKAGECONFIG:append = " ssl"

or remove one:

PACKAGECONFIG:remove = "ssl"

Installing files

Files are installed into ${D} during do_install.

For example:

do_install() {
    install -d ${D}${bindir}
    install -m 0755 my-program ${D}${bindir}/my-program
}

For configuration:

do_install:append() {
    install -d ${D}${sysconfdir}/my-program
    install -m 0644 ${UNPACKDIR}/my.conf \
        ${D}${sysconfdir}/my-program/my.conf
}

${D} is a staging location used while the package is being constructed. It is not the target’s real root filesystem.

Packages

A single recipe can produce several binary packages.

The main package is normally:

${PN}

Other common automatically generated packages include:

${PN}-dev
${PN}-dbg
${PN}-doc
${PN}-staticdev

Add another package

PACKAGES += "${PN}-tools"

FILES:${PN}-tools = "${bindir}/my-tool"

See the packages produced by a recipe

bitbake -e <recipe> | grep '^PACKAGES='

See which files are in a package

After package data has been generated:

oe-pkgdata-util list-pkg-files <package>

For example:

oe-pkgdata-util list-pkg-files busybox

Which package provides a file?

This is one of my favourite Yocto debugging commands.

oe-pkgdata-util find-path <path>

For example:

oe-pkgdata-util find-path /bin/bash

It searches the package data and identifies the package containing that path.

Wildcards are also useful when you only know part of the filename:

oe-pkgdata-util find-path '*/libssl.so*'

Adding packages to an image

Packages can be added to an image with:

IMAGE_INSTALL:append = " strace tcpdump"

The leading space is important.

Package groups

A packagegroup provides a convenient way to group related packages.

For example:

SUMMARY = "Packages for my application"

inherit packagegroup

RDEPENDS:${PN} = " \
    my-application \
    openssh \
    strace \
"

The image can then contain:

IMAGE_INSTALL += "packagegroup-my-application"

This becomes useful as an image grows and its package list starts becoming difficult to manage.

local.conf

conf/local.conf contains configuration specific to a particular build environment.

Typical settings include:

MACHINE = "my-machine"

and development conveniences such as:

EXTRA_IMAGE_FEATURES += "debug-tweaks"

local.conf is useful for local configuration and experiments.

I try not to put product behaviour there. If something is required to reproduce the product, it normally belongs in a layer, distro, machine configuration or image recipe where it can be version controlled.

bblayers.conf

The layers used by the build are configured in:

conf/bblayers.conf

For example:

BBLAYERS ?= " \
    /work/layers/openembedded-core/meta \
    /work/layers/meta-openembedded/meta-oe \
    /work/layers/meta-project \
"

Check what BitBake actually sees with:

bitbake-layers show-layers

Machine configuration

The machine is normally selected with:

MACHINE = "my-machine"

Machine configuration files are typically stored under:

conf/machine/

For example:

conf/machine/my-machine.conf

Machine configuration describes hardware-specific aspects of the build such as the kernel, bootloader, device trees, architecture and image formats.

Distribution configuration

The distribution can be selected with:

DISTRO = "my-distro"

Distribution configuration files normally live under:

conf/distro/

A distro is a good place for product-wide policy such as:

  • package format
  • init system
  • distribution features
  • preferred providers
  • security policy
  • toolchain choices

Hardware-specific configuration belongs in the machine rather than the distro.

DISTRO_FEATURES

See the enabled distribution features with:

bitbake -e | grep '^DISTRO_FEATURES='

Features can be added with:

DISTRO_FEATURES:append = " systemd"

or removed:

DISTRO_FEATURES:remove = "x11"

Some features affect large parts of the dependency graph, so changing DISTRO_FEATURES can have considerably wider effects than simply adding a package to an image.

systemd services

A recipe installing a systemd service commonly contains:

inherit systemd

SYSTEMD_SERVICE:${PN} = "my-service.service"
SYSTEMD_AUTO_ENABLE = "enable"

Install the unit with:

do_install:append() {
    install -d ${D}${systemd_system_unitdir}
    install -m 0644 ${UNPACKDIR}/my-service.service \
        ${D}${systemd_system_unitdir}/my-service.service
}

and make sure it is included in the package if necessary:

FILES:${PN} += "${systemd_system_unitdir}/my-service.service"

Kernel configuration

Open the kernel configuration interface with:

bitbake -c menuconfig virtual/kernel

This is useful for experimentation, but changing options in menuconfig alone is not a reproducible way of maintaining a product configuration.

Once I know which configuration changes I need, I put them into the appropriate kernel metadata.

Inspect kernel configuration

A development shell is often useful:

bitbake -c devshell virtual/kernel

The kernel work directory can also be found with:

bitbake-getvar -r virtual/kernel WORKDIR

Device trees

The device trees built for a machine are commonly controlled through:

KERNEL_DEVICETREE

Inspect it with:

bitbake-getvar -r virtual/kernel KERNEL_DEVICETREE

Built device trees are normally deployed into:

${DEPLOY_DIR_IMAGE}

Find that directory with:

bitbake-getvar -r virtual/kernel DEPLOY_DIR_IMAGE

Development shell

Open a shell containing the environment BitBake uses to build a recipe:

bitbake -c devshell <recipe>

For example:

bitbake -c devshell busybox

This is extremely useful when a build fails and I want to run the configure or compiler commands manually.

Inside the devshell, variables such as the compiler, sysroot and build flags have already been configured for the recipe.

Finding the work directory

bitbake-getvar -r <recipe> WORKDIR

For example:

bitbake-getvar -r busybox WORKDIR

A recipe work directory contains the source, build output, task scripts, logs, package staging areas and other intermediate files.

The exact layout varies between Yocto releases and recipes, but when debugging a failed task the work directory is usually where I look next.

Finding task logs

Task logs are stored beneath the recipe’s temporary directory.

Typically:

${WORKDIR}/temp/

Files include:

log.do_compile
log.do_install
log.do_package

There are also generated task scripts:

run.do_compile
run.do_install
run.do_package

If do_compile fails, I normally start with:

log.do_compile

and then inspect:

run.do_compile

if I need to see exactly what BitBake attempted to execute.

Why did BitBake rebuild something?

BitBake can compare task signatures to help explain why a task changed.

Generate signature data with:

bitbake -S none <target>

or use:

bitbake-diffsigs

against signature data when investigating differences.

This becomes particularly useful when a seemingly harmless metadata change causes a large part of the build to rebuild.

Dependency graphs

BitBake can generate dependency information with:

bitbake -g <target>

For example:

bitbake -g core-image-minimal

This produces files including:

pn-buildlist
recipe-depends.dot
task-depends.dot

Be warned that the dependency graph for a complete image can be very large.

Find recipes providing something

BitBake providers can be inspected through the environment and layer tools.

For virtual providers, check values such as:

bitbake -e | grep '^PREFERRED_PROVIDER_'

This is one of few times bitbake -e is better than bitbake-getvar

Common virtual targets include:

virtual/kernel
virtual/bootloader

For example:

bitbake-getvar -r virtual/kernel PN

helps identify the recipe selected as the kernel provider.

Preferred versions

A particular recipe version can be selected with:

PREFERRED_VERSION_busybox = "1.36.1"

Wildcards are possible:

PREFERRED_VERSION_linux-yocto = "6.6%"

Inspect available recipe versions with:

bitbake-layers show-recipes <recipe>

Before adding a PREFERRED_VERSION, I check why BitBake selected the existing version. Pinning a version can solve the immediate problem while hiding a layer compatibility issue.

Useful directories

These are the paths I most commonly need while debugging a build.

Variable/pathPurpose
${TOPDIR}Build directory
${TMPDIR}Main temporary build output
${WORKDIR}Work directory for a recipe
${UNPACKDIR}Unpacked recipe sources on newer releases
${S}Source directory
${B}Build directory
${D}Installation staging directory
${DEPLOY_DIR_IMAGE}Images, kernels, DTBs and other deployed output
${WORKDIR}/temp/Task logs and generated task scripts

Don’t guess these paths when debugging - ask BitBake:

bitbake-getvar -r <recipe> WORKDIR

or:

bitbake-getvar -r <recipe> S

or:

bitbake-getvar -r <recipe> B

The value BitBake is actually using is more useful than the value you expected it to use.

Quick debugging checklist

When something isn’t behaving as expected, these are some of the commands I reach for first.

Check the layers:

bitbake-layers show-layers

Check which recipe BitBake sees:

bitbake-layers show-recipes <recipe>

Check which appends modify it:

bitbake-layers show-appends | grep <recipe>

Inspect a variable:

bitbake-getvar -r <recipe> VARIABLE

Inspect the complete environment:

bitbake -e <recipe> | less

Find the work directory:

bitbake-getvar -r <recipe> WORKDIR

Look at the failed task:

${WORKDIR}/temp/log.do_<task>

See exactly what the task executed:

${WORKDIR}/temp/run.do_<task>

Open a development shell:

bitbake -c devshell <recipe>

Find which package contains a file:

oe-pkgdata-util find-path '*/filename'

See what a package contains:

oe-pkgdata-util list-pkg-files <package>

Those commands solve a surprisingly large percentage of the Yocto problems I encounter.

A final rule: ask BitBake

One of the easiest mistakes to make when working with Yocto is reading a recipe and assuming you know its final configuration.

A recipe may have been affected by:

  • configuration files
  • classes
  • include files
  • .bbappend files
  • machine overrides
  • distro overrides
  • package overrides
  • PACKAGECONFIG
  • layer priorities

The source metadata tells you what somebody intended to configure.

BitBake can tell you what actually happened.

When in doubt, I start with:

bitbake-getvar -r <recipe>

It is probably the most useful Yocto debugging command in this entire cheat sheet.