Frank’s product#
Frank works for Automatech Solutions GmbH.
His latest product is a device with a graphical user interface that controls all the connected devices in a home automation system.
The different devices are controlled via ETHERNET and a few GPIOs on a custom base board.
The device will communicate with a cloud backend of home automation display Inc. and receives also Over-the-air updates, using the ethernet interface.
For the UI Frank has chosen COG + Vue.js as a framework.
The device will need to meet additional security standards.
The following steps will be needed to turn his project into a product.
Create a project layer
First of all you will need to create a project layer.
You will need the following
a git server to host the repository (e.g. Github, Gitlab, your in-house git)
an empty repository (e.g. meta-myproduct)
Now you can create the project layer, as described by Create your project layer.
Create a project distro
Now it is time to create a custom distro
.
Adjust the highlighted lines
The highlighted lines need to be configured according to your needs
For that create a file with the following content under <path/to/project-layer>/conf/distro/my-distro.conf
.
DISTRO = "my-distro" # change the name according to your preferences
DISTRO_NAME = "My Distro" # change the name according to your preferences
DISTRO_BASEVERSION = "1.2.3" # you can change the version according to your preferences
DISTRO_VERSION = "${DISTRO_BASEVERSION}"
SDK_VENDOR = "-mydistroosdk"
SDK_VERSION = "${DISTRO_VERSION}"
SDK_NAME = "${DISTRO}-${TCLIBC}-${SDKMACHINE}-${IMAGE_BASENAME}-${TUNE_PKGARCH}-${MACHINE}"
SDKPATHINSTALL = "/opt/mydistro/${SDK_VERSION}/${MACHINE}"
TARGET_VENDOR = "-mydistro" # you can change the version according to your preferences
DISTROOVERRIDES .= ":my-distro" # needs to match setting from DISTRO
# add the DISTRO_FEATURES you need here, see below for more details
DISTRO_FEATURES = ""
TCLIBCAPPEND = ""
require conf/distro/include/no-static-libs.inc
require conf/distro/include/yocto-uninative.inc
require conf/distro/include/security_flags.inc
require conf/distro/include/simplecore-distro/preferred_versions.inc
INHERIT += "uninative"
DISTRO_FEATURES
Please select the DISTRO_FEATURES
you require and add them to above’s config file at DISTRO_FEATURES
.
Possible options are
alsa
- Include ALSA audio supportbluetooth
- bluetooth support.directfb
- DirectFB (direct framebuffer graphics) support.ipsec
- IPSec support.ipv6
- IPv6 support.keyboard
- keyboard support .ldconfig
- support for ldconfig and ld.so.conf on the target.nfs
- NFS client support (for mounting NFS exports on device).opengl
- Open Graphics Library, used for rendering two and three-dimensional graphics.pci
- PCI bus support.pcmcia
- PCMCIA/CompactFlash support.ppp
- PPP dialup support (also required for modem connections).smbfs
- SMB networks client support (for mounting Samba/Microsoft Windows shares on device).systemd
- support for SystemD.usbgadget
- USB Gadget Device support (for USB networking/serial/storage).usbhost
- USB Host support (allows to connect external keyboard, mouse, storage, network etc).usrmerge
- Merges the /bin, /sbin, /lib, and /lib64 directories into their respective counterparts in the /usr directory to provide better package and application compatibility.wayland
- the Wayland display server protocol, required for graphical applications.virtualization
- Support for container and other virtualization techniques.wifi
- WiFi support.
Only select what you need
Please select only the mandatory options. In case you are unsure omit the item
Now you can select your custom distribution config by setting in local.conf
DISTRO = "my-distro"
For DISTRO_FEATURES
Frank chooses
ipv6 opengl ppp systemd wayland
Create a build manifest
To define what layers and settings your product image will require, it is needed to define a so called manifest
.
These are files that define repositories and their used revision in a reproducible way.
First you will need to create a new repository on your git server.
There are different tools available, but we would recommend
repo is the reference tool used by Google Android to manage different repositories.
It uses xml
files to configure what repository is checkout with what revision.
Create a manifest
Create a file named default.xml
in your manifest repository.
You can create such a file with the help of Scotty.
Just run
$ scotty export --mode repo
to create a good starting point for your project
Create a config template
As repo only specializes in managing git repositories, you will need to create a bitbake template to configure the Yocto build.
You can create such a files with the help of Scotty.
Just run
$ scotty export --mode local.conf
# and
$ scotty export --mode bblayers.conf
to create a good starting point for your project
Build
$ repo init -u git://<URL to your git server hosting your project layer>.git
$ repo sync
$ TEMPLATECONF=sources/meta-my-project-layer/template/my-template source sources/poky/oe-init-build-env
now you can start your bitbake build with
$ bitbake my-image
kas is a widely known tool to manage bitbake layers and build configuration.
Create a manifest
Everything is controlled by a single yaml
file.
You can create such a file with the help of Scotty.
Just run
$ scotty export --mode kas
to create a good starting point for your project
Build
$ kas build <yaml config file>
Create a license policy
Almost all the time it is advised to create a license policy for your product. This will prevent accidentally using software that isn’t licensed under terms you cannot agree on in your product.
A typical example is that, if you want to use secure boot or measured boot, it is not
possible to use any e.g. GPL-3.0-or-later
licensed software in your product.
Define a policy
You can define the policy on a DISTRO
or on an image level.
We recommend to use it at a DISTRO
level.
To automatically filter out incompatible licenses add
INCOMPATIBLE_LICENSE += ""
to your distro conf.
The following licenses are known to cause issues, so we recommend to add them
to the INCOMPATIBLE_LICENSE
license list
INCOMPATIBLE_LICENSE += "\
AGPL-3.0 \
CC-BY-NC-1.0 \
CC-BY-NC-2.0 \
CC-BY-NC-2.5 \
CC-BY-NC-3.0 \
CC-BY-NC-ND-1.0 \
CC-BY-NC-ND-2.0 \
CC-BY-NC-ND-2.5 \
CC-BY-NC-ND-3.0 \
CC-BY-NC-SA-1.0 \
CC-BY-NC-SA-2.0 \
CC-BY-NC-SA-2.5 \
CDDL-1.0 \
EPL-1.0 \
EPL-2.0 \
EUPL-1.0 \
EUPL-1.1 \
GPL-1.0 \
GPL-3.0 \
GPL-3.0-with-autoconf-exception \
LGPL-3.0 \
MPL-1.1 \
MS-PL \
MS-RL \
"
Now the build is automatically terminated if a piece of software with any of above’s licenses is tried to be installed into your image
What to do if it breaks your build
In case your build breaks, you have the following options
- see if you can remove the need for mentioned component.
e.g. use POSIX shell instead of bash
e.g. replace the dependency with a permissively licensed variant
e.g. ask the author for a commercial license
see if the need can be deactivated by e.g.
PACKAGECONFIG
and of course you can accept the license terms and remove the license from
INCOMPATIBLE_LICENSE
Application recipes
For each of your product application can services you’ll need to create recipes. Please see Create a recipe on how to do that.
After following the guide Frank has created these recipes
homeautomation-backend_git.bb
homeautomation-ui_git.bb
Application services
For each of your recipe (if they don’t ship systemd services on their own), you will need to create SystemD Service Units.
We recommend to create a new recipe for each of your applications/services.
create service unit
Under <path/to/project-layer>/recipes-project/<service name>/files/<service name>.service
create a file with
the following content
[Unit]
# A brief description of your service
Description=My custom service
[Service]
# the following line defines what to run
ExecStart=/usr/bin/my-service
[Install]
WantedBy=multi-user.target
create service recipe
Under <path/to/project-layer>/recipes-project/<service name>/<service name>_1.0.bb
add the following content.
SUMMARY = "${BPN} systemd service"
LICENSE = "CLOSED"
SRC_URI = "file://${BPN}.service"
inherit allarch
S = "${WORKDIR}"
do_install() {
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/${BPN}.service ${D}${systemd_system_unitdir}
}
FILES:${PN} += "${systemd_system_unitdir}"
RDEPENDS:${PN} += "<name of the application this service is for>"
e.g. if the application recipe is called backend
the recipe needs to look like that
SUMMARY = "${BPN} systemd service"
LICENSE = "CLOSED"
SRC_URI = "file://${BPN}.service"
inherit allarch
S = "${WORKDIR}"
do_install() {
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/${BPN}.service ${D}${systemd_system_unitdir}
}
FILES:${PN} += "${systemd_system_unitdir}"
RDEPENDS:${PN} += "backend"
For Frank’s application he will need to create
homeautomation-backend-service
homeautomation-ui-service
Defining the image
Time to create your project specific image, create a recipe in your project specific layer.
E.g. at <path/to/project-layer>/recipes-core/images/my-image.bb
SUMMARY = "My project image"
LICENSE = "CLOSED"
inherit core-image
# Install additional packages/recipe by adding to the
# following line
IMAGE_INSTALL += ""
To add the right recipes for this image Frank adds
IMAGE_INSTALL += "\
homeautomation-backend-service \
homeautomation-ui-service \
"
to his image
Custom hardware/carrier board
Talk to our experts
Customization of the device tree and techniques beyond that, are sometimes tricky. If your product is using a custom carrier board, we recommend to talk to our experts.
Trim down of the U-Boot config
To reduce the used u-boot configuration you will need to run the following commands from a bitbake environment
$ bitbake u-boot-imx-msc -c do_menuconfig
Now remove the options you are sure you don’t need. Hit ? to get an explanation on each item. When done save the configuration.
Now run
$ bitbake u-boot-imx-msc -c do_diffconfig
Save the output to a new file under <path/to/project-layer>/recipes-bsp/u-boot-imx-msc/files/my-project.cfg
and finally create a recipe append at <path/to/project-layer>/recipes-bsp/u-boot-imx-msc/u-boot-imx-msc_%.bbappend
with
the following content
FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:"
SRC_URI:append = " file://my-project.cfg"
Trim down of the Linux kernel config
To reduce the used kernel configuration you will need to run the following commands from a bitbake environment
$ bitbake linux-imx-msc -c do_menuconfig
Now remove the options you are sure you don’t need. Hit ? to get an explanation on each item. When done save the configuration.
Now run
$ bitbake linux-imx-msc -c do_diffconfig
Save the output to a new file under <path/to/project-layer>/recipes-kernel/linux/files/my-project.cfg
and finally create a recipe append at <path/to/project-layer>/recipes-kernel/linux/linux-imx-msc_%.bbappend
with
the following content
FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:"
SRC_URI:append = " file://my-project.cfg"
SimpleCoreIO integration
To integrate SimpleCoreIO create the following recipes
libsimplecoreio
At <path/to/project-layer>/recipes-bsp/simplecoreio/libsimplecoreio_git.bb
SUMMARY = "Add userspace support for simplecoreio"
AUTHOR = "Tria"
LICENSE = "MIT & GPL-2.0-or-later"
LIC_FILES_CHKSUM = "file://LICENSE;md5=76e7da7e63cd7004b01b402febe8bdd0"
DEPENDS += "libgpiod"
SRC_URI = "\
git://git@github.com/avnet-embedded/simplecoreio.git;protocol=ssh;branch=master \
"
SRCREV = "33d837fe54fbdc474443a9404664fd222354d412"
S = "${WORKDIR}/git"
UPSTREAM_CHECK_COMMITS = "1"
inherit python3native
EXTRA_OEMAKE = "PREFIX=${prefix} CC='${CC}' CFLAGS='${CFLAGS}' DESTDIR=${D} LIBDIR=${libdir} INCLUDEDIR=${includedir} PYTHON_LIBDIR=${PYTHON_SITEPACKAGES_DIR}"
do_install() {
oe_runmake install install-python3 install-examples install-mock install-tools
}
PACKAGE_BEFORE_PN += "${PN}-examples ${PN}-python ${PN}-python-examples ${PN}-mock ${PN}-tools"
FILES:${PN} += "${libdir}/libsimplecoreio.so.* ${datadir} ${systemd_system_unitdir}"
FILES:${PN}-examples += "${bindir}/libsimplecoreio-example"
FILES:${PN}-tools += "${bindir}/simplecoreio-list"
FILES:${PN}-python += "${PYTHON_SITEPACKAGES_DIR}/simplecoreio"
FILES:${PN}-mock += "${bindir}/gpio-mock-cdev"
FILES:${PN}-python-examples += "${bindir}/libsimplecoreio-example.py"
RDEPENDS:${PN} += "libgpiod mscio-drivers"
RDEPENDS:${PN}-examples += "${PN}"
RDEPENDS:${PN}-tools += "${PN}"
RDEPENDS:${PN}-python += "${PN} ${PYTHON_PN}-ctypes libgpiod-python"
RDEPENDS:${PN}-python-examples += "${PN}-python"
simplecore-names
and at <path/to/project-layer>/recipes-bsp/simplecoreio/simplecore-names_git.bb
SUMMARY = "Provides rules for providing consistent names to various interfaces"
LICENSE = "MIT"
SRC_URI = "\
git://git@github.com/avnet-embedded/simplecoreio.git;protocol=ssh;branch=master \
"
SRCREV = "33d837fe54fbdc474443a9404664fd222354d412"
LIC_FILES_CHKSUM = "file://../LICENSE;md5=76e7da7e63cd7004b01b402febe8bdd0"
S = "${WORKDIR}/git/configs"
UPSTREAM_CHECK_COMMITS = "1"
inherit systemd
do_install() {
# systemd network files
install -d "${D}${systemd_unitdir}/network"
if [ -e "${S}/systemd/network/all/" ]; then
for file in ${S}/systemd/network/all/*; do
install -m 644 "$file" "${D}${systemd_unitdir}/network"
done
fi
if [ -e "${S}/systemd/network/${MACHINE}/" ]; then
# overwrite by MACHINE specifics
for file in ${S}/systemd/network/${MACHINE}/*; do
install -m 644 "$file" "${D}${systemd_unitdir}/network"
done
fi
# systemd service files
install -m 0755 -d "${D}${systemd_system_unitdir}"
if [ -e "${S}/systemd/services/all" ]; then
for file in ${S}/systemd/services/all/*; do
install -m 644 "$file" "${D}${systemd_system_unitdir}"
done
fi
if [ -e "${S}/systemd/services/${MACHINE}" ]; then
for file in ${S}/systemd/services/${MACHINE}/*; do
install -m 644 "$file" "${D}${systemd_system_unitdir}"
done
fi
# udev rules
install -d "${D}${nonarch_base_libdir}/udev/rules.d"
if [ -e "${S}/udev/rules/all/" ]; then
for file in ${S}/udev/rules/all/*; do
install -m 644 "$file" "${D}${nonarch_base_libdir}/udev/rules.d"
done
fi
if [ -e "${S}/udev/rules/${MACHINE}/" ]; then
# overwrite by MACHINE specifics
for file in ${S}/udev/rules/${MACHINE}/*; do
install -m 644 "$file" "${D}${nonarch_base_libdir}/udev/rules.d"
done
fi
# udev scripts
install -d "${D}${nonarch_base_libdir}/udev/scripts"
if [ -e "${S}/udev/scripts/all" ]; then
for file in ${S}/udev/scripts/all/*; do
install -m 755 "$file" "${D}${nonarch_base_libdir}/udev/scripts"
done
fi
if [ -e "${S}/udev/scripts/${MACHINE}" ]; then
for file in ${S}/udev/scripts/${MACHINE}/*; do
install -m 755 "$file" "${D}${nonarch_base_libdir}/udev/scripts"
done
fi
# other files
if [ -e "${S}/files/all" ]; then
cp -r ${S}/files/all/* ${D}
fi
if [ -e "${S}/files/${MACHINE}" ]; then
cp -r ${S}/files/${MACHINE}/* ${D}
fi
chown -R root:root ${D}/*
}
SYSTEMD_SERVICE:${PN} = "systemd-udev-retrigger.service"
FILES:${PN} += "/ ${systemd_unitdir}/network ${nonarch_base_libdir}/udev ${systemd_system_unitdir}"
Reference in your image
and finally add to your image
IMAGE_INSTALL += "libsimplecoreio simplecore-names"
Integrate OTA
There are several OTA solutions in the market, which offer you an off-the-shelf experience.
Here an overview of a few of the options
Mender is one of the most known platforms to provide over-the-air updates. It also will give you to possibility to self-host update servers.
foundries.io is a well known platform for over-the-air updates. It will add your applications on top on a well maintained BSP coming from foundries.
SWUpdate is a solution that can provide over-the-air updates, but also provides ways to do local updates, like with an USB stick.
RAUC offers you a robust over-the-air update solution.
Secure Boot
Integration of secure boot and techniques beyond that, is sometimes tricky. If your product requires secure boot and/or other techniques, we recommend to talk to our experts.
Setup testing
As you more and more shape your project into a product, we recommend that you consider adding automated testing to your CI/CD pipelines.
From experience we recommend to have a look at
Update/Upgrade policy
When turning a project into a product you also need to think about how and when to update/upgrade your software.
Upgrade of layers
You will need to update the used revisions of your manifest on a regular basis.
It might be best to create CI automation for that to probe for updates to the selected branches on a regular basis.
Upgrade of your project specific recipes
You will also need to probe on a regular basis if there are changes to the recipes in your project specific layer.
Yocto provides a tools called devtool
for that purpose.
First you will need to identify the recipe in your layer. From a sourced bitbake environment run
$ bitbake-layers show-recipes -l <your layer name> -r 2>/dev/null \
| grep -v skipped | grep -v WARNING | grep -v NOTE | grep -v Loading \
| grep -v Loaded | cut -d'=' -f2
# this will return something like
my-custom-recipe
my-application
now you can use on each of them devtool check-upgrade-status
for item in $(bitbake-layers show-recipes -l <your layer name> -r 2>/dev/null \
| grep -v skipped | grep -v WARNING | grep -v NOTE | grep -v Loading \
| grep -v Loaded | cut -d'=' -f2); do
devtool check-upgrade-status $item
done
in case of an available update it will give you information like
INFO: my-recipe 1.0 new commits None fdc6e54294649a30557d2f09bb88f371c8ac0769
with the last information on that line being the new SRCREV
.
Now you can do the update of the recipe.
Define a release process
You will also need to setup a process to create a release, which includes
setting the new release version in your distro configuration (
DISTRO_BASEVERSION
)create a release with this patched information
saving the artifacts from the build
Depending on your product additional steps might be required.
Talk to our experts
In case you need further support
Storage for SBOMs and copy-left artifacts
As your product will most certainly contain Copyleft licensed code, you will need to make these parts of the code available to users that request it.
So one of the tasks will be to create a webserver, that will host archives created by the build.
Enable archive.bbclass
Add the following line to your distro config
INHERIT += "archiver"
Copy archives after each build
After each (release) build you will have to copy the files from
$ cp -r build/tmp/deploy/sources/* /path/to/your/webservers/www-root/
if the webserver is a remote machine you may consider using rsync for that.
Optional registration wall
You are not required to make those sources available to everyone but the actual users of your product, so you may want to add a registration wall to the webserver - speak to your IT department about the options
You will likely be also required to provide Software Bills of Material (or short SBOM)
Enable create-spdx.bbclass
Add the following line to your distro config
INHERIT += "create-spdx"
SPDX_INCLUDE_SOURCES = "1"
SPDX_UUID_NAMESPACE = "<URL of your webserver>/spdx"
SPDX_ORG = "<Your company name>"
Copy SBOMs after each build
After each (release) build you will have to copy the files from
$ cp -r build/tmp/deploy/spdx/* /path/to/your/webservers/www-root/spdx/
if the webserver is a remote machine you may consider using rsync for that.
You may also want to have a look at the following webinar, which provides further steps and recommendations
Talk to our experts
In case you need further support