ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Qt 5.14.2 aarch64静态交叉编译完整手册:从零构建嵌入式Linux应用

Qt 5.14.2 aarch64静态交叉编译完整手册:从零构建嵌入式Linux应用 做嵌入式Linux开发的朋友应该都有过这种体验手里是一块aarch64架构的板子跑着嵌入式Linux想在上面跑一套Qt界面程序。开发机是x86_64的Ubuntu目标板是ARM64通常的做法是交叉编译Qt动态库再把一堆.so动态库和依赖包拷贝到板子上。这套方案一旦遇到复杂的应用场景比如多个Qt程序要分发到不同板卡、客户现场不允许随意装环境、或者板子文件系统精简到只有几十兆动态库方案就会让你头疼。我这次要讲的就是在Ubuntu主机上用交叉编译工具链把Qt 5.14.2整个编译成aarch64架构的静态库最后产出一个可以直接扔到目标板上运行的单体可执行文件整套流程从零开始全部走一遍。这篇手册适合谁想给ARM64开发板做Qt GUI程序、但又受够了动态库依赖的朋友尤其是做工业控制、医疗设备、车载终端这类往往需要“一个程序拷过去就能跑”的场景。你不需要是交叉编译专家但至少用过Linux命令行、知道make是什么。读完这份手册你能得到一套可复现的完整流程以及我在实际搭建过程中踩过、填平的全部坑位。1. 方案选型背后的真实考量1.1 为什么必须用静态交叉编译嵌入式Qt程序的交付方式市面上基本有三条路动态库拷贝、静态编译、以及容器或整镜像分发。容器方案在轻量级板卡上不现实镜像分发适合量产阶段开发和调试阶段最恼人的反而是第一类动态库拷贝。跨架构的动态库依赖远比你想的麻烦。你编译出的Qt应用运行时要依赖libQt5Core.so.5、libQt5Gui.so.5、libQt5Widgets.so.5还可能依赖libstdc.so.6、libgcc_s.so.1甚至libts.so这类触摸屏校准库。这些库要和目标板上系统库的版本完全兼容出一个GLIBCXX版本不匹配就是“段错误”或者“symbol not found”。我在项目初期用动态交叉编译方案三天两头往板子上pscp拉库还要用ldd在板子上逐个验证依赖耗费的精力远超写代码本身。静态编译的好处是直接把Qt核心模块和第三方依赖打包进可执行文件产物只有一个ELF文件拷进板子给上可执行权限就能跑。对于批量部署和现场维护这是肉眼可见的效率提升。代价是最终可执行文件体积偏大一个Hello World级别带UI的程序也可能到5到10MB甚至更大但在嵌入式板卡普遍支持大容量存储的今天用体积换交付稳定性完全值得。另外要说明的是Qt的静态编译在商业授权方面有讲究。Qt 5.14.2这个版本还提供开源的LGPL/GPL选项LGPL要求动态链接以便用户能够替换Qt库静态链接则受到更多条件限制。非开源商业项目请务必购买商业授权或者认真对待LGPL条款下的源码替换义务。我这份手册假设你已确认自己的使用场景合规能做静态链接。1.2 动态编译和静态编译的取舍对照为了让你更直观地判断自己该走哪条路我把两种方式的差异整理成了表格。对比维度动态编译静态编译可执行文件体积通常1~3MB通常5~15MB按需裁剪可缩小依赖库libQt5Core.so等需随程序分发全部内嵌无动态依赖部署流程复制可执行文件 全部依赖库只复制一个文件目标板兼容性依赖板子系统库版本GLIBCXX不匹配会崩受glibc影响仍存在但可控得多调试便利性修改库文件可单独替换改一行代码全部重编迭代慢裁剪空间库文件整体过大不好裁剪可通过configure参数精确裁剪Qt模块动态编译的优势在于开发迭代快改个界面逻辑重新编译可执行文件即可静态编译的优势在于交付状态稳定。对于产品已经进入测试或小批量阶段、板子型号固定、后续迭代节奏放缓的团队静态编译是更优解。如果项目还在快速原型阶段天天改代码先用动态方式做调试、最后切换静态编译出发布版也是可行策略。还有一个需要提前了解的点静态编译后的Qt程序并非完全不受目标板系统环境的影响。程序启动加载的glibc、libstdc、libgcc等底层的C/C运行时库依然是从目标系统的/lib和/usr/lib动态加载的除非你额外指定“完全静态”链接。这一点到第5节排查运行问题时还会再提。2. 环境准备与工具链选型2.1 选择哪一套交叉编译工具链做aarch64的Qt交叉编译首先要确定工具链。我试过两种主流的方案一是Ubuntu官方源里自带的gcc-aarch64-linux-gnu二是Linaro提供的GCC工具链。两种都能用但实测下来有区别。Ubuntu 20.04源里的gcc-aarch64-linux-gnu版本是9.3与Ubuntu 20.04自带的glibc配套编译出的程序对glibc要求较高GLIBC_2.29以上。如果目标板的rootfs是基于Ubuntu 18.04、Debian 10之类较老的嵌入式系统板子上的glibc版本可能偏低运行时会报“version GLIBC_2.29 not found”。这种情况就要用Linaro提供的aarch64-linux-gnu-gcc 7.5或8.3版本编译产物对老glibc更友好。我自己的目标板用的是厂商基于主线内核裁剪的rootfsglibc版本在2.27左右所以我最后选了Linaro官方提供的gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu版本。实践下来这个工具链的兼容性明显优于新版本GCC交叉编译后在老glibc系统上运行的效果。工具链解压到/opt目录配置好PATH就行。这里切记一点工具链的安装路径最好不要带空格和中文也不要随便移动后续所有脚本都会引用这个路径。2.2 准备一个干净的sysroot根文件系统静态交叉编译Qt源码时编译器需要找到目标板对应的头文件和系统库这就是sysroot的作用。简单理解sysroot就是目标板rootfs的拷贝放在你开发机上的某个目录里编译时通过--sysroot参数告诉编译器“你用这里的头文件和库而不是开发机本机的”。sysroot怎么来对于大多数项目正确做法是直接从目标板或者厂商提供的rootfs拷贝出来。可以用scp、rsync或者SD卡挂载读取。我习惯用rsync将自己板卡的整个根文件系统同步到开发机这样做的好处是所有头文件、库文件版本与目标板完全一致Qt的configure脚本在检测系统特性时能得到最真实的结果。rsync -av --delete rootboard-ip:/ /opt/sysroot-aarch64/同步完成后需要排除一些不必要的运行时虚拟目录。建议把/proc、/sys、/dev、/run这些目录排除掉不然rsync会一直报错。真正编译时常用到的目录主要是/usr/include、/usr/lib、/lib、/usr/lib/aarch64-linux-gnu这几个。如果rootfs里没有编译用的头文件比如libc6-dev、libstdc-dev这些包没装那就需要先安装匹配的工具链或者从Ubuntu的arm64软件源下载对应的包解压进sysroot。2.3 交叉编译toolchain的依赖库齐备性检查我在第一次编译时configure脚本卡在一个很奇怪的检测项上Qt源码在检测libfontconfig时一直不过。排查下来是sysroot里缺少libfontconfig1-dev的头文件。这样的问题在交叉编译中特别常见因为很多系统库是运行库而不是开发库头文件不会默认安装在板子上。为了方便后续统一管理我建议给sysroot里的/usr/lib/aarch64-linux-gnu目录多留意一下软链接。有的rootfs因为精简把.so文件做成指向实际.so.x的软链接但编译时需要的是带版本号的精确文件名还是无版本号的链接文件取决于具体链接参数。你在手动编译第三方依赖库时如果遇到“cannot find -lxxx”的错误多半是sysroot里缺少对应的库文件或软链接。要彻底解决依赖齐备性问题可以先把Ubuntu官方arm64源配置到开发机上用apt的跨架构模式直接下载依赖包解包进sysroot。这样比拖一个完整rootfs回来还干净尤其适合只想做Qt编译、不想保留整个板子文件系统的场景。3. Qt 5.14.2源码编译整个流程中挑战最大的环节3.1 获取Qt源码与configure参数逐条解读Qt源码建议直接到官网下载或使用国内镜像站下载qt-everywhere-src-5.14.2.tar.xz这个全量源码包包含了qtbase、qtdeclarative、qtmultimedia这些核心模块约1GB左右。我之前踩过直接从git仓库拉代码的坑5.14.2版本分支拉下来后还要自己同步子模块浪费时间直接下载发布版tar包最稳妥。解压后是源码根目录Qt的configure脚本从qtbase目录启动。命令如下cd qt-everywhere-src-5.14.2/qtbase ./configure \ -prefix /opt/qt-5.14.2-aarch64 \ -xplatform linux-aarch64-gnu-g \ -opensource \ -confirm-license \ -release \ -static \ -no-opengl \ -linuxfb \ -tslib \ -qt-zlib \ -qt-libpng \ -qt-libjpeg \ -qt-freetype \ -qt-pcre \ -qt-harfbuzz \ -no-gtk \ -no-cups \ -no-icu \ -no-feature-cups \ -skip qtwebengine \ -nomake examples \ -nomake tests这些参数每个都有背后的逻辑我拆开来讲。-prefix指定Qt安装路径编译完之后的头文件、库文件、qmake工具都会安装到这个目录。-xplatform linux-aarch64-gnu-g告诉qtbase使用mkspecs目录下的哪个平台配置这个配置在qtbase/mkspecs/linux-aarch64-gnu-g核心内容是让qmake知道交叉编译器叫aarch64-linux-gnu-gcc和aarch64-linux-gnu-g。如果你的工具链前缀不是这个需要修改这个mkspecs文件或者克隆一份改成你自己的前缀。-release和-static是这次编译基调的核心。-release表示编译release版本不带调试符号体积更小运行更快-static表示将Qt自身编译成静态库这是“静态交叉编译”目标的关键。-no-opengl是因为部分嵌入式平台没有GPU和OpenGL支持如果目标板有Mali或PowerVR这类GPU而且你能拿到官方OpenGL ES头文件可以考虑开启-opengl es2。没有的话关掉能节省大量编译时间。-linuxfb启用了Qt的Linux Framebuffer平台插件这就是无显示器环境下用/dev/fb0显示的基础。后面还可以用-eglfs做EGL显示后端但这里先以linuxfb为主。-tslib则开启触摸屏支持它会去检查sysroot里是否安装了tslib库和头文件。如果没安装这个选项会导致configure失败这时候需要在sysroot里预装tslib开发包或者后面手动编译tslib。最后几个-qt-zlib、-qt-libpng、-qt-libjpeg、-qt-freetype的意思是使用Qt源码自带的第三方库而不是依赖系统库这是静态编译的核心配套动作。因为我们要的是不依赖动态库的最终产物如果这里选系统库链接时Qt会去动态链接系统的libpng、libfreetype这种动态依赖在目标板上非常不可控。-qt-pcre、-qt-harfbuzz同理处理正则表达式和字体整形时也尽量用内置版本。-skip qtwebengine跳过WebEngine模块。这个模块不仅编译极其耗时而且依赖一批系统级的动态库在嵌入式静态编译场景下几乎无法直接使用。-nomake examples -nomake tests则跳过示例和测试代码的编译能省去几个小时。3.2 解决tslib、fontconfig等外部依赖configure参数勾画完之后实际编译过程中最容易卡住的就是“系统依赖检测失败”。这里有两个典型的依赖需要提前处理。第一个是tslib。tslib是电阻触摸屏的校准和滤波库很多工业板卡都靠它。因为我们要的是静态库所以需要自己交叉编译一个libts.a并把它安装到sysroot目录下。具体步骤下载tslib源码configure时指定--hostaarch64-linux-gnu--prefix$(pwd)/_install然后make make install。编译完成后把_install下的lib和include目录内容分别拷贝到sysroot的/usr/lib和/usr/include里。第二个是fontconfig。Qt的字体引擎底层依赖fontconfig来管理字体配置如果configure检测到系统没有fontconfig它会自动退回使用基础的freetype渲染。大多数情况下可以接受但涉及到繁体中文字体的加载顺序、字体替代逻辑时还是建议交叉编译一份fontconfig静态库。fontconfig的交叉编译比tslib复杂因为它依赖expat和freetype需要先把这两个库也交叉编译好并安装到同一个sysroot里。我在实际工程中一开始跳过了fontconfig后来发现界面上中文显示出现“豆腐块”排查好久才确认是字体配置文件没法定位的问题最终补上了fontconfig和一套中文字体文件。3.3 make编译与安装过程实录configure通过之后就可以直接make了。这里建议用多线程并行编译但线程数不要无脑拉满。我实测在16核的机器上用make -j8最稳定-j16偶尔会遇到个别源文件头文件竞争引起的编译报错。完整编译一次大概要40到60分钟具体取决于模块数。make -j8 make install编译过程中如果出现某个模块编译失败不要急着整个重来。一定要看报错信息里的“Error 1”上面几行绝大多数情况是缺少某个依赖库的头文件或库文件。我第一次编译时在qtimageformats模块报了libmng的头文件找不到后补安装了libmng-dev到sysroot就解决了。make install执行后/opt/qt-5.14.2-aarch64目录下会生成bin、lib、include、mkspecs、plugins这几个关键目录。bin里有交叉编译版的qmake和moc、uic这些工具lib里有libQt5Core.a这种以.a结尾的静态库plugins下则是各平台的QPA插件静态库。这些就是后续交叉编译应用的全部家底。3.4 校验交叉编译结果是否可用安装结束后先不要急着写应用花两分钟验证这套交叉编译环境可用。用过新工具链的都应该知道如果后面到了应用编译阶段才发现环境问题排查成本会高得多。/opt/qt-5.14.2-aarch64/bin/qmake -v这个命令会打印qmake的版本和构建信息。重点看输出的构建信息里是否出现Using Qt version 5.14.2 in /opt/qt-5.14.2-aarch64/lib。确认无误后再检查一下静态库是否按预期生成ls /opt/qt-5.14.2-aarch64/lib/libQt5Core.a如果上面两个输出都正常说明Qt的交叉编译本身成功。还可以顺手创建一个空QApplication工程用这套qmake做一次编译验证但这一步也可以放到第4节应用搭建时一并做。反正只要qmake版本正确说明Qt交叉编译的底座已经打好了。4. 应用工程搭建与交叉编译实操4.1 构建一个Qt工程并编写专业交叉编译脚本假设你的项目是一个简单的Qt Widgets程序目录是myapp包含main.cpp、mainwindow.cpp、mainwindow.h以及一个myapp.pro。交叉编译应用比编译Qt本身简单得多只要保证环境变量正确qmake指向交叉编译版然后make即可。先给出.pro文件关键配置尤其要注意静态编译时的一些特殊宏QT core gui widgets TARGET myapp TEMPLATE app CONFIG release CONFIG static SOURCES main.cpp mainwindow.cpp HEADERS mainwindow.hCONFIG static很重要它会通知qmake链接Qt静态库而不是动态库。在Qt 5.14里如果缺少这一行即使你的lib目录里只有.a库链接器也可能找不到库。交叉编译时我习惯写一个独立的构建脚本build.sh避免每次手工敲一堆环境变量。脚本大致长这样#!/bin/bash export PATH/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH export QT_ROOT/opt/qt-5.14.2-aarch64 export SYSROOT/opt/sysroot-aarch64 $QT_ROOT/bin/qmake myapp.pro make clean make -j8执行完build.sh后当前目录会生成一个myapp可执行文件。用file命令检查一下架构file myapp输出里必须包含ELF 64-bit LSB executable, ARM aarch64同时还要看一个关键信息statically linked或者dynamically linked。如果是动态链接这里会列出依赖的共享库列表如果是静态Qt库链接依赖里只会出现libc、libstdc、libgcc这些系统级的库。这一步是我判断Qt静态链接是否生效的最快捷方式。4.2 链接器链接顺序问题多发点静态链接最折磨人的是库的链接顺序。GNU ld在解析静态库时是按命令行顺序从左到右扫描的如果库A用到库B的符号而命令行里B放在A前面就会报undefined reference。Qt的qmake在生成Makefile时一般已经把顺序理好但第三方库混进来后就容易出事。最常见的报错是undefined reference to libiconv_open这往往是静态链接了依赖iconv的库比如libfcft、libX11而iconv自身的库没有放在后面。解决方式是在.pro文件里追加LIBSLIBS -liconv还有个情况是libstdc静态链接时的符号重复。如果你用的是Linaro GCC 7.5工具链它自带的libstdc是动态链接版本。程序运行时板子上若没有对应的libstdc.so.6版本会报GLIBCXX_3.4.22 not found。此时可以手工把libstdc.a和libgcc.a加入链接同时加上-static-libstdc -static-libgcc让编译器把这两个运行时库也静态打进去。我踩过最深的坑是在板子上运行典型Qt程序时报QXcbConnection: Could not connect to display。原因不是程序有问题而是QPA平台插件选错。交叉编译的Qt默认可能选择xcb作为平台插件但嵌入式无X环境根本不存在X11服务。解决方式是在目标板上运行前明确指定linuxfb平台或者用fbcon。环境变量设置方式我放在后文部署段落说明。4.3 构建LinuxFB平台插件与静态插件链接方式很多人在configure时没留意LinuxFB插件是否真的编译进了静态库等到目标板上跑起来黑屏才发现问题。Qt 5.14中linuxfb插件默认在配置了-linuxfb选项后编译成plugins/platforms/libqlinuxfb.a。使用qmake构建应用时Qt会自动把这个静态插件与你的应用进行链接前提是configure阶段已经把这个插件生成。但静态插件的工作不只是“链接进去”这么简单它的构造函数需要被即时注册到Qt的程序初始化流程里。Qt通过Q_IMPORT_PLUGIN宏解决这件事而这些宏在qmake工程中默认不导入。你需要在main.cpp里手工导入需要的插件。一个典型的main.cpp开头会长这样#include QApplication #include QDebug #include QtPlugin Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin) int main(int argc, char *argv[]) { QApplication app(argc, argv); qDebug() Qt app running...; // ... }如果你还需要触摸屏tslib支持可能还需要加一行Q_IMPORT_PLUGIN(QTslibPlugin)。缺少插件导入时程序能编译能运行但会在第一次创建QApplication时输出could not find a Qt platform plugin然后退出。我用了一下午才彻底搞明白这点因为configure阶段没报错qmake也没报错所有错误都发生在目标板运行阶段。4.4 目标板部署与运行环境变量静态编译的可执行文件提交到目标板通常只需要scp myapp rootboard-ip:/root/ ssh rootboard-ip chmod x /root/myapp export QT_QPA_PLATFORMlinuxfb export QT_QPA_FB_DRM1 export QT_QPA_GENERIC_PLUGINStslib:/dev/input/event1 /root/myapp这些环境变量的意义如果你完全不设置Qt 5.14默认会尝试使用xcb或eglfs导致程序闪退。QT_QPA_PLATFORMlinuxfb强制使用Linux Framebuffer平台后端QT_QPA_FB_DRM根据板子的DRM驱动情况决定是否开启帧缓冲加速QT_QPA_GENERIC_PLUGINStslib则指定输入插件这里要根据你的触摸屏事件节点调整可能是/dev/input/event0也可能是event2用cat /proc/bus/input/devices查看确认。字体文件方面由于我们可能跳过了fontconfig或者把它静态链接进去了目标板系统里是否安装了字体文件就非常关键。建议提前把一套中文字体比如思源黑体的OTF或文泉驿点阵放到板子的/usr/share/fonts目录下并运行一次fc-cache -f更新字体缓存。如果板子上没有fontconfig工具链可以跳过fc-cacheQt的freetype后端能直接加载指定路径的字体文件在代码里用QFontDatabase::addApplicationFont(/usr/share/fonts/xxx.ttf)显式加载字体也是常用的招数。4.5 跨架构版本不兼容排查从GLIBCXX到QPA插件这一节严格来说是调试话题但因为它和“跨架构”紧密绑定放在这里更自然。静态Qt库把大部分链接问题解决了但平台相关的崩溃往往发生在运行时。如果你在板子上运行myapp时遇到Illegal instruction (core dumped)大概率是编译时启用了超出目标板CPU能力的指令集。Linaro 7.5默认编译armv8-a基础指令集不会用高级扩展。但如果手动给CXXFLAGS加了-marcharmv8.2-a而板子CPU只支持armv8.0就会触发非法指令。排查方法是用cat /proc/cpuinfo查看板子的Features列表对照编译参数。另一个运行时高频问题error while loading shared libraries: libstdc.so.6: cannot open shared object file。虽然Qt库全部静态链接了但libstdc仍是动态链接板子上没有对应版本就会报这个错。对策我前面提过在.pro里加QMAKE_LFLAGS -static-libstdc -static-libgcc重新构建应用。加了这两个选项后file命令的输出里动态依赖只会剩libc这种最基础的系统库。5. 常见问题与排错实录5.1 链接阶段报错排查速查表我把整个搭建过程中最常见的报错和对应处理方式整理成了一张表方便你遇到问题直接对号入座。报错信息根因解决方案cannot find -ltssysroot里缺少tslib静态库libts.a下载tslib源码交叉编译安装到sysroot的/usr/libcannot find -lfontconfig缺少fontconfig的开发库交叉编译fontconfig并安装到sysroot或在configure阶段去掉相关选项undefined reference to libiconv_open依赖了iconv库但链接顺序不对在.pro文件的LIBS末尾追加-liconvGLIBCXX_3.4.x not found目标板libstdc.so.6版本过低使用-static-libstdc -static-libgcccould not find a Qt platform plugin linuxfb未导入QPA静态插件main.cpp增加Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin)QXcbConnection: Could not connect to display目标板无X11服务却用了xcb平台设置export QT_QPA_PLATFORMlinuxfbIllegal instruction (core dumped)编译参数启用了目标板不支持的指令集将-march等参数降级或移除默认armv8-a这张表是我在真实调试过程中的经验汇总每一个报错都对应一个具体的操作步骤。如果你遇到没有列出来的情况排查顺序建议是先看编译端configure是否真的成功、静态库是否真的生成、qmake是否用错再看链接端链接顺序、缺失的库文件最后看运行端插件导入、环境变量、目标板系统库版本。5.2 静态编译时间与体积的进一步裁剪Qt的静态库全部装入可执行文件后体积确实大。我这里提供一个裁剪思路在编译Qt源码的configure阶段可以使用-skip参数跳过不需要的模块来减少体积。比如纯粹做工业仪表盘大概率不需要qtmultimedia、qtlocation、qtwebchannel这些模块在configure阶段被跳过之后最终可执行文件的体积能缩小至少三分之一。链接阶段的裁剪可以用GCC的-ffunction-sections -fdata-sections和-Wl,--gc-sections这两个选项配合使用可以把未被引用的函数和数据段从最终可执行文件中剔除。实测中一个包含Qt Widgets基本控件、显示一张图片、响应按钮点击的程序经过裁剪后体积可以控制在6MB以内。对于Flash存储空间紧张的板卡这个优化还是挺可观的。需要注意的是-ffunction-sections会影响调试体验因为调试器定位符号时会多一层间接映射。发布版本开启这组优化完全没问题但如果是需要频繁gdb调试的开发阶段可以暂时不启用。5.3 老版本glibc的一个隐蔽坑在静态链接一切正常、应用在目标板跑起来后的某天我遇到一个诡异现象程序启动10次里有1次会报double free or corruption。排查了大概一天后来发现是交叉编译工具链7.5编译出的代码与目标板上较老glibc在malloc内存管理上的兼容性隐患。这个问题无法在编译端通过参数完全修复只能更换工具链版本或者升级目标板rootfs里的glibc版本。所以我强烈建议项目进入中期后不要轻易更换工具链版本。整个Qt库和应用全部用同一个GCC版本编译能绕开大部分ABI兼容性问题。如果你确实要升级GCC请把/opt/qt-5.14.2-aarch64整个删除重新走一遍configure、make、make install流程不要只重编应用否则static库与应用的C ABI不同诡异崩溃会让你欲哭无泪。5.4 Qt的qml模块静态编译注意事项如果你的应用不是传统的QWidgets而是Qt Quick/QML界面那么除了QPA插件还需要额外导入QML自身的静态插件。QML引擎在加载QML模块时使用动态加载机制静态编译后这些模块不会被自动打包进可执行文件运行时会报module QtQuick is not installed。解决方式是在main.cpp中导入QML静态插件比如Q_IMPORT_PLUGIN(QtQuick2Plugin) Q_IMPORT_PLUGIN(QtQuickControls2Plugin) Q_IMPORT_PLUGIN(QtQuickLayoutsPlugin)同时在.pro里要加上CONFIG qmltypes QML_IMPORT_NAME MyApp QML_IMPORT_MAJOR_VERSION 1如果你用的QML模块较多建议在configure阶段用-skip保留需要的模块在.pro里通过QTPLUGIN qml1 qml2显式列出需要链接的QML插件。这个环节的坑比QWidgets方案深得多非必要我建议优先使用QWidgets做界面省下的调试时间很可观。5.5 字体与中文显示异常的完整解决方案嵌入式Qt程序中文显示异常是最常见的问题。症状分两类一是中文全部变成方框这说明字体文件没有加载二是中文能显示但出现错位或者重影说明字体引擎或者字体顺序有问题。对于第一类问题检查目标板是否有可用字体文件。Qt静态编译后不会自动携带字体文件需要额外拷贝。我用的是开源字体思源黑体的OTF格式文件比较大但显示效果很好。如果对体积敏感可以用工具将字体裁剪为常用汉字子集体积可以从20MB压缩到2MB以内。在代码里显式加载字体是最保险的方式QFontDatabase::addApplicationFont(/usr/share/fonts/SourceHanSansSC-Regular.otf);对于第二类问题可以考虑配置Qt的字体引擎渲染参数。在环境变量中设置export QT_QPA_FONTDIR/usr/share/fonts export QT_QPA_PLATFORMlinuxfb:fontsize16其中fontsize参数会影响framebuffer下默认字体大小如果你的UI设计使用绝对像素尺寸也可以不设置这个变量由Qt的样式表统一控制。6. 交付与后续维护的一点个人感想整套Qt 5.14.2-aarch64静态交叉编译链路跑通后最直接的感受是现场部署不再是一场“动一发而牵全身”的冒险。过去把动态库拷到板子上才发现少一个依赖的日子一去不复返。在测试阶段我有意识地在板子上卸载掉所有Qt相关运行库然后单独运行那个静态编译的可执行文件它依然运行正常那种“一个文件带走全部”的感觉真的很踏实。我个人建议你把这套流程固化成公司或团队内部的交付规范工具链版本固定、sysroot版本固定、Qt configure参数固定。三个“固定”能杜绝绝大多数因为环境差异导致的问题。后续目标板升级或Qt版本升级时照着本手册的步骤一步步重新执行即可但务必每次只变更一个变量避免一出问题就满头雾水。最后再分享一个小技巧静态编译的可执行文件可以用strip命令去掉符号表体积能再缩小20%到30%。对存储空间锱铢必较的嵌入式场景这个操作虽小收益却实打实。aarch64-linux-gnu-strip myapp如果你的应用里有一段逻辑在不同板卡上表现不一致可以考虑在main函数最开始加一段环境检测代码把/proc/cpuinfo、内存大小、Qt版本信息打印到日志文件。这套小工具在移植阶段和现场问题排查时能帮你省下大量猜测的时间。
返回列表