Lesson
Creating Yocto Recipes
How to create a practical Yocto recipes and add them to an image
Recipes are the normal way to add your own software to a Yocto build. It is basically a set of instructions (known as tasks) to BitBake that tell it how to:
- Obtain the source code
- Configure it
- Compile it
- Install it
along with other bits of information such as:
- The license
- Build dependencies
- Other package dependencies
Where Recipes Live
We have already looked at Image recipes, which are a very specific use case, but the same concepts apply to all recipes.
Keep your own recipes in your own layer.
One of the biggest rules for a maintainable Yocto project is:
This applies to recipes, layers, classes, etc.
For you own recipes that you create yourself, you should be putting them 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"
# How to get the source
SRC_URI = "file://hello-recipe.c"
# Any build (DEPENDS) or run-time (RDEPENDS) dependencies
DEPENDS = ""
RDEPENDS:${PN} = ""
# How to compile
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} ${S}/hello-recipe.c -o hello-recipe
}
# How to install
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"
# was (WORKDIR before wrynose)
S = "${UNPACKDIR}/git"
Pinning SRCREV makes the build reproducible. Avoid using a moving branch head
for production builds.
Tasks
A task is a the function that BitBake will run to do something. The order they are executed in will be set for you in the main classes.
Important Recipe Tasks
These are the tasks that will be run by most recipes.
- 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. (CMAKE, etc)
- do_compile
Builds the software.
- do_install
Copies the final files into the package staging directory ready to be picked up and combined into the final image from the Image recipe.
- 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.conf:
message=Hello from /etc/hello-recipe/hello-recipe.conf
Create files/hello-recipe.c:
#include <stdio.h>
#include <string.h>
#define CONFIG_FILE "/etc/hello-recipe/hello-recipe.conf"
int main(void)
{
FILE *fp;
char line[256];
fp = fopen(CONFIG_FILE, "r");
if (fp == NULL) {
perror("Unable to open configuration file");
return 1;
}
while (fgets(line, sizeof(line), fp) != NULL) {
if (strncmp(line, "message=", 8) == 0) {
char *message = line + 8;
/* Remove trailing newline if present */
message[strcspn(message, "\r\n")] = '\0';
printf("%s\n", message);
break;
}
}
fclose(fp);
return 0;
}
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
Add the Recipe to an Image
Building hello-recipe proves the package can be created. It does not add the
package to an image by itself. To do that, you need to tell the image recipe that
you want to install it.
Add yo your image recipe:
IMAGE_INSTALL:append = " hello-recipe"
Then rebuild the image:
bitbake my-image
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 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
- use
DEPENDSfor build-time requirements andRDEPENDS:${PN}for target runtime requirements - compile with Yocto’s compiler and flags
- install only into
${D}, where possible, using paths such as${bindir}and${sysconfdir} - add the resulting package to an image before expecting it on the target
Check your understanding
Quick quiz: creating recipes
Check the main ideas from the worked recipe example.