How to Set Up QtCreator for Cross-Compilation with CMake in 5 Minutes

Setting up QtCreator for cross-compilation with CMake took me 15-20 hours the first two or three times. The next half a dozen times took me 4 hours on average. The last two times took me 5 minutes – using the script presented in this post. I can cross-compile the Qt application for my embedded device and run the Qt application on the device with one button press in QtCreator.

Users of the commercial Qt for Device Creation license have been enjoying this fast and proper QtCreator setup for several years now. The script in this post is the first time that (L)GPLv3 users of Qt can enjoy the same convenience.

Prerequisites

You have built a Linux image and an SDK for an embedded device with Yocto. For this post, I built the SDK for the reference image tdx-reference-multimedia-image of the Verdin i.MX8M Mini board from Toradex. You can log into the board with SSH using a username and password.

You should use a QtCreator version not older than 4.11 and a CMake version not older than 3.14. Using the latest QtCreator and CMake versions has never caused me any problems. So, I suggest you do the same. In the unlikely event that you must use older versions, I explain a workaround in my post Deploying Qt Projects to Embedded Devices with CMake.

Download the script configure-qtcreator.sh, install it in a directory from $PATH and make it executable.

$ wget https://raw.githubusercontent.com/bstubert/embeddeduse/master/BlogPosts/qtcreator-setup/configure-qtcreator.sh
$ mv configure-qtcreator.sh $HOME/bin
$ chmod a+x $HOME/bin/configure-qtcreator.sh

The script generates the Qt Creator configuration files, which QtCreator displays in the dialog Tools | Options | Kits. The script works for any SDK built with Yocto.

Setting Up QtCreator in 5 Minutes

You choose a name for the QtCreator kit. My kit name is

NAME = Goldfinger

QtCreator uses this name to mark the compiler, Qt version, debugger, CMake version and device as belonging to this kit.

The Yocto SDK is installed in $SDK_DIR_PATH and contains the environment setup file, which sets Linux environment variables like OECORE_*, CXX and CXXFLAGS needed for cross-compilation against the SDK. My settings look as follows:

SDK_DIR_PATH = /private/Projects/Spectre/verdin-dunfell/sdk
ENV_SETUP_FILE_PATH = $SDK_DIR_PATH/environment-setup-aarch64-tdx-linux

The QtCreator version, for which you generate the configuration files, is located in $QT_CREATOR_DIR_PATH, which contains the subdirectories bin and libexec. My settings are

QT_CREATOR_DIR_PATH = /home/burkhard/Qt/Qt5.15.2/Tools/QtCreator

$DEVICE (verdin-imx8mm.local for me) is the name or IP address of the device, on which the Qt application will be running. The call

$ ssh $DEVICE

should allow you to log in to the target device. Once set up, QtCreator will deploy the Qt application to $DEVICE and run it on the $DEVICE.

Close QtCreator and call the script configure-qtcreator.sh:

$ configure-qtcreator.sh --name $NAME --env-setup $ENV_SETUP_FILE_PATH \
    --creator-base $QT_CREATOR_DIR_PATH --device $DEVICE

The script generates two CMake toolchain files – Goldfinger.cmake and OEQt5Toolchain.cmake – in the directory

$SDK_DIR_PATH/sysroots/x86_64-tdxsdk-linux/usr/share/cmake/OEToolchainConfig.cmake.d

Goldfinger.cmake is a normal CMake toolchain file and sets the variables for the C++ compiler, the linker and the system root among others. OEQt5Toolchain.cmake fixes a long-standing bug in the CMake files of the Qt libraries.

The script also generates a couple of QtCreator configuration files – profiles.xml, qtversion.xml, toolchain.xml, debuggers.xml, cmaketools.xml and devices.xml – in the directory

$QT_CREATOR_DIR_PATH/share/qtcreator/QtProject/qtcreator

It is now time to restart QtCreator. When started, QtCreator merges these configuration files with their counterparts in $HOME/.config/QtProject/qtcreator. When you open the dialog Tools | Options | Kits | Kits, you’ll see the new auto-detected kit Goldfinger.

