十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Qt 5.14.2 ARM交叉编译踩坑全记录:从工具链到上板部署

Qt 5.14.2 ARM交叉编译踩坑全记录:从工具链到上板部署 搞嵌入式Qt开发的人应该都有体会PC上跑得好好的程序一交叉编译搬到ARM板子上不是编译报错就是运行段错误折腾几天都是家常便饭。Qt 5.14.2是我用得比较久的一个版本在ARM Linux下做应用界面开发稳定性和模块完整度都比较适中。这篇文章就把我在Qt 5.14.2 ARM交叉编译过程中踩过的坑、试出来的方案、以及最终跑通的配置完整记录下来希望帮你少走弯路。文章会覆盖从工具链选择、sysroot准备、configure参数配置到make编译、上板部署运行的全流程重点放在那些“报错信息看了半天不知道问题出在哪”的坎上。适合正在用Qt 5.14.2或者其他Qt 5.x版本做ARM嵌入式开发、手里有实板要跑起来、但被交叉编译折腾到怀疑人生的朋友。如果你是第一次接触Qt交叉编译这篇也能帮你建立一个从编译到部署的完整认知框架。1. 动手之前先想清楚版本、工具链和sysroot怎么选1.1 为什么偏偏是Qt 5.14.2选Qt 5.14.2不是因为什么“最新最强”相反它在今天已经不算新版本了。但这类LTS线之外的稳定版本有个特点问题沉淀充分网上资料多踩过的坑基本上都有前人填平。5.14.2在5.12和5.15两个LTS之间补丁修得比较勤编译配置习惯和5.15几乎通用所以你现在用5.14.2踩出来的经验将来升级到5.15.2也不会白费。更重要的一点是Qt 5.15之后官方对开源版本的支持策略变了离线安装包和源码包下载都更麻烦很多开发板厂商提供的BSP也跟着停留在5.14左右。如果你用的是全志、瑞芯微、NXP i.MX系列这些常见平台直接拿Qt 5.14.2源码交叉编译跟BSP里的内核驱动、GPU库对接会顺很多。再说一下版本命名的坑Qt 5.14.2对应源码包是qt-everywhere-opensource-src-5.14.2.tar.xz但到了5.15就改叫qt-everywhere-src-5.15.x.tar.xz。去官网或者清华镜像下载的时候别按老名字找不然会一脸懵。1.2 交叉工具链的选择不是越新越好这是我栽过最狠的一个跟头。一开始图省事直接拿了Ubuntu 22.04自带的gcc-arm-linux-gnueabihfGCC 11.x结果Qt 5.14.2源码编译到一半就开始报各种模板错误和隐式声明问题查了很久才发现是工具链版本太新对老代码不够友好。经验教训Qt 5.14.2是2020年初的代码配GCC 9/10是最稳的。具体来说32位ARM硬浮点平台推荐两种方式用开发板BSP自带的工具链比如NXP提供的gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf用Linaro官方提供的GCC 9或10版本工具链例如gcc-linaro-10.3.1-2021.07-x86_64_arm-linux-gnueabihf64位ARM平台则选择aarch64-linux-gnu工具链同样建议GCC 10左右。为什么强调“不是越新越好”因为Qt 5.14.2里有些第三方库和桥接代码写得比较老新编译器对类型检查更严格把原本仅是警告的问题直接升级成error。你当然可以靠给源码打补丁解决但嵌入式开发时间本来就不够用何苦跟编译器较劲换条工具链五分钟就清净了。1.3 sysroot的构建把头文件和库先铺到“目标世界”sysroot这个概念很多人第一次碰会懵。简单说交叉编译的时候编译器和链接器需要一个“假的根文件系统”里面放着目标板上需要的头文件、标准库、依赖库它们的位置结构和ARM板子上的实际文件系统保持一致。这样编译器才知道“目标世界”长什么样。sysroot怎么来两种途径第一种直接从开发板或者你制作的根文件系统里拷贝一份。把板子上/lib、/usr/lib、/usr/include这些目录原样拷到主机的某个目录例如/opt/arm-sysroot。执行命令大致是mkdir -p /opt/arm-sysroot rsync -av root板子IP:/lib /opt/arm-sysroot/ rsync -av root板子IP:/usr/lib /opt/arm-sysroot/usr/ rsync -av root板子IP:/usr/include /opt/arm-sysroot/usr/第二种用Buildroot或Yocto生成根文件系统直接把它作为sysroot。这种方式一般会连带生成配套的工具链环境一致性最好缺点是初始学习成本高。sysroot里特别容易缺的东西是libstdc.so.6和libgcc_s.so.1这两个库必须从工具链目录拷贝到sysroot里否则后面链接阶段会报“找不到libstdc”。还有一点要提醒拷sysroot的时候别把符号链接弄丢了Qt编译时经常要通过libm.so、libdl.so这类软链接去定位真实的库文件。建议用rsync -a保留符号链接属性。2. configure配置这一步决定你后面会不会返工2.1 源码获取与离线安装的现实问题Qt 5.14.2官方下载页面现在还在但速度感人而且在线安装器对交叉编译场景没什么用——交叉编译必须要源码包qt-opensource-linux-x64-5.14.2.run是给x86主机安装用的不能用来交叉编译ARM版本。推荐到国内镜像站下载源码包例如清华镜像站或中科大镜像站路径大概是/qt/archive/qt/5.14/5.14.2/找文件名里带everywhere和src的tar.xz文件。下载后先校验一下文件大小和解压完整性tar -xJf qt-everywhere-opensource-src-5.14.2.tar.xz cd qt-everywhere-opensource-src-5.14.2注意完整源码包解压后有好几个GB建议用-skip参数裁剪不需要的模块能大幅缩短编译时间。这个在2.2节详细说。2.2 一份经过验证的configure参数清单附逐项解释我最终跑通的configure命令如下用的工具链是Linaro GCC 10.3 arm-linux-gnueabihf./configure \ -prefix /opt/qt5.14.2-arm \ -hostprefix /opt/qt5.14.2-host \ -release -shared \ -xplatform linux-arm-gnueabi-g \ -sysroot /opt/arm-sysroot \ -opensource -confirm-license \ -no-opengl -no-xcb \ -linuxfb -no-eglfs \ -no-dbus -no-iconv -no-ssl \ -nomake examples -nomake tests \ -skip qtwebengine -skip qtdeclarative \ -no-feature-cups逐个说下关键参数背后的逻辑-prefixQt最终安装到目标板文件系统的哪个目录。这里设为/opt/qt5.14.2-arm之后make install会把头文件、库、插件都装到这个目录结构下。这个前缀要和板子上实际部署路径一致否则运行时找插件和库会出问题。-hostprefix在主机上安装qmake、moc、uic等工具的位置。交叉编译时这些工具必须是可执行的x86版本它们负责在编译过程中处理界面文件和元对象。这个参数很多人漏掉导致make install把所有东西一股脑装到hostprefix里尤其会有版本冲突。-xplatform linux-arm-gnueabi-g指定目标平台mkspec。Qt的mkspecs目录里预置了常见交叉编译平台linux-arm-gnueabi-g对应32位ARM硬浮点。如果你的平台不在预设列表里参考2.3节定制。-sysroot指向之前准备的ARM根文件系统目录这是configure能正确找到目标系统头文件和库的关键。-no-opengl -no-xcb如果你的板子没有GPU或不需要OpenGL就关掉。很多configure失败都卡在OpenGL检测上因为找不到GLES2/gl2.h。如果板子有Mali GPU并提供了配套库可以改成-opengl es2。-linuxfb -no-eglfs显示后端。linuxfb是最朴素、最不容易出问题的方案基于Linux framebuffer设备。eglfs需要GPU/EGL支持先关掉保证能跑通。-no-dbus -no-iconv -no-ssl按需裁剪。如果板子系统里没有dbus、iconv、OpenSSL库就关掉避免configure检测失败。-skip qtwebengine这个大块头模块在ARM上编译时间极长而且依赖大量系统库没有特殊需求直接跳过。-no-feature-cups嵌入式板子几乎没有打印需求关掉防止检测CUPS库失败。2.3 定制qmake.conf或使用-device模式如果你的工具链命名和Qt预设不一致configure会报错找不到编译器。这时候不用慌有两种解决办法。第一种直接修改mkspecs里的qmake.conf。以linux-arm-gnueabi-g为例打开qtbase/mkspecs/linux-arm-gnueabi-g/qmake.conf把编译器变量改成你的实际工具链名字QMAKE_CC arm-linux-gnueabihf-gcc QMAKE_CXX arm-linux-gnueabihf-g QMAKE_LINK arm-linux-gnueabihf-g QMAKE_LINK_SHLIB arm-linux-gnueabihf-g QMAKE_AR arm-linux-gnueabihf-ar cqs QMAKE_OBJCOPY arm-linux-gnueabihf-objcopy QMAKE_STRIP arm-linux-gnueabihf-strip改完保存再执行configure。第二种用-device模式适合开发板有专门mkspec的情况比如-device linux-imx6-g。这种方式需要板级mkspec文件适合特定BSP通用性差一些。我自己更推荐先改qmake.conf跑通因为可控性强出问题知道去哪儿查。等团队规范了板级支持再考虑做-device层。3. make与make install编译中途报错时该去哪里找原因3.1 并发参数与资源控制configure通过之后进入编译阶段。第一次编译建议先用make -j4试探不要一上来就-j$(nproc)拉满。原因很简单Qt源码模块众多有些模块依赖另一个模块先编译完并发太高容易遇到诡异的临时文件丢失错误。如果4线程编译正常再逐步往上加。我实际使用的命令是make -j8 21 | tee build.logtee把编译日志存下来很重要编译中途如果断掉可以快速定位到具体报错文件。编完后make install 21 | tee install.log3.2 编译期的三类典型失败以及对策编译期遇到的报错归纳起来大致三类。第一类跟编译器版本有关比如报error: uint32_t does not name a type这是工具链GCC版本太新、某些头文件包含顺序不兼容导致的。对策是换回GCC 9/10或者在该模块的Makefile里追加-include stdint.h的CFLAG。但我不建议硬改源码编译选项容易埋雷换工具链更干净。第二类是链接错误比如undefined reference to __aeabi_ddiv。这跟浮点ABI有关系常见于32位ARM平台。工具链是硬浮点eabihf的编译选项里却带了-mfloat-abisoftfp或没有浮点参数导致调用约定不一致。解决方案是确认qmake.conf里没有硬性指定软浮点参数检查工具链默认类型是arm-linux-gnueabihf而非arm-linux-gnueabi。第三类是头文件找不到典型是GLES2/gl2.h: No such file or directory。这属于configure阶段没检测到某个依赖但检测失败被跳过了编译时才爆发。这时候别在Makefile里乱加路径回到configure参数检查是否漏关了某个模块。比如不需要OpenGL就用-no-opengl。3.3 make install之后必须检查的几件事编译安装完成后别急着打包往板子上拷。先检查/opt/qt5.14.2-host/bin下有没有qmake、moc这些工具这些是x86版本之后在PC上配置Qt项目就用它们。再检查/opt/qt5.14.2-arm/lib下是否有libQt5Core.so.5系列库以及plugins/platforms/目录下是否有libqlinuxfb.so这个framebuffer插件。还有一个隐蔽问题make install安装的头文件里qconfig.h和qconfig.cpp会记录configure时的feature配置。如果你改了configure参数重新编译旧版本的qconfig.h会缓存下来导致后续qmake生成的项目配置和实际库不一致。尤其当你重新configure但-prefix路径没变时一定要提前清掉旧安装目录rm -rf /opt/qt5.14.2-arm /opt/qt5.14.2-host4. 上板部署阶段跑到目标板上才是真正的检验4.1 运行库拷贝与库路径的正确姿势编译好的Qt目录打包拷到板子仍然有讲究。最笨也最有效的办法是把整个/opt/qt5.14.2-arm目录用tar或rsync传到板子的/opt/qt5.14.2。这个目录里包括了头文件、库、插件、翻译文件等虽有些冗余但有效避免了“缺了某个libts”这种低空飞行式的排错。拷贝完成后板子上设置环境变量是重头戏export LD_LIBRARY_PATH/opt/qt5.14.2/lib:/usr/lib:/lib export QT_QPA_PLATFORMlinuxfb export QT_QPA_FONTDIR/opt/qt5.14.2/lib/fonts:/usr/share/fonts export QT_QPA_PLATFORM_PLUGIN_PATH/opt/qt5.14.2/plugins/platformsLD_LIBRARY_PATH千万别把Qt的lib目录放在最后面否则板子系统原本的Qt库可能被覆盖或者找错。另外Qt程序启动时会去/opt/qt5.14.2/plugins找插件目录这个路径默认相对于prefix生成的如果你部署路径和prefix不同必须显式设置QT_PLUGIN_PATH否则程序会报“could not find or load the Qt platform plugin”。4.2 platform插件和显示后端的取舍linuxfb是默认最稳的选择它不需要X11也不需要GPU直接写/dev/fb0。如果你的板子有LCD屏幕内核里有fbmem驱动linuxfb基本就能出画面了。但linuxfb有几个肉眼可见的缺点没有硬件加速、刷新率低、窗口系统支持有限。如果你的界面比较炫有半透明、动画linuxfb可能达不到效果。这时候就需要eglfs后端配合GPU厂商的OpenGL ES库。切换到eglfs的前提是configure时用了-opengl es2 -eglfs并且板子sysroot里放好了Mali或PowerVR的libEGL.so和libGLESv2.so。运行时要设置export QT_QPA_PLATFORMeglfs export QT_QPA_EGLFS_INTEGRATIONeglfs_kms如果遇到黑屏但没有报错先排查显示屏对应的DRM/KMS接口是否设置正确很多情况下是QT_QPA_EGLFS_KMS_CRTC需要指定到具体的crtc编号。这块每个GPU厂商差异很大建议先从官方BSP的demo配置入手。4.3 字体、中文、触摸屏这些“细节决定成败”程序跑起来后第一眼看到的多半是中文显示成了方块。Qt默认字体查找路径是/usr/lib/fonts或者QT_QPA_FONTDIR指定的目录。板子系统里的字体文件往往很少需要手动拷入中文字体比如文泉驿微米黑或Droid Sans Fallback。我习惯把字体放到板子/usr/share/fonts/truetype/wqy/然后在环境变量里加上这个路径。注意别只设QT_QPA_FONTDIR不设FONTCONFIG_PATH部分Qt模块仍会扫fontconfig。触摸屏的问题更麻烦。如果板子用的是电阻屏或通过tslib上报事件那除了在configure时加-tslib参数运行时还要设置tslib设备export TSLIB_TSDEVICE/dev/input/event1 export TSLIB_CALIBFILE/etc/pointercal export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS/dev/input/event1:rotate90如果是电容屏通过/dev/input/eventX直接上报多点触控Qt的evdevtouch插件会读取它。一个容易犯的错是设备节点确定不了。可以用cat /proc/bus/input/devices查看触摸设备名确认event编号后再设置。4.4 时区与本地化的暗坑最后说一个不起眼但偶尔让人抓狂的点Qt程序默认使用UTC时间如果你的板子没有设置时区程序显示的时间会和本地时间差8小时。解决方式是在板上设置环境变量export TZAsia/Shanghai export QT_QPA_FB_NATIVE_FORMATrgb32TZ变量对Qt的QDateTime::currentDateTime()生效前提是系统里存在/usr/share/zoneinfo数据。embed板子的文件系统经常精简掉这些数据直接从主机拷贝对应时区文件过去即可。5. 高频错误避坑速查表建议收藏5.1 configure阶段的问题configure阶段报错最多因为它在全面探测系统依赖。常见的错误码和解决思路整理成表格报错现象可能原因解决办法找到不到交叉编译器mkspec里QMAKE_CC没有指向工具链打开对应qmake.conf改成实际编译器如arm-linux-gnueabihf-gccOpenGL功能测试失败sysroot里缺GLES头文件和库使用-no-opengl或正确安装GPU厂商库xcb相关测试失败系统没有libxcb库-no-xcb或安装libxcb-dev到sysroot找不到libstdc等库sysroot里缺少工具链的库软链接从工具链的arm-.../lib目录拷贝libstdc.so.6、libgcc_s.so.1到sysroot提示The system library icu must be used缺少ICU库configure加-no-icu或者先交叉编译ICU到sysroot5.2 编译阶段的问题configure过了编译期遭遇的问题要靠看日志定位。常见情况如下表报错现象可能原因解决办法uint32_t不识别GCC版本太新换GCC 9/10工具链或加-include stdint.h__aeabi_ddiv等链接错误软硬浮点ABI不匹配确保编译器为gnueabihf且编译选项无软浮点参数GLES2/gl2.h找不到configure阶段漏检测加-no-opengl重新configure某个子模块一直编译失败模块内部依赖缺失用-skip跳过该模块或单独编译该模块查看详细错误make run出的qmake是x86还是ARM混淆-hostprefix没设置好重新configure时指定-hostprefix /opt/qt5.14.2-host5.3 运行阶段的问题程序拷到板子上运行时报错排查起来比较烦人因为没有编译期那么直观。这类问题我强烈建议先控制变量运行现象可能原因解决办法could not find platform plugin linuxfbplugins目录没找到或缺失插件设置QT_QPA_PLATFORM_PLUGIN_PATH为actual plugin路径程序启动后秒退无输出缺少依赖库用ldd检查可执行文件依赖逐一补齐中文乱码或方块缺少中文字体或字体路径不对拷贝中文字体设置QT_QPA_FONTDIR触摸屏点击无反应设备节点号不对或未配置tslib检查/dev/input/eventX、设置TSLIB_TSDEVICE显示画面撕裂framebuffer双缓冲未启用设置QT_QPA_FB_DOUBLE_BUFFER1时间差8小时时区未设置设置TZAsia/Shanghai并确保zoneinfo存在你可能会问lark跑起来之后怎么判断是不是Qt的问题还是自己的业务代码问题我自己的习惯是先编一个只有QWidget空窗口的最小demo上板跑如果空窗口能正常显示再逐步叠加业务模块。这种二分法定位比在几百行代码里猜要快得多。5.4 一个非常隐蔽的坑hostprefix路径残留最后分享一个让我排查了很久的问题。以前我在configure时没有设-hostprefixQt默认就把host工具装到了/usr/local/Qt-5.14.2。后来重新配置时改了prefix但host工具路径还残留着导致qmake生成Makefile时引用了旧路径下的工具编译出来的库版本和宿主工具版本不一致界面资源始终加载不出来。解决办法很简单再编译之前把旧的/usr/local/Qt-5.14.2整个删掉确认qmake -v输出的是新配置的版本信息。这类路径残留问题我之前在多个版本的Qt上见过不光5.14.2有5.12也有强烈建议每次重新configure前清理干净。在实际项目里我会把交叉编译的一次性脚本固化下来包括configure和make、make install命令并且把工具链版本、Qt版本、host工具路径都写清楚。这样不光是换电脑、换工程师的时候能快速复现同样环境自己过俩月回来看也还能记起来当初是怎么配的。如果你正卡在某个具体报错上建议按照上面的速查表先定位阶段——configure、编译、运行——然后只关注那个阶段的问题不要去动无关的模块配置。很多时候交叉编译搞不定不是技术有多难而是你一急就乱改导致问题叠加。先稳定住一个可用的最小配置从-skip模块裁剪开始能过就跑起来性能优化和功能扩展留到下一步再说。我现在自己新接ARM板子的活儿惯例就是先跑通这个“最小链路”源码包5.14.2、GCC 10工具链、sysroot、linuxfb、空窗口demo。这套链路通了后面加业务进度的速度比一开始就想把所有硬件特性全开要快得多。
返回列表