Debian Qt Toolchain installation

From Variscite Wiki
Warning: This page is designed to be used with a 'release' URL parameter.

This page is using the default release mx8mp-debian-bullseye-5.10.52_2.1.0-v1.1.
To view this page for a specific Variscite SoM and software release, please follow these steps:

  1. Visit variwiki.com
  2. Select your SoM
  3. Select the software release


DART-MX8M-PLUS - Debian Qt6.4.0 Toolchain Installation


Introduction

Qt is a powerful cross-platform application framework that enables developers to create high-performance applications for various operating systems, including embedded Linux. It provides a comprehensive library of pre-defined components and tools for creating graphical user interfaces, network applications, and more. This guide provides a walkthrough on how to cross-compile Qt for Variscite i.MX8-based SOMs, allow you to compile applications on a host machine and deploy them to the SOM running Debian Bullseye.

The generated Qt toolchain consists of the following:

  • qt6-x86_64: Qt installation (headers, libraries, plugins, etc.) for the host architecture
  • qt6-mx8mp-debian-11-wayland: Qt installation (headers, libraries, plugins, etc.) for the target architecture

The Qt toolchain with the Cross Compiler and the target sysroot enables Qt application programming from CLI or using the Qtcreator IDE.

You can find a list of useful Qt documentation references in the section: Qt Creator guide

Info:

As we're going to deal with several folders, here is a short summary to keep track:

~/debian_imx8mp-var-dart/src/qt6 Folder for the Qt6 framework sources
~/debian_imx8mp-var-dart/src/qt6/.x86_64_build Build folder for Qt6 host compilation
~/debian_imx8mp-var-dart/src/qt6/.mx8mp-debian-11-wayland_build Build folder for Qt6 target compilation
~/debian_imx8mp-var-dart/toolchain/sysroot Debian sysroot, created using the Create a Debian sysroot guide
~/debian_imx8mp-var-dart/toolchain/qt6-x86_64 Installation folder for the Qt6 host variant
~/debian_imx8mp-var-dart/toolchain/qt6-mx8mp-debian-11-wayland Installation folder for the Qt6 target variant

Setup Host Computer environment

We will create the Qt toolchain in the Debian build environment using a Ubuntu 20.04 machine. Therefore, please follow the steps:

  1. Setup Development Environment
  2. Download Debian build script
  3. Fetch/Deploy sources

Now, we need to install the packages needed for the Qt toolchain generation, please run the following to install those:

$ sudo apt-get update
$ sudo apt-get install -y make build-essential libclang-dev ninja-build gcc git bison python3 gperf pkg-config libfontconfig1-dev \
libfreetype6-dev libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxcb-glx0-dev \
libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-shape0-dev \
libxcb-randr0-dev libxcb-render-util0-dev libxcb-util-dev libxcb-xinerama0-dev libxcb-xkb-dev libxkbcommon-dev \
libxkbcommon-x11-dev libatspi2.0-dev libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev \
libwayland-dev libwayland-bin libwayland-egl1-mesa libwayland-client0 libwayland-server0 libwayland-cursor0 libgles2-mesa-dev

Next, we will install the GNU C/C++ Cross Compiler:

$ sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
Note: In the Debian toolchain installation guide, we're referring to the Cross Compiler which is shipped with the Debian script. For the Qt6 toolchain generation and Qt6 application programming, we need a newer one.

Finally, we will need to update cmake manually to a newer version:

$ cd ~
$ sudo apt-get remove -y cmake
$ wget https://github.com/Kitware/CMake/releases/download/v3.22.0/cmake-3.22.0.tar.gz
$ tar -zvxf cmake-3.22.0.tar.gz
$ cd cmake-3.22.0
$ ./bootstrap
$ make -j$(nproc)
$ sudo make install

Once the installation is done, it is reasonable to double-check the version:

$ cmake --version
cmake version 3.22.0

Obtain Qt6 framework sources

Download the Qt6 sources from the official Qt download archive:

$ cd ~/debian_imx8mp-var-dart/src
$ wget https://download.qt.io/archive/qt/6.4/6.4.0/single/qt-everywhere-src-6.4.0.tar.xz

And untar the archive:

$ tar -xf qt-everywhere-src-6.4.0.tar.xz
$ mv qt-everywhere-src-6.4.0 qt6
$ cd ..

The Qt6 framework sources are now located at ~/debian_imx8mp-var-dart/src/qt6.

Configure, compile and install Qt6 for the host

Create the build and installation folder:

$ mkdir -p ~/debian_imx8mp-var-dart/src/qt6/.x86_64_build
$ mkdir -p ~/debian_imx8mp-var-dart/toolchain/qt6-x86_64

Build and install the Qt framework for the host:

