Cross-Compile Qt 6 for Raspberry Pi: Difference between revisions
m (Adding CMake documentation page of CMake toolchains in the 'Further Readings' section.) |
m (→Building Qt 6 for Host Machine: -> added a list of Qt submodules installed for the guide) |
||
Line 112: | Line 112: | ||
===Building Qt 6 for Host Machine=== | ===Building Qt 6 for Host Machine=== | ||
For the detailed step-by-steps of this process, I will refer to this great [https://wiki.qt.io/Building_Qt_6_from_Git wiki article] that explained the whole process in detail. One thing to remember is that we will install Qt on <code>~/qt-host</code>, and we want to store all build configuration files on <code>~/qt-hostbuild</code>. | For the detailed step-by-steps of this process, I will refer to this great [https://wiki.qt.io/Building_Qt_6_from_Git wiki article] that explained the whole process in detail. One thing to remember is that we will install Qt on <code>~/qt-host</code>, and we want to store all build configuration files on <code>~/qt-hostbuild</code>. | ||
Notice that you can also choose to install only Qt submodules that you need. This is good if you have a limited space in your host machine. At minimum, you only need the <code>qtbase</code> submodule to be able to develop and run a simple Qt Widget application, but to access other Qt features you need to install other Qt submodules. In this guide, I installed the following submodules: <code>qtbase,qtsvg,qtdeclarative,qtquick3d,qtshadertools,qtimageformats,qtquicktimeline</code>. | |||
The rest of the guide is quite straightforward, so I will leave you to read it. | The rest of the guide is quite straightforward, so I will leave you to read it. |
Revision as of 07:44, 21 June 2022
Introduction
This is a step-by-step guide to cross-compile Qt 6 for Raspberry Pi OS. The end result of this guide allows you to compile Qt 6 applications on a host machine and deploy it on the Raspberry Pi 4 Model B 2GB running a Raspberry Pi OS image. The instructions in this guide are targeted for beginners while also allowing a more advanced user to follow through. It was tested on Ubuntu 20.04 targeting Raspberry Pi OS, but should work with any Debian-based OS.
For an older guide, check this Qt Wiki guide or this one.
Getting Started: Planning Phase
Before we get our hands dirty and mess around with the configurations, let's familiarise ourselves with essential components needed to cross-compile Qt and decide which directory names we want to use to store these components. This way, we can easily track the location of these important components when we are typing commands in the terminal.
At its most basic setup, we need at least the following components in a host machine to cross-compile Qt:
- Sysroot, which basically is the scaled down version of our target's filesystem inside our host machine. For this example, we will store it on
~/rpi-sysroot
- A cross-compiler. To make it as straightforward as possible, for this guide we will use the cross-compiler for Aarch64 from the Ubuntu repository.
- Qt source code with its submodules, at least the qtbase submodule. For this guide, we will download Qt source code from Qt official GitHub repository. By default, it will be stored in .
~/qt5
- CMake toolchain. CMake is the recommended build generator for Qt 6. Inside this toolchain file, we will specify in detail about the exact specifications of our cross-compiled Qt build. We will store this file on our
toolchain.cmake
directory.$HOME
We also have to decide the locations of our Qt builds, one for host machine and one for our Raspberry Pi in our PC before we transfer it to the Raspberry Pi.
- We will store a build of Qt 6 for host machine on . This is necessary because when building a Qt for our Raspberry Pi target, we will refer to this build of Qt.
~/qt-host
- Finally, our Qt 6 build for Raspberry Pi will be stored on in the host machine temporarily before we move it to a location in our Raspberry Pi (for example,
~/qt-raspi
)./usr/local/Qt 6
Following the best practices of building C/C++ applications, we also want to make sure that our final build directories are clean from all build configuration files that CMake will create during the configuration phase. For that, we will store build configuration files on
~/qthost-build
for configuration files of Qt for our host machine and
~/qtpi-build
for configuration files of Qt for Raspberry Pi.
After we decided these locations, let's create them all!
On host machine,
cd ~
mkdir rpi-sysroot rpi-sysroot/usr rpi-sysroot/opt
mkdir qt-host qt-raspi qthost-build qtpi-build
The second line will create the sysroot directory with its necessary subfolder, while the third line will create the directory for the rest of locations defined previously.
Installing Raspberry Pi OS
Let's now install an image of Raspberry Pi OS. You can download the latest Raspberry Pi Imager from https://www.raspberrypi.com/software/, and then flash an image of Raspberry Pi OS to your microSD card with only a few clicks on the Imager's GUI. This one should be straightforward.
Setup Raspberry Pi
After successfully flashing an image of Raspberry Pi OS to a microSD card, insert it into the microSD card reader of our Raspberry Pi, connect a keyboard and monitor to the device and boot it up. The system will ask for a basic first-time setup. Please follow the setup according to the instruction.
After successfully setting up the device, the system will reboot to the home desktop. From here, open Terminal. We want to install a few packages needed by Qt 6.
Install Dependencies
Update and upgrade to the newest version.
sudo apt update
sudo apt full-upgrade
sudo reboot
Next, install the package dependencies.
sudo apt-get install 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
sudo apt-get install 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
This will install a bunch of libraries from the Debian repository that Qt needed to deploy various kinds of applications on Raspberry Pi.
We also will make a directory that will hold Qt binaries for Raspberry Pi, as specified in the previous section. Actually this can be done at every time before transferring Qt 6 build from host machine to the Raspberry Pi, but in this guide we will create it at this stage
sudo mkdir /usr/local/Qt 6
Setup Host Machine
Update System
Let's configure our Ubuntu/Debian host machine. First, let's update our packages to the latest version.
sudo apt update
sudo apt upgrade
Install Qt Dependencies
Next, we will install some more package dependencies. Some of these packages are build tools needed to compile Qt 6 that you might or might not have.
sudo apt-get install 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
Install Cross Compiler
Next, let's get our cross-compiler. For this guide, we will install it from the Ubuntu/Debian package repository as this is the easiest way to get an ARM64 cross-compiler.
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
You can also download the cross-compiler from somewhere on the internet. If you want to do so, my recommendation is to check the ARM official download page and choose which one fits your purpose.
Setup SSH Connection
We also want to setup an SSH connection between Raspberry Pi and a host machine. This is needed to transfer data between both devices. It is disabled by default, so we need to enable it. For this example, we will use an Ethernet cable to connect both devices. You can also setup an SSH connection via Wi-Fi, and there is a lot of guides on how to set that up on the internet so I will not talk about that here.
Enabling SSH on Raspberry Pi
Go to Preferences > Raspberry Pi Configuration. Inside the configuration window, switch to Interfaces tab, then toggle SSH on. Click OK, then check whether the SSH service is already enabled by executing
systemctl status ssh.service
in the terminal. Make sure it outputs
Active: active (running)
in one of the output lines.
Establish SSH Connection
Connect an Ethernet cable to your Raspberry Pi on one end and to a host machine on the other end.
Then, on Raspberry Pi, find the IP Address of your Raspberry Pi ethernet network by typing
ifconfig
. This will be important in our SSH setup. Now, go back to the home machine. Establish an SSH connection by running
ssh <pi_username>@<pi_ip_address>
. Change
<pi_username>
and
<pi_ip_address>
with your Raspberry Pi username and IP Address, respectively.
If this is the first time you connected your Raspberry Pi with the host machine, you will be prompted by SSH asking you to add your host machine's SSH key to Raspberry Pi. Answer 'yes'.
Now your Raspberry Pi is connected via SSH to your host machine
Build Sysroot
We need to build a Sysroot from our own Raspberry Pi device in the host machine. To do this, we will copy and paste a few directories from Raspberry Pi using rsync through SSH. If rsync is not installed yet in your host machine, simply install it by executing
sudo apt install rsync
. After the host machine and Raspberry Pi is connected,
rsync -avz --rsync-path="sudo rsync" --delete <pi_username>@<pi_ip_address>:/lib sysroot
rsync -avz --rsync-path="sudo rsync" --delete <pi_username>@<pi_ip_address>:/usr/include sysroot/usr
rsync -avz --rsync-path="sudo rsync" --delete <pi_username>@<pi_ip_address>:/usr/lib sysroot/usr
rsync -avz --rsync-path="sudo rsync" --delete <pi_username>@<pi_ip_address>:/opt/vc sysroot/opt
Change
<pi_username>
and
<pi_ip_address>
with your Raspberry Pi username and IP Address, respectively. Note: Your Raspberry Pi might not have a directory named
/opt/vc
, and it is fine. Usually this directory contains proprietary Broadcom libraries, but during the testing the author did not find any issue with the lack of this directory. After building our own sysroot, we need to fix absolute symbolic links that were broken during the syncing process by converting them to relative symlinks. The easiest, fastest way to do this is to use a command-line tool called
symlinks
, which is available from the Ubuntu official repository.
sudo apt install symlinks
cd ~
symlinks -rc rpi-sysroot
Check the symlinks documentation to learn more.
Building Qt 6
After all the setup process, finally we can start building Qt 6! This will be divided into two parts: Building Qt 6 for host machine, and building Qt 6 for our target device (i.e. the Raspberry Pi).
Building Qt 6 for Host Machine
For the detailed step-by-steps of this process, I will refer to this great wiki article that explained the whole process in detail. One thing to remember is that we will install Qt on
~/qt-host
, and we want to store all build configuration files on
~/qt-hostbuild
. Notice that you can also choose to install only Qt submodules that you need. This is good if you have a limited space in your host machine. At minimum, you only need the
qtbase
submodule to be able to develop and run a simple Qt Widget application, but to access other Qt features you need to install other Qt submodules. In this guide, I installed the following submodules:
qtbase,qtsvg,qtdeclarative,qtquick3d,qtshadertools,qtimageformats,qtquicktimeline
.
The rest of the guide is quite straightforward, so I will leave you to read it.
Building Qt 6 for Target Device
This is the interesting part. We will compile Qt 6 on a host machine, store it temporarily in the host machine, and send it to the Raspberry Pi. The instruction for configuring and building are similar to building Qt 6 for host machine, but there are a few things we need to do to get the intended results.
Create a Toolchain File
First, we want to create our own CMake toolchain file. This file is important to link the right compilers and libraries so that CMake can produces the suitable Qt build for Raspberry Pi. For this guide, we will use the following CMake code and save it as
toolchain.cmake
.
cmake_minimum_required(VERSION 3.18)
include_guard(GLOBAL)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(TARGET_SYSROOT /path/to/your/sysroot)
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 have other gcc and g++ versions than gcc/g++ 9, you can 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)
set(XCB_RENDER_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_RENDER_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-render.so)
set(XCB_SHAPE_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_SHAPE_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-shape.so)
set(XCB_XFIXES_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XFIXES_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-xfixes.so)
set(XCB_SHM_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_SHM_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-shm.so)
set(XCB_COMPOSITE_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_COMPOSITE_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-composite.so)
set(XCB_CURSOR_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_CURSOR_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-cursor.so)
set(XCB_DAMAGE_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_DAMAGE_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-damage.so)
set(XCB_DPMS_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_DPMS_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-dpms.so)
set(XCB_GLX_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_GLX_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-glx.so)
set(XCB_ICCCM_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_ICCCM_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-icccm.so)
set(XCB_IMAGE_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_IMAGE_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-image.so)
set(XCB_KEYSYMS_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_KEYSYMS_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-keysyms.so)
set(XCB_PRESENT_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_PRESENT_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-present.so)
set(XCB_RANDR_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_RANDR_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-randr.so)
set(XCB_RECORD_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_RECORD_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-record.so)
set(XCB_RENDERUTIL_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_RENDERUTIL_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-render-util.so)
set(XCB_RES_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_RES_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-res.so)
set(XCB_SCREENSAVER_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_SCREENSAVER_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-screensaver.so)
set(XCB_SYNC_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_SYNC_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-sync.so)
set(XCB_UTIL_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_UTIL_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-util.so)
set(XCB_XF86DRI_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XF86DRI_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-xf86dri.so)
set(XCB_XKB_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XKB_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-xkb.so)
set(XCB_XTEST_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XTEST_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-xtest.so)
set(XCB_XV_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XV_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-xv.so)
set(XCB_XVMC_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XVMC_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-xvmc.so)
set(XCB_XINPUT_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_XINPUT_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-xinput.so)
set(XKB_INCLUDE_DIR ${GL_INC_DIR})
set(XKB_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxkbcommon.so)
set(XCB_DRI2_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_DRI2_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-dri2.so)
set(XCB_DRI3_INCLUDE_DIR ${GL_INC_DIR})
set(XCB_DRI3_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb-dri3.so)
set(X11_XCB_INCLUDE_DIR ${GL_INC_DIR})
set(X11_XCB_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libX11-xcb.so)
set(X11_X11_INCLUDE_PATH ${GL_INC_DIR})
set(X11_X11_LIB ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libX11.so)
Now that we have our toolchain, as well as the cross compiler and sysroot, we can continue with configuring and building Qt for our Raspberry Pi! As decided in the planning process, we will put all build configuration files on
qtpi-build
, so let's
cd
into that directory:
cd ~/qtpi-build
Now, we can run the configuration command inside this directory.
../qt5/configure -release -opengl es2 -nomake examples -nomake tests -qt-host-path $HOME/qt-host -extprefix $HOME/qt-raspi -prefix /usr/local/qt6 -- -DCMAKE_TOOLCHAIN_FILE=$HOME/toolchain.cmake -DQT_FEATURE_brotli=OFF -DQT_FEATURE_libudev=OFF -DQT_QMAKE_TARGET_MKSPEC=devices/linux-rasp-pi4-v3d-g++ -DQT_FEATURE_xcb=ON -DFEATURE_xcb_xlib=ON -DQT_FEATURE_xlib=ON -DFEATURE_dbus=OFF
Command above will run the configuration file that came with the Qt source code and complementing it with the toolchain that we just created. This is equivalent to this CMake command:
cmake ../qt5/ -GNinja -DCMAKE_BUILD_TYPE=Release -DINPUT_opengl=es2 -DQT_BUILD_EXAMPLES=OFF -DQT_BUILD_TESTS=OFF -DQT_HOST_PATH=$HOME/qt-host -DCMAKE_STAGING_PREFIX=$HOME/qt-raspi -DCMAKE_INSTALL_PREFIX=/usr/local/qt6 -DCMAKE_TOOLCHAIN_FILE=$HOME/toolchain.cmake -DQT_FEATURE_brotli=OFF -DQT_FEATURE_libudev=OFF -DQT_QMAKE_TARGET_MKSPEC=devices/linux-rasp-pi4-v3d-g++ -DQT_FEATURE_xcb=ON -DFEATURE_xcb_xlib=ON -DQT_FEATURE_xlib=ON -DFEATURE_dbus=OFF
Note: During the testing process, it was found that the DBus installation inside our sysroot was broken, so it was decided to exclude this feature from the build, hence the
-DFEATURE_dbus=OFF
flag. Your mileage may vary, and we will try to update this page once we found out why. These commands will configure Qt to be installed on
qt-raspi
folder on the host machine, with expectation that it will populate
usr/local/Qt 6
on the Raspberry Pi. The Qt installation from this build will be configured to run on the ARM64 architecture. You can make sure by running
file ~/qt-raspi/bin/qt-cmake
on terminal after installing. Then proceeds with building and installing.
cmake --build . --parallel 4
cmake --install .
After Qt is successfully installed on
~/qt-raspi
, we will then send this Qt installation back to the Raspberry Pi. To do this, we can use
rsync
as we did when building sysroot, or
scp
. Both will accomplish the same thing, and there should be no significant difference between both of them. To use
rsync
:
rsync -avz --rsync-path="sudo rsync" /path/to/qt-raspi <pi_username>@<pi_ip_address>:/usr/local/Qt 6
To use
scp
:
cd ~/qt-raspi
scp -r * <pi_username>@<pi_ip_address>:/usr/local/qt6
Final Configuration on Raspberry Pi
To ensure our Qt 6 installation can be run on Raspberry Pi, we need to setup a few environment variables. All of this can be done on the host device via SSH.
ssh pi@169.254.187.77
## enter your raspberry pi user password, if prompted
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/qt6/lib/
This is the most important environment variable that we need to set, as it will enable Raspberry Pi to find our Qt 6 library path. If you intend to run the application through SSH from your host machine and see the application running on a monitor that is connected to the Raspberry Pi, you need to execute
export DISPLAY=:0
in addition to the commands above.
Compiling an Application
Now we can compile a Qt application and get a binary that is suitable to deploy on Raspberry Pi.
We can try with one of the examples from the Qt source code.
cd ~/qt5/qtbase/examples/gui/analogclock/
ls ## make sure there is a CMakeLists.txt file
~/qt-raspi/bin/qt-cmake CMakeLists.txt
### CMake is configuring here. After it is done without any error, we can proceed to build and install it
cmake --build . --parallel 4
cmake --install .
We now have the binary of our application ready to be run on the Raspberry Pi. We can then send the binary to our Raspberry Pi and run the application (either through SSH or directly on the Raspberry Pi with keyboard and mouse connected).
scp -r gui_analogclock <pi_username>@<pi_ip_address>:/home/pi
ssh <pi_username>@<pi_ip_address>
cd ~ ## or cd to the directory where you send the binary
./gui_analogclock
Further Readings
For further readings, go check:
- Qt Docs: Qt for Embedded Device (alongside a guideline on Configuring an Embedded Linux Device).
- CMake Docs: cmake-toolchains.