Lesson
Creating Yocto Recipes
How to create a practical Yocto recipe for a small C application and add it to an image
Recipes are the normal way to add your own software to a Yocto build.
An image recipe decides which packages belong in the final root filesystem. A normal software recipe decides how one component is fetched, built, installed, and packaged so an image can use it.
This lesson builds a small C application recipe from local files in your own layer. That keeps the example small enough to inspect, while still covering the same structure you use for larger applications.
What a Recipe Does
A recipe is a .bb file read by BitBake.
It describes:
- where the source comes from
- which licence applies
- which build-time and runtime dependencies are needed
- how to configure and compile the software
- which files are installed into the package staging area
- how those files become packages
The recipe does not install directly into the final image. It installs into
${D}, BitBake packages that staged output, and the image build later installs
the selected package.
Where Recipes Live
Keep your own recipes in your own layer.
For an application recipe, a common layout is:
The filename follows this pattern:
<recipe-name>_<version>.bb
For example, hello-recipe_1.0.bb gives you:
- recipe name:
hello-recipe - recipe version:
1.0
The layer must also be enabled in conf/bblayers.conf. If the layer is not in
BBLAYERS, BitBake will not see the recipe at all.
Recipe File Structure
Most simple recipes have the same basic shape:
SUMMARY = "Short one-line description"
DESCRIPTION = "Longer description of what the recipe builds."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://hello-recipe.c"
S = "${WORKDIR}"
DEPENDS = ""
RDEPENDS:${PN} = ""
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} ${S}/hello-recipe.c -o hello-recipe
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello-recipe ${D}${bindir}/hello-recipe
}
The exact tasks may come from a class such as cmake, autotools, or
meson. For this small example, the recipe defines the compile and install
steps directly.
Key Variables
You will see these variables often when reading or writing recipes.
- SUMMARY
A short one-line description. This appears in package metadata and is useful when searching recipes.
- DESCRIPTION
A longer explanation of what the software does. Use it when
SUMMARYis not enough.- LICENSE
The licence expression for the software. Use SPDX identifiers where possible, such as
MIT,BSD-3-Clause, orGPL-2.0-only.- LIC_FILES_CHKSUM
A checksum for the licence text. BitBake uses this to notice if the licence file changes.
- SRC_URI
The source inputs for the recipe. This can point to local files, patches, archives, or source repositories.
- SRCREV
The exact revision to fetch when
SRC_URIuses source control, especially Git.- S
The source directory used by configure and compile tasks after fetch and unpack have completed.
- DEPENDS
Build-time recipe dependencies. Use this when headers, libraries, tools, or native utilities are needed during the build.
- RDEPENDS
Runtime package dependencies. Use
RDEPENDS:${PN}for packages needed on the target when the main package is installed.
For a Git-based recipe, SRC_URI and SRCREV usually look like this:
SRC_URI = "git://github.com/example/hello-recipe.git;branch=main;protocol=https"
SRCREV = "0123456789abcdef0123456789abcdef01234567"
S = "${WORKDIR}/git"
Pinning SRCREV makes the build reproducible. Avoid using a moving branch head
for production builds.
Important Recipe Tasks
BitBake runs recipes as a graph of tasks. These are the main ones to understand when creating a recipe:
- do_fetch
Retrieves everything listed in
SRC_URI.- do_unpack
Unpacks archives or stages fetched files into the recipe work directory.
- do_configure
Prepares the source tree for compilation. Build classes often provide this.
- do_compile
Builds the software.
- do_install
Copies built files into
${D}, the package staging directory.- do_package
Splits the staged files into binary packages.
You can run a task directly while debugging:
bitbake -c fetch hello-recipe
bitbake -c unpack hello-recipe
bitbake -c compile hello-recipe
bitbake -c install hello-recipe
Use the task name without the do_ prefix on the command line.
Complete Worked Example
Create the recipe directory in your own layer:
mkdir -p meta-my-software/recipes-apps/hello-recipe/files
Create files/hello-recipe.c:
#include <stdio.h>
int main(void)
{
puts("Hello from a Yocto recipe");
return 0;
}
Create files/hello-recipe.conf:
message=Hello from /etc/hello-recipe/hello-recipe.conf
Now create hello-recipe_1.0.bb:
SUMMARY = "Small C application recipe example"
DESCRIPTION = "Builds and installs a small C application plus one configuration file."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = " \
file://hello-recipe.c \
file://hello-recipe.conf \
"
S = "${WORKDIR}"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} ${S}/hello-recipe.c -o hello-recipe
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello-recipe ${D}${bindir}/hello-recipe
install -d ${D}${sysconfdir}/hello-recipe
install -m 0644 ${WORKDIR}/hello-recipe.conf ${D}${sysconfdir}/hello-recipe/hello-recipe.conf
}
The important install paths are:
${D}${bindir}/hello-recipe, which becomes/usr/bin/hello-recipeon the target${D}${sysconfdir}/hello-recipe/hello-recipe.conf, which becomes/etc/hello-recipe/hello-recipe.confon the target
Because the files are installed into standard locations, the main package usually
collects them automatically. If you install into unusual locations, you may need
to extend FILES:${PN}.
Build and Test the Recipe
First, make sure the layer is enabled:
bitbake-layers show-layers
If your layer is missing, add it:
bitbake-layers add-layer ../meta-my-software
Then build the recipe by itself:
bitbake hello-recipe
To inspect a single stage:
bitbake -c install hello-recipe
Look in the recipe work directory for staged output:
tmp/work/<machine>/hello-recipe/1.0/image/
You should see paths like:
usr/bin/hello-recipe
etc/hello-recipe/hello-recipe.conf
Add the Recipe to an Image
Building hello-recipe proves the package can be produced. It does not add the
package to an image by itself.
To add it to an image recipe:
IMAGE_INSTALL:append = " hello-recipe"
Then rebuild the image:
bitbake my-image
For a quick local test, you can also add the package from conf/local.conf:
CORE_IMAGE_EXTRA_INSTALL += "hello-recipe"
Keep long-term product package lists in image recipes or package groups rather
than relying on local.conf.
After booting the image, test the files:
hello-recipe
cat /etc/hello-recipe/hello-recipe.conf
Troubleshooting
Nothing Provides the Recipe
If BitBake reports that nothing provides hello-recipe, check:
- the layer is listed by
bitbake-layers show-layers - the recipe is under a directory matched by the layer’s
BBFILES - the filename is
hello-recipe_1.0.bb, nothello-recipe.bb - the recipe is not marked as incompatible by overrides or machine settings
Fetch or Unpack Fails
If do_fetch or do_unpack fails, check:
- local files listed in
SRC_URIare underfiles/ - each local file uses the
file://prefix - Git URLs include the right protocol and branch
SRCREVnames a real commit when fetching from Git
Licence Check Fails
If BitBake complains about licence metadata, check:
LICENSEis setLIC_FILES_CHKSUMpoints to a real licence file- the checksum matches the file content
- the licence identifier is accurate for the software
For early local examples using the common MIT licence text, the shared
${COMMON_LICENSE_DIR}/MIT path is acceptable. For real project source, point
at the licence file shipped with that source when possible.
Compile Fails
If do_compile fails, inspect:
tmp/work/<machine>/hello-recipe/1.0/temp/log.do_compile
Common causes include:
- source files are not where
Ssays they are - missing build dependencies in
DEPENDS - using host tools instead of Yocto-provided tools
- forgetting
${CC},${CFLAGS}, or${LDFLAGS}in a hand-written compile command
Package Builds but Files Are Missing
If the recipe builds but files do not appear in the image, check the flow in order:
- Did
do_install()stage the files under${D}? - Did
do_packageput them into the expected package underpackages-split/? - Did the image install that package with
IMAGE_INSTALLor a package group? - Are you checking the rebuilt image, not an older deployed image?
The staged install tree is usually under:
tmp/work/<machine>/hello-recipe/1.0/image/
The package split output is usually under:
tmp/work/<machine>/hello-recipe/1.0/packages-split/
Summary
A recipe is practical build metadata for one software component.
For a simple C application, the important habits are:
- keep the recipe and local files in your own layer
- declare licence and source inputs clearly
- set
Sto the directory BitBake should build from - use
DEPENDSfor build-time requirements andRDEPENDS:${PN}for target runtime requirements - compile with Yocto’s compiler and flags
- install only into
${D}using paths such as${bindir}and${sysconfdir} - add the resulting package to an image before expecting it on the target
Once that flow is clear, larger recipes mostly add more source inputs, inherited classes, dependencies, and packaging details.
Check your understanding
Quick quiz: creating recipes
Check the main ideas from the worked recipe example.