Due to its flexibility, Yocto might seem a bit complex the first time we use it and can be very scary. In this article I would like to demonstrate the basic usage of this wonderful build system by building a first image for your specific target, that is the first thing to do when starting with Yocto.
For this article, I decided to use Debian 9 (stretch) but any compatible distribution should do the trick, for more information, please refer to the manual.
Preparing the host system
First make sure that you have all the tools required by Yocto. It tries to compile everything from scratch but some tools are still required during the boostrap process. Moreover, it is written with python3 that must be available on your system.
Under Debian, the following packages need to be installed:
sudo apt-get install \ git \ python3 \ build-essential \ chrpath \ diffstat \ gawk \ texinfo
Download Yocto sources
We will prepare a working directory to host all sources and build artifacts that I will call YOCTO-DIR for convenience, just adapt it to a path that makes sense for you and make sure that you have at least 100Gb available on the partition:
$ mkdir -p <YOCTO-DIR> && cd <YOCTO-DIR>
We will then download the poky meta layers that contains the base distribution definition and minimal recipes to build a bootable image.
<YOCTO-DIR>$ mkdir meta-layers <YOCTO-DIR>$ git clone https://git.yoctoproject.org/git/poky meta-layers/poky
Add a meta-layer for your target
We also need a machine configuration file for our target. To find a layer containing your machine, refer to the OpenEmbedded recipe index and search for the machine you want to build for. In my case, I have found this meta-layer that as machine definition file for the raspberry PI. If you don't find an exact match for your machine, try to find a similar one as a starting point.
Now clone the meta layer in the folder we prepared previously:
<YOCTO-DIR>$ git clone git://git.yoctoproject.org/meta-raspberrypi meta-layers/meta-raspberrypi
Checkout a stable branch
To avoid headaches I don't recommend using the development branch that might break often. It is also discouraged to use meta-layers of different releases that should not be compatible with each other. This is why we will checkout that latest stable branch of each layer that is sumo the day this article was written:
<YOCTO-DIR>$ cd meta-layers/poky <YOCTO-DIR>/poky$ git checkout sumo <YOCTO-DIR>/poky$ cd ../meta-raspberrypi <YOCTO-DIR>/meta-raspberrypi$ git checkout sumo
There is a new release every six monthes. Ususally upgrading to the next stable is not so painful as long as this is done regularly to avoid a big gap between two stable releases. This is why I recommend testing the build with the new stable once a release is done, even though you decide to use an previous release for your product.
Preparing the build directory
The next step is to prepare a build directory that will contain a configuration file for the build and the list of meta-layers you want to use. Yocto is able to generate a default configuration that works for most of the use cases. This can be done by sourcing a shell script and giving a path to the build directory. Once this is done, you will be inside the build directory given as argument:
<YOCTO-DIR>$ . ./meta-layers/poky/oe-init-build-env build <YOCTO-DIR>/build$
Now we also need to add the raspberry PI meta-layer in the <YOCTO-DIR>/build/conf/bblayer.conf file. Simply edit and add the layer cloned previously:
<YOCTO-DIR>/meta-layers/poky/meta \ <YOCTO-DIR>/meta-layers/poky/meta-poky \ <YOCTO-DIR>/meta-layers/poky/meta-yocto-bsp \ + <YOCTO-DIR>/meta-layers/meta-raspberrypi \ "
It is possible to tweak the build configuration by editing the <YOCTO-DIR>/build/conf/local.conf configuration file. The one provided by default is self explanatory and should be easy to adapt to your needs. For this article, I'm not changing anything.
Start the build
Now everything is ready to start a build. This can be done by calling the bitbake command. Bitbake needs a recipe to build, in our case we will build an image called core-image-minimal. We can also configure the target machine by editing the MACHINE variable in the <YOCTO-DIR>/build/conf/local.conf configuration file. It is also possible to overwrite this variable on the command line as follow:
<YOCTO-DIR>/build$ MACHINE=raspberrypi3 bitbake core-image-minimal
Now be patient, the first time the build takes a lot of time. However Yocto is very smart to keep trace of what was built that makes incremental build much faster, consequently future builds will take much less time.
Writing the image on an SD card
Once the build is done, built artifacts such as device trees, kernels, root file systems and so on are available in the <YOCTO-DIR>/build/tmp/deploy/images/<MACHINE> folder. For the raspberry pi, you should find a file with the rpi-sdimg extension that is an image than can be written on an SD card with the dd utility. After inserting the SD card in my computer, I could write the image as follow:
<YOCTO-DIR>$ sudo dd if=build/tmp/deploy/images/raspberrypi3/core-image-minimal-raspberrypi3.rpi-sdimg of=/dev/sdd
Now eject the SD card, insert it in the raspberry PI, power it on and voilà!
For other platforms, it is possible that you have other images types such as ubifs, ext4, u-boot, and many others. In that case, flashing the image might be less convenient, in the worst case you might need to use a JTAG interface. For further details, refer to the board vendor documentation.
Now we have just built an image with the bare minimal to boot but there are still many things we need to do in order to have something customized to our needs such as:
- Writing a custom meta layer
- Writing a custom distribution
- Writing a custom machine definition file
- Writing a custom image recipes
- Building a custom kernel
- Adapting existing recipes
- Adding other applications to the image
- Adding custom applications to the build system
- Prividing an SDK
- Providing a package repository
Moreover, we don't want to clone each recipe manually each time we start a new build. This is why I recommend automatising this with a shell script like this one, a Makefile, a python script or anything that suits to you.
You now know all the basics you need to build an existing image for your target, that is the starting point when using Yocto. Customizing the image is the fun part but I will keep this for another article, stay tuned!