First of all, thank you for checking out this post. If this is the first time you are here, it may be worth your time to take a look at previous posts in this series.
Yocto: Part 1 – A Definitive Introduction
Yocto: Part 2 – Setting up Ubuntu host
Yocto: Part 3 – Build & run your first ever image!
Yocto: Part 4 – Building a basic image for Raspberry Pi
Yocto: Part 5 – Creating & adding a new layer to your image
In the last post, we talked about how creating a custom Yocto layer is the first step towards creating a custom image for the machine of your interest. This post will build on the work done in the previous one (rather loosely!).
In this post, we talk about creating your first ever recipe! Excited yet? Hold your horses! Before we do that, we will discuss some of the basics of Yocto recipes – how to name them, how they are found by bitbake
, how to write one yourself for a very basic use-case and finally end with learning what people do with recipes!
Recipes – a crash-course
In Part 1, we saw how Yocto is an extremely scalable build system which is used ubiquitously to create linux distributions for a variety of machines around us. It employs a tool called BitBake which is basically a scheduler-cum-execution engine that parses metadata passed to it to and performs actions as specified by the metadata. It can be said that bitbake
is the workhorse of the entire build process.
A bitbake recipe or simply a recipe is a form of metadata that contains information about how bitbake can obtain the source code, configure the source code, patch it, build it, install it, and basically everything that bitbake should be doing with the individual software component for whom the recipe is written.
Basically, a bitbake recipe is analogous to a recipe for a food item in the real life – the ingredients, the process, the presentation, the serving, and so on till you are ready to gobble it up!
The official Yocto documentation specifies the process of creating a bitbake recipe as shown in the below image. It is important to understand that not all recipes are created using the exact same process below. Some steps can be skipped if they are not needed. For example: some recipes may not require any patches to be applied or may not need to be compiled at all!
How to name a bitbake recipe
The nomenclature of a bitbake recipe is very simple. It looks something like this.
basename_version.bb
As the name suggests, the basename
is the fundamental name of the recipe. You are not allowed to use reserved suffixes or prefixes like cross, native, lib, etc. in this name. Of course, if your basename itself makes use of these words, it should be fine. For example, if you have a magic recipe to irritate the user, go ahead and name it turnusercross_0.1.bb
– you are just fine. The idea is to not tie down the recipe to an architecture or a type. This can be done by yocto (more on this in a later post).
The version can be a dot-separated string. The convention is to use a major.minor
or a major.minor.patch
notation. For example, the below are acceptable.
somerecipe_1.2.bb anotherrecipe_1.2.1.bb
Where to place a bitbake recipe
Bitbake recipes are always located within a Yocto layer. For the recipe to be visible to bitbake, that particular Yocto layer should be added to the current build configuration. Need a refresher on adding your layer to your build? Check out part 5 of this series!
Suppose you have added the layer containing your recipe to the build configuration – how does bitbake find it?
To answer this, let us look at the conf/layer.conf
file in the layer we created in the previous post.
# We have a conf and classes directory, add to BBPATH BBPATH .= ":${LAYERDIR}" # We have recipes-* directories, add to BBFILES BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ ${LAYERDIR}/recipes-*/*/*.bbappend" ..... .....
The most relevant configurations are BBPATH
and BBFILES
.
In the auto-generated layer.conf,
- The
BBPATH
variable (similar to PATH variables in our system) is used by BitBake to locate.bbclass
and configuration files. - The
BBFILES
variable is a space-separated list of recipe files BitBake uses to build software. Most importantly, it is possible to use wild-cards like*
in the file above that allows you to specify a generic path. The syntax is similar to theglob
functionality in Python.
Based on the above info, it is clear to us that as long as your recipe is located in a folder inside the layer where the folder name is something like recipes-
, your recipe will be found by bitbake.
A skeletal bitbake recipe
The below can be used as the starting point when starting your own custom recipe. This skeleton is directly taken from Yocto’s manual. It is not necessary to have all of these mandatorily – this is just a recommendation.
DESCRIPTION = "This is a short description of the recipe" HOMEPAGE = "You can specify your home page " LICENSE = "Type of license. Eg: GPLv2, MIT, etc." SECTION = "Useful if package management is required - can omit" DEPENDS = "Specify the recipes on which your recipe depends" LIC_FILES_CHKSUM = "md5sum or sha256sum of the license" SRC_URI = "Where to get the source code from"
PRO-TIP: Save this skeleton recipe for future reference!
Writing a recipe for a simple hello world printer application
When we created our custom layer in the last post, the layer directory looked something like this.
- meta-ke
- conf/
- COPYING.MIT
- README
- recipes-example/
Let us create a new folder named recipes-ke
to hold our custom recipe that we want to create. This is in accordance with the requirements of the BBFILES
setting in conf/layer.conf
. The folder structure should now look like this.
- meta-ke
- conf/
- COPYING.MIT
- README
- recipes-example/
- recipes-ke/
Let us quickly write a hello world printer C code and name this file hello-world-local.c
.
#include <stdio.h> int main(void) { printf("Hello, World!\r\n"); return 0; }
If a recipe uses a locally available source tree, then it is supposed to be located in a folder named files
which is located in the same folder as the recipe i.e *.bb
file. To keep things clean, let us create a folder inside recipes-ke
called hwlocal
and add the files
folder to it along with the source code. Also, let us create a recipe named hwlocal_0.1.bb
inside hwlocal
.
Now, the meta-ke folder looks like this.
- meta-ke
- conf/
- COPYING.MIT
- README
- recipes-example/
- recipes-ke/
- hwlocal/
- files/
- hello-world-local.c
- hwlocal_0.1.bb
- files/
- hwlocal/
Using the skeletal recipe as a base, let us write hwlocal_0.1.bb
and then try to understand what we did and why we did it.
DESCRIPTION = "This is a simple Hello World recipe - uses a local source file" HOMEPAGE = "https://kickstartembedded.com" LICENSE = "MIT" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" SRC_URI = "file://hello-world-local.c" S = "${WORKDIR}" do_compile() { ${CC} ${LDFLAGS} hello-world-local.c -o hello-world-local } do_install() { install -d ${D}${bindir} install -m 0755 hello-world-local ${D}${bindir} }
Let us understand what is happening here piece-by-piece.
DESCRIPTION
This is a simple description of what is happening in this recipe.
HOMEPAGE
Generally, this points to the web address you want others to refer to know more about the recipe, the author or who owns it.
LICENSE
This is a very important information and critical for a build to succeed. It tells bitbake about the kind of license associated with the source code that will be used in the recipe. Poky ships with a large number of licenses that are most commonly used in the industry. You can view these license details at this path in your poky installation: poky/meta/files/common-licenses
.
LIC_FILES_CHKSUM
This information is equally critical for a build to succeed. It informs bitbake about where the license file can be found and what its checksum is. If the checksum calculated by bitbake does not match the checksum mentioned here, the build will fail – it happens during the QA stage. Need a refresher on when the QA check happens? Check out the part 1 of this series!
We just saw a number of license types. You can choose to copy one of these licenses into your source tree and then refer to it or you can directly refer to a license present in your poky installation using the bitbake environment variable called COMMON_LICENSE_DIR
.
We have chosen the latter approach in our recipe.
SRC_URI
This tells bitbake where to find the source code. To keep things simple, we have pointed bitbake to a locally available file called hello-world-local.c
. By default, bitbake will look at the files
folder for this file. Notice how we have referred to file path as file://
to indicate that it is a local resource.
In the next post, we will learn how to specify our sources if they are located in a git repository or a remote tarball or a local tarball.
Let us now move on to some critical configurations.
S
This is the location in the build directory where unpacked recipe source code resides. When dealing with tarballs, there is generally no need to specify this explicitly (unless the tarball’s extracted folder is not named <recipe name>-<version name>
– we shall see such a case in the next post).
But since we are dealing with a local source tree, it is imperative to tell bitbake where this source code will reside while the bitbake operations like patching, compilation, configuration, etc. are happening. We specify this as the environment variable called WORKDIR
which is the active working directory for bitbake. More about WORKDIR
and other Yocto variables here.
do_compile()
Here, we tell bitbake what command should be executed to compile the source tree. Ours is a relatively simple one-line C command but this can often become complex as the source tree grows in size.
Note how the compiler name and flags are passed as CC
and LDFLAGS
– these are environment variables populated correctly by bitbake.
do_install()
Here, we tell bitbake where we want to install the resulting binary output that we got after compilation. Notice how the output of compilation is a binary named hello-world-local
.
We here tell bitbake to install the binary into the /bin
folder of the target machine’s image that will be generated by bitbake. The environment variable named bindir
expands as /bin
and the environment variable named D
points to the path of the target machine’s image.
PRO-TIP: For the curious mind, here’s a tip. To know all the environment variables for your build configuration, you can just run the below command once the oe-init-build-env
script is sourced.
$ bitbake -e
Study the output of the above command and see for yourself the values of WORKDIR
, S
, D
, COMMON_LICENSE_DIR
, AVAILABLE_LICENSES
, MACHINE
, etc.
What’s next?
This post covered the basics of creating a custom recipe and helped you write your first ever bitbake recipe. But often, the real world has much more complex requirements – the source tree is either located in a remote git repository or a tarball. Also, the source tree also specifies a way to build it (eg: CMake, autotools, etc.).
Fortunately, bitbake supports such use-cases and what’s more – it also allows you to easily switch versions of your recipe with minimal changes.
Let us see some advanced recipe use-cases in the next post! See you soon!
This was a great tutorial and very helpful for someone like me trying to learn! Thank you!
Thank you, Rob! Keep making!