嵌入式Qt 5.14.2静态交叉编译完整指南(AArch64实战)
搞嵌入式Qt有几年了最让我记忆犹新的不是某个复杂业务界面而是给一块aarch64板卡做Qt 5.14.2静态交叉编译的那段经历。板子存储不到1GB还得跑一个带图表的控制软件动态库方案光是部署依赖就得小两百兆根本塞不下。没办法只能走“静态编译交叉编译”这条路。这篇文章把流程完整捋一遍从交叉工具链怎么装、依赖怎么解决、Qt源码怎么配置到应用怎么静态链接、最后上板怎么调每一步都给出我实测过的命令和参数顺带把踩过的坑也一并写清楚。适合打算在ARM64设备上部署Qt应用、或者正在被交叉编译折磨的开发者。1. 需求拆解与方案选型为什么非要静态交叉编译不可1.1 静态编译和动态编译差的不是一个后缀名很多刚接触嵌入式Linux的同事对“静态”的理解就是“编译出一个很大的可执行文件”这个方向是对的但背后的取舍远不止体积这么简单。动态编译的可执行文件里只保留对共享库的引用运行时由系统动态加载器去找libQt5Widgets.so.5、libQt5Core.so.5这些文件。优点是多个进程可以共享同一份库代码节省物理内存缺点是目标板上必须精确具备对应版本、对应路径的共享库版本稍微一偏程序起来就是雪花屏或者直接error while loading shared libraries。静态编译则把所有Qt库和第三方库的代码直接链接进ELF文件运行时不依赖任何Qt相关so。为此要付出的代价是体积大、内存里没有共享的可能、链接时对库的依赖顺序非常敏感。但对于一个独立运行、单进程、不叠加其他Qt程序的嵌入式设备来说静态链接的部署优势是压倒性的——一个文件拷过去直接跑不用折腾根文件系统。两种方案的实际区别我列个表对比项静态编译动态编译可执行文件体积大常见30~50MB起步小几MB目标板依赖仅Linux内核和基础C库需要完整Qt运行库及依赖树部署复杂度拷贝单个文件即可需配置LD_LIBRARY_PATH或/etc/ld.so.conf多进程内存共享不支持支持升级维护整体重新编译替换单个so文件静态链接顺序要求高库顺序错就undefined reference低我这次选静态原因很直接设备出厂后没人去维护库依赖固件更新就是替换一个主程序。1.2 为什么偏偏选Qt 5.14.2Qt版本那么多为什么锁定5.14.2我当时的考量有三点一是生态成熟度。5.14.2是2019年底发布的补丁版本修复了一堆QPA平台插件相关的问题同时对嵌入式Linux的设备无关平台抽象层支持得很完整。网上搜“Qt5.14.2”能找到大量论坛帖子、厂商SDK都是基于这一版遇到问题基本都能查到解法。二是授权和下载问题。5.15以后虽然也是不错的选择但开源包下载流程变复杂了很多团队并不想掺和授权判断的事情。5.14.2走开源路线configure时加-opensource -confirm-license一次性通过没有卡顿。三是实际硬件适配。我手里这块板卡的SoC厂商BSP里就给Qt5.14做了输入设备和framebuffer适配自己手动编Qt往往要处理芯片特有的输入事件校准选5.14可以顺带复用厂商的补丁思路。当然5.12 LTS也是一个稳妥的选择但我需要一些5.13/5.14才有的QPA改进所以没有进5.12的序列。1.3 交叉编译的整体思路三条线同时推进交叉编译说白了就是“在A机器上编译出B机器能运行的代码”。这里A泛指x86_64宿主机B是aarch64目标板。要做到这件事三个条件缺一不可工具链是aarch64的。编译器虽然跑在x86上但生成的汇编指令是指向ARM64的。依赖库是aarch64的。Qt静态库本身要用aarch64编出来Qt依赖的zlib、libpng、freetype等系统库也必须是aarch64版本。最终的应用程序二进制是aarch64的并且没有对x86库的引用。这里面最容易翻车的就是第二点。很多人以为给g加上-marcharmv8-a就万事大吉结果configure过了、make到一半报cannot find -lXtst或者链接时一堆未定义引用基本都是“库的架构不对”或“压根没有交叉版静态库”。所以整体流程我是这样安排的先准备好宿主和工具链再解决aarch64依赖库然后用Qt源码编出aarch64静态库最后用这套库去编应用。后面的章节就是沿着这条线展开的。2. 环境准备宿主机装好双架构依赖这是半个重点2.1 宿主机系统与基础软件我使用的宿主机是Ubuntu 20.04 x86_64一块常规开发机。用22.04也可以但要注意个别依赖包名可能有调整。先更新系统并安装编译Qt所需的基础工具sudo apt update sudo apt install build-essential git perl python3 g make \ libglib2.0-dev libfontconfig1-dev libfreetype6-dev \ libxkbcommon-dev libx11-dev \ ninja-buildninja-build现在不是硬性需求但后面如果要配合CMake使用会很方便。Python是Qt构建系统工具链的一部分5.14.2的configure脚本和构建脚本会调用所以提前装好。这里有个小经验如果宿主机上已经装了大量Python 2相关的工具链务必在编译之前确认默认python命令指向哪个版本。Ubuntu 20.04默认只有python3但Qt 5.14的构建脚本在某些场景下仍会查找python或python2。为了避免后期卡壳建议显式指定PYTHONpython3环境变量后面配置那一步我会写出来。2.2 安装aarch64交叉编译工具链Ubuntu官方仓库里可以直接装到aarch64的交叉工具链虽然工具链版本不是最新但对Qt 5.14来说完全够用sudo apt install gcc-aarch64-linux-gnu g-aarch64-linux-gnu装完以后编译器的前缀是aarch64-linux-gnu-也就是说普通gcc的位置上现在有aarch64-linux-gnu-gcc、aarch64-linux-gnu-g、aarch64-linux-gnu-ld等一整套工具。验证一下aarch64-linux-gnu-gcc -v能看到类似这样的输出就说明工具链可用了Target: aarch64-linux-gnu gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)Ubuntu的交叉工具链默认使用/usr/aarch64-linux-gnu作为sysroot根目录里面会通过符号链接挂载multiarch的include和lib目录。这个机制等下配合arm64库安装非常方便等于我们不用手工搭建一整套目标系统根文件系统。2.3 启用arm64 multiarch并安装Qt依赖库这里就是整个环境准备阶段最核心的一步让宿主机能直接apt安装arm64版本的库。Ubuntu的multiarch机制允许系统同时存在不同架构的软件包但需要先显式声明sudo dpkg --add-architecture arm64 sudo apt update这之后就能用:arm64后缀安装aarch64版本的库了。以Qt静态编译常用的依赖为例我装的是这组sudo apt install libz-dev:arm64 \ libpng-dev:arm64 \ libjpeg-dev:arm64 \ libfreetype-dev:arm64 \ libfontconfig1-dev:arm64 \ libglib2.0-dev:arm64 \ libx11-dev:arm64 \ libxkbcommon-dev:arm64 \ libgl1-mesa-dev:arm64 \ libharfbuzz-dev:arm64这里面有几个库Qt静态编译时不是非必需但装上以后可以减少很多意外。比如libgl1-mesa-dev:arm64即使你在configure里禁用OpenGL某些版本的系统头文件检查仍然可能触发对GL库的探测有头文件和库文件在configure就少一个自找麻烦的路径。注意这里装的很多库只有动态版本。Ubuntu的multiarch仓库普遍不提供.a静态库所以如果你非要Qt去静态链接系统的libxcb等库光靠这一步不够。这也影响了我后面的configure参数选择——直接弃用xcb平台插件改用Linux framebuffer平台避免陷入“找静态xcb库”的泥潭。2.4 pkg-config必须指向arm64否则configure会捡到x86的配置交叉编译Qt时最容易忽略的是pkg-config。默认情况下pkg-config只搜索宿主机的/usr/lib/x86_64-linux-gnu/pkgconfig如果Qt的configure探测某个库时调用到pkg-config它可能拿到x86版本的头文件路径最后编排出来的命令不伦不类。我采用的做法是强制pkg-config只认arm64的.pc文件。在编译的整个过程中保持如下环境变量export PKG_CONFIG_LIBDIR/usr/lib/aarch64-linux-gnu/pkgconfig export PKG_CONFIG_SYSROOT_DIR/这里PKG_CONFIG_LIBDIR告诉pkg-config去哪个目录找.pc文件PKG_CONFIG_SYSROOT_DIR/表示.pc文件里的prefix路径就是当前根目录下的路径不需要再加前缀。这个组合在Ubuntu交叉编译中比较顺。为了让两个环境变量在终端重新打开后依然生效建议把它们写进一个env_aarch64_qt.sh每次编译前source一下。后面每次打开新终端我都执行一次以免配置漂移。# env_aarch64_qt.sh export PATH/usr/bin:/bin:/usr/local/bin:$PATH export PKG_CONFIG_LIBDIR/usr/lib/aarch64-linux-gnu/pkgconfig export PKG_CONFIG_SYSROOT_DIR/ export PYTHONpython3还有一个细节在宿主机同时装有x86和arm64库的情况下configure有时候会直接调用系统编译器去编译测试程序导致探测结果完全错误。所以PATH里一定要确保aarch64-linux-gnu-工具链在前面并且configure要显式指定-platform和-xplatform后面实战那一步会看到。3. Qt 5.14.2源码获取与configure配置成败都在这一步3.1 下载源码与初始化构建目录Qt 5.14.2的完整源码是一个qt-everywhere-src-5.14.2.tar.xz包官方archive站点能下到大概500MB左右。有些镜像站也提供比如中科大、清华的镜像里会有qtproject目录。下载后用tar解压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这里要解释一下Qt源码包的结构它包含qtbase、qtdeclarative、qttools、qtsvg等几十个模块而我们实际经常用的核心模块全在qtbase里。configure脚本、qmake、QtCore、QtGui、QtWidgets都在qtbase所以主流程编译时间主要集中在qtbase。建议把编译目录单独放在一个磁盘剩余空间大于30GB的分区上因为完整编译一次Qt 5.14.2占用的磁盘空间非常大。我自己碰到过/目录满了导致make崩溃的尴尬后来直接把源码放到/work/build再软链接到home目录方便管理。3.2 configure参数逐项拆解这是全篇最重要的一个命令。我最终跑通的configure命令如下./configure -release -static -opensource -confirm-license \ -platform linux-g \ -xplatform linux-aarch64-gnu-g \ -prefix /opt/qt5142-aarch64-static \ -no-opengl -no-glib -no-icu \ -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype \ -linuxfb -no-xcb -no-eglfs -no-kms \ -no-feature-cups -no-feature-printdialog \ -skip qtwebengine -skip qt3d -skip qtcanvas3d \ -nomake examples -nomake tests -nomake benchmarks一个参数一个参数来看。-release编译发布版本裁剪掉调试符号和一些调试辅助逻辑生成的可执行文件小很多链接也快。-static这是核心中的核心。它通知Qt的构建系统所有库以静态库形式编译生成.a文件而不是.so文件。这个参数会影响qmake生成Makefile时的链接配置后面应用编译时也会自动带上-static相关选项。-opensource -confirm-license选择开源协议并跳过交互式确认。不写这个configure会停下来问你接受不接受开源协议。-platform linux-g宿主机使用的Qt编译平台。因为我们是在x86_64 Ubuntu上跑configure这个平台用来编译构建工具所以用linux-g即可。-xplatform linux-aarch64-gnu-g目标平台即交叉编译目标。Qt5.14的源码里自带了一份qtbase/mkspecs/linux-aarch64-gnu-g的配置里面默认的编译器就是aarch64-linux-gnu-g跟Ubuntu工具链前缀完全吻合所以不需要自己写mkspec。-prefix /opt/qt5142-aarch64-static安装目录。编译完成后所有头文件、静态库、qmake工具都会装到这里。选用/opt下的路径主要是为了和应用项目的路径隔离也方便多版本切换。-no-opengl -no-glib -no-icu这三个都是降低依赖的开关。-no-opengl对很多嵌入式系统适用因为我们用的是linuxfb不走GPU加速。-no-glib去掉GLib事件循环集成-no-icu去掉Unicode ICU库依赖。ICU在静态编译场景中是个大头有时能省掉好几MB的体积而且在纯文本界面或简单英文界面下完全用不到。如果你的程序需要复杂的Unicode排序规则或某些本地化特性再考虑保留ICU不过代价是交叉编译ICU本身又是一层麻烦。-qt-zlib -qt-libpng -qt-libjpeg -qt-freetype这四个参数让Qt使用源码里自带的第三方库而不是去链接系统的arm64版本。为什么要这样因为Qt bundled的第三库会确保和Qt本身同时编译、同时静态链接从源头规避“系统库没有静态版本”的问题。尤其对于libpng和libjpeg很多平台的交叉环境中只有动态库而Qt又需要它们所以干脆用自带的。-linuxfb -no-xcb平台插件的选择。-linuxfb启用Linux framebuffer QPA插件这是嵌入式Linux最常用的无窗口系统平台方案直接写/dev/fb0。-no-xcb则禁止编译X11的xcb平台插件因为xcb的依赖树非常深想全静态链接几乎要自己把xcb相关的一整套库都交叉编译成.a工作量太大而且Ubuntu multiarch也没提供静态xcb库。对硬件没有X11服务器的板卡来说xcb插件本来也用不上。-no-eglfs -no-kms这两个通常和GPU绑定。如果没有对应的Mali、Adreno等GPU EGL库编译eglfs插件只会引入一堆GL相关的链接错误所以直接禁用。-no-feature-cups -no-feature-printdialog禁用打印支持。嵌入式设备几乎不接打印机关掉能砍掉QtPrintSupport模块里一部分依赖。-skip qtwebengine -skip qt3d -skip qtcanvas3d跳过体积巨大且编译难度高的模块。qtwebengine要动Chromium交叉编译难度指数级上升静态编译更是噩梦qt3d和qtcanvas3d依赖OpenGL我们关了OpenGL它们也用不了。-nomake examples -nomake tests -nomake benchmarks只编译库本身不编译示例和测试省下大量时间。这些参数不是拍脑袋定的而是基于两个原则第一目标板上没有X11、没有GPU、只有一个framebuffer设备第二把所有能砍的依赖都砍掉减少静态链接过程中“缺库、找库、库顺序错”的概率。3.3 编译安装耐心等顺便准备ccacheconfigure通过后会提示你接下来运行make。这里我直接用多线程编译但不要一次性打满所有线程避免内存耗尽。我当时是8核机器用make -j8整个编译过程视机器性能大约需要40分钟到两个小时。期间如果你用top观察会发现编译器确实是aarch64-linux-gnu-g在处理源码这就说明xplatform解析成功。编译完成后执行安装make install这一步会把aarch64静态Qt库、头文件、mkspec和qmake全部复制到/opt/qt5142-aarch64-static目录下。这里有一个实用建议如果你平时要来回编多遍或者还要尝试不同配置给ccache留个位置。Ubuntu上装一个sudo apt install ccache然后在/usr/local/bin/下建几个软链让ccache包住交叉编译器sudo ln -sf /usr/bin/ccache /usr/local/bin/aarch64-linux-gnu-gcc sudo ln -sf /usr/bin/ccache /usr/local/bin/aarch64-linux-gnu-g因为/usr/local/bin通常在/usr/bin之前Qt在检测交叉编译器时会优先找到ccache包装后的命令后续重编不同功能组合时能省一半时间。4. 应用侧静态编译实战从qmake到单个可执行文件4.1 一个最小的Qt Widgets示例工程库已经编好现在来验证能不能用它编出一个aarch64可执行文件。我建了一个非常简单的窗口程序包含一个按钮和一个文本标签用来验证QtWidgets、qtbase的静态链接是否通畅。先写C源文件main.cpp#include QApplication #include QPushButton #include QVBoxLayout #include QWidget int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; window.setWindowTitle(AArch64 Static Qt Test); QVBoxLayout *layout new QVBoxLayout(window); QPushButton *btn new QPushButton(Click Me); QObject::connect(btn, QPushButton::clicked, []() { qDebug() button clicked on aarch64; }); layout-addWidget(btn); window.resize(480, 320); window.show(); return app.exec(); }然后写test.proQT core gui widgets CONFIG release static TARGET qt-test TEMPLATE app SOURCES main.cpp这里CONFIG static是qmake层面的静态链接表态它会通知Qt提供的链接脚本去寻找静态版本的Qt库。同时因为我们要用linuxfb平台我在pro里补充了一个关键项QTPLUGIN qlinuxfb为什么需要QTPLUGIN因为静态编译时Qt的平台插件和各类插件不再由动态加载器在运行时按需加载而是直接作为库的一部分被链接进主程序。QTPLUGIN qlinuxfb告诉qmake需要链接libqlinuxfb.a这个静态插件库。4.2 使用交叉qmake编译编译应用要用我们自己刚刚构建出的qmake而不是系统里可能安装的其他版本。直接用绝对路径调用source /work/env_aarch64_qt.sh export PATH/opt/qt5142-aarch64-static/bin:$PATH cd /work/application /opt/qt5142-aarch64-static/bin/qmake test.pro -o Makefile make -j4这里PATH中最重要的是Qt的bin目录这样make调用的qmake和编译过程中一些工具才会正确。编译完成后马上用file命令验证file qt-test看到类似输出qt-test: ELF 64-bit LSB executable, ARM aarch64, dynamically linked, ...这里注意目标文件虽然是ARM aarch64但仍可能显示dynamically linked因为Linux基础C库libc、libm默认仍然是动态链接的。完全不动态链接需要用到-static-libgcc -static-libstdc以及全静态的libc但嵌入式环境里我们通常保留动态链接的libc因为静态libc会有DNS解析、NSS等不少怪问题。我们的目标只是“不依赖Qt的额外so”不要求100%动静态。验证一下它没有引用任何Qt动态库aarch64-linux-gnu-readelf -d qt-test | grep NEEDED正常输出里应当只有libc.so.6、libstdc.so.6、libm.so.6、libgcc_s.so.1这些基础运行时库而不会有libQt5Widgets.so.5等。这说明Qt相关代码全部被静态吞进去了。4.3 关于strip和体积控制静态编译的可执行文件体积感人我编完的测试程序有37MB。没strip之前可能超过50MB因为里面包含了完整的符号表。对嵌入式部署来说体积直接影响Flash占用和加载速度所以发布前执行aarch64-linux-gnu-strip qt-teststrip完体积能降到28MB左右。如果要进一步压缩可以在pro里加上编译选项微调QMAKE_CXXFLAGS_RELEASE -Os -s-Os让编译器以优化体积为目标-s在链接时直接去掉所有符号表。这两者结合起来能再挤出几MB。但是别指望压到10MB以内Qt Widgets静态链接的体量摆在那里除非你改用Qt Quick/QML再外加裁剪模块否则这个数量级很正常。4.4 拷贝到目标板运行把qt-test拷贝到目标板任意目录确保权限可执行chmod x qt-test ./qt-test板子上如果有/dev/fb0且当前用户有权限访问framebuffer就能看到窗口。按下按钮终端会打印出qDebug内容。这说明Qt应用在无窗口系统的Linux framebuffer上已经跑通了。如果板子上没有framebuffer设备也可以加QT_QPA_PLATFORMoffscreen环境变量让它跑在离屏模式用于验证程序逻辑但不能看到真实UI。还有一种做法是配一个linuxfb:fb/dev/fb0参数指定设备路径。5. 交叉编译排雷手记我实际踩过的坑5.1 configure卡在“Unknown option”或“Missing Python”这类问题通常不是选项本身不存在而是configure脚本对系统环境过于敏感。Qt 5.14的configure是perl脚本和shell脚本混合实现它需要python命令存在。Ubuntu 20.04默认只有python3且许多版本里没有提供python软链。于是configure在探测python时返回空直接报错。解决方法很简单在环境变量里显式声明PYTHONpython3或者建一个软链sudo ln -s /usr/bin/python3 /usr/bin/python。我第一次就是被这个卡了十分钟后来加了环境变量瞬间通过。5.2 make时报“cannot find -lGL”出现这个错误说明Qt里某些模块仍然在尝试链接OpenGL库通常是Qt SVG或Qt 3D等模块的问题也可能是configure的-no-opengl没有生效又或者是某个依赖模块依然引用了GL库。我的处理分两步第一确认命令行里-no-opengl真的传进去了——有时候configure的缓存会干扰建议每次修改configure参数前删除源码目录下的.qmake.super等缓存文件。第二用grep搜索构建失败时对应的Makefile里是否有-lGL字样找到以后手工把那个LIBS变量里的-lGL删掉。更彻底的做法是确保系统里有libgl1-mesa-dev:arm64这样即使误引用也能链接上。5.3 静态链接应用时“undefined reference toqt_plugin_query_metadata”这个错误很有代表性说明程序虽然链接了静态平台插件但qmake的插件元数据注册机制没有生效。根本原因是QTPLUGIN qlinuxfb只在qmake的某些配置环境下会自动生成plugin import语句但如果你用了CMake或者手写了复杂的构建脚本这个自动机制就失灵了。解决办法是在源码里强制声明导入插件#include QtPlugin Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin)Q_IMPORT_PLUGIN宏会把插件的初始化代码和元数据静态注册到当前编译单元。加了这行以后再编译链接错误就会消失。如果你的程序需要其他平台插件比如qoffscreen还要同时加Q_IMPORT_PLUGIN(QOffscreenIntegrationPlugin)。5.4 目标板运行时报“Failed to open /dev/fb0”或“Permission denied”这是linuxfb最常见的运行问题本质取决于目标板的framebuffer设备是否存在、当前用户是否有权限访问。可以用下面几个步骤排查检查是否有设备节点ls -l /dev/fb*有些板卡在启动时没有创建fb0需要检查内核配置和设备树。检查权限chmod 666 /dev/fb0或者把运行用户加入video组。开发阶段用chmod 666最快上线时建议走udev规则。如果板子支持HDMI或LCD屏必须确保内核驱动已经把显示输出从终端切到了fb设备否则可能出现黑屏但进程不报错的情况。5.5 链接时出现一堆“undefined reference”且都与libpng/libjpeg有关静态链接时库之间的依赖是单向的。比如QtGui.a里有png解码代码引用png_read_image之类的符号而libpng.a在命令行的顺序如果排在QtGui之前链接器扫到QtGui的时候还不知道后面会有一个libpng.a能补上它的符号于是直接报undefined reference。常规解法是调整LIBS顺序让被依赖的库排在依赖它的库后面。Qt的qmake在静态模式下已经自动做了大量排序但遇到自定义的第三方静态库时还是会乱。这时候最简单的兜底方案是给链接器加-Wl,--start-group和-Wl,--end-group让链接器反复扫描这组库QMAKE_LFLAGS -Wl,--start-group -lpng -ljpeg -lz -Wl,--end-group--start-group会告诉链接器这组库之间可以来回引用解决循环依赖和顺序敏感问题。注意这只是一个手段不能无脑堆所有库进去否则链接时间会变长。5.6 configure明明设置了-prefix但安装到别的目录这类问题通常是环境变量INSTALL_PREFIX或者旧配置缓存干扰。如果之前在同一目录跑过不同配置的configureconfig.cache和.qmake.cache会残留导致新参数不生效。所以每次调整configure参数前最稳的做法是重新解压源码包或者至少执行make distcleandistclean会清理所有生成的Makefile和缓存文件。我后来养成了一个习惯一个源码目录只跑一种配置换参数就解压到新目录从根上杜绝缓存串扰。5.7 静态编译的Qt库太大能不能裁剪能。Qt支持功能裁剪通过-no-feature-xxx可以把不需要的特性从编译中排除。5.14.2里特性名可以查qtbase/src/corelib/global/qfeatures.h或者直接翻configure -feature-* -list-features。比如不需要老式的QTextCodec、不需要某些网络模块都能减少一些体积。但功能裁剪要谨慎减多了会让你的代码被迫兼容“残缺版Qt”。我一般只裁剪掉打印对话框、CUPS、少数不常用模块主要靠-no-opengl -no-icu和-skip qtwebengine来控体积这样既省空间又不至于在写业务功能时被Qt限制。5.8 编译速度慢内存不足Qt源码编译时大量模板实例化内存和CPU都是瓶颈。如果make -j8导致内存耗尽可以降为make -j4或-j2。还可以利用ccache大幅加速后续重编前提是前面已经用ccache包装了交叉编译器。另一个影响因素是磁盘I/O尤其是NTFS挂载盘或网络文件系统上编译会慢到怀疑人生。把源码解压到本地固态盘的分区上编译时间能缩短一半。6. 这套方案可以扩展到哪些场景搞定Qt 5.14.2的aarch64静态交叉编译后我顺手把同样的思想复用了几个地方。一个是nginx的aarch64移植。nginx本身依赖pcre和zlib把工具链换成aarch64-linux-gnu、源码里通过./configure --with-ccaarch64-linux-gnu-gcc就能编出ARM64版可执行文件。核心逻辑和Qt交叉编译完全一样工具链先行、依赖库用arm64版本、注意configure检测。有了Qt这块硬骨头的经验移植nginx只花了不到半小时。另一个是给第三方C/C库做交叉编译。我在板卡上用到了sqlite和openssl都是源码configure后指定CCaarch64-linux-gnu-gcc和--hostaarch64-linux-gnu编译出静态库后塞进Qt应用里。整体思路都可以复用总结起来就是四个步骤选对工具链、选对sysroot、理清依赖关系、控制configure选项。最后说一点个人体会静态交叉编译本质上是个“把复杂留给自己把简单留给部署”的过程。编译期确实辛苦了点configure参数要反复试、库顺序要一遍遍调但换来的是目标板上近乎零依赖的部署体验。后来这块板子量产升级固件就是替换一个二进制文件从来没有因为缺库、版本冲突在客户现场翻过车。如果你也正在折腾Qt的交叉编译照着这份手册踩一遍大概率能省下好几个通宵。