$ cd ~/debian_imx8mp-var-dart/src/qt6/.x86_64_build
$ cmake ../ -Wno-dev -GNinja -DCMAKE_BUILD_TYPE=Release -DQT_BUILD_EXAMPLES=OFF -DQT_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX=$HOME/debian_imx8mp-var-dart/toolchain/qt6-x86_64 -DBUILD_qtwayland=ON
$ cmake --build . --parallel $(nproc)
$ cmake --install .

Cross-Compile Qt6 for the target

Create a Qt cross-compilation sysroot

In order to perform cross-compilation of the Qt framework for the target system on the host, we would require a Debian sysroot containing all the necessary libraries. The essential libraries for the Debian rootfs (which will later be extracted as sysroot) will be added by utilizing the G_USER_PACKAGES variable in the build script. Please refer to the Adding packages to the Debian image guide for more information where to find the G_USER_PACKAGES variable, and add:

readonly G_USER_PACKAGES="\
    libboost-all-dev libudev-dev libinput-dev libts-dev libmtdev-dev libjpeg-dev libfontconfig1-dev libssl-dev libdbus-1-dev libglib2.0-dev \
    libxkbcommon-dev libegl1-mesa-dev libgbm-dev libgles2-mesa-dev mesa-common-dev libasound2-dev libpulse-dev gstreamer1.0-omx libgstreamer1.0-dev \
    libgstreamer-plugins-base1.0-dev  gstreamer1.0-alsa libvpx-dev libsrtp2-dev libsnappy-dev libnss3-dev "^libxcb.*" flex bison libxslt-dev \
    ruby gperf libbz2-dev libcups2-dev libatkmm-1.6-dev libxi6 libxcomposite1 libfreetype6-dev libicu-dev libsqlite3-dev libxslt1-dev \
    \
    libavcodec-dev libavformat-dev libswscale-dev libx11-dev freetds-dev libsqlite3-dev libpq-dev libiodbc2-dev firebird-dev libgst-dev \
    libxext-dev libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev \
    libxcb-icccm4 libxcb-icccm4-dev libxcb-sync1 libxcb-sync-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev \
    libxcb-randr0-dev libxcb-glx0-dev libxi-dev libdrm-dev libxcb-xinerama0 libxcb-xinerama0-dev libatspi2.0-dev libxcursor-dev libxcomposite-dev libxdamage-dev \
    libxss-dev libxtst-dev libpci-dev libcap-dev libxrandr-dev libdirectfb-dev libaudio-dev libxkbcommon-x11-dev \
    \
    libwayland-dev libwayland-egl1-mesa libwayland-client0 libwayland-server0 libwayland-cursor0 libgles2-mesa-dev \
    "

Next, execute the build script to build the complete Debian system:

$ cd ~/debian_imx8mp-var-dart
$ sudo MACHINE=imx8mp-var-dart ./var_make_debian.sh -c all

Once Debian has been built, please refer to the Create a Debian sysroot guide to learn how to create the sysroot.
The sysroot should then be located at ~/debian_imx8mp-var-dart/toolchain/sysroot and can be used to compile the Qt framework in the next step.

Configure, compile and install Qt

Create the build and installation folder:

$ mkdir -p ~/debian_imx8mp-var-dart/src/qt6/.mx8mp-debian-11-wayland_build
$ mkdir -p ~/debian_imx8mp-var-dart/toolchain/qt6-mx8mp-debian-11-wayland

Create a file, named toolchain.cmake, and locate it at ~/debian_imx8mp-var-dart/src/qt6/.mx8mp-debian-11-wayland_build, with the following content:

cmake_minimum_required(VERSION 3.18)
include_guard(GLOBAL)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(TARGET_SYSROOT <<<TARGET_SYSROOT_PATH>>>)
set(CMAKE_SYSROOT ${TARGET_SYSROOT})

set(ENV{PKG_CONFIG_PATH} $PKG_CONFIG_PATH:/usr/lib/aarch64-linux-gnu/pkgconfig)
set(ENV{PKG_CONFIG_LIBDIR} /usr/lib/pkgconfig:/usr/share/pkgconfig/:${TARGET_SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig:${TARGET_SYSROOT}/usr/lib/pkgconfig)
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})

# if you use other version of gcc and g++ than gcc/g++ 9, you must change the following variables
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc-9)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++-9)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${TARGET_SYSROOT}/usr/include")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")

set(QT_COMPILER_FLAGS "-march=armv8-a")
set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_BUILD_RPATH ${TARGET_SYSROOT})


include(CMakeInitializeConfigs)

function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING)
  if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS")
    set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}")
        
    foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
      if (DEFINED QT_COMPILER_FLAGS_${config})
        set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}")
      endif()
    endforeach()
  endif()


  if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS")
    foreach (config SHARED MODULE EXE)
      set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}")
    endforeach()
  endif()

  _cmake_initialize_per_config_variable(${ARGV})
