资讯动态

aarch64下Qt 5.14.2静态交叉编译完整指南:从环境配置到部署

发布时间:2026/9/15 7:27:23 来源:尧图企业网站定制
1. 为什么在aarch64下做Qt静态交叉编译先说结论如果你手里的目标板是ARM 64位架构又希望编译出来的Qt程序拷过去就能跑不依赖目标板上一堆乱七八糟的动态库那静态交叉编译就是绕不开的一条路。我最近刚用Qt 5.14.2在aarch64架构下完整走了一遍从零开始的交叉编译流程踩了不少坑觉得值得整理成一份手册分享出来。Qt 5.14.2这个版本选得其实挺讲究。它不是一个太新的版本LTS支持周期长网上的资料和现成patch也多同时又比5.12那一代对aarch64的支持成熟不少尤其是对ARM 64位指令集的优化和QPA插件的适配。如果你正在做嵌入式Linux相关的项目这个版本会是一个很稳的落脚点。很多人会问动态交叉编译不是更省事吗为什么非要静态这里有个很现实的场景我手头有一块基于aarch64的板子根文件系统是裁剪过的体积很小里面既没有Qt运行库也没有多余的依赖。如果采用动态编译我需要把Qt的一堆so文件libQt5Core.so.5、libQt5Gui.so.5、libQt5Widgets.so.5等全部拷贝到板子上还要保证路径正确、权限正确、依赖链完整。有时候板子上跑的是精简内核连glibc的某些版本都不完全匹配动态部署很容易在最后一公里翻车。而静态交叉编译出来的可执行文件除了glibc的极小部分crt文件等以外Qt自身逻辑全部打包在了一个elf里。拷到板子上只需要关注它用到的外设节点、帧缓冲设备或显示接口不用跟动态库的依赖关系纠缠。实测下来静态编译的一个Qt图形程序在目标板上的启动速度往往也比动态版快因为省去了动态链接器逐一解析so的时间。当然代价也有编译出来的文件体型明显偏大一个简单的Hello World加上Qt Widgets静态编译后轻松超过10MB。但这在现在的存储条件下不是什么大问题换来的是部署时的极大省心。如果你们项目里已经把目标板的文件系统空间规划得很紧张——比如只有几十MB的可用空间——那可能需要权衡但绝大多数场景下这个体量是可接受的。这份手册适合的人也很明确正在做aarch64嵌入式设备开发、需要给Qt应用做交叉编译的工程师或者你想在x86的Linux开发机上构建ARM 64位的Qt程序但不想在目标板上装一整套开发环境。无论你用的是哪种开发板只要CPU是aarch64架构这套流程都通用。提示本文基于x86_64 Linux主机交叉编译到aarch64目标板的场景。如果你用的是ARM架构的开发机而不是x86部分工具链的获取方式会变化思路仍是一致的。2. 核心思路与方案选型2.1 整体设计思路整个方案的核心其实只有一件事在x86的宿主机上用交叉编译器把aarch64架构的Qt库和目标程序一次性全部构建出来得到一个完全静态链接的可执行文件。听起来简单实际执行时涉及三层第一层是交叉编译器。它决定了你能生成哪种架构的机器码。aarch64的交叉编译器有很多种包管理器直接装的有gcc-aarch64-linux-gnuLinaro也提供预编译的toolchain还有ARM官方维护的arm-gnu-toolchain。我实际对比过这几种最省心的是直接用Ubuntu/Debian的apt包因为依赖处理得干净不用手动配一堆路径。如果你的开发环境是CentOS或RHEL系也可以装对应的交叉编译包名字略有差异。第二层是Qt库本身。Qt源码是跨平台的但configure的时候要给它指定目标架构、编译选项、依赖库路径。这一层是整个流程中最容易出错的地方。configure参数几百个不是每个都需要但关键的几个参数如果错了轻则编译出来的库用不了重则编译到一半直接报错退出。第三层是目标板运行环境。静态编译不代表完全与目标板无关。比如目标板上的Linux内核版本、帧缓冲设备路径、输入设备类型都会影响Qt程序运行时的行为。这一层需要在部署阶段验证。2.2 为什么选择Qt 5.14.2而非其他版本这个话题值得展开说。Qt的版本迭代很快6.x系列已经出了好几年但嵌入式领域大量存量项目依然停留在5.x。我在选版本时主要考虑了三点理由其一5.14.2是5.14系列的最后一个patch版本修复了大量已知bug。同一大版本内能用小版本号更高的就不要用更低的版本。许多人在网上搜到的Qt交叉编译教程是基于5.9或5.12那些版本的configure参数在5.14上基本还能用但有些选项已经废弃或改名直接照抄老教程很容易踩坑。其二5.14.2对aarch64的支持比较完善。Qt对ARM 64位架构的支持从5.6开始逐渐成熟到5.12已经达到可生产水平而5.14进一步优化了eglfs和linuxfb这两个QPA插件的稳定性。如果你用的是带GPU的板子配合eglfs效果会很好如果用纯轻量级场景linuxfb也足够。这两个插件在5.14.2里都有可靠表现。其三资料可查性高。5.14.2出来的时候正是嵌入式开发社区讨论Qt交叉编译最热闹的阶段中文和英文资料都很多。真遇到问题了Stack Overflow上一搜一大片不会像6.x那样因为版本太新而找不到现成答案。2.3 静态编译与动态编译的取舍细节我这里说的静态编译不是指完全静态——即musl那种把C库也编译进可执行文件的做法。Qt官方对glibc的静态支持并不好强行静态链接glibc会带来大量兼容性问题包括NSSName Service Switch、DNS解析、localtime等会受到影响。所以实际项目中常说的Qt静态交叉编译绝大多数是libQt5*.a的静态链接但C库仍然使用目标板上的glibc。这个决策对我们的部署有直接影响最终的可执行文件虽然不再依赖Qt的so文件但仍依赖目标板上的glibc。这就引出两个要求一是目标板的glibc版本不能比编译时的交叉工具链版本新太多不然可能链接不上或运行时报GLIBC_XX not found二是交叉工具链最好指向目标板的sysroot而不是用它自带的默认sysroot。这两点我在后面流程中会详细讲。为什么不做成真正的全静态呢体验过一次全静态的人都知道glibc的DNS解析会失效程序里如果用QNetworkAccessManager去请求域名直接解析不出来。如果你需要ssl那更麻烦openssl静态链接又是一堆坑。所以宁可让glibc保持动态依赖把Qt彻底静态化这样平衡点最好。3. 编译环境准备与交叉工具链安装3.1 确认目标板信息在动手编译之前务必先确认目标板的几个关键信息。这些信息会直接影响后续的编译参数CPU架构必须是aarch64arm64。可以用uname -m在板子上确认。Linux内核版本影响帧缓冲、DRI/DRM等设备的支持情况一般3.10以上都没问题。glibc版本用ldd --version查看。这个版本不能比交叉工具链的glibc版本新太多。文件系统是否有libstdc.so.6、libgcc_s.so.1等C运行时库虽然Qt是C写的但静态链接Qt后并不需要目标板上额外有Qt的C运行时依赖但如果glibc相关库不全还是会有影响。这块做扎实了后面编译出来的程序才可能在目标板上跑起来。很多人跳过了这一步结果编译时一路顺风部署到板子上跑不起来再回头查环境效率低不少。我刚入手那块板子时先跑了一圈uname和ldd确认是aarch64、4.19内核、glibc 2.28心里就有底了。这个glibc版本对应gcc工具链的版本选8.x的aarch64编译器是比较稳妥的gcc 10以上的编译器加上高版本glibc会产生较高的最低glibc要求可能被目标板拒绝。3.2 在开发机上安装aarch64交叉编译器我开发机用的是Ubuntu 20.04 LTS x86_64。直接在apt源里安装sudo apt update sudo apt install gcc-aarch64-linux-gnu g-aarch64-linux-gnu装完以后验证一下aarch64-linux-gnu-gcc --version如果正常显示版本号说明工具链已经可用。这条命令能编译出aarch64架构的可执行文件用file命令可以看到echo int main(){return 0;} test.c aarch64-linux-gnu-gcc test.c -o test_aarch64 file test_aarch64输出里有ELF 64-bit LSB executable, ARM aarch64就是对的。如果不用apt也可以从Linaro下载预编译的工具链但需要手动设置PATH和SYSROOT。对比下来apt装的省心很多而且sysroot路径很规整在/usr/aarch64-linux-gnu下方便后面配置。3.3 安装Qt静态编译所需的部分依赖库Qt编译时有些三方依赖库虽然不是每次都需要但很多功能模块会用到。常见的几个libts触摸屏库如果你的设备带触摸屏需要编译tslib并安装到sysroot里。libjpeg、libpng、zlib处理图片编解码的基础库Qt的QImage加载jpeg/png会依赖它们。libudev设备热插拔检测Qt的evdev插件会用到。fontconfig、freetype字体渲染依赖嵌入式Qt如果不做文本渲染这一部分也需要考虑。如果全部从源码交叉编译这些库工作量会指数级上升。我是这么处理的只对触摸屏相关的tslib做了源码交叉编译并安装到sysroot其它图片、字体相关的库先用系统自带的arm64版本的运行库再在需要的时候用Qt自带的第三方库源码。别忘了Qt源码的src/3rdparty目录下本身就带了libpng、libjpeg、zlib、freetype等常用库configure时可以控制使用内部库还是外部库所以不用我们额外去编它们。如果你确实需要tslib交叉编译过程大致是这样git clone https://github.com/libts/tslib.git cd tslib ./autogen.sh ./configure --hostaarch64-linux-gnu --prefix/usr/aarch64-linux-gnu CCaarch64-linux-gnu-gcc make -j$(nproc) sudo make install它的作用是把编译好的库文件安装到交叉工具链的sysroot目录/usr/aarch64-linux-gnu下。回头编译Qt时configure会自动在这个目录里查找tslib的头文件和库文件。3.4 配置sysroot时的注意事项sysroot是交叉编译里最容易理解错的东西。简单说它就是目标板根文件系统的一个镜像目录交叉编译器在链接时会把-L路径、头文件查找路径都放进这个目录里。apt装的gcc-aarch64-linux-gnu默认sysroot是/usr/aarch64-linux-gnu它包含了aarch64版本的glibc头文件和库文件、C标准库等基础内容。为什么强调“指向目标板的sysroot”因为如果你的目标板glibc版本比较旧而编译器自带sysroot里的glibc版本很新链接和运行时的行为可能不一致。解决办法是在configure时明确指定--sysroot/usr/aarch64-linux-gnu这样Qt在查找aarch64的库时就去这个目录里找头文件、库文件都统一在这个sysroot之下混乱的可能性会大幅降低。如果你用的是Linaro那种解压即用的工具链它的sysroot就在解压目录下的aarch64-linux-gnu/libc里configure时指过去同样可以。注意sysroot只解决编译时的查找路径不保证运行时的绝对兼容。最稳妥的做法还是让目标板glibc版本与工具链glibc版本保持在一个可接受区间内比如目标板glibc 2.28搭配gcc 8/9的aarch64工具链就非常稳。4. Qt 5.14.2源码获取与 configure 参数详解4.1 获取Qt源码Qt 5.14.2的源码有几种获取方式推荐直接去Qt官网下载源码包或者用apt源里的qtbase源码。我推荐从Qt官方镜像下载离线源码包因为国内访问Qt官网可能慢可以选国内镜像站点比如清华镜像或腾讯镜像。wget https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.14/5.14.2/submodules/qtbase-everywhere-src-5.14.2.tar.xz wget https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.14/5.14.2/submodules/qttools-everywhere-src-5.14.2.tar.xz这里有个取舍完整的Qt源码包把所有模块打包在一起但体积大、configure时间长。对于基本的Widgets程序只编qtbase就够了如果你想用Qt Designer之类的工具再编qttools。qtbase里包含了QtCore、QtGui、QtWidgets、QtNetwork、QtSql等常用模块是基础中的基础。其它模块需要的时候再单独下载编译反正configure都支持后续添加模块。4.2 configure的核心参数解析configure是整个编译流程的灵魂参数没选对后面全白费。我把自己验证过可用的一套参数贴出来逐个说明含义./configure \ -prefix /usr/aarch64-linux-gnu/qt5.14.2 \ -opensource \ -confirm-license \ -release \ -static \ -no-opengl \ -xplatform linux-aarch64-gnu-g \ -no-xcb \ -no-icu \ -no-dbus \ -nomake examples \ -nomake tests \ -no-compile-examples \ -no-pch \ -no-avx \ -no-avx2 \ -linuxfb \ -no-eglfs逐个说-prefix /usr/aarch64-linux-gnu/qt5.14.2指定Qt库的安装路径。我直接放到sysroot下这样后面Qt程序编译时用头文件和库文件都方便。-opensource -confirm-license接受开源协议。-release编译release版。调试版会大很多且对运行库依赖更多。-static这就是静态编译的总开关。编译出来的libQt5Core.a不再生成对应的.so文件。-no-opengl如果你确认目标板没有GPU用不上OpenGL直接关掉。这是为了减少依赖库。如果板子有GPU可以考虑留空并搭配-opengl es2。-xplatform linux-aarch64-gnu-g告诉Qt使用aarch64交叉平台配置。这个文件在qtbase/mkspecs/linux-aarch64-gnu-g目录下对应gcc工具链名称。-no-xcb目标板不需要X11环境关掉xcb插件。很多人在x86环境下用Qt会带上xcb但在嵌入式aarch64下用了反而会引入一大堆X11依赖。需要显示就靠linuxfb或eglfs。-no-icuICUInternational Components for Unicode在国际化与正则处理方面很强但库很大裁剪过的嵌入式系统基本用不到关掉可以省掉大量编译时间。-no-dbusD-Bus是Linux桌面的进程间通信机制对多数嵌入式应用来说没必要关闭。-nomake examples -nomake tests -no-compile-examples不编译示例和测试程序大幅缩短编译时间。-no-pch关闭预编译头。交叉编译下PCH有时会引发奇怪的编译错误关掉求稳。-no-avx -no-avx2禁用AVX指令集。AVX是x86_64的指令集aarch64根本没有Qt configure有时候会检测宿主机的CPU特性这些指令集开关在交叉编译时没必要开启统一关掉避免意外。-linuxfb启用Linux帧缓冲插件。这是无界面/轻量界面的最低成本显示方案。如果目标板需要有GPU加速或DRM渲染则改用-eglfs。可能有人会问为什么这里不写-no-gui那当然不能写我们是要编译带界面的Qt程序GUI模块是必须的。还有几个参数值得了解-qt-libpng -qt-libjpeg -qt-zlib这三个参数的含义是让Qt用自己源码里内置的libpng/libjpeg/zlib。默认情况下configure会自动选择系统库但sysroot里可能缺少对应库建议显式加上以提高确定性。-qt-freetype如果做字体渲染用Qt自带的freetype避免外部依赖问题。-fontconfig可留空。如果你的Qt程序对字体渲染要求高反锯齿、字体回退等可以打开但如果板子上没有fontconfig配置文件运行时反而会麻烦。我这里是关闭的。4.3 三个性能相关参数的考虑-no-sse2 -no-sse3 -no-ssse3 -no-sse4.1 -no-sse4.2这些是针对x86的SIMD指令集开关在aarch64上虽然configure一般不会自动启用但加上更保险保证不会因检测到宿主的CPU特性而生成x86指令。-no-rpath关闭rpath记录。交叉编译时rpath可能会记录宿主机的路径部署到目标板上后反而找错库关闭省心。-no-libproxy -no-libudev -no-libxkbcommon这些库在嵌入式场景多数不需要系统里也未必有aarch64版本省略。configure成功后输出里会出现Qt is now configured for building...这样的结束语。如果没有成功它会报出缺失的依赖库名称或者某个不支持的选项名提示信息比较友好按提示处理即可。4.4 使用qmake影子构建管理编译输出Qt官方建议在源码目录之外建一个build目录然后用绝对或相对路径执行源码目录里的configure脚本。这样能保持源码树的干净编译出的中间文件都放在build目录下想重来或换一套configure参数时直接删build目录就行不需要重新解压源码。我的做法是mkdir build-qt5.14.2 cd build-qt5.14.2 ../qtbase-everywhere-src-5.14.2/configure \ -prefix /usr/aarch64-linux-gnu/qt5.14.2 \ # ... 以上参数省略 -sysroot /usr/aarch64-linux-gnu这里的-sysroot参数就用来给Qt指定系统的根目录。如果不指定Qt会把交叉工具链的默认sysroot当作用户环境目录来用在查找/usr/lib等库文件时容易出错。理论上-sysroot这个参数在Qt 5.14上写法是-sysroot /path它会把这个路径以--sysroot的方式传给编译器等价于在CFLAGS里手动加。这个参数对交叉编译至关重要别漏。4.5 执行make与make installconfigure成功后进入编译环节make -j$(nproc)这一步会持续比较长时间我机器上8核编译qtbase大概花了20多分钟。期间如果出现报错多半是依赖库缺失或某个模块编译失败。可以先记下错误模块名称针对性地排查不必总是从头重新configure。编译完成且无致命错误后执行安装sudo make install安装完成后在/usr/aarch64-linux-gnu/qt5.14.2目录下应能找到lib/libQt5Core.alib/libQt5Gui.alib/libQt5Widgets.aplugins/platforms/libqlinuxfb.a确认这几个文件存在就说明Qt 5.14.2的静态库已经就位了。5. 交叉编译一个Qt程序的完整过程5.1 准备一个最小的Qt Widgets测试程序写一个最简单的带QPushButton的窗口程序#include QApplication #include QPushButton int main(int argc, char *argv[]) { QApplication app(argc, argv); QPushButton button(Hello aarch64 Qt); button.resize(320, 200); button.show(); return app.exec(); }文件命名为main.cpp然后写一个test.pro工程文件QT widgets SOURCES main.cpp TARGET qt_test如果之前已经配置好qmake的PATH直接执行export PATH/usr/aarch64-linux-gnu/qt5.14.2/bin:$PATH qmake test.pro make这里有个关键点所有工具链和Qt库都必须用aarch64版本。qmake是/usr/aarch64-linux-gnu/qt5.14.2/bin/qmakemake时用的编译器来自交叉工具链。在编译输出目录里检查file qt_test正确输出应该是ELF 64-bit LSB executable, ARM aarch64。再用ldd看到的结果通常只包含类似linux-vdso.so.1、libstdc.so.6、libgcc_s.so.1、libc.so.6这几类C库依赖没有Qt的库。如果ldd输出里出现了libQt5Core.so.5之类的内容说明编译出来的不是静态库需要回过去检查一是confirm了-static参数二是make后链接的是.a文件而不是.so文件。5.2 解决qmake在交叉编译时找不到编译器的问题在交叉编译Qt程序过程中最常见的问题是qmake生成Makefile时CC/CXX变量仍然指向宿主gcc。qmake会从/usr/aarch64-linux-gnu/qt5.14.2/mkspecs/qmake.conf里读取编译器配置但有些情况下它读取到的仍然是默认值。我的做法是直接在调用qmake前设置环境变量export CCaarch64-linux-gnu-gcc export CXXaarch64-linux-gnu-g export ARaarch64-linux-gnu-ar export RANLIBaarch64-linux-gnu-ranlib qmake test.pro make这样Makefile里的CC/CXX就会被正确替换。如果你用的是自己下载的Linaro工具链路径把环境变量指到对应路径即可。还有一种可能qmake输出”Could not find qmake configuration file”错误这种情况一般是-prefix路径下缺少mkspecs目录或者-xplatform指定的编译平台名称不匹配。检查一下qtbase/mkspecs下是否存在linux-aarch64-gnu-g目录以及该目录下的qmake.conf是否存在。5.3 在Makefile层面处理链接参数有时候项目比较复杂比如需要链接第三方静态库。这里要注意链接顺序必须把第三方库放在Qt库之后或者用-l参数重复指定。GNU ld的链接规则是从左到右搜索符号如果前一个库用到了后一个库的符号但两者顺序反了就会出现undefinied reference错误。比如我要链接一个libfoo.a它内部用到了QtCore的符号同时也被QtGui用到了那么正确的LDLIBS顺序应该是-lQt5Widgets -lQt5Gui -lQt5Core -lfoo如果顺序错乱即使都加了可能还是链接失败。静态编译下这个问题尤其突出因为动态库会延迟符号解析而静态库必须当场解析完成。5.4 部署到目标板的配置清单编译完成后把qt_test文件用adb、scp或U盘拷贝到目标板。放到目标板后建议检查这几项确认Linux帧缓冲设备存在路径通常为/dev/fb0ls -la /dev/fb0没有这个设备的话linuxfb插件就没有输出目标。确认执行权限chmod x ./qt_test然后运行./qt_test -platform linuxfb如果板子上只有一个显示设备且Qt编译时就只启用了linuxfb不加-platform参数也能正常工作。不过我建议显式指定便于排查问题。看到屏幕上出现一个带按钮的窗口说明Qt程序已经在aarch64上跑起来了。按CtrlC退出程序后可以用dmesg | tail看看有没有报错信息如果有与fb相关的信息要重点检查。提示静态编译程序若运行提示Failed to connect to Mir或Could not connect to display多半是-platform参数未指定且Qt没编译进linuxfb插件。加-platform linuxfb再试。6. 常见问题与实际排查经验6.1 configure阶段就报错的常见原因第一类错误是提示某个依赖库找不到。典型的报错形式是The test for linking against libts continues to fail或者Could not find libjpeg之类。这要看是哪一个库然后回到sysroot里确认是否已安装对应aarch64版本的库和头文件。第二类是编译器相关错误。比如The specified xplatform linux-aarch64-gnu-g does not exist这时去qtbase/mkspecs目录下看是否有这个文件。不同版本的Qt这个mkspecs的命名可能略有差异有的版本叫linux-arm64-gnu-g有的叫linux-aarch64-gnu-g要对上号。第三类是flex/bison相关的错误。Qt源码里的解析器生成需要flex和bison宿主机必须安装。如果编译时提示找不到yacc或lex就是这两个没装sudo apt install flex bison6.2 编译时遇到的典型报错与对策编译报错最烦人但大部分都有迹可循。我整理了一个速查表报错特征原因分析解决办法cannot find -ltstslib库没有安装到sysroot先交叉编译tslib并make install到sysroot或不使用触摸屏则在configure里去掉-tslib参数QMAKE_CC was not foundqmake.conf里的编译器路径不对检查/usr/aarch64-linux-gnu/qt5.14.2/mkspecs/qmake.conf里的CC/CXX配置确保指向交叉编译器undefined reference to glXGetProcAddressARB编译时自动检测到了OpenGL相关的库但配置冲突使用-no-opengl明确关闭OpenGLsysroot error: ... No such file or directory找不到sysroot下的基础库文件检查-sysroot参数指向是否正确以及sysroot内是否有对应的libc.so、libstdc.so等文件cannot find -lGL开源Mesa/OpenGL库缺失在-no-opengl基础上再加-no-gui属于因噎废食不能这样做正确做法是确认不要用OpenGL功能Qt5Core.lib(qglobal.o) ... relocation truncated to fit链接时数据段过大或文件格式问题在32位目标上更多见aarch64上一般是库版本或编译选项不一致重新确认-static与-release参数搭配尽量使用干净源码树重建6.3 运行时打不开显示设备怎么办Qt程序编译好、拷到板子上跑不起来的第一现场往往在显示设备上。我遇到过的几种情况帧缓冲设备不存在或权限不足。ls -la /dev/fb0确认设备存在用root运行试试。有些板子需要往/udev规则里加用户组权限。Qt程序用了默认的QPA插件但插件没被编译进静态库。检查方式是strings qt_test | grep linuxfb看输出里有没有linuxfb相关的字符串。没有的话说明qlinuxfb插件没有链接进可执行文件。解决办法是Qt静态链接时需要手动导入QPA插件。在main.cpp中加入#include QtPlugin Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin)然后重新qmake、make。这是因为静态编译下QPA插件是被链接进可执行文件的不能像动态加载一样按需加载必须显式声明。还有个怪问题程序能跑但没有窗口内容或全是黑屏。多半是目标板的帧缓冲位深与Qt默认配置不匹配。部分开发板的framebuffer是16位或32位Qt的linuxfb插件默认支持后面多数情况但如果帧缓冲的驱动方式特殊需要设置环境变量QT_QPA_FB_BYTES_PER_LINE或使用QT_QPA_FB_NO_ALPHA来适配。如果有细微条纹或颜色错乱还可以考虑调整QT_QPA_FB_ALPHA相关参数。这块需要针对你的具体板卡驱动做判断没有统一答案。6.4 字库与中文显示问题的排查静态编译的另一个隐形坑是中文字体。因为宿主机的字体在目标板上不存在而且Qt静态版往往不自动扫描目标板字体目录程序里的中文就显示成方框或乱码。解决思路是这样的先确认目标板上有中文字体文件通常.ttf或.otf。如果板子上没有可以直接从开发机上拷贝一个开源中文字体到目标板比如文泉驿微米黑或思源黑体的ttf。然后设置Qt的字体路径环境变量export QT_QPA_FONTDIR/usr/share/fonts/或者在代码里全局设置QFont font(/usr/share/fonts/wqy-microhei.ttc); font.setPixelSize(18); QApplication::setFont(font);字体路径和文件必须真实存在于目标板否则设置也白搭。这点很多人会忽略程序在x86上好好的一到板子上中文全变成豆腐块就是没考虑字体文件。6.5 静态编译后体积过大的优化策略静态编译出来的Qt程序体积确实可观。一个简单的Widgets程序跑下来常年15MB以上。如果项目对体积敏感可以考虑去掉用不到的Qt模块。比如工程文件里只写QT core gui widgets不要写greaterThan(QT_MAJOR_VERSION, 4): QT widgets这一行还顺便把network、sql也带进来。Qt的模块直接影响静态链接时把哪些.a文件拉入最终二进制。很多模块即便你没显式#include如果qmake发现你QT 了也会链接相关库。另外Release编译会启用优化和符号剥离对减小体积帮助很大。如果还嫌大可以考虑用-s参数在链接时去掉符号表aarch64-linux-gnu-strip qt_test实测strip后能从16MB降到约12MB上下。要是板子存储空间极其紧张还能考虑用upx压缩静态编译出的可执行文件效果有时可达70%压缩比。但upx对部分嵌入式场景存在解压时需要额外内存的问题——内存足够大的板子无所谓内存只有几百MB的设备要慎重测试。6.6 一个容易被忽视的问题时区与DNS静态链接Qt库后如果你的程序用到网络请求要注意Qt的DNS解析。Qt默认的DNS解析是调用系统的getaddrinfo这依赖glibc。因为我们的静态编译方案并没有屏蔽glibc所以这个问题通常不大。但如果某个版本的Qt编译时带了-qt-sql-sqlite等自定义网络堆栈或者链接了对libnss的引用偶尔也会遇到解析不了域名的现象。猎奇的是有的嵌入式环境没有libnss库getaddrinfo就直接失败。表现为程序里QNetworkAccessManager请求域名一直超时但ping域名是通的。解决办法比较粗暴但有效在目标板rootfs中补齐对应的libnss_dns.so和libnss_files.so放到/lib/aarch64-linux-gnu等目录下并保持链接正确。至于时区静态编译的Qt读的是目标板上的/etc/localtime所以只要目标板时区文件正确QDateTime的本地时间转换就没问题。这个坑不是每次都会触发但一旦触发网上答案很少容易卡住小半天所以特意记在这里。7. 高级操作与经验补充7.1 用pkg-config还是用qmake管理Qt项目在交叉编译Qt程序时两种项目管理方式都有各自的问题。用qmake是最自然的选择因为Qt官方就是围绕qmake设计的模块依赖关系清清爽爽。但写CMakeLists的人也不少尤其是那些从Linux桌面开发转过来的工程师。qmake的好处在于它会自动设置QMAKE_CXXFLAGS和QMAKE_LIBS中与Qt相关的部分。你只需要写QT core gui widgets CONFIG static QMAKE_CXXFLAGS -static但-static加不加要看场景。如果你只是想链接Qt静态库用qmake时在CONFIG里写static即可如果你希望整个程序不依赖C运行时的某些库比如libstdc那要额外处理通常不建议。如果你偏爱CMake那需要注意交叉编译工具链文件。写一个toolchain-aarch64.cmakeset(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g) set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)然后在CMakeLists.txt里加入set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)这个设置是为了让CMake只在指定的sysroot里查找依赖库和头文件防止它不小心使用了宿主机x86库。两种方式我都试过如果你的项目已经用qmake在管理没必要轻易换CMake。qmake对Qt库的静态链接处理得非常顺滑它知道Qt库的准确依赖链用CMake则需要自己维护这些规则有时候Qt库的依赖顺序变了也够折腾一阵子的。7.2 后续扩展集成第三方C/C库的交叉编译如果你的Qt项目要集成OpenCV、Protobuf、sqlite3这样的第三方库那整个交叉编译体系就要再扩展一层。思路是把第三方库在编译时指定--hostaarch64-linux-gnu --prefix/usr/aarch64-linux-gnu安装到sysroot里然后Qt程序编译时通过qmake的LIBS -lopencv_core等方式引用。这里有个麻烦事opencv这种库自身依赖很多每个依赖都要交叉编译一遍。更稳妥的做法是先确认库的CMake或Autotools能正确识别aarch64不识别就看看有没有现成的交叉编译脚本来简化。比如sqlite3这种轻量级库直接configure交叉编译只要几分钟。Opencv这种重库构建时间会到几十分钟甚至几小时而且配置参数极为复杂。遇到这种场景我的一个体会是静态链接的依赖不要无限扩大。能动态链接的目标库尽量保持动态只把Qt这一层做静态。不然依赖链条一长任何一环有问题都要重头排查效率太低。混合方式取长补短部署复杂度增加不多但编译负担显著降低。7.3 为项目写好交叉编译脚本我通常会在项目根目录放一个cross_build.sh脚本把环境变量、configure、make、make install全部串起来。这样换机器、换人接手、后续重新部署都能快速复现避免靠记忆敲命令。一个精简的脚本骨架#!/bin/bash set -e export SYSROOT/usr/aarch64-linux-gnu export PREFIX$SYSROOT export CCaarch64-linux-gnu-gcc export CXXaarch64-linux-gnu-g export ARaarch64-linux-gnu-ar export RANLIBaarch64-linux-gnu-ranlib cd qtbase-everywhere-src-5.14.2 ./configure \ -prefix $PREFIX/qt5.14.2 \ -opensource \ -confirm-license \ -release \ -static \ -sysroot $SYSROOT \ # ... 其余参数略 -nomake examples \ -nomake tests make -j$(nproc) make install cd .. export PATH$PREFIX/qt5.14.2/bin:$PATH cd app qmake make其中set -e很关键任何一步编译或配置失败都会立刻退出不会一路错下去还接着跑排查问题时少踩不少坑。实际项目中可以把外部依赖库的交叉编译也加进来让整个构建流程一键化。7.4 版本记录与工程化建议交叉编译这个事一次搭好了确实能省很长一段时间的心但它也特别吃“版本一致性”。同样的参数Qt 5.14.2能过换成Qt 5.15可能就报错Ubuntu 20.04上能编译的第三方库在Ubuntu 22.04上也可能遇到glibc版本问题。所以强烈建议做工程化时会沉淀两个东西一个是把宿主机版本、交叉编译器版本、Qt源码版本、sysroot里各依赖库的版本记在README或构建脚本注释里。哪天有问题需要回滚直接按版本清单重建环境。另一个是把自己的sysroot目录打包归档。cd /usr/aarch64-linux-gnu tar czf aarch64-sysroot-date.tar.gz .整个目录打包下来可以作为后续项目的构建基础。换开发机时解压即可不用重新编译一大堆第三方库。我用这个方案后面又新建了几个aarch64项目都是直接复用当时的sysroot包和构建脚本从解压到跑通Qt程序总共不到半小时。磨刀不误砍柴工前期多花点时间整理脚本和文档后面省下的时间远不止这些。这块内容很多人不重视但真实项目里“能复现”往往比“能跑通”更值钱。在一次交付后客户换了台电脑要重新编译如果你只丢给他一段history没整理的命令他大概率要重新踩一遍你踩过的坑。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价