配置嵌入式Linux设备
为特定设备交叉编译Qt需要一个工具链和一个sysroot。工具链应包含gcc或其他编译器的版本以及用于交叉编译的关联工具。这意味着这些工具在主机系统(通常是x64)上运行,同时为目标架构(例如32位或64位ARM)生成二进制文件。sysroot包含目标系统的头文件和库,允许在主机上编译和链接库和应用程序。
本概述页面描述了通用的方法,其中没有使用如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,它使用drv
进行基于EGL和OpenGL ES的渲染,并使用gbm
进行表面和缓冲区管理。较旧的设备,如NXP i.MX6,将继续使用继承的、GPU供应商特定的方法将EGL窗口表面连接到帧缓冲区,并使用专用的eglfs后端,如eglfs_viv
。
注意:请注意,Qt只是嵌入式设备软件堆栈的一个组件。特别是当涉及加速图形时,Qt期望有一个功能性图形堆栈,包括用户空间和内核组件(如显示驱动程序)的适当配置。这些组件超出了Qt的范围,确保基础系统完全功能化和最优化的责任在于系统集成商,包括加速图形。
有关嵌入式Linux系统图形和输入配置的更多信息,请参阅Qt for Embedded Linux。
工具链文件与设备makspecs的对比
在Qt 5中,您通常会在qtbase/mkspecs/devices目录下使用设备规范。这些包含了针对特定设备的适当编译器和链接器标志,也确保从sysroot中非标准位置的EGL和OpenGL ES库正确选中。
例如,您可以为Raspberry Pi 2配置一个Qt 5构建,使用如下所示的configure命令:
./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
注意:如果可用,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
下的工具链和sysroot; - 位于
$HOME/qt-cross
下的Qt的检查结果,至少包含qtbase模块; - 位于
$HOME/qt-host
下的Qt宿主构建。
在配置之前,以下事项必须确定:
- Qt构建完成后,将在本地系统上的哪里安装?在示例中,我们将使用
$HOME/qt6-rpi
。 - Qt构建将在设备上的哪里部署?在示例中,我们将使用
/usr/local/qt6
。
在本例中,我们将使用通过Yocto生成的Raspberry Pi 4 SDK(工具链+sysroot),但这里所示的操作完全通用,不依赖于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
目录中的 qt-configure-module
脚本(例如,示例中的 $HOME/qt6-rpi
)来配置其他模块,例如 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
下有 sysroot 和工具链可用。必须根据所使用的工具链和 sysroot 来调整 TARGET_SYSROOT
和 CROSS_COMPILER
。这里的示例仅适用于一个特定的、由 Yocto 生成的 SDK。对 CMAKE_C_COMPILER
和 CMAKE_CXX_COMPILER
也是如此。
我们不依赖任何提供环境变量(如 PKG_CONFIG_*)的包装脚本。相反,在工具链文件中指定了 .pc 文件的路径。另一个 sysroot 可能需要对 PKG_CONFIG_LIBDIR
进行调整。例如,使用从 Raspberry Pi OS(以前称为 Raspbian)映像生成的 sysroot,将使用 /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
目录中的生成的 qt-cmake
脚本(例如,示例中的 $HOME/qt6-rpi
)来配置,然后运行 ninja
。例如
$HOME/qt6-rpi/bin/qt-cmake . cmake --build .
生成的应用程序二进制文件然后可以部署到设备。使用 qt-cmake
辅助脚本非常方便,因为该脚本确保加载了用于构建 Qt 的工具链文件,因此无需为每个应用程序重复指定。
与Qt本身不同,在Qt 6.0中,只要提供了合适的设备规范,并且配置Qt时传入了适当的遗留参数给CMake或configure,仍然支持使用qmake构建应用程序。如果这些都成立,那么运行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
(STRING
) - 默认的EGLFS后端。
这些变量也可以在工具链文件中设置。
有关配置Qt的更多信息,请参阅Qt配置选项。
© 2024 Qt公司 Ltd。此处包含的文档贡献均为各自所有者的版权。此处提供的文档按照自由软件基金会发布的GNU自由文档许可证版本1.3
的条款进行许可。Qt及其相关标志是芬兰以及世界其他地区的Qt公司 Ltd的商标。所有其他商标均为其各自所有者的财产。