尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Qt5.14.2 aarch64静态交叉编译实战:从工具链到开发板部署

Qt5.14.2 aarch64静态交叉编译实战:从工具链到开发板部署 1. 为什么值得折腾Qt5.14.2的aarch64静态交叉编译如果你手上有Orange Pi、树莓派这类aarch64开发板又打算用Qt做界面迟早会撞上交叉编译这堵墙。动态编译的Qt库拷到板子上运行时各种找不到.solibQt5Core.so.5版本对不上板子自带的Qt版本又老又缺模块尤其是serialport这种常用模块经常报unknown module(s) in qt: serialport。静态编译就是把这些麻烦一次性解决掉——把Qt的核心库直接编进你的可执行文件里拷一个二进制过去就能跑不依赖目标板上的任何Qt环境。我这次的目标很明确在x86_64的Ubuntu主机上用aarch64交叉编译工具链把Qt5.14.2编译成静态库最终产出一个能在aarch64开发板上独立运行的可执行文件。选5.14.2而不是5.15.x是因为5.14.2是LTS版本社区资料多坑基本都被踩过了而且它对老一些的GCC工具链兼容性更好。5.15.x虽然新但静态编译时某些模块的依赖关系更复杂新手容易卡住。这篇手册适合谁看如果你已经会用Qt写点小程序但对交叉编译只有模糊概念或者你之前尝试过动态交叉编译但被运行时依赖搞崩溃了那这篇就是给你写的。我会从工具链准备开始一步步走到最终在板子上跑起来中间所有踩过的坑、参数为什么这么设、哪些地方容易翻车都会讲清楚。整个过程大概需要2-4小时取决于你机器的性能编译Qt本身是个体力活。2. 动手前的整体设计与关键决策2.1 为什么选静态编译而不是动态编译动态编译的思路是在主机上交叉编译出Qt的动态库.so文件然后把库和可执行文件一起拷到板子上运行时通过LD_LIBRARY_PATH找到这些库。听起来简单但实际操作中问题很多。板子上的系统库版本可能和你的Qt库不匹配比如板子自带libstdc.so.6版本太老你的Qt库需要更新的C符号运行时直接报version GLIBCXX_3.4.26 not found。再比如你编译时链接了某个第三方库拷过去发现板子上没有又得重新找。静态编译则是把Qt库以.a静态库的形式链接进最终的可执行文件。最终产物就是一个二进制文件不依赖任何Qt的动态库。代价是文件体积大一个简单的窗口程序可能就有20-30MB但换来的是部署极其简单——scp过去chmod x直接跑。对于开发板这种资源有限、系统环境不可控的场景静态编译的省心程度远超那点存储空间。还有一个关键点静态编译时Qt的插件比如平台插件libqlinuxfb.a、libqeglfs.a也需要静态编译进去。动态编译时插件是独立的.so文件运行时通过QPluginLoader加载静态编译时需要用Q_IMPORT_PLUGIN宏显式导入否则运行时会报This application failed to start because no Qt platform plugin could be initialized。这个坑后面会详细讲怎么填。2.2 工具链选型为什么用官方Linaro而不是板子厂商的交叉编译工具链的选择直接决定了后续所有环节的顺利程度。市面上常见的aarch64工具链有几种板子厂商提供的比如Orange Pi官方SDK里的、Linaro官方发布的、以及各种第三方打包的。我的建议是优先用Linaro的gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu或者gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu。为什么不用板子厂商的因为厂商的工具链往往版本较老而且和他们的系统镜像绑定你拿到的可能是一个裁剪过的版本缺少某些头文件或库。Linaro的工具链是通用性的经过大量项目验证兼容性最好。我这次用的是gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnuGCC 10.3支持C17编译Qt5.14.2绰绰有余。工具链的命名规则要搞清楚x86_64是主机架构aarch64是目标架构none表示没有指定操作系统厂商linux-gnu表示目标系统是Linux with GNU libc。前缀aarch64-none-linux-gnu-就是所有工具的通用前缀比如aarch64-none-linux-gnu-gcc、aarch64-none-linux-gnu-g、aarch64-none-linux-gnu-ld。2.3 Qt源码版本与配置参数的整体思路Qt5.14.2的源码包可以从官方下载文件名是qt-everywhere-src-5.14.2.tar.xz大概500多MB。解压后是一个大目录里面包含了Qt的所有子模块。我们不需要全部编译可以通过-skip参数跳过不需要的模块比如qtwebengine这个编译极其耗时且依赖多、qtwebkit、qtdeclarative如果不用QML等。配置参数的核心思路是告诉qmake用哪个交叉编译器、装到哪里、要哪些模块、不要哪些模块、静态编译、以及目标平台的特性。关键参数包括-prefix安装路径、-static静态编译、-release发布版不要调试符号、-opensource开源协议、-confirm-license自动确认、-xplatform目标平台mkspec、-skip跳过模块、-nomake不编译examples和tests以节省时间。-xplatform这个参数特别重要它指定了qtbase/mkspecs/下的哪个目录作为目标平台配置。Qt源码里自带了一些aarch64的mkspec比如linux-aarch64-gnu-g但通常需要根据你的工具链微调。我一般会复制一份出来改而不是直接改原文件这样保持源码干净。3. 核心细节解析与实操要点3.1 主机环境准备与依赖安装主机我用的是Ubuntu 20.04 x86_64这是最稳妥的选择。Ubuntu 22.04也可以但某些依赖包的版本更新可能需要额外处理。不建议用CentOS 7.9做主机虽然它稳定但自带的GCC版本太老4.8.5编译Qt5.14.2时会有C14特性不支持的问题升级GCC又很麻烦。先装一堆编译Qt必需的依赖包。这些包在Ubuntu上用apt安装sudo apt update sudo apt install -y build-essential libgl1-mesa-dev libglu1-mesa-dev \ libxkbcommon-dev libxkbcommon-x11-dev libfontconfig1-dev \ libfreetype6-dev libpng-dev libjpeg-dev libsqlite3-dev \ libssl-dev libicu-dev libxslt1-dev libxml2-dev \ python3 perl git wget flex bison gperf \ libx11-dev libxext-dev libxrender-dev libxcb1-dev \ libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev \ libxcb-icccm4-dev libxcb-sync-dev libxcb-xfixes0-dev \ libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev \ libxcb-xinerama0-dev libxcb-xkb-dev libxcb-glx0-dev这些依赖里libxcb-*系列是编译Qt的X11平台插件用的libfontconfig1-dev和libfreetype6-dev是字体渲染libssl-dev是网络模块的SSL支持libsqlite3-dev是SQLite数据库驱动。如果你不需要X11平台比如板子上只用framebuffer或EGLFS可以少装一些但建议全装上避免编译到一半报缺头文件。注意libicu-dev这个包在Ubuntu 20.04上是66版本Qt5.14.2默认需要ICU 60以上没问题。但如果你在Ubuntu 18.04上编译ICU版本是60也够用。如果报ICU相关的链接错误可以在配置时加-no-icu跳过ICU支持代价是失去国际化文本处理能力。3.2 交叉编译工具链的下载与验证从ARM官方开发者网站下载gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz。下载后解压到一个固定路径比如/opt/toolchain/sudo mkdir -p /opt/toolchain sudo tar -xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/toolchain/解压后目录结构是/opt/toolchain/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/里面的bin/目录下就是所有工具。把这个bin目录加到PATH里export PATH/opt/toolchain/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH验证工具链是否可用aarch64-none-linux-gnu-gcc --version aarch64-none-linux-gnu-g --version应该输出GCC 10.3.1。再写个简单的C程序测试一下echo int main(){return 0;} test.c aarch64-none-linux-gnu-gcc test.c -o test_arm file test_armfile命令应该输出ELF 64-bit LSB executable, ARM aarch64说明工具链工作正常。实操心得工具链的路径不要有空格和中文否则qmake配置时可能出问题。另外如果你之前装过其他版本的交叉工具链确保PATH里当前工具链的路径在最前面避免调用了错误的gcc。3.3 Qt源码下载与目录结构梳理Qt5.14.2的源码包从官方下载站点获取文件名qt-everywhere-src-5.14.2.tar.xz。下载后解压tar -xf qt-everywhere-src-5.14.2.tar.xz cd qt-everywhere-src-5.14.2解压后的目录里qtbase/是最核心的模块包含了QtCore、QtGui、QtWidgets、QtNetwork等基础库。其他子目录如qtdeclarative/QML、qtserialport/串口、qtmultimedia/多媒体等都是独立模块。configure脚本在根目录qtbase/mkspecs/下是各种平台的配置。我们要做的第一件事是创建一个自定义的mkspec。进入qtbase/mkspecs/复制linux-aarch64-gnu-g目录为linux-aarch64-custom-gcd qtbase/mkspecs cp -r linux-aarch64-gnu-g linux-aarch64-custom-g cd linux-aarch64-custom-g然后编辑qmake.conf文件把里面的编译器路径改成你的工具链路径。原始文件里可能是aarch64-linux-gnu-gcc我们要改成aarch64-none-linux-gnu-gcc。完整的qmake.conf内容如下MAKEFILE_GENERATOR UNIX CONFIG incremental QMAKE_INCREMENTAL_STYLE sublib include(../common/linux.conf) include(../common/gcc-base-unix.conf) include(../common/g-unix.conf) QMAKE_CC aarch64-none-linux-gnu-gcc QMAKE_CXX aarch64-none-linux-gnu-g QMAKE_LINK aarch64-none-linux-gnu-g QMAKE_LINK_SHLIB aarch64-none-linux-gnu-g QMAKE_AR aarch64-none-linux-gnu-ar cqs QMAKE_OBJCOPY aarch64-none-linux-gnu-objcopy QMAKE_NM aarch64-none-linux-gnu-nm -P QMAKE_STRIP aarch64-none-linux-gnu-strip load(qt_config)关键点是QMAKE_CC和QMAKE_CXX必须指向正确的交叉编译器。QMAKE_AR后面的cqs是创建静态库的参数c创建、q快速追加、s生成索引这是标准写法。注意有些教程会让你在qmake.conf里加QMAKE_CFLAGS和QMAKE_CXXFLAGS来指定--sysroot但如果你用的是Linaro工具链且目标板系统库和工具链自带的sysroot匹配通常不需要。加了反而可能因为路径问题导致找不到头文件。先不加遇到问题再排查。4. 实操过程与核心环节实现4.1 configure配置参数的完整解读回到Qt源码根目录开始配置。这是我实际使用的配置命令./configure -prefix /opt/qt5.14.2-aarch64-static \ -static -release -opensource -confirm-license \ -xplatform linux-aarch64-custom-g \ -nomake examples -nomake tests \ -skip qtwebengine -skip qtwebkit -skip qtwebkit-examples \ -skip qtdeclarative -skip qtquickcontrols -skip qtquickcontrols2 \ -skip qtscript -skip qtdoc -skip qtwayland \ -no-opengl -no-xcb -no-eglfs -linuxfb \ -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-pcre \ -no-glib -no-icu -no-cups -no-pch \ -sqlite -openssl-linked \ -I /opt/toolchain/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/usr/include \ -L /opt/toolchain/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/usr/lib逐条解释这些参数为什么这么设-prefix /opt/qt5.14.2-aarch64-static安装路径。编译完成后make install会把所有头文件、静态库、qmake等安装到这个目录。这个路径是主机上的路径不是板子上的。-static静态编译生成.a库而不是.so。-release发布版开启优化去掉调试符号。静态编译如果带调试符号文件会大得离谱。-opensource -confirm-license使用开源协议自动确认许可避免交互式提问。-xplatform linux-aarch64-custom-g指定我们刚才创建的mkspec。-nomake examples -nomake tests不编译示例和测试节省大量时间。Qt的示例代码很多编译它们纯属浪费时间。-skip qtwebengine -skip qtwebkit ...跳过不需要的模块。qtwebengine基于Chromium编译需要大量内存和时间而且静态编译极其困难直接跳过。qtdeclarative是QML相关如果你不用QML也可以跳过。qtwayland是Wayland合成器相关一般开发板用不到。-no-opengl -no-xcb -no-eglfs -linuxfb这是平台插件配置。-no-opengl禁用OpenGL支持因为很多aarch64开发板的GPU驱动不完善静态编译OpenGL支持反而容易出问题。-no-xcb禁用X11平台插件板子上通常没有X Server。-no-eglfs禁用EGLFS如果你确定板子支持EGL且你需要GPU加速可以开启。-linuxfb启用Linux Framebuffer平台插件这是最通用的直接往/dev/fb0写像素不需要任何图形系统支持。-qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-pcre使用Qt自带的这些第三方库版本而不是系统库。静态编译时用自带的更可控避免链接到主机上的动态库。-no-glib -no-icu -no-cups -no-pch禁用GLib板子上不一定有、ICU国际化可选、CUPS打印板子用不到、PCH预编译头交叉编译时容易出问题。-sqlite启用SQLite数据库驱动Qt自带的SQLite源码静态编译进去。-openssl-linked链接系统的OpenSSL。这里有个坑静态编译时-openssl-linked需要你提供aarch64版本的OpenSSL静态库。如果你没有可以改成-no-openssl禁用SSL或者用-openssl-runtime在运行时动态加载。我这次为了简单直接用了-no-openssl因为板子上不一定有OpenSSL而且静态链接OpenSSL需要先交叉编译OpenSSL又是一轮折腾。-I和-L指定工具链的sysroot头文件和库路径。这两个路径要根据你的工具链实际位置调整。aarch64-none-linux-gnu/libc/usr/include是工具链自带的libc头文件libc/usr/lib是libc库文件。加上这两个路径是为了让Qt的configure能找到目标平台的标准头文件和库。提示configure过程大概需要5-10分钟会输出大量检查信息。如果中途报错仔细看错误信息通常是某个依赖没装或者路径不对。configure成功后会在最后输出一个摘要列出哪些模块会被编译、哪些被跳过。4.2 编译过程中的资源管理与并行策略configure成功后执行make -j$(nproc)开始编译。-j参数指定并行编译的job数一般设为CPU核心数。比如你的机器是8核就make -j8。如果内存不足8GB建议make -j4因为Qt编译时单个job可能占用1-2GB内存并行太多会触发OOM。编译时间取决于机器性能。我在一台8核16GB的机器上跳过了webengine和declarative大概用了40分钟。如果全模块编译可能要2小时以上。编译过程中可能遇到的错误第一个常见错误是fatal error: bits/libc-header-start.h: No such file or directory。这是因为工具链的sysroot路径没设对或者-I参数指向的路径不对。检查工具链目录下aarch64-none-linux-gnu/libc/usr/include是否存在如果不存在说明你下载的工具链版本目录结构不同需要找到正确的include路径。第二个常见错误是error: numeric_limits is not a member of std。这是C标准库头文件缺失通常是-I路径没包含C标准库的头文件。Linaro工具链的C头文件在aarch64-none-linux-gnu/include/c/10.3.1/下需要确保这个路径也被包含。可以在qmake.conf里加QMAKE_CXXFLAGS -I/opt/toolchain/.../aarch64-none-linux-gnu/include/c/10.3.1。第三个常见错误是链接时cannot find -lstdc。这是因为-L路径没包含C标准库。Linaro工具链的C库在aarch64-none-linux-gnu/lib64/或aarch64-none-linux-gnu/lib/下需要确保-L指向了正确的路径。实操心得编译过程中如果某个模块报错可以先make -k继续编译其他模块最后看哪些模块失败了。有时候某个模块的失败不影响你需要的核心功能。比如qtmultimedia编译失败但你不用多媒体就可以忽略。4.3 安装与静态库的验证编译完成后执行make install。这个过程会把所有编译好的文件拷贝到-prefix指定的目录。安装完成后检查/opt/qt5.14.2-aarch64-static/目录结构ls /opt/qt5.14.2-aarch64-static/ # 应该看到 bin/ include/ lib/ mkspecs/ plugins/ 等目录 ls /opt/qt5.14.2-aarch64-static/lib/ # 应该看到 libQt5Core.a libQt5Gui.a libQt5Widgets.a 等静态库文件验证qmake是否可用/opt/qt5.14.2-aarch64-static/bin/qmake -v应该输出QMake version 3.1和Using Qt version 5.14.2。注意这个qmake是主机上运行的但它生成的Makefile会调用交叉编译器。再验证一下静态库的架构aarch64-none-linux-gnu-ar t /opt/qt5.14.2-aarch64-static/lib/libQt5Core.a | head file /opt/qt5.14.2-aarch64-static/lib/libQt5Core.afile命令应该输出current ar archive里面的.o文件是aarch64架构。4.4 编写测试程序并静态链接写一个最简单的Qt Widgets程序来验证整个工具链// main.cpp #include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Hello aarch64 static Qt!); label.resize(400, 200); label.show(); return app.exec(); }创建一个test.pro文件QT core gui widgets TARGET hello_arm TEMPLATE app SOURCES main.cpp用交叉编译版的qmake生成Makefile/opt/qt5.14.2-aarch64-static/bin/qmake test.pro make编译完成后用file检查产物file hello_arm # 应该输出 ELF 64-bit LSB executable, ARM aarch64再用ldd检查动态依赖aarch64-none-linux-gnu-ldd hello_arm # 应该输出 not a dynamic executable 或者只依赖 libc/libm 等系统库如果ldd显示依赖libQt5Core.so.5说明静态链接没生效检查.pro文件里是否正确使用了静态版的qmake以及-static参数是否在configure时生效。注意静态编译的Qt程序仍然会依赖系统的libc、libm、libpthread等基础库这些是不可避免的。但Qt自身的库全部静态链接进去了板子上不需要安装Qt。5. 常见问题与排查技巧实录5.1 运行时找不到平台插件这是静态编译Qt程序最常见的问题。运行时报错This application failed to start because no Qt platform plugin could be initialized.原因是静态编译时平台插件如linuxfb、eglfs虽然被编译成了静态库但没有被自动链接进可执行文件。Qt的插件机制在静态编译时需要手动导入。解决方法是在main.cpp里加两行#include QtPlugin Q_IMPORT_PLUGIN(QLinuxFbPlatformPlugin)如果你用的是EGLFS就改成Q_IMPORT_PLUGIN(QEglFSPlatformPlugin)。如果你不确定插件类的名字可以在Qt源码里搜QPlatformIntegrationPlugin的子类或者看qtbase/plugins/platforms/下对应插件的源码。加完这两行后还需要在.pro文件里链接对应的插件库QTPLUGIN qlinuxfb或者在main.cpp里用Q_IMPORT_PLUGIN后在.pro里加LIBS -lqlinuxfb。具体库名取决于你的Qt版本和配置可以在/opt/qt5.14.2-aarch64-static/plugins/platforms/下找对应的.a文件。实操心得我一般会在.pro里加QTPLUGIN qlinuxfb qminimal把linuxfb和minimal两个插件都链进去。minimal插件是个空实现不显示任何东西但能保证程序不崩溃方便调试。5.2 串口模块unknown module的解决热词里很多人搜unknown module(s) in qt: serialport这个问题在交叉编译时特别常见。原因是qtserialport是一个独立模块默认可能没被编译或者编译了但qmake找不到。首先确认qtserialport模块是否被编译了。在Qt源码目录下检查qtserialport/是否存在以及configure时是否被skip了。如果你在configure时加了-skip qtserialport那自然没有。去掉这个skip重新configure。如果模块编译了但你的项目.pro里写QT serialport仍然报unknown module检查/opt/qt5.14.2-aarch64-static/mkspecs/modules/下是否有qt_lib_serialport.pri文件。如果没有说明安装时没装上。可以手动从qtserialport/目录下make install。还有一个可能是你的qmake版本不对。如果你用了主机系统的qmake而不是交叉编译版的qmake它自然找不到aarch64的serialport模块。确保用/opt/qt5.14.2-aarch64-static/bin/qmake。5.3 静态链接时的符号冲突与重复定义静态链接时如果多个静态库定义了相同的符号链接器会报multiple definition of错误。这在Qt静态编译中偶尔遇到尤其是当你同时链接了libQt5Core.a和某个第三方库而它们都包含了同一个工具函数。解决方法是加链接器参数-Wl,--allow-multiple-definition允许重复定义链接器会选择第一个遇到的符号。在.pro里加QMAKE_LFLAGS -Wl,--allow-multiple-definition另一个常见错误是undefined reference to dlopen之类的。这是因为静态链接时libdl的符号需要显式链接。在.pro里加LIBS -ldl -lpthread -lrt5.4 部署到开发板的完整流程编译出静态可执行文件后部署到开发板scp hello_arm root192.168.1.100:/root/ ssh root192.168.1.100 chmod x /root/hello_arm运行前确保开发板的framebuffer可用ls /dev/fb0 cat /sys/class/graphics/fb0/virtual_size如果/dev/fb0不存在可能需要在内核里启用framebuffer支持或者加载对应的驱动模块。运行程序时指定平台插件./hello_arm -platform linuxfb如果报错Failed to open framebuffer device检查/dev/fb0的权限或者用fbset命令查看当前framebuffer配置。提示有些开发板的framebuffer默认是关闭的需要在/boot/config.txt或设备树里启用。Orange Pi CM5的话检查/boot/orangepiEnv.txt里的overlays配置确保fb相关的overlay被加载。5.5 常见问题速查表问题现象可能原因解决方法configure报找不到编译器PATH未设置或mkspec里编译器名不对检查qmake.conf里的QMAKE_CC确保工具链在PATH中编译时报bits/libc-header-start.h缺失sysroot路径不对在configure时加-I和-L指向工具链的sysroot链接时报cannot find -lstdcC库路径未包含在qmake.conf里加QMAKE_LFLAGS -L/opt/toolchain/.../lib64运行时报找不到平台插件静态插件未导入在main.cpp加Q_IMPORT_PLUGIN在.pro加QTPLUGINQT serialport报unknown module模块未编译或qmake不对重新configure不skip serialport用交叉版qmake程序在板子上跑起来但黑屏framebuffer未启用或权限不对检查/dev/fb0用-platform linuxfb指定插件静态链接报符号重复定义多个库定义了相同符号加-Wl,--allow-multiple-definition编译Qt时OOM被kill并行job太多内存不足减少make -j的job数或增加swap6. 编译优化与后续扩展的实操建议6.1 裁剪Qt模块以减小体积静态编译的产物体积是个绕不开的问题。一个带Widgets的简单程序静态链接后大概25MB。如果加上Network、SQL、Multimedia可能到50MB以上。对于存储空间有限的开发板裁剪是必要的。裁剪的方法是在configure时用-skip跳过不需要的模块。除了前面提到的webengine、declarative还可以跳过qtmultimedia、qtlocation、qtconnectivity、qtwebsockets等。如果你只用Widgets做本地界面连qtnetwork都可以跳过。另一个裁剪手段是编译时加-optimize-size让GCC优先优化体积而不是速度。在qmake.conf里加QMAKE_CFLAGS_RELEASE -Os QMAKE_CXXFLAGS_RELEASE -Os-Os会在-O2的基础上进一步优化体积代价是性能略有下降。对于界面程序性能通常不是瓶颈体积更重要。还可以用strip命令去掉可执行文件里的符号表aarch64-none-linux-gnu-strip hello_arm这能减少几MB的体积。注意要在编译完成后、部署前执行不要strip静态库否则链接时会出问题。6.2 用ccache加速重复编译如果你需要反复configure和编译Qt比如调整参数、增删模块每次全量编译40分钟很痛苦。用ccache可以缓存编译结果第二次编译同样的文件时直接命中缓存速度提升明显。安装ccachesudo apt install ccache在qmake.conf里把编译器前面加上ccacheQMAKE_CC ccache aarch64-none-linux-gnu-gcc QMAKE_CXX ccache aarch64-none-linux-gnu-gccache的缓存在~/.ccache/下默认大小5GB。可以用ccache -M 20G调大。第一次编译仍然慢但后续修改少量文件重新编译时大部分文件都能命中缓存。实操心得ccache对交叉编译特别有效因为交叉编译的编译命令通常很长ccache能正确识别。我实测第二次全量编译从40分钟降到8分钟。6.3 在开发板上调试静态Qt程序静态编译的程序在板子上调试比动态编译麻烦因为没有动态库的符号信息。如果程序崩溃gdb的backtrace可能只有地址没有函数名。解决方法是在编译时保留调试符号但用objcopy把调试信息分离出来aarch64-none-linux-gnu-objcopy --only-keep-debug hello_arm hello_arm.debug aarch64-none-linux-gnu-strip hello_arm aarch64-none-linux-gnu-objcopy --add-gnu-debuglinkhello_arm.debug hello_arm这样可执行文件体积小了但调试时gdb能通过.debug文件找到符号。把hello_arm.debug也拷到板子上gdb会自动加载。如果板子上没有gdb可以用gdbserver# 板子上 gdbserver :1234 ./hello_arm # 主机上 aarch64-none-linux-gnu-gdb hello_arm (gdb) target remote 192.168.1.100:1234 (gdb) continue这样可以在主机上用交叉版gdb调试板子上的程序符号表在主机上调试体验好很多。6.4 后续扩展交叉编译第三方库并静态链接实际项目里你很可能需要链接第三方库比如boost、openssl、sqlite3、halcon等。这些库也需要交叉编译成aarch64的静态库然后链接进你的程序。以boost为例交叉编译boost的步骤./bootstrap.sh --prefix/opt/boost-aarch64 echo using gcc : arm : aarch64-none-linux-gnu-g ; user-config.jam ./b2 toolsetgcc-arm --user-configuser-config.jam \ linkstatic runtime-linkstatic \ --with-system --with-thread --with-filesystem \ install关键参数是linkstatic runtime-linkstatic生成静态库并静态链接C运行时。编译完成后在.pro里加INCLUDEPATH /opt/boost-aarch64/include LIBS -L/opt/boost-aarch64/lib -lboost_system -lboost_thread -lboost_filesystemopenssl的交叉编译类似但它的configure脚本需要指定OS和编译器./Configure linux-aarch64 \ --cross-compile-prefixaarch64-none-linux-gnu- \ --prefix/opt/openssl-aarch64 \ no-shared no-tests make -j8 make installno-shared生成静态库no-tests跳过测试编译。编译完成后Qt的configure里就可以用-openssl-linked并指定-I和-L路径了。注意第三方库的静态编译顺序很重要。如果A库依赖B库链接时要先写-lA再写-lB否则链接器找不到B库里的符号。在.pro里LIBS的顺序就是链接顺序。6.5 关于Qt版本选择的补充说明热词里有人搜qt5.12.10交叉编译和qt-everywhere-src-5.15.10交叉编译。简单说一下版本差异5.12.10是更老的LTS对老工具链兼容性更好但缺少一些新特性比如QCalendar、改进的QTextDocument等。5.15.10是5.15系列的最后一个版本模块更全但静态编译时qtwebengine的依赖更复杂而且5.15系列对GCC版本要求更高至少GCC 8。如果你只是做简单的串口通信、GPIO控制、本地界面显示5.14.2是最平衡的选择。如果你需要QML和现代OpenGL5.15.10更好但编译难度更大。5.12.10适合工具链特别老GCC 5.x的场景。我个人在Orange Pi CM5上用的是5.14.2因为CM5的官方系统镜像里GCC版本是9.4和5.14.2配合很好。如果你用5.15.10可能需要升级板子系统的GCC或者用更新的工具链。最后再分享一个小技巧编译Qt之前先在一个小模块上测试工具链是否工作。比如只编译qtbase用make module-qtbase而不是全量make。这样如果工具链有问题10分钟就能发现不用等40分钟全量编译完才报错。qtbase编译通过后再全量编译其他模块。这个习惯帮我省了很多时间。
返回列表