Yocto: Part 10 – Building and using SDK for Raspberry Pi

Welcome to part 10 in this series that we are doing on Yocto! We have come a looong way!

In the previous parts, we started from scratch, learnt to build basic images for QEMU and Raspberry Pi machines, learnt how to create a new layer, write bitbake recipes for this layer and how to add this layer with our custom recipes to a base image.

Check out all of these posts using the links below!

Yocto: Part 1 – A Definitive Introduction

Yocto: Part 2 – Setting up Ubuntu host

Yocto: Part 3 – Build & run your first ever image!

Yocto: Part 4 – Building a basic image for Raspberry Pi

Yocto: Part 5 – Creating & adding a new layer to your image

Yocto: Part 6 – Understanding and creating your first custom recipe

Yocto: Part 7 – Writing recipes for tarballs (local and remote)

Yocto: Part 8 – Writing recipes that fetch from a Git repository

Yocto: Part 9 – Customising images by adding your recipes

In this part, talk about a very important aspect of application development in the Yocto world – building and using an SDK (Software Development Kit). We will use Raspberry Pi as the machine as an example.

Types of SDKs in Yocto

Yocto provides tools to generate an SDK that can then be used by application developers to write applications that can then be added to the image that will be deployed to your machine of choice. There are two types of SDKs that you can build – a standard SDK and an extensible SDK.

A standard SDK contains the bare minimum requirements as below.

  • Cross-development toolchain: This toolchain contains a compiler, debugger and various miscellaneous tools which are necessary for being able to compile and analyse applications.
  • Libraries, headers, symbols: The libraries, headers and symbols are specific to the image for which the SDK is being built.
  • Environment setup script: This script has to be source once at the start of a new development session. It sets up the cross-development environment by defining variables and preparing the system for SDK use.

The extensible SDK (eSDK) contains everything that a standard SDK has and additionally contains some useful tools and settings that help the developer. For instance, the eSDK allows the developer to add new applications and libraries to an image and even modify the source of a component. If using the eSDK, one can test changes on the target hardware without needing to build a full-fledged image every time.

The eSDK packs in many features on top of the standard SDK as detailed below.

Typical Development using SDK

If you are a solo developer or doing only quick proof-of-concept development work, it is not uncommon to have both the SDK and the actual Yocto project on the same machine as the development is often not too complex.

However, for larger teams the development model often looks like the below.

Building an SDK

The process of building an SDK is very simple. Assuming you have sourced the oe-init-build-env script inside your poky installation and have baked the image of interest successfully, just running the bitbake command as below will build the SDK for you. We will be building an SDK for the Raspberry Pi machine running the image we chose earlier in a previous post i.e. rpi-test-image.

// To get a standard SDK installer
$ bitbake rpi-test-image -c populate_sdk
// To get an extensible SDK installer
$ bitbake rpi-test-image -c populate_sdk_ext

It may take a while for the SDK installer to be built.. grab a cup of coffee if you can! 🙂

Installing the SDK

Once the build process has ended, you should see an image- and machine-specific SDK installer script (.sh) inside the tmp/deploy/sdk folder.

To install the SDK, simply run the installer as shown below.

$ ./tmp/deploy/sdk/<name of your sdk>.sh

The default installation path is /opt/poky/<poky version>.

Using the SDK

This is the part that you have probably been waiting for!

Once the SDK is installed, you would see that it contains an environment setup script that has to be run only once at the start of your development session. Once this setup script is sourced, you will see that the environment has a lot of variables that are useful for building applications for the target machine and image.

For instance, here’s a snapshot of what the environment looks like for the SDK based on the rpi-test-image that we built earlier for Raspberry Pi.

$ source /opt/poky/3.1.2/environment-setup-cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi
$ printenv
...
...

