ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

aarch64 平台 Qt5.14.2 静态交叉编译实战指南

aarch64 平台 Qt5.14.2 静态交叉编译实战指南 1. 为什么要在 aarch64 上折腾 Qt5.14.2 静态编译如果你手头有一块 Orange Pi CM5、树莓派 4B 或者任何一块 aarch64 架构的开发板又恰好想在上面跑一个带图形界面的 Qt 程序那你大概率绕不开“交叉编译”这四个字。而“静态交叉编译”更是把难度直接拉满——它意味着你编译出来的可执行文件不依赖目标板上的任何 Qt 动态库拷过去就能跑省去了在板子上装一堆 .so 文件的麻烦。我第一次接触这个需求是因为一个工业现场的项目。设备是一台基于 aarch64 的工控板系统是精简过的 CentOS 7.9 aarch64上面连 Qt 的运行库都没有。客户要求程序开机自启界面响应要快而且不允许在现场设备上安装任何额外的开发环境。这种情况下动态链接的方案直接出局——你没法保证目标板上 Qt 库的版本和编译时完全一致更没法保证那些依赖库都齐全。静态编译成了唯一的选择。Qt5.14.2 这个版本在嵌入式领域用得特别多它属于 LTS 版本稳定性好对 aarch64 的支持也成熟。但问题在于Qt 官方并没有提供 aarch64 的静态编译版本你得自己从源码开始用交叉编译工具链一步步构建。这个过程涉及工具链选型、源码配置、依赖库处理、编译参数调优等一系列环节任何一个地方出错都可能导致编译失败或者运行时段错误。这篇文章面向的是有一定 Linux 基础、了解交叉编译概念、但可能没有完整走过一遍 Qt 静态编译流程的开发者。我会从工具链的准备开始一直讲到最终在目标板上验证运行把每个环节的“为什么”和“怎么做”都讲清楚。文章里提到的所有命令和配置都是我在实际项目中反复验证过的你可以直接参考使用。注意静态编译 Qt 会显著增加可执行文件的体积一个简单的窗口程序可能达到 20MB 以上。如果你的目标板存储空间非常紧张需要提前评估这个代价是否可接受。2. 交叉编译工具链的选型与验证2.1 为什么工具链的选择比你想的重要很多人觉得交叉编译工具链随便下一个就行反正都是 gcc。但实际上工具链的选择直接决定了你后续编译 Qt 时会不会遇到各种奇怪的链接错误。aarch64 架构的工具链主要有几个来源Linaro 的 GCC 工具链、ARM 官方维护的 GNU Toolchain、以及各大芯片厂商提供的定制版本。我推荐使用 ARM 官方维护的 GNU Toolchain for aarch64具体版本选择 10.3-2021.07 或者更新的版本。原因很简单这个工具链的 glibc 版本比较新对 C17 的支持完善而且经过大量项目的验证兼容性最好。Linaro 的工具链虽然也不错但有些版本在链接 Qt 的 WebEngine 模块时会出现问题而我们做静态编译通常不需要 WebEngine所以影响不大但为了省心还是选 ARM 官方的。工具链的命名规则一般是gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu其中x86_64是你的宿主机架构aarch64是目标架构none-linux-gnu表示使用标准 glibc。下载下来之后解压到/opt目录下然后把它加到 PATH 里。# 解压工具链到 /opt 目录 sudo tar -xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/ # 添加到环境变量建议写进 ~/.bashrc export PATH/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH export CROSS_COMPILEaarch64-none-linux-gnu- export ARCHaarch64验证工具链是否可用直接运行aarch64-none-linux-gnu-gcc -v如果能看到版本信息就说明配置成功了。这里有个小细节工具链的 sysroot 路径需要确认一下一般在aarch64-none-linux-gnu/libc目录下。这个 sysroot 包含了目标系统的头文件和库文件Qt 编译时会用到。2.2 宿主机环境的准备宿主机我建议用 Ubuntu 20.04 或者 22.04不要用 CentOS 7 做宿主机。虽然 CentOS 7.9 在目标板上很常见但作为编译宿主机它的 gcc 版本太老很多 Qt 的构建脚本需要 Python3.6 以上的版本CentOS 7 默认的 Python 是 2.7会带来很多额外的麻烦。Ubuntu 20.04 的软件源里 Python3 和必要的构建工具都很齐全省事。需要安装的依赖包包括build-essential、python3、python3-pip、libgl1-mesa-dev、libglu1-mesa-dev、libxkbcommon-dev、libfontconfig1-dev、libfreetype6-dev、libx11-dev、libxext-dev、libxfixes-dev、libxi-dev、libxrender-dev、libxcb1-dev、libxcb-xfixes0-dev、libxcb-shape0-dev、libxcb-randr0-dev、libxcb-render-util0-dev、libxcb-xinerama0-dev、libxcb-icccm4-dev、libxcb-image0-dev、libxcb-keysyms1-dev、libxcb-util0-dev、libxcb-xkb-dev、libxkbcommon-x11-dev、libssl-dev、libxcursor-dev、libxcomposite-dev、libxdamage-dev、libxrandr-dev、libxtst-dev、libasound2-dev、libpulse-dev、libcups2-dev、libdrm-dev、libgstreamer1.0-dev、libgstreamer-plugins-base1.0-dev。这些包看起来多但都是 Qt 的 xcb 平台插件和多媒体模块需要的。如果你确定不需要某些模块可以在 configure 阶段用-skip参数跳过对应的依赖也可以不装。但第一次编译我建议全装上避免因为缺依赖导致 configure 失败排查起来很浪费时间。sudo apt update sudo apt install -y build-essential python3 python3-pip \ libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev \ libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev \ libxfixes-dev libxi-dev libxrender-dev libxcb1-dev \ libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev \ libxcb-render-util0-dev libxcb-xinerama0-dev libxcb-icccm4-dev \ libxcb-image0-dev libxcb-keysyms1-dev libxcb-util0-dev \ libxcb-xkb-dev libxkbcommon-x11-dev libssl-dev libxcursor-dev \ libxcomposite-dev libxdamage-dev libxrandr-dev libxtst-dev \ libasound2-dev libpulse-dev libcups2-dev libdrm-dev \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev2.3 目标板 sysroot 的提取交叉编译最关键的一步是准备目标板的 sysroot。sysroot 简单说就是目标板上的头文件和库文件的集合交叉编译工具链需要它来找到正确的依赖。有两种方式获取 sysroot一种是从目标板上直接拷贝/usr/include和/usr/lib目录另一种是用工具链自带的 sysroot。我强烈建议从目标板上拷贝因为工具链自带的 sysroot 是通用的可能缺少你目标板上特有的库。具体操作是把目标板的根文件系统挂载到宿主机上或者用rsync把/usr/include、/usr/lib、/lib这三个目录同步到宿主机的某个路径下比如/opt/aarch64-sysroot。# 假设目标板可以通过 SSH 访问 mkdir -p /opt/aarch64-sysroot rsync -avz root目标板IP:/usr/include /opt/aarch64-sysroot/usr/ rsync -avz root目标板IP:/usr/lib /opt/aarch64-sysroot/usr/ rsync -avz root目标板IP:/lib /opt/aarch64-sysroot/拷贝完成后需要修正 sysroot 里的符号链接。因为很多库文件是软链接直接拷贝会变成断链。可以用symlinks -cr命令来修复或者手动检查关键库的链接关系。这一步不做的话编译时会出现“找不到库文件”的错误但库文件明明就在那里很迷惑人。提示如果你的目标板是 CentOS 7.9 aarch64它的 glibc 版本是 2.17而 ARM 工具链 10.3 自带的 glibc 是 2.33。版本不匹配会导致链接错误。解决办法是使用目标板的 sysroot 来编译并且在 configure 时指定-sysroot /opt/aarch64-sysroot让编译器优先使用目标板的库。3. Qt5.14.2 源码的配置与裁剪策略3.1 源码下载与目录结构Qt5.14.2 的源码包可以从 Qt 官方下载站获取文件名是qt-everywhere-src-5.14.2.tar.xz。这个包包含了 Qt 的所有模块源码解压后大概有 5GB 左右。如果你只需要核心模块可以在 configure 时用-skip参数跳过不需要的模块比如-skip qtwebengine、-skip qtwebkit等能节省大量编译时间。wget https://download.qt.io/archive/qt/5.14/5.14.2/single/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 等基础库。qtserialport是串口模块如果你做嵌入式开发这个模块几乎必用。qtdeclarative是 QML 相关的模块如果不用 QML 可以跳过。qtmultimedia是多媒体模块依赖 GStreamer编译起来比较麻烦不需要的话建议跳过。3.2 configure 参数详解configure 是整个过程里最关键的环节参数配错了后面全白搭。Qt5.14.2 的 configure 脚本支持大量的参数我挑最核心的几个讲。首先是-prefix这个参数指定安装路径。静态编译的 Qt 不需要安装到系统目录指定一个独立的路径就行比如/opt/qt5.14.2-aarch64-static。这样后续在 Qt Creator 里配置 Kit 的时候直接指向这个路径即可。-static是静态编译的核心开关加上它之后编译出来的库都是.a文件链接时会把 Qt 的代码直接打包进可执行文件。-release表示编译发布版本去掉调试符号减小体积。-opensource和-confirm-license是许可证确认必须加上否则 configure 会交互式询问。-xplatform指定目标平台的 mkspec 文件。Qt 源码里自带了很多 mkspec但 aarch64 的静态编译需要自己写一个。在qtbase/mkspecs/devices/目录下创建一个linux-aarch64-gnu-g目录里面放一个qmake.conf文件。# qtbase/mkspecs/devices/linux-aarch64-gnu-g/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) QT_QPA_DEFAULT_PLATFORM linuxfb QMAKE_CFLAGS_RELEASE -O2 -marcharmv8-a QMAKE_CXXFLAGS_RELEASE -O2 -marcharmv8-a 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)这个文件里QT_QPA_DEFAULT_PLATFORM指定默认的平台插件。嵌入式设备通常用linuxfb或者eglfs。如果你的板子有 GPU 并且用 OpenGL ES选eglfs如果只是简单的 framebuffer选linuxfb。这个选择会影响 Qt 启动时加载哪个平台插件选错了程序会报“This application failed to start because no Qt platform plugin could be initialized”。完整的 configure 命令大概是这样./configure -prefix /opt/qt5.14.2-aarch64-static \ -static -release -opensource -confirm-license \ -xplatform linux-aarch64-gnu-g \ -sysroot /opt/aarch64-sysroot \ -no-opengl -no-eglfs -linuxfb \ -no-xcb -no-glib -no-dbus -no-icu \ -no-feature-accessibility \ -skip qtwebengine -skip qtwebkit -skip qtdeclarative \ -skip qtmultimedia -skip qtsensors -skip qtconnectivity \ -nomake examples -nomake tests \ -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype \ -qt-pcre -qt-harfbuzz \ -I /opt/aarch64-sysroot/usr/include \ -L /opt/aarch64-sysroot/usr/lib这里有几个参数需要解释一下。-no-opengl和-no-eglfs是因为很多嵌入式板子的 GPU 驱动不完善用 OpenGL 反而容易出问题直接用 linuxfb 更稳定。-no-xcb是因为目标板没有 X11 环境不需要 xcb 平台插件。-no-glib、-no-dbus、-no-icu是为了减少依赖glib 和 dbus 在嵌入式环境里往往不需要icu 是国际化支持不需要多语言的话也可以去掉。-qt-zlib、-qt-libpng这些参数表示使用 Qt 自带的第三方库而不是系统库。静态编译时强烈建议用 Qt 自带的因为系统库的版本和编译选项不可控容易出问题。-nomake examples -nomake tests跳过示例和测试能节省大量编译时间。3.3 裁剪模块的取舍逻辑哪些模块该跳过哪些必须保留这个需要根据你的实际项目来定。我列一个常见的模块取舍表模块名是否保留理由qtbase必须保留核心模块包含 QtCore、QtGui、QtWidgetsqtserialport建议保留嵌入式串口通信常用体积小qtdeclarative按需用 QML 就保留不用就跳过编译耗时长qtmultimedia按需依赖 GStreamer编译复杂不用就跳过qtwebengine建议跳过体积巨大静态编译几乎不可行qtwebkit建议跳过已废弃不建议使用qtsvg按需需要显示 SVG 图片就保留qtimageformats按需需要额外图片格式如 TIFF、WEBP就保留qtsensors建议跳过嵌入式很少用qtconnectivity建议跳过蓝牙/NFC 相关很少用我个人的经验是第一次编译时尽量多保留一些模块等编译成功、程序跑起来之后再根据实际用到的功能做二次裁剪。因为静态编译的调试成本很高如果因为缺了某个模块导致链接错误排查起来很费劲。宁可多编译几个模块也不要为了省时间而跳过可能用到的模块。注意-skip qtdeclarative会跳过 QML 模块如果你的项目用到了 QML千万不要加这个参数。另外Qt5.14.2 的qtdeclarative模块在静态编译时有一些已知的 bug可能需要打补丁才能编译通过。如果必须用 QML建议考虑 Qt5.15 或者 Qt6。4. 编译过程中的典型报错与排查路径4.1 链接阶段找不到符号编译 Qt 静态库本身通常比较顺利真正容易出问题的是链接阶段。最常见的错误是“undefined reference toxxx”这通常是因为某个依赖库没有正确链接。比如编译qtserialport时如果 sysroot 里没有libudev就会报找不到udev_new等符号。排查这类问题的第一步是确认 sysroot 里有没有对应的库文件。用find /opt/aarch64-sysroot -name libudev*查一下如果没有就需要从目标板拷贝或者用交叉编译工具链重新编译一个。第二步是检查 configure 的输出看看有没有“XXX not found”的警告。configure 阶段会检测各种依赖如果某个依赖没找到它会自动禁用对应的功能但有时候禁用不彻底编译到一半才报错。还有一种情况是符号版本不匹配。比如目标板的 glibc 是 2.17而工具链的 glibc 是 2.33链接时会报“versionGLIBC_2.33 not found”。解决办法是在 configure 时加上-sysroot /opt/aarch64-sysroot并且确保-I和-L 参数指向的是目标板的 sysroot而不是工具链自带的。4.2 configure 阶段的 Python 报错Qt5.14.2 的 configure 脚本依赖 Python3如果宿主机上 Python3 的版本太低或者缺少某些模块会报错。常见的错误是“ModuleNotFoundError: No module named importlib.metadata”这是因为 Python3.7 及以下版本没有这个模块。解决办法是升级 Python3 到 3.8 以上或者手动安装importlib-metadata包。pip3 install importlib-metadata另一个常见错误是“Python version too old”Qt5.14.2 要求 Python3.6 以上。Ubuntu 20.04 默认的 Python3 是 3.8没问题。如果你用的是 CentOS 7 做宿主机默认 Python 是 2.7需要手动安装 Python3 并设置python3命令指向正确的版本。4.3 编译到一半内存不足Qt 的编译非常吃内存尤其是qtdeclarative和qtwebengine模块单个编译单元可能占用 2GB 以上的内存。如果你的宿主机内存小于 8GB编译到一半可能会被 OOM Killer 杀掉。解决办法有两个一是增加 swap 空间二是用-j参数控制并行编译的任务数。# 创建 8GB 的 swap 文件 sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 编译时根据内存大小调整并行数 make -j4 # 8GB 内存建议 -j416GB 可以 -j8我实测下来16GB 内存的机器用-j8编译整个 Qt5.14.2跳过 WebEngine大概需要 40 分钟到 1 小时。如果内存只有 8GB建议用-j2或者-j4虽然慢一点但不会中途崩溃。4.4 静态编译后的插件加载问题静态编译的 Qt 程序在运行时平台插件如 linuxfb需要显式导入否则会报“no Qt platform plugin could be initialized”。解决办法是在main.cpp里加上Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin)宏或者在.pro文件里加上QTPLUGIN qlinuxfb。// main.cpp #include QApplication #include QPushButton #include QtPlugin Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin) int main(int argc, char *argv[]) { QApplication app(argc, argv); QPushButton button(Hello Qt Static!); button.show(); return app.exec(); }.pro文件里需要加上对应的配置QT core gui widgets CONFIG static QTPLUGIN qlinuxfb qjpeg qgifQTPLUGIN里列出的插件会被静态链接进可执行文件。如果你用到了 JPEG 图片显示就需要加上qjpeg用到了 GIF就加上qgif。漏掉的话程序运行时加载图片会失败但不会报错只是图片显示不出来这个问题很隐蔽。5. 在目标板上验证与部署5.1 可执行文件的传输与运行编译完成后在qtbase/bin目录下会生成qmake工具在qtbase/lib目录下会生成libQt5Core.a、libQt5Gui.a、libQt5Widgets.a等静态库。用这个 qmake 编译你的项目生成的可执行文件就是静态链接的。# 用静态编译的 qmake 编译项目 /opt/qt5.14.2-aarch64-static/bin/qmake your_project.pro make -j4 # 查看可执行文件的依赖 file your_app # 输出应该是ELF 64-bit LSB executable, ARM aarch64, statically linked如果file命令的输出显示“statically linked”说明静态编译成功了。如果显示“dynamically linked”说明还有动态库依赖需要用ldd命令查看具体依赖了哪些库然后在.pro文件里检查是不是漏掉了CONFIG static。把可执行文件拷贝到目标板上直接运行。如果程序启动后没有界面或者报“no Qt platform plugin”错误检查一下Q_IMPORT_PLUGIN宏有没有加对。如果程序启动后界面显示不正常比如颜色错乱或者分辨率不对可能是 framebuffer 的配置问题需要检查/dev/fb0设备是否存在以及linuxfb插件的参数是否正确。# 指定 framebuffer 设备和分辨率 ./your_app -platform linuxfb:fb/dev/fb0:size1024x6005.2 静态编译程序的体积优化静态编译的程序体积通常比较大一个简单的窗口程序可能就有 20MB 以上。如果目标板的存储空间有限可以用strip命令去掉符号表能减小 30% 左右的体积。aarch64-none-linux-gnu-strip your_app还可以在编译时加上-Os参数优化体积而不是-O2。在.pro文件里加上QMAKE_CFLAGS_RELEASE -Os QMAKE_CXXFLAGS_RELEASE -Os另外Qt 的很多功能可以通过-no-feature-xxx参数在 configure 阶段禁用比如-no-feature-accessibility禁用无障碍支持-no-feature-printdialog禁用打印对话框。这些功能在嵌入式环境里通常用不到禁用后能显著减小体积。5.3 常见运行时问题与解决静态编译的程序在目标板上运行时最常见的问题是字体显示异常。因为静态编译时字体配置可能没有正确打包程序启动后中文显示为方块。解决办法是在目标板上安装中文字体或者在程序里用QFontDatabase::addApplicationFont加载字体文件。// 加载中文字体 int fontId QFontDatabase::addApplicationFont(/usr/share/fonts/wqy-zenhei.ttc); if (fontId ! -1) { QStringList fontFamilies QFontDatabase::applicationFontFamilies(fontId); QFont font(fontFamilies.at(0)); app.setFont(font); }另一个常见问题是触摸屏校准。如果你的设备用触摸屏需要配置tslib或者evdev触摸插件。在linuxfb平台下可以通过-plugin evdevtouch:/dev/input/eventX参数指定触摸设备。./your_app -platform linuxfb:fb/dev/fb0 -plugin evdevtouch:/dev/input/event1如果触摸坐标不对需要设置环境变量QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS来校准export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS/dev/input/event1:rotate1806. 从踩坑到顺手我的实操心得6.1 工具链和 sysroot 的版本匹配是最大的坑我前两次尝试静态编译 Qt5.14.2 都失败了原因都是工具链的 glibc 版本和目标板的 glibc 版本不匹配。第一次用的是 Linaro 7.5 的工具链glibc 是 2.25目标板是 CentOS 7.9glibc 是 2.17链接时报了一堆“version not found”。第二次换了 ARM 官方 10.3 的工具链glibc 是 2.33还是报错。最后是把目标板的 sysroot 完整拷贝过来并且在 configure 时显式指定-sysroot才解决了问题。这个坑的教训是不要迷信工具链自带的 sysroot一定要用目标板的 sysroot。而且拷贝 sysroot 时要注意符号链接的修复否则会出现“库文件存在但链接器找不到”的诡异问题。6.2 configure 参数宁多勿少第一次编译时我为了省时间跳过了很多模块结果程序编译时发现用到了QtSerialPort又得重新 configure 和编译。后来我学乖了第一次编译时把可能用到的模块都保留等程序跑通了再做二次裁剪。虽然第一次编译时间长一点但总体效率更高。另外-no-xcb这个参数要慎用。如果你的目标板将来可能接 HDMI 显示器并且跑 X11那就不能加这个参数。我有个项目一开始用 linuxfb后来客户要求接显示器结果发现 Qt 没有编译 xcb 插件只能重新编译。6.3 静态编译的调试技巧静态编译的程序调试起来比动态链接的麻烦因为符号表被链接进了可执行文件gdb加载起来比较慢。我的做法是在开发阶段先用动态链接编译调试通过后再改成静态编译。这样调试效率高而且能提前发现依赖问题。如果静态编译的程序在目标板上崩溃了可以用gdb在目标板上调试或者用coredump分析。不过目标板上通常没有gdb需要交叉编译一个gdbserver放到目标板上然后在宿主机上用aarch64-none-linux-gnu-gdb远程调试。# 目标板上启动 gdbserver gdbserver :2345 ./your_app # 宿主机上连接 aarch64-none-linux-gnu-gdb ./your_app (gdb) target remote 目标板IP:2345 (gdb) continue6.4 关于 Qt5.14.2 和 Qt5.15 的选择Qt5.14.2 是 LTS 版本但 Qt5.15 在静态编译方面做了一些改进比如对 aarch64 的支持更好configure 脚本的 Python 依赖也更宽松。如果你的项目不强制要求 5.14.2我建议直接用 Qt5.15.10 或者更高的 5.15.x 版本。热词里提到的qt-everywhere-src-5.15.10就是一个不错的选择编译过程基本一致但踩坑会少一些。不过要注意Qt5.15 之后Qt 官方对开源版本的二进制包政策有变化源码编译不受影响但如果你需要预编译的离线安装包可能需要自己从源码构建。热词里提到的“qt5.14.2离线安装包下载”和“qt离线安装包下载5.14”这些通常是指官方提供的预编译版本但那些都是 x86_64 架构的aarch64 架构没有现成的离线包必须自己编译。6.5 串口模块的静态编译注意事项热词里多次出现“unknown module in qt:serialport”和“unknown module(s) in qt: serialport”这个问题在静态编译时特别常见。原因是qtserialport模块在 configure 时可能被自动跳过了或者编译出来的静态库没有被正确链接。解决办法是在 configure 时明确加上-qt-serialport参数确保串口模块被编译。然后在.pro文件里加上QT serialport并且在main.cpp里加上Q_IMPORT_PLUGIN宏如果需要串口插件的话。如果还是报“unknown module”检查一下qtbase/lib目录下有没有libQt5SerialPort.a文件没有的话说明模块没编译成功需要重新 configure。# 确认串口模块是否编译 ls /opt/qt5.14.2-aarch64-static/lib/libQt5SerialPort.a如果这个文件不存在回到 Qt 源码目录重新运行 configure加上-qt-serialport然后make module-qtserialport单独编译这个模块不用全量重新编译。7. 编译完成后的目录结构与部署清单编译安装完成后/opt/qt5.14.2-aarch64-static目录下会有几个关键子目录。bin目录里有qmake、moc、uic、rcc等工具这些是编译 Qt 项目时需要的。lib目录里是所有的静态库文件.a后缀。include目录里是头文件。plugins目录里是平台插件、图片格式插件等静态编译时这些插件会被链接进可执行文件但目录结构还是会保留。部署到目标板时只需要把可执行文件拷贝过去就行不需要拷贝 Qt 的库文件。但有几个例外如果程序用到了 OpenSSL需要把libssl.so和libcrypto.so拷贝到目标板如果用到了字体需要把字体文件拷贝过去如果用到了 GStreamer需要确保目标板上有对应的插件。我一般会做一个部署清单每次部署前对照检查检查项命令预期结果可执行文件架构file your_appARM aarch64, statically linked动态库依赖ldd your_appnot a dynamic executable平台插件strings your_app | grep linuxfb包含 linuxfb 相关字符串字体文件ls /usr/share/fonts/存在中文字体Framebufferls /dev/fb0设备存在触摸设备ls /dev/input/event*触摸设备存在这个清单看起来简单但每次部署前过一遍能避免 90% 的运行时问题。特别是ldd那项如果输出不是“not a dynamic executable”说明还有动态库依赖需要回到.pro文件检查CONFIG static有没有生效。8. 后续扩展与版本升级的考虑静态编译 Qt5.14.2 的流程走通之后升级到 Qt5.15 或者 Qt6 的迁移成本其实不高主要的差异在 configure 参数和 mkspec 文件上。Qt6 的构建系统改成了 CMakeconfigure 脚本的用法有较大变化但核心思路是一样的准备工具链、准备 sysroot、配置裁剪参数、编译安装。如果你将来需要支持更多的 aarch64 设备比如 Orange Pi CM5、Rockchip RK3588 等建议把 mkspec 文件和 configure 参数做成脚本方便复用。我自己的做法是写一个build_qt.sh脚本把工具链路径、sysroot 路径、configure 参数都做成变量换设备时只需要改几个变量就行。#!/bin/bash # build_qt.sh TOOLCHAIN_PATH/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu SYSROOT_PATH/opt/aarch64-sysroot QT_INSTALL_PATH/opt/qt5.14.2-aarch64-static QT_SOURCE_PATH~/qt-everywhere-src-5.14.2 export PATH$TOOLCHAIN_PATH/bin:$PATH cd $QT_SOURCE_PATH ./configure -prefix $QT_INSTALL_PATH \ -static -release -opensource -confirm-license \ -xplatform linux-aarch64-gnu-g \ -sysroot $SYSROOT_PATH \ -no-opengl -linuxfb -no-xcb \ -nomake examples -nomake tests \ -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype \ -qt-pcre -qt-harfbuzz \ -I $SYSROOT_PATH/usr/include \ -L $SYSROOT_PATH/usr/lib make -j$(nproc) make install这个脚本我用了好几个项目每次换板子只需要改SYSROOT_PATH和QT_INSTALL_PATH两个变量。脚本里没有加-skip参数因为不同项目需要的模块不一样我习惯在脚本外面手动加。如果你确定不需要某些模块可以在./configure后面追加-skip qtdeclarative -skip qtmultimedia等参数。最后再分享一个小技巧编译完成后用qmake -query命令可以查看 Qt 的配置信息确认静态编译是否生效。如果输出里有QT_CONFIG包含static说明配置正确。这个命令在排查“为什么程序还是动态链接”的问题时特别有用。/opt/qt5.14.2-aarch64-static/bin/qmake -query # 输出中应该有 QT_CONFIG 包含 static
返回列表