Lesson
Simple Recipes
How to write a simple Yocto recipe to fetch, install, and package software
With the project structure in place, the next step is to create a recipe of your own.
A recipe is the file that tells BitBake how to:
- fetch the source
- unpack it
- configure it
- compile it
- install it
- package it
These are ‘tasks’ and are represented in recipes as do_something(), e.g. do_install() is the task
responsible for installing software into the staging area ready for the final image.
In practice, a recipe is just metadata that describes the build process for one piece of software.
What is a Recipe
A recipe is normally a file ending in .bb.
For example:
mytool_1.0.bb
The filename matters - it is <RECIPE_NAME>_<VERSION>.bb:
mytoolis the recipe name1.0is the recipe version
Yocto uses that metadata, together with the variables inside the file, to decide how the software should be built.
Where the Recipe Lives
Your recipe should live in one of your own layers.
For example:
This keeps your software metadata separate from vendor and upstream layers.
A Minimal Recipe
Here is a very simple recipe that installs a file into your image:
SUMMARY = "My first example recipe"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://mytool"
S = "${WORKDIR}"
do_install() {
install -d ${D}${bindir}
install -m 0755 ${WORKDIR}/mytool ${D}${bindir}/mytool
}
This is enough to package a single local binary or script.
Core Recipe Variables
There are a few variables you will see in almost every recipe.
- SUMMARY
-
A short description of the recipe.
- LICENSE
-
The declared licence for the software. This must be one of the SPDX licence identifiers
- LIC_FILES_CHKSUM
-
A checksum used to verify the referenced licence file.
- SRC_URI
-
Where the source comes from.
- S
-
The directory in the working tree containing the unpacked source used for the build.
LICENSE and LIC_FILES_CHKSUM
Every recipe must declare its licence.
For example:
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
The checksum exists so BitBake can detect if the licence file changes.
This is important for licence tracking and compliance, and it is one of the first things BitBake will complain about if you omit it.
SRC_URI
SRC_URI tells BitBake where to get the source.
For simple local files:
SRC_URI = "file://mytool"
For a tarball:
SRC_URI = "https://example.com/releases/mytool-1.0.tar.gz"
For a Git repository:
SRC_URI = "git://github.com/example/mytool.git;branch=main;protocol=https"
SRCREV = "0123456789abcdef0123456789abcdef01234567"
This is one of the most important recipe variables, because it controls what source BitBake fetches and stages for the build.
WORKDIR and S
BitBake places fetched and unpacked source in the working area for the recipe.
Two important variables are:
- WORKDIR
-
The working directory for the recipe. This is created by BitBake.
- S
-
The source directory BitBake should use when building.
For local file recipes, it is common to set:
S = "${WORKDIR}"
For unpacked source archives, S is often set automatically, but not always.
If BitBake is building from the wrong source directory, S is one of the first
things to check.
Installing Files with do_install()
The do_install() task copies files into the package staging area.
For example:
do_install() {
install -d ${D}${bindir}
install -m 0755 ${WORKDIR}/mytool ${D}${bindir}/mytool
}
In this example:
${D}is the destination staging directory${bindir}is usually/usr/bin
So the final file is staged under /usr/bin/mytool.
Common Path Variables
Some of the most useful install path variables are:
${bindir}for/usr/bin${sbindir}for/usr/sbin${sysconfdir}for/etc${libdir}for/usr/lib${datadir}for/usr/share
Using these variables makes your recipe more portable and consistent with the rest of the build system.
Package Contents
By default, BitBake places files into packages based on standard locations.
Often, if you install to normal places like ${bindir}, you do not need to do
anything extra.
Sometimes you need to extend the package contents explicitly:
FILES:${PN} += " /usr/local/bin/mytool"
${PN} means the main package name If you have not explicitly defined a package name, it will be
the same name as the recipe.
This is a list of files that have been added to the system by the recipe - allowing for searches and package management.
A Practical First Example
Suppose you have a simple shell script called hello-demo.sh.
Your file tree might look like this:
The script in files/hello-demo.sh might contain:
#!/bin/sh
echo "Hello from Yocto"
And the recipe:
SUMMARY = "Simple demo script"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://hello-demo.sh"
S = "${WORKDIR}"
do_install() {
install -d ${D}${bindir}
install -m 0755 ${WORKDIR}/hello-demo.sh ${D}${bindir}/hello-demo.sh
}
That is a perfectly valid first recipe.
Building the Recipe Standalone
Once the layer is enabled in bblayers.conf, you can build the recipe with:
bitbake hello-demo
If the recipe builds successfully, BitBake will create the package output and make it available for installation into an image.
Building just the recipe you are working on is common while you are developing it. Once you know it builds cleanly, then it can be built automatically as part of the standard image build.
Adding the Package to an Image
To include the new package in an image:
IMAGE_INSTALL:append = " hello-demo"
Then rebuild the image:
bitbake my-image
This is the point where the relationship between recipes, packages, and images becomes very clear:
- the recipe builds
hello-demo - the package is emitted
- the image installs that package
Recipe Versions
You can have multiple versions of the same recipe by having different version numbers in the filename, e.g.
hello-demo_1.0.bbhello-demo_1.1.bbhello-demo_2.0.bb
By default, BitBake will use the highest version number for the build. If you want to specify a version to
use, BitBake will look for a variable, PREFERRED_VERSION_<packagename> = XXX.
In your image file configuration, you can write:
PREFERRED_VERSION_hello-demo = "1.1"
Extending Existing Recipes with .bbappend
Sometimes you do not need a new recipe.
If an upstream, vendor, or community layer already provides the recipe, and you
only need to adjust it for your project, use a .bbappend file in your own layer.
A .bbappend file extends or adjusts an existing recipe without modifying the
original recipe file. This lets you modify how the original recipe behaves while
keeping your changes in your own layer (and part of the reason why it is good to
have the split between your layers and third-party layers).
For example, if an upstream layer provides:
busybox_1.36.0.bb
you can create:
busybox_1.36.0.bbappend
or, more commonly:
busybox_%.bbappend
Wildcards
The character ‘%’ in BitBake is a wildcard character and can match anything within the filename or version
sections of packages.
Matching Versions
This is the most common use case where you want the % to match different versions of a package.
linux-yocto_%.bbappend
will match all versions of linux-yocto.
linux-yocto_6.%.bbappend
will match all 6.x versions of linux-yocto.
linux-yocto_6.6.%.bbappend
will match all 6.6.x versions of linux-yocto.
Matching Names
As an example, there are many versions of the Linux kernel - especially as manufacturers tweak their kernel sources instead of pushing the changes up to the mainstream kernel source. This now means as you add various layers you may end up with:
- linux-yocto
- linux-boundary
- linux-kontron
- linux-qoriq
- linux-toradex
- linux-variscite
(These are examples from adding a single layer - meta-freescale-3rdparty)
If your change was applicable to all the kernels, you could have an append of:
linux-%.bbappend
Wildcards in PREFERRED_VERSION
You can also have wildcards in PREFERRED_VERSION, e.g.
PREFERRED_VERSION_hello-demo = "1.%"
Would mean that you wanted to have the highest version of the 1.x releases.
When to Use a .bbappend
Use a .bbappend when:
- the base recipe already exists
- you only need to tweak or extend it
- the behaviour is specific to your project, machine, or distro
Typical examples include:
- adding a patch
- overriding a file
- adding configuration fragments
- appending dependencies
- changing install behaviour
- adding package content
When to Create a New Recipe Instead
Use a new recipe when:
- the software does not already have a recipe
- you are introducing a new application, library, or tool
- you are packaging your own source tree
- the behaviour is fundamentally different, not just a tweak
Use a .bbappend when the recipe already exists and you only need to modify it.
Example: Appending a Recipe
Suppose an upstream recipe installs a default configuration file that you want to replace.
You might create:
meta-my-distro/
└── recipes-core/
└── base-files/
├── base-files_%.bbappend
└── files/
└── issue
And the append file might contain:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
This tells BitBake to also search your local files/ directory when resolving
file://... entries.
Why FILESEXTRAPATHS Matters
Many append files need extra local files:
- replacement config files
- patches
- service files
- scripts
Without updating FILESEXTRAPATHS, BitBake may not know where to find those
files.
This is one of the most common reasons a new .bbappend appears not to work.
Example: Adding a Patch
If you want to patch an upstream recipe, your append might look like this:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " file://fix-startup.patch"
And your patch would live under:
recipes-example/example/files/fix-startup.patch
Using Wildcards in Append Files
You will often see:
foo_%.bbappend
This means the append should apply regardless of the exact version of the recipe.
That is often convenient, because you do not need to rename your append file every time the upstream recipe version changes.
However, it is sometimes better to target a specific version if:
- the append depends on version-specific behaviour
- the patch only applies to one release
- you want upgrades to fail obviously rather than silently
How to Decide Where a Change Belongs
A useful mental checklist is:
- Does the recipe already exist?
- Is this a small extension or override?
- Is the change board-specific, distro-specific, or application-specific?
If the recipe exists and you are only extending it, use a .bbappend in the layer
that matches the responsibility of the change.
Common Mistakes
Forgetting to Add the Layer
If your layer is not in bblayers.conf, BitBake will not find the recipe.
Installing to the Wrong Place
If you install files outside the standard paths, BitBake may not package them the
way you expect unless you update FILES:${PN}.
Using Host Paths
Do not write recipes that install directly to paths on your host. Always install
through ${D}.
Forgetting the Licence Checksum
Missing or incorrect LIC_FILES_CHKSUM is one of the most common errors in new
recipes.
Editing Upstream Metadata Directly
This makes upgrades painful and hides what is actually project-specific.
Forgetting FILESEXTRAPATHS
Your append may parse correctly but still fail to find the files you intended to add.
Using a .bbappend for Something That Should Be a New Recipe
If you are adding a completely new software component, write a proper recipe instead of trying to force it into an append.
How to Inspect What Happened
Useful commands include:
bitbake hello-demo
bitbake -e hello-demo | less
And useful variables to inspect include:
WORKDIRSDPACKAGESFILES:${PN}
If the output is not what you expected, these usually show where the logic went wrong.
A Good First Milestone
Your first recipe does not need to compile a complex C application.
A good first milestone is simply:
- create a recipe in your own layer
- fetch a local file
- install it into
${bindir} - build it successfully
- add it to an image
Once that works, moving on to source archives, Git fetches, patches, and build systems becomes much easier.
Summary
A recipe tells BitBake how to fetch, install, and package software.
The main ideas from this lesson are:
- a
.bbfile defines how one software component is built SRC_URItells BitBake where the source comes fromScontrols the source directory used for the builddo_install()stages files into${D}- path variables like
${bindir}keep recipes portable - images install the packages produced by recipes
.bbappendfiles extend existing recipes without changing upstream metadata- use
FILESEXTRAPATHSwhen an append adds local files
Check your understanding
Quick quiz: first recipe
A short review of the basic recipe-building workflow.