Yoctoο
Yocto is a framework for building customised embedded linux images for a device.
Nomenclatureο
Bitbake: a generic task executor
OpenEmbedded: a metadata set used by bitbake
Metadata: files containing information about how to build an image
Recipe: file with instructions to build one or more packages
Layer: directory containing grouped meta-data
BSP: board support package, layer that defines how to build for a board
Distribution: specific implementation of Linux, kernel version, rootfs etc.
Machine: defines the architecture, pins, buses, BSP etc.
Image: output of build process (bootable and executable Linux OS)
Bitbake Commandsο
Note
Before running a lot of these commands, you might have to source the environment. e.g. source poky/oe-init-build-env <build_dir>
bitbake <image-name>This will build your linux image using the <image-name> as the root .bb file.
bitbake-layers create-layer meta-<name>This creates the basic directory structure for a layer. The name should start with meta.
bitbake -c <cmd> <image>Run a command from an image. The command is a task available to the image.
bitbake -c cleanall my_imagebitbake -c clean my_imagebitbake -c compile my_image
bitbake -eShows the bitbake environment for the most recent build
bitbake -g <package_name>Creates a dependancy tree for the package, which you can view in task-depends.dot
BBDEBUG=3 bitbake custom-imagePrints debug information from your build
bitbake -c listtasks <image>View all the tasks bitbake has to perform to build a target image.
Add
-fto force a task to run, invalidating any existing stamp file.
bitbake -c devshell <target>This is a shell where you can run bitbake commands, and see environment variables like
$CC
Adding a custom C program/applicationο
This section will describe adding a program (C) to usr/bin
- Tutorials:
You will want something similar to this in your CMakeLists.txt, which installs the program in usr/bin
cmake_minimum_required(VERSION 1.9)
project (hellocmake)
add_executable(hellocmake helloworld.c)
install(TARGETS hellocmake RUNTIME DESTINATION bin)
If you want pull files from GitHub for example, you something like in the example:
SRCREV = "${AUTOREV}"
PV = "0.1+git${SRCPV}"
SRC_URI = "git://github.com/mcbemlogic/yocto_pull_test;protocol=http;branch=main"
S = "${WORKDIR}/git"
inherit cmake
EXTRA_OECMAKE = ""
AUTOREVwill pull the latest commit from git. You can use this revision to update the package name too.You can also pull specific commits with something like:
SRCREV = "01351f639907247a2ecd2309865dffcd11930d8f"inherit cmakeis required if your package is using CMakeEXTRA_OECMAKEallows you to pass extra arguments to the CMake process
Appending to a Recipeο
This is achieved using the .bbappend where the name of the file is the same as the recipe file you want to append to.
The append file can overwrite or add new things to an existing .bb file.
Use the % in the name so that it disregards the version number of the .bb file.
e.g. hello_0.1.bb can be appended with both hello_%.bbappend and hello_0.1.bbappend
Tasks / Adding Custom Tasksο
You can write a task in a .bb file with either regular or python syntax:
do_custom_task(){
echo "CUSTOM TASK is running!"
}
addtask custom_task
python do_custom_task(){
import time
bb.plain("Hello")
time.sleep(3)
}
You can specify where the task should run within the recipeβs task list by using the before and after keywords.
addtask custom_task after do_fetch before do_compile
Warning
The task you are adding doesnβt need the the do_ prefix, but the tasks specified by before and after need the do_ prefix to be registered correctly.
From the python task you can print various levels of information:
bb.plain("Hello")
bb.note("Note")
bb.warn("Oh no a warning")
bb.error("Oh no an error!!!")
bb.fatal("Crash and burn")
Note
You can look into log.taskorder to check the order of tasks
Package Groupsο
A package group is a group of packages (recipes) that achieve the same/similar things.
Adding a package groupο
PACKAGE_ARCH = "${MACHINE_ARCH}"
inherit packagegroup
PACKAGES = "\
${PN}-helloworld \
"
RDEPENDS:${PN}-helloworld = " \
hello \
"
${PN} is βpackagegroup-customβ, or the package name. You can have multiple
images included in the RDEPENDS section.
IMAGE_INSTALL += " packagegroup-custom-helloworld"
Making your own Distroο
To make your own distro, you will need to create a new layer. In this layer, create a file in conf/distro called <my_distro>.conf.
require conf/distro/poky.conf
DISTRO_NAME = "mydistro"
DISTRO_VERSION = "0.1"
Add DISTRO ?= "mydistro" to your local.conf to implement your distro.
SystemDο
If you want to use SystemD instead of Sysvinit, you can add the following lines to distro configuration file.
DISTRO_FEATURES:append = " systemd"
DISTRO_FEATURES:remove = "sysvinit"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""
Adding a Serviceο
Create a new layer:
βββ systemd βββ files β βββ hello.service βββ hellosystemd.bb
In the .bb file, youβll want something like this:
LICENSE = "CLOSED"
inherit systemd
SYSTEMD_AUTO_ENABLE = "enable"
SYSTEMD_SERVICE:${PN} = "hello.service"
SRC_URI:append = " file://hello.service "
FILES:${PN} += "${systemd_unitdir}/system/hello.service"
do_install:append() {
install -d ${D}/${systemd_unitdir}/system
install -m 0644 ${WORKDIR}/hello.service ${D}/${systemd_unitdir}/system
}
In the service file, you specify your service config. Here we specify what program this service runs and to print to the standard output
[Unit]
Description=GNU Hello World startup script for KOAN training course
[Service]
ExecStart=/usr/bin/hellocmake
StandardOutput=tty
[Install]
WantedBy=multi-user.target
Inlude it in your final image, with IMAGE_INSTALL_append or add it to a package group
e.g.
IMAGE_INSTALL:append = " hellosystemd"added to layer.conf
Patch Filesο
The basic idea of a patch is that you create a diff file for the file you want to patch. The name of the patch file doesnβt matter too much. The file it targets is named within the file. There are some conventions though of the naming of patch files that you can look up.
git diff --no-index socfpga_common.h.orig socfpga_common.h > 0001-add-uboot-bootcmd.patch
The patch is applied by including it in the SRC variable. Yocto/Bitbake automatically will search for and apply patch files.
FILESEXTRAPATHS:prepend := "${THISDIR}:"
SRC_URI += "file://0001-add-uboot-bootcmd.patch"
This could go in a .bbappend file, pointing to the recipe which fetches the source you want to patch.
In Bitbake, the workflow of tasks is like this:
Fetch - get the source code
Extract - unpack the sources
Patch - apply patches for bug fixes and new capability
Configure - set up your environment specifications
Build - compile and link
Install - copy files to target directories
Package - bundle files for installation
Patching the Kernelο
See this video on I2C patch.
Create a diff file for the kernel file you want to patch. This goes into your custom layer under
recipes-kernel, where you can append your patch when building the kernel. You can specify
where the patch is applied at the top of the patch file.
When creating the diff, put the original file first:
git diff --no-index socfpga_common.h.orig socfpga_common.h > 0001-add-uboot-bootcmd.patch
Patching Device Treeο
Device tree files are .dtsi and .dts files. .dtsi files are include files, and are used generally. .dts files are the ones that are used in the end, so these are the ones you want to patch if working on device tree. Device tree also have a function to override certain attributes, by using a reference to the device tree tag. This example updates the watchdog0 node to have status okay:
&watchdog0 {
status = "okay";
};
You can find more info here about device trees.
Editing Bitbake Filesο
Assigning Valuesο
Soft assignment: value is only assigned if not already, it can be overriden later by a hard assignment
IMAGE_ROOTFS_SIZE ?= "204800"Hard assignment: Cannot be overriden
IMAGE_ROOTFS_SIZE = "204800"
Adding Packagesο
You can add packages to the final image by using: IMAGE_INSTALL += <package_name>
The package could also be a package_group, with a list of packages to be installed.
CORE_IMAGE_EXTRA_INSTALL is a convenience variable for adding extra packages to images
based on the core-image class:
CORE_IMAGE_EXTRA_INSTALL:append = " package-name"
Include and Requireο
You can include some more info in your .bb files. You can either use include - which includes the file in your .bb file - or require - which will check the file exists first before processing the .bb file. If the file doesnβt exist require will throw an error.
require recipes-extended/hello-extend.bb
Avoiding recipe name clashesο
BBFILE_PRIORITY_custom = "6"
This can be assinged in the conf/layer.conf file. The higher the number, the higher the priority. This helps resolve cases where two recipes have the same name.
Using Pythonο
CFLAGS := "${@'${CFLAGS}'.replace('-frename-registers', '')}"
The @ symbol allows you to use python within an expression
You can also use python functions:
python do_compile(){
bb.plain("Hello from Python")
bb.note("Hello from a note")
bb.warn("Hello from a warn")
bb.error("Hello from a error")
bb.fatal("Hello from a fatal")
}
Optomising Image Sizeο
Turning on buildhistory will help track parameters we can tweak
INHERIT += " buildhistory"
BUILDHISTORY_COMMIT = "1"
This turns on buildhistory and allows it be version controlled. There will be some useful files like: image_info.txt, depends.dot, installed-package-sizes.txt
Use these to help you determine which packages you can remove:
IMAGE_INSTALL_remove += " <package_name>"
Creating an SDKο
An SDK (Software Development Kit) provides all the utilities for building and compiling software for a target.
There is an SDK bbclass in the poky classes-recipes directory. It can be included in your
build by using: inherit populate-sdk
This will create files in build/tmp/deploy/sdk where you can run a script to install on your machine.
An example of using an SDK after it is installed is given here. This sources the environment variables which will point to the correct tools and environment variables to build software for your target.
. /opt/poky/4.1.3/environment-setup-cortexa9t2hf-neon-poky-linux-gnueabi
Recipe Namingο
Recipes are built using their name. They can optionally include a version number
at the end of their name, e.g. my_recipe_0.1.bb
You can reference specific recipe versions or take the latest by using just the name.
You can make append files and other files ignore the version of the recipe too by using the
% symbol. e.g. my_recipe_%.bbappend will apply to all versions of my_recipe
Example layer structure for a recipe:
βββ COPYING.MIT
βββ README
βββ conf
β βββ layer.conf
βββ recipes-hello
βββ hello
βββ files
β βββ helloworld.c
βββ hellos_0.1.bb
To look up the recipe name for a given target (e.g. the kernel):
oe-pkgdata-util lookup-recipe kernel
Updating Bitbake Syntaxο
Bitbake has changed it syntax recently so sometimes if you copy something that uses the old syntax bitbake will throw an error.
There is a script that helps update old syntax:
cd poky
scripts/contrib/convert-overrides.py <your-layer>