相关新闻

Cadence DSP算子开发实战:从架构认知到性能优化

Cadence DSP算子开发实战:从架构认知到性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 11:19:05 阅读更多 →
ES2026 + Wasm + WebNN:前端工程师的端侧AI实战指南

ES2026 + Wasm + WebNN:前端工程师的端侧AI实战指南

1. 这不是预测,是正在发生的现场直播:JS 正在重构前端工程师的生存地图你打开控制台,敲下navigator.ai?.deviceMemory,回车——页面没报错,返回了8。你愣了一下,这不是 Chrome Canary 里那个被灰掉的实验性…

2026/9/19 11:19:05 阅读更多 →
ValidX集成Maven与Gradle:依赖配置、镜像加速与踩坑排查指南

ValidX集成Maven与Gradle:依赖配置、镜像加速与踩坑排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 11:19:05 阅读更多 →

最新新闻

行测数量关系备考攻略:模型识别、提速技巧与数据化复盘

行测数量关系备考攻略:模型识别、提速技巧与数据化复盘

简介:这是一份面向公务员考试行测备考者的数量关系专题复习资料,聚焦长期困扰考生的数量关系模块,帮助从畏难弃题转向主动突破。资源以PDF形式呈现,总文件数1个,压缩包大小约84KB,内容精炼便携,…

