配置嵌入式 Linux 设备
为特定设备交叉编译 Qt 需要一个工具链和一个系统根。工具链应包含一个版本的 gcc 或其他编译器,以及为交叉编译而构建的相关工具。这意味着这些工具在主机系统(通常为 x64)上运行,同时为目标架构(例如 32 或 64 位 ARM)生成二进制文件。系统根包含目标系统的头文件和库,允许在主机上编译和链接库和应用程序。
本概述页面介绍的是通用方法,即不使用 Yocto 或 Buildroot 等发行版构建系统。只要有合适的工具链和 sysroot,就可以在设备上交叉编译和部署 Qt。
警告: 本页只能提供一般的、高层次的概述。大量细节会因编译环境、目标设备和工具链的不同而变化。如有疑问,请咨询系统集成商。有关预构建参考映像和 SDK,请参阅 Boot to Qt提供。
在没有 X11 或 Wayland 等窗口系统的情况下运行基于 Qt 的应用程序时,某些设备需要特定于供应商的适配代码来支持 EGL 和 OpenGL ES。这些代码以 EGLFS 平台插件后端形式提供。这与非加速平台无关,例如使用 LinuxFB 平台插件的平台,该插件仅用于基于软件的渲染。从 Qt 6 开始,许多嵌入式系统使用drm来设置视频模式、管理显示连接器和图形表面。例如,基于NXP i.MX8 的设备或 Raspberry Pi 4 就会使用这种方法,因此最常用的 EGLFS 后端是eglfs_kms,它可以通过drm
启用基于 EGL 和 OpenGL ES 的渲染,并使用gbm
进行曲面和缓冲区管理。较旧的设备,如NXP i.MX6,将继续使用传统的、GPU 厂商特定的方法将 EGL 窗口表面连接到帧缓冲器,使用专用的 eglfs 后端,如eglfs_viv
。
注: 请注意 Qt 只是嵌入式设备软件栈中的一个组件。特别是在涉及加速图形时,Qt 需要一个功能图形栈,并为用户空间和内核组件(如显示驱动程序)提供适当的配置。这些组件不属于 Qt 的职责范围,系统集成商有责任确保基本系统功能齐全、性能最佳,包括加速图形。
有关嵌入式 Linux 系统图形和输入配置的更多信息,请参阅Qt for EmbeddedLinux。
工具链文件与设备制作规范
在 Qt 5 中,您通常会使用qtbase/mkspecs/devices目录下的设备规格。这些文件包含适用于特定设备的编译器和链接器标志,还能确保获取正确的 EGL 和 OpenGL ES 库,以防它们位于 sysroot 中的非标准位置。
例如,你可以使用如下 configure 命令为 Raspberry Pi 2 配置 Qt 5 版本:
./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
注意: 如果有ninja
可执行文件,configure 总是使用Ninja生成器和构建工具。Ninja 跨平台、功能丰富、性能卓越,建议在所有平台上使用。使用其他生成器可能可行,但未获官方支持。
有了 Qt 6 和 CMake,这种方法本身已不再足够。相反,在配置之前,必须提供CMake 工具链文件。在该文件中,编译器和链接器标志以及工具链和 sysroot 的特定怪癖都可以自定义。
在下面的章节中,我们将介绍一个工具链文件,它可以在很多情况下使用,只需进行最少的定制。它基于本博文中介绍的方法。
注: 下面介绍的工具链文件只是一个示例,通常需要针对特定设备进行进一步定制。用户和系统集成商也可以以任何他们认为合适的方式创建自己的工具链文件。
虽然 CMake 是唯一支持构建 Qt 本身的构建系统,但在 Qt 6.0 中仍可使用qmake
构建应用程序。为了获得一个可进行交叉编译的qmake
设置,需要向 CMake 或 configure 指定一些传统参数。
主机工具
交叉编译 Qt 需要 Qt 的主机构建。在编译过程中,会调用moc
,rcc
,qmlcachegen
,qsb
等工具。例如,如果在 x64 机器上交叉编译 ARM,则必须首先提供相同 Qt 版本的本地 x64 构建。该 Qt 构建的路径将传递给 configure 或 cmake。
配置 Qt
让我们假设以下条件都已具备:
$HOME/rpi-sdk
下的工具链和系统根、$HOME/qt-cross
下的 Qt 签出,至少包括 qtbase 模块、$HOME/qt-host
中的 Qt 主机构建。
此外,在配置前还必须确定以下事项:
- 构建完成后,Qt 将安装在本地系统的哪个位置?在示例中,我们将使用
$HOME/qt6-rpi
。 - Qt 构建将部署到设备的哪个位置?在示例中,我们将使用
/usr/local/qt6
。
在示例中,我们将使用通过 Yocto 生成的 Raspberry Pi 4 SDK(工具链+系统根),但这里的说明完全通用,不依赖 Yocto。只要用正确的交叉编译器和其他路径更新了工具链文件,使用其他工具链和系统根的步骤都是一样的。
创建并切换到build
目录后:
$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
实际上,这条 configure 命令等同于下面的 CMake 直接调用:
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
有了适当的工具链文件,就足以生成 Qt 联编,然后使用 CMake 联编应用程序。要使应用程序也能使用qmake
构建,除了上面显示的所有参数外,还必须指定 Qt 5 风格的设备规格和设备选项:
$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" \ ...
默认情况下,交叉编译时只编译在目标设备上运行的 Qt 库和工具。与编译相关的工具(如moc
和uic
)不会被编译。可通过将QT_FORCE_BUILD_TOOLS
设置为ON
来启用编译此类工具。
注意: 启用QT_FORCE_BUILD_TOOLS
时,qmake
等工具的目标二进制文件将安装到暂存位置。因此,如果qmake
用于构建应用程序,请调用host-qmake
脚本。
配置无误完成后,运行cmake --build . --parallel
进行构建。构建完成后,运行cmake --install .
将结果安装到$HOME/qt6-rpi
。在那里,可以使用 rsync、scp 或其他方法将 Qt 构建部署到设备上。
如果要构建单个 Qt 模块,可以使用暂存位置bin
目录(示例中为$HOME/qt6-rpi
)中的qt-configure-module
脚本来配置其他模块,如 qtdeclarative、qtquick3d 等。然后就可以使用cmake --build .
构建这些模块,并通过运行cmake --install .
注意: 在开始构建之前,请务必仔细检查配置步骤的输出结果:是否启用了所有预期功能?如果在配置时未启用基本功能,那么构建并部署到设备上是徒劳的。
例如,当需要通过 OpenGL 加速图形时,请格外注意以下功能:
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
以 Raspberry Pi 4 为例,我们希望 EGL、OpenGL ES 和EGLFS GBM
都报告为yes
,否则 EGLFS 平台插件及其eglfs_kms后端将无法在设备上运行。要获得鼠标、键盘和触摸输入功能,必须启用evdev
或libinput
。
同样,如果计划将 X11 用作设备上的窗口系统(或其中之一),则应确保 xcb 和 X11 相关功能被标记为yes
。
工具链文件示例
我们假设$HOME/rpi-sdk
下有一个系统根和工具链。TARGET_SYSROOT
和CROSS_COMPILER
必须根据使用的工具链和系统根进行调整。此处的示例仅适用于一个特定的、由 Yocto 生成的 SDK。CMAKE_C_COMPILER
和CMAKE_CXX_COMPILER
也是如此。
我们不依赖任何提供 PKG_CONFIG_* 等环境变量的封装脚本。相反,我们在工具链文件中指定了 .pc 文件的路径。另一个系统根可能需要在PKG_CONFIG_LIBDIR
中进行调整。例如,对于从 Raspberry Pi OS(原 Raspbian)镜像生成的系统根,可以使用/usr/lib/arm-gnueabihf/pkgconfig
代替。
在示例中,编译器和链接器标志并非必要的最优选择。请根据目标设备的需要进行调整。
有关示例工具链文件中 CMake 的详细信息,请参阅本博文和CMake 文档。
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()
为目标设备构建应用程序
完成 Qt 构建并安装到暂存位置后,就可以构建示例或应用程序了。
使用 CMake,在暂存位置的bin
目录(示例中为$HOME/qt6-rpi
)中使用生成的qt-cmake
脚本进行配置,然后运行ninja
。例如
$HOME/qt6-rpi/bin/qt-cmake . cmake --build .
然后就可以将生成的应用程序二进制文件部署到设备上。使用qt-cmake
辅助脚本非常方便,因为该脚本可确保加载用于构建 Qt 的工具链文件,因此无需为每个应用程序重复指定。
与 Qt 本身不同的是,在 Qt 6.0 中仍支持使用 qmake 构建应用程序,只要有合适的设备规格,并在配置 Qt 时向 CMake 或 configure 传递了适当的传统参数即可。如果这一切属实,那么运行qmake
和make
也将为目标设备生成应用程序二进制文件。
平台插件和 EGLFS 的默认值
配置完成后,将选择一个默认平台插件。在没有-platform
参数和没有设置QT_QPA_PLATFORM
环境变量的情况下启动应用程序时,将使用该插件。
同样,EGLFS 平台插件也有多个后端。默认后端根据可用性和预定义的优先顺序进行选择。如果 drm 和 gbm 可用,则默认使用eglfs_kms后端。运行时可通过设置QT_QPA_EGLFS_INTEGRATION
环境变量来覆盖默认值。
要在构建时更改这些默认值,而不必在运行时强制设置特定值,可以在 CMake 运行一次后使用以下两个 CMake 缓存变量:
QT_QPA_DEFAULT_PLATFORM
( ) - 默认平台插件的名称。STRING
QT_QPA_DEFAULT_EGLFS_INTEGRATION
( ) - 默认 EGLFS 后端。STRING
这些变量也可在工具链文件中设置。
有关配置 Qt 的更多信息,请参阅Qt 配置选项。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.