CC=arm-poky-linux-gnueabi-gcc  -mthumb -mfpu=neon-vfpv4 -mfloat-abi=hard -mcpu=cortex-a7 -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/poky/3.1.2/sysroots/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi
GDMSESSION=ubuntu
CFLAGS= -O2 -pipe -g -feliminate-unused-debug-types 
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
CROSS_COMPILE=arm-poky-linux-gnueabi-
CONFIGURE_FLAGS=--target=arm-poky-linux-gnueabi --host=arm-poky-linux-gnueabi --build=x86_64-linux --with-libtool-sysroot=/opt/poky/3.1.2/sysroots/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi

...
...

Observe that the CC variable is now pointing to the compiler from the Yocto SDK instead of the native GCC compiler.

Let us quickly write a helloworld application (helloworld.c) as below. and build it using two methods:

  • Using the command-line – good for quick development and PoC
  • Using CMake – good for complex applications with dependencies and configurability
#include <stdio.h>
int main(void)
{
    printf("Hello, World! I am built using a Yocto SDK!\r\n");
    return 0;
}

Building using the command-line

Assuming that you have sourced the setup script as shown above, you just need to execute the below command to build the helloworld application. The file output indicates that this executable is built for an ARM machine (hard float).

$ ${CC} helloworld.c -o helloworld_rpi -O1
$ file helloworld_rpi
helloworld_rpi: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=39d73a448214d5a34e542bc647c22ed01cc5f9f2, for GNU/Linux 3.2.0, with debug_info, not stripped

That’s how simple it is to use the SDK’s toolchain to quickly build an application!

Building using CMake

The Yocto SDK comes with a CMake toolchain file that you can directly provide to CMake so that it can build using the correct toolchain and other settings. The path to this toolchain file can be found in the variable named OE_CMAKE_TOOLCHAIN_FILE after the environment has been set up as shown above.

For instance, this is what the value looks like on my system.

$ printenv | grep OE_CMAKE_TOOLCHAIN_FILE
OE_CMAKE_TOOLCHAIN_FILE=/opt/poky/3.1.2/sysroots/x86_64-pokysdk-linux/usr/share/cmake/OEToolchainConfig.cmake

Let us now build our helloworld application using CMake. The CMakeLists.txt should look something like below. If you are looking for a practical introduction to CMake, check out this post I wrote previously.

cmake_minimum_required(VERSION 2.8.12)
project(helloworld)
add_executable(helloworld_rpi helloworld.c)
set(CMAKE_TOOLCHAIN_FILE $ENV{OE_CMAKE_TOOLCHAIN_FILE})

After creating the CMakeLists.txt, create a folder called build and cd into it. Now, from the build folder, execute CMake and then make as below. You should see the outputs similar to what is shown below.

$ mkdir build
$ cd build
$ cmake ..
-- Toolchain file defaulted to '/opt/poky/3.1.2/sysroots/x86_64-pokysdk-linux/usr/share/cmake/OEToolchainConfig.cmake'
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Check for working C compiler: /opt/poky/3.1.2/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc
-- Check for working C compiler: /opt/poky/3.1.2/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /opt/poky/3.1.2/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++
-- Check for working CXX compiler: /opt/poky/3.1.2/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/shashank/hw_testing/build

$ make
[ 50%] Building C object CMakeFiles/helloworld_rpi.dir/helloworld.c.o
[100%] Linking C executable helloworld_rpi
[100%] Built target helloworld_rpi

$ file helloworld_rpi
helloworld_rpi: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=21ff063f8e1d53225b73c7ab5c0b4bac43bef40a, for GNU/Linux 3.2.0, with debug_info, not stripped

Summary

During the course of this post, we talked about what a Yocto SDK is, how to build an SDK, install it and then use this SDK to build a simple helloworld application – using a simple and quick method and then using a standard method prevalent in the industry i.e. CMake.

Hope this was useful and helps you build your next custom application for your favourite hardware machine that runs a Yocto image!

See you next time… soon!

Leave a Reply