One of the most common issues in embedded device engineering is how to create a development toolchain and following this how to create a root filesystem to run on the target. These are sometimes also called development and runtime systems. There are several solutions to do this but let us look at some of the challenges first.
There has been some talk recently about reproducibility on a completely new level, where one tries to verify binary programs by rebuilding them from source code and compare the binary result. If the binary matches the build result then it can be assumed that the source code matches the binary.
But this is not what I am looking for. What I want to assure is that I can successfully recreate the toolchain and root filesystem at some later point in time, like in some months, after a year or two. If you build products you will need to fix or change stuff even in old products. If you can not reproduce what you did before you can easily get into trouble.
A build system must be in a way transparent so that you can follow all its steps to create the targets. It must not use external pre-built binaries or if it does, it must be clear where these originate from, how these have been built and which licenses these fall under (also see below ‚License compliance‘).
- Dependency handling
Ideally the build system should handle and resolve dependencies automatically. Today’s software has become complex and a single component can easily depend on dozens of other packages which again depend on other packages etc. You do not want to resolve all of these on your own.
- License compliance
Building even a small development and runtime system involves handling (downloading, storing, unpacking, patching, compiling etc.) a huge amount of sourcecode – unpacked easily several gigabytes. If you want to distribute your work’s result, even if it is on a non-profit private basis, you have to honor all license terms that have been involved in the building process to create your product. Only compatible licenses must be combined, some require that you can provide the exact sourcecode, some others are so called ‚viral‘ and influence other packages.
- Cross building
In most cases your target system is not the same as your build system. It starts with a potentially different library setup you want to use for your embedded device and in most cases you will have to target a completely different CPU too. This process is called cross compiling or cross building. You will be building / compiling software on machine A for machine B – e.g. example you will build on a Intel x86 GLibc host for a µcLibc ARM system. The build system must make sure that no parts of the host system are used within the target build process.
There are some possible solutions to the above mentioned challenges:
- Just search the internet and cherry pick $binaries to copy it together
- Roll your own – how hard can it be?
- Buildroot, the classic embedded swiss army knife
- OpenEmbedded / Yocto
- Debian & debootstrap
- kind of 5b approach, install a basic system and compile the rest natively on the target
During the next days and weeks I will describe each of these approaches and how I get along with them. The requirements are
- host is Intel x86, 64 bit, GLibc (Debian)
- target is ARM, little endian 32 bit, Cortex A9 core, hard float
- cross toolchain
- with C++ support and GLibc
- must cross compile a recent mainline 4.12.x Linux kernel
- must cross compile a recent U-Boot bootloader
- must cross compile a simple terminal application, I take GNU Hello:
- root filesystem
- must boot into a serial console with login
- must be able to run the cross compiled GNU Hello program
Stay tuned! This will be fun and hopefully also be somewhat educational – at least for me it will definitely be.