====== Recipes ======
Recipes are the fundamental building block of bitbake and therefore of [[ .: | the yocto project]].
Recipes are stored in [[ .:layer | layers ]] and contain the instructions on how to build a particular piece of software.
There should be a recipe for each 'thing' to be built.
Usually this means that one recipe builds one package, but a single recipe can also build multiple packages.
For example, libraries are usually described by several different packages. For example, a repository might contain code for the library and also some utility programs that use this library. Here it may make sense to create 2 packages: ''myLib'' and ''myLib-utils'', where the former contains the library and the latter contains the utility programs.
Another example is libraries being split into ''-dev'' (shared objects), ''-staticdev'' (static libraries), and ''-src'' (source code) packages.
===== Tasks =====
Each recipe undergoes multiple stages while being built:
^ name ^ function ^ description |
| fetch | do_fetch* | fetch sources |
| unpack | do_unpack* | unpack sources |
| patch | do_patch | apply patches (if any) |
| compile | do_compile | build from sources |
| install | do_install | install into temporary destination |
| package | do_package | package installed files into their package(s) |
| package_qa | do_package_qa | perform sanity checks on package |
*: it seems that ''do_fetch'' and ''do_unpack'' can not be modified/overridden in a recipe. More about overriding see below.
Each of the listed functions is called when building the package, or can explicitly be invoked using
bitbake -c
where '''' corresponds to a name in the 'name' column above.
They can also be customized in the recipe by appending their corresponding functions (see below).
===== Anatomy of a Recipe =====
In essence, a recipe needs to provide 4 things:
* where is the source (code)
* how to build it
* how/where to install it (in the image)
* its license
This information is contained in a bitbake recipe file, with ''.bb'' as a file extension.
This file is plain text adhering to its own syntax to specify all things needed by bitbake.
==== The Sources ====
Almost all recipes need some source code or other source from which to build the software.
In bitbake this step is called ''fetch''.
Sources are specified by setting the ''SRC_URI'' variable and can be fetched from a variety of sources.
For example, setting ''SRC_URI'' as shown below will fetch 2 local files ''heloworld.c'' and ''Makefile''.
There is a variety of fetchers available for downloading files from the internet or cloning git repositories and more.
Each has their own requirements and options, which can be found [[ https://www.yoctoproject.org/docs/1.6/bitbake-user-manual/bitbake-user-manual.html#bb-fetchers | here]].
SRC_URI = "file://helloworld.c file://Makefile"
''SRC_URI'' contains a list of all sources needed by the recipe deliminated by spaces. Each source entry starts with its fetcher and then its source location.
Note that '\' before a line break can be used to allow the list to span multiple lines like so:
SRC_URI = " \
file://helloworld.c \
file://Makefile \
"
This is often used in recipes with multiple sources, since it significantly increases readablitity with multiple sources, especially when URLs are involved.
=== The Source Directory ===
Each recipe needs to explicitly set its source directory ''S''.
This is will be a subdirectory of ''WORKDIR'', the recipe's working directory, but the specific location depends on the fetchers and recipe.
If the source is a git repository, ''S'' needs to be set to
S = "${WORKDIR}/git"
=== Patches ===
When a source entry ends in ''.patch'' or ''patch.xz'' (and probably other similar extensions) it is recognized it as a patch and will automatically be applied when calling ''do_patch'', which is invoked before compilation or explicitly by using ''bitbake -c patch''.
==== Building and Installation ====
Building usually consists of 2 steps: configuration and the actual build itself. bitbake comes with classes to handle standard build systems such as ''cmake'' and ''autotools''. These can be used by inheriting from them. For cmake based projects:
inherit cmake
or for autotools based projects:
inherit autotools
For makefile based projects, see [[ https://docs.yoctoproject.org/dev-manual/new-recipe.html?highlight=makefile#building-a-makefile-based-package | Building a Makefile-Based Package]].
==== Appending a Function ====
Sometimes it is necessary to perform extra steps for a particular task. This can be achieved by appending custom commands to a function. The syntax for this is
_append () {
# shell commands go here
}
for example
do_install_append () {
install -d ${D}/usr/bin/
install -m 755 my_extra_executable ${D}/usr/bin
}
''D'' is the destination direcory, which points to the package's install directory.
All destination paths must be relative to it to end up in the right place to be packaged (this is unique to do_install, the other functions should not need to access ''D'').
===== Creating a Recipe using devtool =====
[[ .:devtool ]] is a command line utility that comes with [[ .: | yocto ]] that can be used to facilitate development.
One of the things it can be used for is semi-automatically creating recipes using the following command:
devtool add [name]
The name is optional, as [[ .:devtool | devtool ]] will try to infer it from the source (e.g. from the name of the repository).
If it fails to infer one, it will return an error asking to specify one.
If successful, the recipe can be found in ''build/workspace/recipes/''.
[[ .:devtool | devtool ]] will also print where it placed the recipe for further modification.
The recipe can now be built using
bitbake
===== Manually Creating a Recipe =====
Manual creation of recipes may be required when [[ .:devtool | devtool ]] fails to automatically create one.
=== Adding a Recipe to the Layer ===
''bitbake'' will look for recipes in any sub directory of ''recipes-ost'', **but not in ''recipes-ost'' itself!** Create a folder inside ''recipes-ost'' called ''helloworld'' and inside that create a file called ''helloworld_1_0.bb'' with the following content:
DESCRIPTION = "Example Hello World"
SECTION = "ost"
DEPENDS = ""
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=96af5705d6f64a88e035781ef00e98a8"
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}-${PV}:"
SRCREV = "b9fb7785e9e1f357f29bef63dce8f1d91adb6170"
SRC_URI = "git://github.com/DynamicDevices/bbexample.git"
S = "${WORKDIR}/git"
inherit autotools
''bitbake'' is very particular about licenses to ensure no closed source software ends up in a project that only wants open source software. Hence the recipe needs to specify the license used (MIT in this case) and a checksum for the license file to ensure it has not been altered. All this is handled by the ''LIC_FILES_CHKSUM'' field. The other two interesting fields are ''SRC_URI'' and ''SRC_REV''. ''SRC_URI'' specifies where the source for the application/module/... for this recipe is. In this case ''git:%%//%%'' lets ''bitbake'' know the source lives in a git repository. Note that ''https:%%//%%'' will not work. Also note that ''inherit autotools'' is only necessary because the hello world uses autotools.
To add the new recipe to the build edit ''oe-core/build/conf/local.conf'' and append ''IMAGE_INSTALL_append = " helloworld"'' (make sure to include the space before ''helloworld'').
Now running ''bitbake core-image-minimal'' should run without errors and the image should include a ''hello'' executable that prints ''Hello World'' when executed.
== Using cmake ==
replace ''inherit autotools'' from above with the following:
inherit cmake
OECMAKE_FIND_ROOT_PATH_MODE_PROGRAM = "BOTH"
''OECMAKE_FIND_ROOT_PATH_MODE_PROGRAM = "BOTH"'' allows cmake to find ''git'' from within yocto, otherwise cmake will generate an error (''missing: GIT_EXECUTABLE'').
== Additional Information ==
[[ https://blog.mbedded.ninja/programming/embedded-linux/yocto-project/adding-a-custom-app-to-a-yocto-build/ ]]
[[ https://wiki.yoctoproject.org/wiki/Building_your_own_recipes_from_first_principles ]]
==== Adding a kernel module ====
This part is an extension of the previous and assumes the ''meta-ost'' layer exists and was added to ''bblayers.conf'' as described above. This part uses the [[ https://github.com/zechenturm/fpga_loader | fpga_loader ]] kernel module to demonstrate how to add an out-of-tree kernel module to yocto.
Create a new directory ''recipes-kernel'' under ''meta-ost'' and in that create another folder inside that called ''fpga-loader''. Create a file called ''fpga-loader_1.0.bb'' in ''fpga-loader'' and add the following content:
DESCRIPTION = "FPGA Loader Kernel Module"
SECTION = "ost"
DEPENDS = ""
LICENSE = "CLOSED"
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}-${PV}:"
SRCREV = "df83789290cd8fe8b9d9af8dd7ad509ea25c6b46"
SRC_URI = "git://github.com/zechenturm/fpga_loader.git"
S = "${WORKDIR}/git"
inherit module
export KERNELDIR="${KERNEL_SRC}"
Since there is no LICENSE file in the ''fpga-loader'' git repository, the simplest way to not have ''bitbake'' complain about it is to set ''LICENSE'' to ''CLOSED'' even though it actually is open (source). Note that in comparison with ''helloworld'' there is no ''inherit autotools'' since ''fpga-loader'' does not rely on ''autotools''. However, thjere is ''inherit module'' which lets ''bitbake'' know that this recipe is building a kernel module.
Since ''fpga_module''s Makefile uses ''KERNELDIR'' but yocto uses ''KERNEL_SRC'', ''KERNEL_DIR'' needs to be explicitly set in the recipe for the build to succeed.
Finally, ''fpga-loader'' needs to be added to the image in ''oe-core/build/conf/local.conf'':
IMAGE_INSTALL_append = " fpga-loader"
MACHINE_EXTRA_RRECOMMENDS = " kernel-modules"
While ''IMAGE_INSTALL_append'' was sufficient for ''helloworld'', kernel modules need an extra step to make sure kernel modules are included in the image/rootfs. By default, minimal yocto images do not include them which would mean that ''fpga-loader'' would be built but not added to the rootfs. Setting ''MACHINE_EXTRA_RECOMMENDS = " kernel-modules" ensures they are included''.