
1. 为什么要自己编译Qt 5.15.121.1 这个版本的特殊性Qt 5.15.12是Qt 5.15 LTS分支的最后一个补丁版本发布于2023年上半年左右。自5.15.2之后开源版用户就无法从官方渠道获取预编译的二进制安装包了这意味着你想用5.15系列的新补丁只能走源码编译这一条路。5.15系列官方支持周期已经结束但对于大量存量项目来说从5.12或更早版本迁移到5.15.12仍然是最稳妥的升级路线——API变化小、生态兼容性好。我自己最早接触Qt编译是因为接手了一个老项目的维护工作项目基于5.12开发但客户那边提出新需求需要用到新特性又不想直接跳到Qt 6——毕竟QWidget那套东西在Qt 6里虽然兼容但第三方库和内部组件的适配成本摆在那里。当时搜了一圈发现5.15.12就是整个5.15分支最稳定的版本bug修复最全面于是决定动手编译。1.2 哪些场景必须源码编译不是所有场景都需要源码编译但你一旦遇到下面这几种情况预编译包根本没法用静态链接需求官方早就不提供静态库版本了想要单个exe扔出去就能跑只能自己编。模块裁剪只需要Widgets、Network、Core这几个模块想把QWebEngine、QML这类大块头去掉自己编译才能控制产物大小。交叉编译要给ARM板子或树莓派编译Qt主机的二进制包根本用不上。定制补丁比如自己改了Qt源码或者要集成第三方的Qt插件也必须走源码编译。调试版本想深入调试Qt内部逻辑需要带调试符号的完整版本。这篇文章整理的就是我实际编译过程中总结的完整流程和踩坑记录覆盖Windows和Linux两个平台默认使用MinGW以及GCC工具链静态和动态两种编译模式都会提到。如果你也在和5.15系列死磕这篇备忘应该能帮你省掉一大部分弯路。2. 编译前的环境准备2.1 Windows平台Perl、Python与编译器版本对齐Windows上编译Qt最忌讳的就是依赖版本随意搭配。configure.bat时代它对环境的检查虽然比以前宽松但Perl、Python、编译器的版本不对齐经常会在configure阶段报出让人摸不着头脑的错误。我推荐的环境组合是PerlActivePerl 5.28 或 Strawberry Perl 5.32都可以注意一定要装64位版本和后面编译出来的Qt位数保持一致。Python3.7到3.9之间都可以Qt 5.15的构建脚本对Python 3.10支持不太好会在生成qml相关代码时报语法错误。编译器MinGW 8.1.0是经典搭配我自己长期用这个版本。如果你用MSVC那建议VS2019VS2022理论上能编但需要修改个别构建脚本才能顺利通过。Ninja建议装上比nmake和mingw32-make快不少配合多核编译优势明显。CMake虽然有configure.bat入口但最终构建走的是qmake生成的Makefile。这里特别提醒一点MinGW的路径中不要有空格C:\Qt\Tools\mingw810_64这种就合适C:\Program Files\MinGW这种绝对会出问题。我当初图省事装到Program Files目录下结果编译到一半报各种路径错误最后只能重装白白浪费了半天时间。2.2 Linux平台依赖包安装与基础环境检查Linux平台相对清爽一些Ubuntu 20.04/22.04系统直接通过apt安装依赖即可。sudo apt update sudo apt install build-essential perl python3 ninja-build libgl1-mesa-dev \ libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxfixes-dev \ libxi-dev libxrender-dev libxcb1-dev libx11-xcb-dev libxcb-glx0-dev \ libxcb-util0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev \ libxcb-icccm4-dev libxcb-randr0-dev libxcb-render-util0-dev \ libxcb-xinerama0-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev这里有个容易忽略的点libxkbcommon-x11-dev这个包很容易漏装缺少它会导致Qt的xcb插件无法编译最后运行程序时直接报could not load the xcb plugin。别问我是怎么知道的GitHub上有一堆Qt交叉编译issue都是这个原因。检查环境的命令也很简单gcc --version perl -v python3 --version ninja --version确认这些工具都能正常输出版本号之后就进入下一步源码包的准备。3. 配置阶段configure参数的选择逻辑3.1 常见配置组合与适用场景获取源码包之后先解压到工作目录比如D:\qt-everywhere-src-5.15.12或者~/qt-everywhere-src-5.15.12。源码包可以从Qt官方仓库的archive地址下载。进入源码根目录后Windows平台使用cmd或者PowerShell执行configure.bat -prefix D:\Qt\5.15.12\mingw81_64 -opensource -confirm-license -release -shared -nomake examples -nomake tests -skip qtwebengine -skip qt3d -skip qtscript -submodules qtbase,qtdeclarative,qtmultimedia,qtsvg,qttools,qttranslationsLinux平台则执行对应的shell脚本./configure -prefix ~/Qt/5.15.12/gcc_64 -opensource -confirm-license \ -release -shared -nomake examples -nomake tests \ -skip qtwebengine -skip qt3d -skip qtscript \ -submodules qtbase,qtdeclarative,qtmultimedia,qtsvg,qttools,qttranslations几个关键参数我解释一下选择逻辑-skip qtwebengine几乎是必选的QWebEngine需要单独下载Chromium源码体积巨大不说编译时间动辄三四个小时大部分桌面应用根本用不到它。类似的还有qt3d、qtgamepad这些模块不需要就直接skip。-nomake examples和-nomake tests能节省大量编译时间两个加起来能少编将近四分之一的内容。如果你是第一次编Qt建议先把这个选项加上反正后面想看示例代码直接对着源码看就行。-submodules不是必须的但如果你已经明确知道自己只需要哪几个模块限定submodule能把编译时间压缩到二十分钟以内。不加这个参数默认会编译你没skip的那些模块。3.2 静态编译与OpenSSL的坑静态编译是很多Windows桌面开发者的刚需关键在于configure参数的选择configure.bat -prefix D:\Qt\5.15.12\static -opensource -confirm-license -release -static -nomake examples -nomake tests -skip qtwebengine -skip qt3d -skip qtscript -openssl-linked -I D:\OpenSSL-Win64\include -L D:\OpenSSL-Win64\lib这里第一个大坑就出现了OpenSSL不是Qt源码包自带的。Qt的某些网络模块比如QSslSocket依赖OpenSSL但只提供了头文件和动态链接的接口实际运行时的DLL是要自己准备的。没有OpenSSL直接静态编译的后果是编译能通过但QSslSocket相关功能全部不可用QSslSocket::supportsSsl()会返回false。对于HTTPS请求来说这可能是致命的。我有一次接手一个金融项目的维护跑起来之后所有HTTPS请求都报证书错误查了半天发现就是编译时没带OpenSSL导致的。所以正经的静态编译流程应该是先下载OpenSSLWindows下可以从openssl官方或者slproweb下载已经编好的版本。解压OpenSSL到固定目录比如D:\OpenSSL-Win64。再运行上面的configure命令通过-I和-L指定头文件和库文件位置。如果你的模块确实用不到SSL也可以添加-no-openssl这个参数显式关闭SSL支持这样就不会有残留的模糊问题。但大多数情况下我建议还是把OpenSSL编进去鬼知道哪天客户就要对接个HTTPS接口。另外需要注意静态编译出来的程序在Windows下如果要开启SSL需要把openssl.cnf这个文件放到可执行文件旁边或者system32目录下。这个配置文件在OpenSSL安装包里有很多人编完发现程序运行时报OPENSSL_conf相关错误就是这个文件缺失。4. 编译执行与资源优化4.1 多线程编译的正确打开方式Windows下用MinGW环境多线程编译有两条路直接用mingw32-make或者在configure时加上-make ninja参数。我建议优先用ninja原因是它的并行度控制更好输出更友好失败时错误定位也更精准。configure配置完成后执行mingw32-make -j8如果你的电脑是16核甚至更高可以适当加大-j的值但不建议超过核心数的两倍。编译Qt的过程主要吃内存-j数值开太高会触发OOM操作系统开始swap之后整个编译就废了。我实测16G内存的机器-j8和-j16差距不明显但-j16更容易卡死所以保守一点更稳妥。Linux平台同理用make的时候直接make -j$(nproc)nproc会自动获取核心数大多数场景下直接用这个值就行。如果你的Linux服务器内存不高建议手动限制一下并发数比如make -j4。4.2 编译产物与磁盘空间管理Qt源码编译的磁盘占用需要提前规划好。完整编译默认配置主要包括qtbase、qtdeclarative等核心模块的情况下编译中间文件加最终安装文件至少需要15G的剩余空间。如果你什么都编连qtwebengine都编那建议留出40G。这里有个很多人都会忽略的点编译好之后make install或ninja install之前的中间文件可以清理掉尤其是obj文件和临时生成的moc文件。具体做法是make clean但注意这个操作会同时清掉编译产物只保留configure生成的头文件和Makefile。所以建议流程是先make install安装到你指定的prefix目录确认没问题后再make clean清理中间文件。装好的Qt放在prefix目录里跟源码目录是分离的清理源码目录不会影响已安装的Qt。Windows下还有一个很隐蔽的问题路径长度限制。Qt源码完整解压后目录层级很深有些文件路径长度接近260字符的上限。如果你把源码放在C:\Users\你的名字\Desktop\qt-everywhere-src-5.15.12这种长路径下编译时经常碰到莫名其妙的“file not found”错误实际上就是路径长度超标。解决方案一个是开启Windows 10/11的LongPathsEnabled注册表项另一个简单粗暴的办法直接把源码放到根目录下比如C:\qt-src-5.15.12。不要放在桌面或者文档目录下。4.3 环境变量与链接配置编译安装完成后还需要把Qt的bin目录加入PATH环境变量同时设置QTDIR。Windows下编辑环境变量追加这样几项QTDIRD:\Qt\5.15.12\mingw81_64 PATH...;D:\Qt\5.15.12\mingw81_64\bin;C:\Qt\Tools\mingw810_64\binLinux下写入~/.bashrcexport QTDIR~/Qt/5.15.12/gcc_64 export PATH$QTDIR/bin:$PATH export LD_LIBRARY_PATH$QTDIR/lib:$LD_LIBRARY_PATH配置好之后写个最简单的小程序测试一下#include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Qt 5.15.12 build test); label.resize(240, 80); label.show(); return app.exec(); }用qmake编译qmake test.pro make -j4然后运行生成的程序如果能正常弹窗这就算基本编译成功了。5. 常见错误与排查技巧实录5.1 Windows平台典型错误错误1编译时找不到moc或者相关工具这个问题的典型表现是编译自己的程序时报错Could not find a command line interface for moc。原因是环境变量里没有把Qt的bin目录路径加进去或者多个版本的Qt混装在PATH里导致qmake和moc版本不匹配。排查方法很简单打开cmd输入qmake -v看显示的Qt版本是否是你刚编好的那个。如果不是检查PATH变量的顺序确保你刚装的Qt bin目录排在前面。错误2链接阶段大量unresolved external symbol这个错误通常是因为不匹配导致的最常见的就是你在MSVC的qt环境里用了MinGW编出来的lib。Qt 5.15的库文件命名清晰libQt5Core.a是MinGW格式Qt5Core.lib是MSVC格式别搞混了。错误3编译qml相关模块时报qmlcachegen错误这个问题一般出现在Python环境不干净的情况下。qmlcachegen会在编译QML模块时去生成缓存文件如果Python版本太新或者有多个Python版本同时存在它的脚本生成过程就会报语法错误。解决办法是把PATH环境变量清理干净确保只有一个Python版本然后重新configure再make。不要试图单独重编qmlcachegen那只会浪费更多时间。错误4make: *** [qmake] Error 2这个报错信息很笼统实际原因往往是源码目录里有中文或空格路径或者是用了管理员权限运行的cmd导致编译脚本访问权限异常。建议始终使用纯英文的路径并且用普通用户权限跑configure和make。5.2 Linux平台典型错误错误1GL/gl.h: No such file or directory缺少OpenGL开发头文件安装对应依赖即可sudo apt install mesa-common-dev libgl1-mesa-dev libglu1-mesa-dev错误2xcb相关错误缺少xcb相关依赖库会导致Qt运行环境无法启动报错QXcbConnection: Could not connect to display或者could not load the xcb plugin。把我在2.2节列出来的依赖包装全就行重点检查libxcb-util0-dev、libxcb-icccm4-dev、libxkbcommon-x11-dev这几个冷门包。错误3perl: warning: Setting locale failed这个警告一般不影响编译但如果你实在看着烦可以忽略。它只是Perl检测到系统locale配置不完整和Qt源码编译本身无关。错误4编译过程中内存不足导致进程被杀这个最直接Linux下编译Qt期间如果内存被耗尽会直接触发OOM Killer编译进程直接被系统强杀没有任何报错。这时候看dmesg能发现大量Out of memory: Kill process的日志。解决办法是限制编译并发数例如make -j2如果你要编的是arm板的交叉编译也建议把并发数控制在4以内交叉编译器本身就比较敏感容易出内存相关的问题。6. 交叉编译的一点补充6.1 嵌入式场景下的交叉编译如果你的目标平台是ARM板子界面程序需要跑在Linux嵌入式环境下交叉编译Qt是一个绕不开的课题。我这边用树莓派做过一次完整的Qt交叉编译简单分享一下思路。核心思路是使用-xplatform参数指定目标平台配置比如树莓派3B可以用./configure -prefix /usr/local/qt5 -opensource -confirm-license \ -release -shared -nomake examples -nomake tests \ -xplatform linux-arm-gnueabi-g \ -device linux-rasp-pi3-g \ -device-option CROSS_COMPILEarm-linux-gnueabihf-这里-device和-device-option CROSS_COMPILE两个参数负责指定目标芯片架构如果你换了其他板子需要到qtbase/mkspecs/device-config/linux*目录下查对应的conf文件。交叉编译的关键在于sysroot的配置你得先交叉编译出目标系统上用的zlib、libpng、fontconfig等库或者从板子系统里拷贝出来然后通过-sysroot参数告诉Qt去哪里找这些依赖。这一步是最容易踩坑的部分,很多人编译失败都是因为sysroot里的依赖库头文件和库文件对不上。交叉编译之前强烈建议先在PC上用目标架构的编译器写个小程序测试确保交叉编译工具链本身是可用的再回来跑Qt的configure。不然的话configure出的错误不知道是工具链的问题还是Qt配置的问题排查起来会非常痛苦。7. 编译完成后的验证清单编译安装完成后不要急着去写业务代码先跑一遍完整验证确保这个Qt真的能用检查版本号运行qmake -v确认输出的是5.15.12。检查模块完整性打开安装目录下的lib文件夹观察是否包含了你需要的模块文件比如libQt5Widgets、libQt5Network、libQt5Qml等。检查编译用的qt.conf在bin目录下找到qt.conf文件确认Prefix字段指向的内容正确。编译一个带有窗口的程序验证基础GUI功能。测试一下网络请求连接一个HTTPS地址确认SSL功能正常。如果你编的是静态版本验证一下生成的exe能否在没有Qt环境的机器上独立运行。这个清单看似简单但每次都有人忽略其中的一两项。我就遇到过一个人编了四五个小时最后发现忘了检查SSL发布到客户机器上才暴露问题那时候再去重新编译Qt工期压力就非常大了。8. 一些实操体会与建议在我把Qt 5.15.12编译流程反复跑过好几遍之后总结出几条值得写下来的经验第一记录配置命令。把configure那行命令保存到一个文本文件里每次编译的时候直接复制避免忘记参数或者打错字母。官方文档给的参数虽然没错但并不是每个都适合你的场景。第二编译日志必须留存。编译Qt过程中的输出信息量很大如果中途报错直接看command窗口会刷屏看不完。把日志输出成文件失败的时候从log里定位错误make -j8 21 | tee build.log第三按需裁剪模块比优化编译器参数更有用。Qt的模块结构决定了编译时间主要浪费在那些你用不到的模块上。精简模块是缩减编译时间最有效的手段。第四别舍不得跳过WebEngine。Qt WebEngine模块编译需要单独下载约3GB的Chromium源码但桌面应用中使用WebEngine的场景其实非常有限。除非你有明确需求不然果断跳过。写这篇备忘的初衷就是把自己在Qt 5.15.12编译这条路上踩过的坑系统梳理一遍。从环境准备到参数配置从多线程编译到交叉编译每个环节都有自己的坑但每个坑解决之后后续的编译流程就会顺很多。如果你也在跟Qt编译较劲希望这份记录能帮你少走几步弯路。