endfunction()

set(XCB_PATH_VARIABLE ${TARGET_SYSROOT})

set(GL_INC_DIR ${TARGET_SYSROOT}/usr/include)
set(GL_LIB_DIR ${TARGET_SYSROOT}:${TARGET_SYSROOT}/usr/lib/aarch64-linux-gnu/:${TARGET_SYSROOT}/usr:${TARGET_SYSROOT}/usr/lib)

set(EGL_INCLUDE_DIR ${GL_INC_DIR})
set(EGL_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libEGL.so)

set(OPENGL_INCLUDE_DIR ${GL_INC_DIR})
set(OPENGL_opengl_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libOpenGL.so)

set(GLESv2_INCLUDE_DIR ${GL_INC_DIR})
set(GLIB_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libGLESv2.so)

set(GLESv2_INCLUDE_DIR ${GL_INC_DIR})
set(GLESv2_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libGLESv2.so)

set(gbm_INCLUDE_DIR ${GL_INC_DIR})
set(gbm_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libgbm.so)

set(Libdrm_INCLUDE_DIR ${GL_INC_DIR})
set(Libdrm_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libdrm.so)

set(XCB_XCB_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XCB_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb.so)

And replace the token <<<TARGET_SYSROOT_PATH>>> with /home/USER/debian_imx8mp-var-dart/toolchain/sysroot, where USER is your user name, in the toolchain.make file, to declare the sysroot path.

Next, we can cross-compile the Qt framework for the target:

$ cd ~/debian_imx8mp-var-dart/src/qt6/.mx8mp-debian-11-wayland_build
$ cmake ../ -GNinja -Wno-dev -DCMAKE_BUILD_TYPE=Release \
-DQT_HOST_PATH=$HOME/debian_imx8mp-var-dart/toolchain/qt6-x86_64 \
-DCMAKE_INSTALL_PREFIX=/usr/local/qt6 \
-DCMAKE_TOOLCHAIN_FILE=$HOME/debian_imx8mp-var-dart/src/qt6/.mx8mp-debian-11-wayland_build/toolchain.cmake \
-DCMAKE_STAGING_PREFIX=$HOME/debian_imx8mp-var-dart/toolchain/qt6-mx8mp-debian-11-wayland \
-DQT_QMAKE_TARGET_MKSPEC=devices/linux-imx8-g++ \
-DQT_BUILD_EXAMPLES=OFF -DQT_BUILD_TESTS=OFF \
-DBUILD_qtwayland=ON \
-DINPUT_opengl=es2 \
-DQT_FEATURE_xcb=ON -DFEATURE_xcb_xlib=ON -DQT_FEATURE_xlib=ON \
-DBUILD_qt3d=OFF
$ cmake --build . --parallel $(nproc)

And finally, install it locally (cmake will install it here: ~/debian_imx8mp-var-dart/toolchain/qt6-mx8mp-debian-11-wayland, the dot in the command refers to the cmake root):

$ cmake --install .

Install Qt6 on the target

In order for the Qt applications to find the Qt6 runtime libraries, plugins, and so on, it is necessary to install Qt6 on the target. The installation path must be: /usr/local/qt6, and the following environment variables should be declared:

LD_LIBRARY_PATH /usr/local/qt6/lib (The LD_LIBRARY_PATH already exists, append the value to it)
QT_QPA_PLATFORM_PLUGIN_PATH /usr/local/qt6/plugins
QT_QPA_PLATFORM wayland

A Qt application invoke (for a test) could look, for example, like this:

Export the environment variables:

# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/qt6/lib
# export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/local/qt6/plugins
# export QT_QPA_PLATFORM=wayland

And run the Qt application:

# XDG_RUNTIME_DIR=/run/user/0 DISPLAY=:0 <qt6-application>

Numerous methods exist for creating a static environment setup, with plenty of explanatory examples available on the internet.

Install Qt at compile-time

You can add an install routine to the Debian build script to install Qt6 at compile-time. Please refer to Adding an application to the Debian image to learn how to do it.

Or alternatively (for a test), you can copy it locally to the rootfs:

$ cd ~/debian_imx8mp-var-dart
$ sudo mkdir -p rootfs/usr/local/qt6
$ sudo cp ~/debian_imx8mp-var-dart/toolchain/qt6-mx8mp-debian-11-wayland/. rootfs/usr/local/qt6

And re-create the rootfs archive:

$ MACHINE=imx8mp-var-dart ./var_make_debian.sh -c rtar

Creating an SD card, the Qt6 installation should then be in place.

Install Qt at runtime

One can use SCP to install Qt to the target at runtime, for example:

$ cd ~/debian_imx8mp-var-dart/toolchain/qt6-mx8mp-debian-11-wayland
$ scp -r * root@<target-ip-address>:/usr/local/qt6