mirror of https://github.com/qt/qtdoc.git
339 lines
16 KiB
Plaintext
339 lines
16 KiB
Plaintext
// Copyright (C) 2019 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
|
|
|
/*!
|
|
\page configure-linux-device.html
|
|
\title Configure an Embedded Linux Device
|
|
\brief Provides information about how to configure an Embedded Linux device in Qt.
|
|
\ingroup explanations-platforms
|
|
|
|
Cross-compiling Qt for a given device requires a \b toolchain and a \b sysroot. The
|
|
toolchain is expected to contain a version of gcc, or another compiler, and associated
|
|
tools built for cross-compilation. This means these tools run on the \b host system
|
|
(typically x64), while producing binaries for the target architecture (for example, 32
|
|
or 64 bit ARM). The sysroot contains the headers and libraries for the target system,
|
|
allowing compiling and linking libraries and applications on the host.
|
|
|
|
This overview page describes to the generic approach, where no distribution building
|
|
systems, such as Yocto or Buildroot, are used. It is always possible to cross-compile
|
|
and deploy Qt onto a device as long as a suitable toolchain and sysroot are available.
|
|
|
|
\warning This page can only provide a generic, high-level overview. There are a
|
|
vast number of details that can vary depending on the build environment, the
|
|
target device, and the toolchain. When in doubt, refer to your system
|
|
integrator. For pre-built reference images and SDKs, refer to the \l{Boot to Qt}
|
|
offering.
|
|
|
|
When running Qt-based applications without a windowing system, such as X11 or Wayland,
|
|
some devices require vendor-specific adaptation code for EGL and OpenGL ES
|
|
support. This is provided in form of backends for the EGLFS platform plugin. This is
|
|
not relevant for non-accelerated platforms, such as those that use the LinuxFB
|
|
platform plugin, which is meant for software-based rendering only. As of Qt 6, many
|
|
embedded systems use \l{https://en.wikipedia.org/wiki/Direct_Rendering_Manager}{drm}
|
|
to set a video mode, manage display connectors and graphical surfaces. For example, an
|
|
NXP i.MX8-based device or a Raspberry Pi 4 will use this approach, and therefore the
|
|
most commonly used backend for EGLFS is \b eglfs_kms, which enables EGL and OpenGL
|
|
ES based rendering with \c drm, using \c gbm for surface and buffer management. Older
|
|
devices, such as the NXP i.MX6, will continue to use the legacy, GPU vendor-specific
|
|
approach to connect EGL window surfaces to the framebuffer, using dedicated eglfs
|
|
backends, such as \c eglfs_viv.
|
|
|
|
\note Be aware that Qt is just one component in the software stack for an embedded
|
|
device. Especially when accelerated graphics are involved, Qt expects a functional
|
|
graphics stack, with an appropriate configuration for the userspace and kernel
|
|
components, such as the display driver. These components are outside of Qt's domain,
|
|
and it is the system integrator's responsibility to ensure the base system is fully
|
|
functional and optimal, including accelerated graphics.
|
|
|
|
For further information on graphics and input configuration for Embedded Linux
|
|
systems, refer to \l{Qt for Embedded Linux}.
|
|
|
|
\section1 Toolchain Files versus Device Makespecs
|
|
|
|
In Qt 5, you would typically use a device spec under the \e qtbase/mkspecs/devices
|
|
directory. These contain the appropriate compiler and linker flags for a certain
|
|
device, also making sure the correct EGL and OpenGL ES libraries are picked up, in
|
|
case they are in a non-standard location in the sysroot.
|
|
|
|
For example, you could have configured a Qt 5 build for a Raspberry Pi 2 with a
|
|
configure command like the following:
|
|
|
|
\badcode
|
|
./configure -release -opengl es2 -device linux-rasp-pi2-g++ -device-option CROSS_COMPILE=$TOOLCHAIN/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf- -sysroot $ROOTFS -prefix /usr/local/qt5
|
|
\endcode
|
|
|
|
\include use-ninja-note.qdocinc ninja-note
|
|
|
|
With Qt 6 and CMake, this approach is \b{no longer sufficient} on its own. Rather, a
|
|
\l{https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html}{CMake toolchain
|
|
file} must be provided before configuring can happen. It is in this file where
|
|
customization with regards to compiler and linker flags, and toolchain and sysroot
|
|
specific quirks, happens.
|
|
|
|
In the below sections we will present a toolchain file that can be used in many cases,
|
|
with minimal customization. It is based on the approach presented in
|
|
\l{https://www.qt.io/blog/standalone-boot2qt-/-yocto-sdk-cmake-toolchain}{this blog
|
|
post}.
|
|
|
|
\note The toolchain file presented below is an example, that will often need further
|
|
customization for a given device. Users and system integrators are also free to
|
|
create their own toolchain files in any way they see fit.
|
|
|
|
While CMake is the only supported build system for building Qt itself, applications
|
|
may still be built using \c qmake in Qt 6.0. In order to get a \c qmake setup that is
|
|
functional with cross-compilation, one will need to specify some of the legacy
|
|
arguments to CMake or to configure.
|
|
|
|
\section1 Host Tools
|
|
|
|
Cross-compiling Qt requires a host build of Qt being available. During the build,
|
|
tools such as \c moc, \c rcc, \c qmlcachegen, \c qsb, and others, are invoked from
|
|
there. For example, if one cross-compiles for ARM on an x64 machine, a local x64 build
|
|
of the same Qt version must be made available first. The path to this Qt build will be
|
|
passed to configure or cmake.
|
|
|
|
\section1 Configuring Qt
|
|
|
|
Let's assume that the following are available:
|
|
|
|
\list
|
|
\li a toolchain and sysroot under \c{$HOME/rpi-sdk},
|
|
\li a checkout of Qt, at minimum the qtbase module, under \c{$HOME/qt-cross},
|
|
\li a host build of Qt in \c{$HOME/qt-host}.
|
|
\endlist
|
|
|
|
In addition, the following must be decided before configuring:
|
|
|
|
\list
|
|
|
|
\li Where will the Qt build be installed on the local system once the build completes?
|
|
In the example we will use \c{$HOME/qt6-rpi}.
|
|
|
|
\li Where will the Qt build be deployed on the device? In the example we will use
|
|
\c{/usr/local/qt6}.
|
|
|
|
\endlist
|
|
|
|
In the example we are going to use a Raspberry Pi 4 SDK (toolchain+sysroot) generated
|
|
via Yocto, but the instructions here are completely generic, with no dependency on
|
|
Yocto. The steps are the same with any other toolchain and sysroot, once the toolchain
|
|
file is updated with the correct cross compiler and other paths.
|
|
|
|
After creating and switching to a \c build directory:
|
|
|
|
\badcode
|
|
$HOME/qt-cross/qtbase/configure -release -opengl es2 -nomake examples -nomake tests \
|
|
-qt-host-path $HOME/qt-host \
|
|
-extprefix $HOME/qt6-rpi \
|
|
-prefix /usr/local/qt6 \
|
|
-- -DCMAKE_TOOLCHAIN_FILE=$HOME/qt-cross/toolchain.cmake
|
|
\endcode
|
|
|
|
In practice this configure command is equivalent to the following direct CMake call:
|
|
|
|
\badcode
|
|
cmake -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/qt6-rpi \
|
|
-DCMAKE_INSTALL_PREFIX=/usr/local/qt6 \
|
|
-DCMAKE_TOOLCHAIN_FILE=$HOME/qt-cross/toolchain.cmake \
|
|
$HOME/qt-cross/qtbase
|
|
\endcode
|
|
|
|
Given the appropriate toolchain file, this is sufficient to generate a Qt build that
|
|
then allows applications to be built using CMake. To enable applications to be built
|
|
with \c qmake as well, the Qt 5 style device spec and device options must be
|
|
specified, in addition to all arguments shown above:
|
|
|
|
\badcode
|
|
$HOME/qt-cross/qtbase/configure ...
|
|
...
|
|
-device linux-rasp-pi4-v3d-g++ \
|
|
-device-option CROSS_COMPILE=$HOME/rpi_sdk/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi- \
|
|
-device-option DISTRO_OPTS="hard-float" \
|
|
...
|
|
\endcode
|
|
|
|
By default, when cross-compiling, only the Qt libraries and tools that are
|
|
supposed to run on the target device are built. Build-related tools like \c
|
|
moc and \c uic are not built. Building such tools can be enabled by setting
|
|
\c{QT_FORCE_BUILD_TOOLS} to \c{ON}.
|
|
|
|
\note When \c{QT_FORCE_BUILD_TOOLS} is enabled, target binaries of tools
|
|
like \c qmake will get installed to the staging location. Therefore, if \c qmake is
|
|
used to build applications, call the \c{host-qmake} script instead.
|
|
|
|
Once configuration completes without errors, run \c{cmake --build . --parallel} to
|
|
build. Once built, run \c{cmake --install .} to install the results to
|
|
\c{$HOME/qt6-rpi}. From there the Qt build can be deployed to the device by using
|
|
rsync, scp, or another method.
|
|
|
|
If building individual Qt modules, one can use the \c qt-configure-module script from
|
|
the \c bin directory of the staging location (\c{$HOME/qt6-rpi} in the example) to
|
|
configure additional modules, such as qtdeclarative, qtquick3d, and so on. They can
|
|
then be built using \c{cmake --build .} and installed to the staging location by
|
|
running \c{cmake --install .}
|
|
|
|
\note Before starting the build, always inspect the output of the configuration step
|
|
carefully: does it have all the expected features enabled? Making a build and
|
|
deploying it to the device is futile if essential features are not enabled at
|
|
configuration time.
|
|
|
|
For example, when accelerated graphics via OpenGL is desired, pay extra attention to
|
|
the following features:
|
|
|
|
\badcode
|
|
EGL .................................... yes
|
|
OpenGL:
|
|
Desktop OpenGL ....................... no
|
|
OpenGL ES 2.0 ........................ yes
|
|
OpenGL ES 3.0 ........................ yes
|
|
...
|
|
evdev .................................. yes
|
|
libinput ............................... yes
|
|
...
|
|
EGLFS .................................. yes
|
|
EGLFS details:
|
|
EGLFS OpenWFD ........................ no
|
|
EGLFS i.Mx6 .......................... no
|
|
EGLFS i.Mx6 Wayland .................. no
|
|
EGLFS RCAR ........................... no
|
|
EGLFS EGLDevice ...................... yes
|
|
EGLFS GBM ............................ yes
|
|
EGLFS VSP2 ........................... no
|
|
EGLFS Mali ........................... no
|
|
EGLFS Raspberry Pi ................... no
|
|
EGLFS X11 ............................ no
|
|
LinuxFB ................................ yes
|
|
\endcode
|
|
|
|
With the Raspberry Pi 4 example, we expect that EGL, OpenGL ES and \c{EGLFS GBM} are
|
|
all reported as \c yes, otherwise the EGLFS platform plugin and its \b eglfs_kms
|
|
backend will not be functional on the device. For getting function mouse, keyboard,
|
|
and touch input, either \c evdev or \c libinput must be enabled.
|
|
|
|
Similarly, if X11 is planned to be used as the (or one of the) windowing systems on
|
|
the device, then ensure the xcb and X11 related features are marked as \c yes.
|
|
|
|
\section1 An Example Toolchain File
|
|
|
|
We will assume there is a sysroot and toolchain available under \c{$HOME/rpi-sdk}. \c
|
|
TARGET_SYSROOT and \c CROSS_COMPILER must be adjusted to the toolchain and sysroot in
|
|
use. The example here is only suitable for one specific, Yocto-generated SDK. The same
|
|
is true for \c CMAKE_C_COMPILER and \c CMAKE_CXX_COMPILER.
|
|
|
|
We do not rely on any wrapper scripts that would provide environment variables such as
|
|
PKG_CONFIG_*. Rather, the path to the .pc files is specified in the toolchain file. It
|
|
is likely that another sysroot will need adjustments in \c PKG_CONFIG_LIBDIR. For
|
|
example, with a sysroot generated from a Raspberry Pi OS (formerly Raspbian) image one
|
|
would use \c{/usr/lib/arm-gnueabihf/pkgconfig} instead.
|
|
|
|
The compiler and linker flags are not necessary optimal in the example. Adjust them as
|
|
necessary for the target device.
|
|
|
|
For further information on the CMake specifics in the example toolchain file, refer to
|
|
\l{https://www.qt.io/blog/standalone-boot2qt-/-yocto-sdk-cmake-toolchain}{this blog
|
|
post} and \l{https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html}{the
|
|
CMake documentation}.
|
|
|
|
\badcode
|
|
cmake_minimum_required(VERSION 3.18)
|
|
include_guard(GLOBAL)
|
|
|
|
set(CMAKE_SYSTEM_NAME Linux)
|
|
set(CMAKE_SYSTEM_PROCESSOR arm)
|
|
|
|
set(TARGET_SYSROOT /home/user/rpi-sdk/sysroots/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi)
|
|
set(CROSS_COMPILER /home/user/rpi-sdk/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi)
|
|
|
|
set(CMAKE_SYSROOT ${TARGET_SYSROOT})
|
|
|
|
set(ENV{PKG_CONFIG_PATH} "")
|
|
set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
|
|
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
|
|
|
|
set(CMAKE_C_COMPILER ${CROSS_COMPILER}/arm-poky-linux-gnueabi-gcc)
|
|
set(CMAKE_CXX_COMPILER ${CROSS_COMPILER}/arm-poky-linux-gnueabi-g++)
|
|
|
|
set(QT_COMPILER_FLAGS "-march=armv7-a -mfpu=neon -mfloat-abi=hard")
|
|
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)
|
|
|
|
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()
|
|
\endcode
|
|
|
|
\section1 Building Applications for the Target Device
|
|
|
|
Once the Qt build is done and installed to the staging location, examples or
|
|
applications can be built.
|
|
|
|
With CMake, use the generated \c qt-cmake script in the \c bin directory of the
|
|
staging location (\c{$HOME/qt6-rpi} in the example) to configure, then run \c
|
|
ninja. For example:
|
|
|
|
\badcode
|
|
$HOME/qt6-rpi/bin/qt-cmake .
|
|
cmake --build .
|
|
\endcode
|
|
|
|
The resulting application binary can then be deployed to the device. Using the \c
|
|
qt-cmake helper script is convenient, because the script ensures the toolchain file
|
|
that was used for building Qt is loaded, so there is no need to repeatedly specify it
|
|
for each application.
|
|
|
|
Unlike for Qt itself, building applications with qmake is still supported in Qt 6.0,
|
|
as long as a suitable device spec is available, and the appropriate legacy arguments
|
|
were passed to CMake or configure when configuring Qt. If this is all true, then
|
|
running \c qmake and \c make will also generate an application binary for the target
|
|
device.
|
|
|
|
\section1 Defaults for Platform Plugins and EGLFS
|
|
|
|
Once configured, a default platform plugin is chosen. This is used when launching an
|
|
application without the \c{-platform} argument and without having the \c
|
|
QT_QPA_PLATFORM environment variable set.
|
|
|
|
Similarly, the EGLFS platform plugin has multiple backends. The default is chosen
|
|
based on availability and a pre-defined priority order. If drm and gbm are available,
|
|
the default will be the \b eglfs_kms backend. This can always be overridden at runtime
|
|
by setting the \c QT_QPA_EGLFS_INTEGRATION environment variable.
|
|
|
|
To change these defaults for the build, without having to force a specific value at
|
|
run time, the following to CMake cache variables are available once CMake has been run
|
|
once:
|
|
|
|
\list
|
|
\li \c QT_QPA_DEFAULT_PLATFORM (\c STRING) - The name of the default platform plugin.
|
|
\li \c QT_QPA_DEFAULT_EGLFS_INTEGRATION (\c STRING) - The default EGLFS backend.
|
|
\endlist
|
|
|
|
These variables can also be set inside the toolchain file.
|
|
|
|
For more information on configuring Qt, see \l {Qt Configure Options}.
|
|
*/
|