QtCreator shows the kit Goldfinger that you just created with the script configure-qtcreator.sh

QtCreator auto-detected the configurations for the Device Goldfinger, the sysroot, the C compiler GCC (Goldfinger), the C++ compiler (Goldfinger), the debugger GDB Goldfinger, the Qt version Goldfinger, the CMake tool CMake Goldfinger, the CMake generator and the CMake configuration. You can inspect these configurations by clicking through the tab pages. If you want to change the disabled configurations, you must clone them.

One manual post-processing step is required: creating and deploying a public key on the device. Go to Tools | Options | Devices, select the Device Goldfinger, press the Create | New for Private key file and press the Deploy Public Key button. You can test your setup by pressing the Test button. I have described the setup of a device in detail in my post Docker Builds from QtCreator.

You are now able to cross-build your Qt application against the Yocto SDK. To run the Qt application on the device, you adapt the deployment and run settings as described in my post Cross-Compiling Qt Embedded Applications with QtCreator and CMake.

CMake Toolchain Files

The Yocto SDK comes with a CMake toolchain file $ORIG_TOOLCHAIN_FILE, where

TOOLCHAIN_DIR = $SDK_DIR_PATH/sysroots/x86_64-tdxsdk-linux/usr/share/cmake/
ORIG_TOOLCHAIN_FILE = $TOOLCHAIN_DIR/OEToolchainConfig.cmake

You could source the environment setup file $ENV_SETUP_FILE_PATH of the Yocto SDK in a Linux terminal and start QtCreator from the same terminal. This approach has two problems.

  • It will only work, if you explicitly set the variable OE_QMAKE_PATH_EXTERNAL_HOST_BINS in the CMake toolchain file. This is a long-standing bug Qt’s CMake files from Yocto 2.x, which unfortunately made it into Yocto 3.x.
  • You cannot switch kits in QtCreator without restarting QtCreator in another terminal. The original toolchain file reads the environment variables (e.g., $ENV{CFLAGS}) set in the environment setup script.

The script configure-qtcreator.sh solves these two problems by generating a CMake toolchain file for each problem. It generates $QT5_TOOLCHAIN_FILE for the first problem and $KIT_TOOLCHAIN_FILE for the second problem, where

QT5_TOOLCHAIN_FILE = $TOOLCHAIN_DIR/OEToolchainConfig.cmake.d/OEQt5Toolchain.cmake
KIT_TOOLCHAIN_FILE = $TOOLCHAIN_DIR/OEToolchainConfig.cmake.d/Goldfinger.cmake

$ORIG_TOOLCHAIN_FILE includes all the *.cmake files from the subdirectory OEToolchainConfig.cmake.d at the end.

file( GLOB toolchain_config_files "${CMAKE_TOOLCHAIN_FILE}.d/*.cmake" )
foreach(config ${toolchain_config_files})
    include(${config})
endforeach()

If you didn’t source the environment setup file, the command

set( CMAKE_C_FLAGS $ENV{CFLAGS} CACHE STRING "" FORCE )

from $ORIG_TOOLCHAIN_FILE will assign the empty string to CMAKE_C_FLAGS. The $KIT_TOOLCHAIN_FILE overwrites CMAKE_C_FLAGS with the command

set( CMAKE_C_FLAGS " -O2 -pipe -g -feliminate-unused-debug-types " 
     CACHE STRING "" FORCE )

$KIT_TOOLCHAIN_FILE replaces all the environment variables from $ORIG_TOOLCHAIN_FILE with the values of these environment variables. Otherwise, the toolchain files are the same.

$QT5_TOOLCHAIN_FILE sets the CMake variable OE_QMAKE_PATH_EXTERNAL_HOST_BINS to its correct value. If this variable is not set, the CMake files for the Qt modules will be skipped with the message “WARNING Skipping because OE_QMAKE_PATH_EXTERNAL_HOST_BINS is not defined”.

QtCreator Configuration Files