2026/9/19 15:03:43 阅读更多 →
ComfyUI Impact-Subpack 安装与 UltralyticsDetectorProvider 节点修复全指南

ComfyUI Impact-Subpack 安装与 UltralyticsDetectorProvider 节点修复全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 15:03:43 阅读更多 →
ChatGPT Voice 总打断、error-bank.md 不更新?TaoToken 这样改 Codex 的 Base URL

ChatGPT Voice 总打断、error-bank.md 不更新?TaoToken 这样改 Codex 的 Base URL

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 15:03:43 阅读更多 →
866页IEC 60601-1 2020:用文本层检索将标准变为设计清单

866页IEC 60601-1 2020:用文本层检索将标准变为设计清单

简介:IEC 60601-1:2020(合并版)是国际电工委员会发布的最新版医用电气设备基础安全标准,完整替代早期独立版本,面向医疗器械硬件设计、安规测试、质量体系及注册法规人员。压缩包内仅含1个PDF文件,大小14.4…

2026/9/19 15:03:43 阅读更多 →
SpringBoot医院住院系统设计与MySQL数据一致性实践

SpringBoot医院住院系统设计与MySQL数据一致性实践

简介:本资源是一份完整的Spring Boot医院住院管理系统毕业设计论文,面向计算机类本科毕业生及Java Web开发初学者,聚焦医疗信息化场景下的B/S架构系统实现。论文全文约1.59万字,涵盖绪论、技术选型(Spring Boot、Vue、…

2026/9/19 15:03:43 阅读更多 →
PyTorch高频波动率预测:特征工程与LSTM+Attention实战

PyTorch高频波动率预测:特征工程与LSTM+Attention实战

简介:本资源是一份面向量化研究初学者与金融科技从业者的专题文档,聚焦PyTorch在股票价格波动率预测中的特征工程设计,覆盖高频交易背景、模型选型、统计与技术指标提取、特征选择与转换、训练优化及回测实践等完整知识链条。全包仅1个PDF文件…

2026/9/19 15:02:43 阅读更多 →

日新闻

BP神经网络时序预测:滑窗长度与多窗口平均策略

BP神经网络时序预测:滑窗长度与多窗口平均策略

简介:面向机器学习、深度学习与数据建模学习者的一份完整研究文献,聚焦BP神经网络在农业产量预测中的应用。文档以1980—2018年全国棉花产量为样本,系统讲解数据归一化处理、激活函数原理、多层神经网络结构搭建及训练流程,展示敏…

2026/9/19 0:00:30 阅读更多 →
Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

上个月调一个Deformable DETR模型,在单卡上要跑将近两天。第二天早上我下意识打开终端翻日志,发现loss从凌晨两点就开始往上爬,一路从0.8涨到1.35,整整六个小时没人发现。那六个小时的训练不仅白跑,还霸占着卡——等于…

2026/9/19 0:00:30 阅读更多 →
OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南 【免费下载链接】opencloud 🌤️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign. 项目地址: htt…

2026/9/19 0:00:30 阅读更多 →

周新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/19 3:59:36 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/19 3:53:08 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/19 4:02:43 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/16 22:31:27 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/15 21:39:18 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/16 22:32:59 阅读更多 →