The options --name, --env-setup and --creator-base of the script configure-qtcreator.sh are mandatory. The example call included the option --device $DEVICE to generate a device configuration. If you already have a device configuration, you leave out the option.

The default ABI flavour is arm-linux-poky-elf-64bit. If you had a 32-bit device, you’d pass the option --device arm-linux-poky-elf-32bit. The ABI flavour is shown in QtCreator’s compiler settings Tools | Options | Kits | Compilers.

The script configure-qtcreator.sh first removes the configurations from the QtCreator configuration files and then adds the new configurations. If you pass the option --remove, the script will only remove configurations.

The script configure-qtcreator.sh uses the QtCreator utility sdktool to remove and add configurations. The utility is located in

SDKTOOL = $QT_CREATOR_DIR_PATH/libexec/qtcreator/sdktool

It reads the XML configuration files (e.g., toolchains.xml, profiles.xml) from the directory $QT_CREATOR_DIR_PATH/share/qtcreator/QtProject/qtcreator, removes or adds sections and writes the modified XML files. When restarted, QtCreator reads these XML files, adds a unique ID and merges the configurations with those from $HOME/.config/QtProject/qtcreator.

The command $SDKTOOL --help lists all the available add and remove operations. The command $SDKTOOL --help <operation> shows help for a specific operation like addTc and rmTC. You will not find sdktool mentioned even once in the Qt documentation. The source code provides the only other source of information. The rest is trial and error.

The script configure-qtcreator.sh needs two sdktool calls to remove any configuration.

# BASEID = byos.Goldfinger.5.14.2
# CREATOR_CONFIG_DIR = $HOME/.config/QtProject/qtcreator

${SDKTOOL} rmQt --id ${BASEID}.qt 2>/dev/null || true
${SDKTOOL} --sdkpath=${CREATOR_CONFIG_DIR} rmQt --id ${BASEID}.qt 2>/dev/null || true

The first call removes the Qt version from

$QT_CREATOR_DIR_PATH/share/qtcreator/QtProject/qtcreator/qtversion.xml

The second call removes the Qt version from

$HOME/.config/QtProject/qtcreator/qtversion.xml

Note that the option --sdkpath=<path> cannot be rewritten as --sdkpath <path> like all the other options. sdktool would fail with a meaningless message. All the other options must have the format --name <value>.

Adding a configuration requires only one sdktool call. For example, a new Qt version is added with this sdktool command:

# BASEID = byos.Goldfinger.5.14.2
# NAME = Goldfinger

${SDKTOOL} addQt --id "${BASEID}.qt" --name "${NAME}" \
    --type "Qdb.EmbeddedLinuxQt" --qmake "$(type -p qmake)"

This command adds the Qt version to the file

$QT_CREATOR_DIR_PATH/share/qtcreator/QtProject/qtcreator/qtversion.xml

When restarted, QtCreator adds an ID to the Qt version and merges it into the file

$HOME/.config/QtProject/qtcreator/qtversion.xml

I figured out the type Qdb.EmbeddedLinuxQt by looking up the Qt version of a manually created configuration.

All the configurations show up as auto-detected in the QtCreator dialog Tools | Options | Kits. You would have to change the source code of sdktool, which hard-wires auto-detect to true. It is not enough to change the auto-detect property to false in the XML configuration files. QtCreator changes the value back to true on the next restart.

Conclusion

When you install a commercial Boot2Qt edition, the Qt installer creates the CMake toolchain files missing from the Yocto SDK, guides you through setting up a device and generates the QtCreator configuration files. When you start QtCreator after the installation, you are ready to cross-build your Qt application. You don’t have to setup the kit and its parts manually in QtCreator.

Most of the magic comes from the original configure-qtcreator.sh script. I added the generation of the CMake toolchain files and made sure that all parts of a generated kit get removed. My modified configure-qtcreator.sh script provides you with the same convenience as the installer of the commercial Boot2Qt versions. It enables you to cross-build your Qt application for your device and run them on the device within 5 minutes!

Scroll to top