简介:面向在Kylin-Server-10-SP2(arm64)上编译Qt 5.12.8应用的工程师,这份压缩包提供了编译完成后所需的完整头文件集合,共2000个.h文件,包体约65.91MB,全部为C++头文件格式,属于典型的Qt构建产物头文件目录。这些头文件不仅覆盖Qt Core、Qt GUI、Qt OpenGL等常用模块的公共接口,还包含本地化数据、URL解析、OpenGL扩展等内部私有声明,能够满足从基础组件引用到底层机制排查的多种开发需求。直接引用该头文件集合,可省去从源码逐一收集或手动整理的时间,也避免因系统自带Qt版本不一致而引发的编译错误,在麒麟ARM服务器上搭建Qt开发环境或进行交叉编译时尤其顺手。对于国产化系统适配、嵌入式界面开发、模块裁剪与定制化构建等场景,这份材料都能提供清晰的依赖参照,配合Qt源码阅读还能帮助深入理解实现细节。目前已有245人学习下载,可作为长期保留的开发参考。 Kylin-Server ARM64上亲手编译Qt 5.12.8这事,我前后折腾了大概两天,中间踩了不少坑,但最终成果很值。先说清楚这玩意儿是干嘛的:在麒麟服务器系统(Kylin-Server-10-SP2-Release-Build09-20210524-arm64)上,从源码编译出一套完整的Qt 5.12.8开发环境,让Qt程序能跑在ARM64架构的机器上。如果你在国产化替代项目里做界面开发,或者需要在飞腾、鲲鹏这类ARM平台服务器上部署Qt应用,这篇文章就是给你准备的实操笔记。
很多人会问:服务器上要界面干嘛?还非得上Qt?实际项目里,有的是需要本地GUI配置工具、有的是跑数据可视化面板、有的是部署工业现场的上位机程序,这些场景都绕不开在服务器上直接跑Qt。而系统自带的源里Qt版本老、插件不全,想要完整可用的开发环境,源码编译是唯一靠谱的路。下面把整个过程中的方案思路、环境准备、编译配置、问题排查完整记录下来。
1. 方案选型与整体思路拆解
1.1 为什么选Qt 5.12.8而不是其他版本
Qt版本众多,5.12、5.15、6.x各有拥趸,我在这个项目里选了5.12.8,原因很实际。5.12系列是LTS长期支持版本,官方支持周期长、社区资料多、踩坑记录丰富——这对ARM64平台尤其重要,因为x86上能直接搜到的经验,换到ARM架构上往往得自己重新试。5.15虽然也是LTS,但它从开源包到二进制包做了不少调整,部分模块开始强制走商业授权框架,离线编译的合规性不好把控。6.x版本干脆切到了CMake构建系统,和传统的qmake流差异太大,如果项目里有大量基于qmake的旧代码,迁移成本是实打实的。
另外还有一个关键指标:依赖库的兼容性。5.12.8对xcb、fontconfig、mesa等图形栈的版本要求不算激进,在Kylin Server的默认源范围内基本能找齐依赖,不用为了某个库再单独编译一个新版,可以省掉一大半的折腾时间。
1.2 为什么不用apt直接安装而是源码编译
如果服务器能联网,直接apt install qtbase5-dev其实是最省事的方案,几分钟搞定。但实际场景下,这套方案往往满足不了需求。首先是版本太旧,麒麟源里自带的Qt版本通常停留在5.11甚至更低,缺失很多新模块特性。其次是插件不完整,服务器版的Qt二进制包经常不带齐全的xcb平台插件,结果程序编译出来了,一运行报"could not find or load the Qt platform plugin 'xcb'",直接就给你把GUI程序判了死刑。再一个是定制需求,比如需要静态编译、需要裁剪到最小体积、需要接入特定的设备插件,这些靠apt装是做不到的,必须从源码控制。
所以我一直觉得,ARM64平台上编译Qt,不是一道选做题,而是必做题。源码编译能够百分百确认每个组件都针对当前架构生成了正确的指令集,避免二进制包跨架构不兼容的隐形问题。
2. 编译前的环境准备
2.1 确认系统版本与架构信息
动手之前一定要把环境信息看明白。系统是Kylin-Server-10-SP2-Release-Build09-20210524-arm64,注意这个"SP2"表示第二服务包,Build09是构建序号,20210524是构建日期,这些都是确认系统基础状态的关键信息。执行uname -m确认架构输出aarch64,说明是ARM64架构。
查看系统的几个常用命令:
uname -m cat /etc/os-release cat /etc/.productinfo lscpu | grep Architecture在ARM64平台上,有些源码包需要关注大小端和页大小,不过Qt的主流模块对这些问题处理得比较透明,确认架构一致就可以,不用过度焦虑。
2.2 检查编译工具链
Qt 5.12.8对编译器的基础要求是支持C++11,GCC版本至少4.8以上。Kylin Server 10 SP2自带的gcc一般是7.3或7.5,完全够用。配置前先检查:
gcc --version g++ --version make --version perl --version这里特别提一句,Perl版本不能太老,Qt的构建脚本大量依赖Perl做文本处理和代码生成,早期CentOS上Perl 5.10就能干活,但如果你遇到莫名其妙的构建中止错误,优先怀疑Perl版本太低。
2.3 一次性装齐依赖库
这是整个编译过程里最"磨人"的一步,依赖缺一个编译就崩一次,每个都是随机位置报错。我把Kylin Server下编译Qt 5.12.8需要的依赖整理成了一张表,这是踩了N次坑换来的清单:
| 依赖包 | 作用 | 缺失时的典型症状 |
|---|---|---|
| libxcb1-dev libxcb-xkb-dev | xcb平台插件构建 | configure时xcb特性被置为no |
| libx11-dev libxkbcommon-dev | X11协议支持 | 无xcb、xkb扩展 |
| libfontconfig1-dev | 字体渲染 | Qt无法识别系统中文字体 |
| libfreetype6-dev | 字形渲染引擎 | 字体显示方块、乱码 |
| libgl1-mesa-dev | OpenGL软件实现 | Qt Quick内容crashes |
| libegl1-mesa-dev | EGL图形接口 | eglfs插件编译失败 |
| libglib2.0-dev | 事件循环整合 | 某些模块无法启用glib |
| libdbus-1-dev | 进程间通信 | QtDBus模块缺失 |
| libssl-dev | OpenSSL支持 | QtNetwork的SSL功能不可用 |
| libicu-dev | Unicode与国际化 | 中文显示和排序异常 |
安装命令:
yum install -y libxcb-devel xcb-util-devel xcb-util-image-devel xcb-util-keysyms-devel xcb-util-renderutil-devel xcb-util-wm-devel libX11-devel libxkbcommon-devel libxkbcommon-x11-devel fontconfig-devel freetype-devel mesa-libGL-devel mesa-libEGL-devel glib2-devel dbus-devel openssl-devel libicu-devel这里有个血泪教训:configure脚本本身检查依赖时并不严格,有些模块检测失败它就默默跳过,不会中断编译。问题到了编译后期才爆发:QtQuick模块编了一半,发现EGL头文件没有,整个build目录直接废掉。所以依赖检查一定要做在configure之前,不要指望检测脚本帮你拦。
3. configure配置与编译实操
3.1 获取源码并解压
源码包我用的是qt-everywhere-opensource-src-5.12.8.tar.xz,这个包包含全部模块(qtbase、qtdeclarative、qtquickcontrols、qtmultimedia等),总计约500多MB解压后更大。下载时可以优先选国内镜像,速度差距很明显。解压命令:
tar -xf qt-everywhere-opensource-src-5.12.8.tar.xz cd qt-everywhere-opensource-src-5.12.83.2 configure参数详解
configure这步是整个编译的"定盘星",参数选对了,后面顺风顺水;选错了,后面编译几十次都得推倒重来。我最终用的配置是:
./configure \ -prefix /usr/local/qt5.12.8 \ -opensource \ -confirm-license \ -release \ -optimized-qmake \ -nomake examples \ -nomake tests \ -no-rpath \ -qt-libpng \ -qt-libjpeg \ -qt-zlib \ -qt-pcre \ -qt-xcb \ -no-opengl \ -skip qtwebengine \ -skip qtwayland \ -skip qtscript \ -skip qtgamepad逐个解释一下几个高频参数的作用,方便你按需调整。
-prefix参数指定安装路径。我建议路径固定绝对路径,别用默认的/usr/local,因为Qt的qmake会把路径写进工具链配置里,后面qmake生成Makefile时会引用这个路径,跨目录移动容易出麻烦。
-qt-xcb特别关键。这一项让Qt使用自带的xcb库组合构建xcb平台插件,对于国产化系统里经常出现的xcb版本偏旧问题,这是最稳妥的解法。
-no-opengl是权衡后的选择。ARM64服务器上大多没有独立GPU,OpenGL硬件加速意义不大,关掉它还可以少装一堆依赖。但代价是Qt Quick的某些渲染特效会退化为软件渲染,对纯业务界面来说影响不大。如果目标机器有GPU,可以改成-opengl desktop,然后把相关libGL-devel补齐。
-skip qtwebengine必须解释一下。QtWebEngine是Chromium内核的浏览器组件,体积大、依赖重、编译时间能以小时计,而且在ARM64环境里经常过不了编译。绝大多数项目用不到这个组件,别犹豫,直接跳过。
3.3 正式编译
configure通过后,我的经验是先用make -j4跑,不要一上来就无脑-j16。ARM服务器CPU核数多的话-j12或者-j16能明显提速,但内存不够就尴尬了——GCC的C++编译非常吃内存,Qt整个项目并发编译时的内存峰值能到8GB以上,如果系统内存只有8GB,并发太高直接OOM,编译进程被内核干掉,完事儿还不知道原因。
编译命令:
make -j12实测下来,在16核ARM64 CPU + 32GB内存的环境下,完整编译大概需要40到60分钟。如果配置里带了multimedia模块,时间还会更长。编译过程中输出会非常密集,做好心理准备。最后看到类似"Qt is now configured for building"和编译进度跳完100%,就基本成了。
安装:
make install3.4 环境变量配置
安装完成后,为了让系统找到新装的Qt,要编辑/etc/profile或者用户目录的.bashrc:
export QT_HOME=/usr/local/qt5.12.8 export PATH=$QT_HOME/bin:$PATH export LD_LIBRARY_PATH=$QT_HOME/lib:$LD_LIBRARY_PATH export QT_PLUGIN_PATH=$QT_HOME/plugins export QML2_IMPORT_PATH=$QT_HOME/qmlsource之后验证一下:
qmake -v输出显示QMake version 3.1 Using Qt version 5.12.8 in /usr/local/qt5.12.8/lib,说明环境变量生效,编译工作区正式可用。
4. 编译期与运行期的坑:问题排查实录
4.1 configure阶段:xcb特性检测失败
这是最常见的问题,configure日志里能看到"xcb features disabled"之类的字眼。原因就是缺libxcb相关的dev包,我前面列的那张依赖表就是针对这个情况准备的。解决办法不是去configure脚本里改开关,而是把依赖包补齐,重新configure。
检查方法:
./configure -verbose | grep xcb如果看到"xcb"后面跟着"no",就说明xcb插件没启用。补包之后把build目录清掉(直接删掉源码目录重新解压也行,别想着增量重来,Qt的configure缓存很顽固),重新跑一遍。
4.2 编译期:c++ internal compiler error
编译过程中偶尔会出现GCC内部错误,常见触发点是内存不足或者磁盘空间不足。检查方法:
dmesg | grep -i "killed process" df -h free -h如果是内存问题,降低make并发数,比如make -j4。如果是磁盘问题,编译目录至少要预留10GB以上空间。我遇到过编译到一半/根分区被占满的情况,那真是欲哭无泪,只能重新解压源码包,前面的编译时间全部白费。
4.3 运行期:QXcbConnection failed to initialize xrandr
这是个高频报错,第一次跑Qt程序时直接弹出来:
QXcbConnection: Failed to initialize XRandr Qt: XKEYBOARD extension is not present on the X server.这个报错的信息很容易让人以为是系统缺少X服务或者xrandr工具,其实根因是xcb插件在连接X Server时,没能正确初始化RandR扩展。尤其是当你用X11转发(ssh -X)跑Qt程序时,远程的X Server对RandR扩展的支持不完整,就会卡在这。排查方法是先在目标机器本地跑一次:
export QT_DEBUG_PLUGINS=1 ./your_qt_app如果本地正常、远程报错,基本确认是X转发的问题,可以换成x11vnc之类的工具在远程桌面环境里操作,或者检查X Server的扩展加载。确保系统里装了xrandr相关组件:
yum install -y xrandr4.4 qmake构建的项目找不到Qt模块
编译完Qt后自己用qmake创建的工程,make时提示找不到QtNetwork之类的头文件,多半是环境变量没生效,或者qmake缓存了旧的路径。执行qmake -query确认QT_INSTALL_PREFIX指向哪里,如果是错误的路径,检查环境变量后再重新qmake一遍。这里有个小技巧:把export命令写入/etc/profile.d/qt.sh文件里,重启终端就不会失效,比手动source靠谱得多。
4.5 xcb平台插件加载失败速查表
| 报错特征 | 可能原因 | 快速定位 |
|---|---|---|
| could not find or load the Qt platform plugin "xcb" | QT_PLUGIN_PATH不对,或xcb插件缺失 | 检查plugins/platforms目录 |
| The X11 connection broke | X Server断连,权限问题 | 检查DISPLAY变量和xhost |
| Got empty QXcbScreen | xcb插件和X Server版本不匹配 | 用系统libxcb重新编译xcb插件 |
| libxkbcommon-x11.so.0: cannot open | 运行时缺少xkbcommon | 检查LD_LIBRARY_PATH |
| No fonts could be found | fontconfig配置问题 | 安装fontconfig并执行fc-cache -f |
5. 部署到其他机器与交叉编译的扩展思路
5.1 直接把编译产物搬到目标机器
编译完成后的Qt目录可以整体打包,拷贝到另一台同架构的机器上直接使用。ARM64系统之间搬移,架构一致就可以,不用重新编译。注意如果是只装运行环境不发开发环境,可以只拷贝lib、plugins、qml三个目录,bin路径下的工具可以大部分省略,体积能小不少。用tar打包时要保留符号链接,加上-h参数之前先想清楚,不能把符号链接指向的真实文件全部打进包里,否则包体积会膨胀两三倍。
5.2 静态编译还是动态编译
如果目标机器上有多个Qt程序要跑,动态库方案最省内存,共享一份.so文件就行。但国产化项目里经常遇到要拷到内网机器、不便安装依赖的场景,静态编译就成了刚需。配置时加-static -static-libgcc -static-libstdc++三个参数,libqxcb相关依赖一定要用静态方式链接进去,否则运行时报错找libxcb.so.1,又得从头趟一遍坑。
静态编译的坑在于部分插件(比如inputmethod)有依赖循环或者GPL授权问题,比如QtXkbCommonSupport在静态链接时容易报undefined reference。这个问题的处理方案是:编译时明确-enable-static -disable-shared,同时把xcb相关依赖全部使用-devel包提供的静态库。我在ARM64平台上实测过,静态编译出来的Qt程序执行文件约20MB至40MB,依赖干净,适合部署到无网络环境。
5.3 交叉编译的扩展场景
如果开发机是x86的PC,目标机是ARM64服务器,不想在服务器上跑编译,可以搞交叉编译。用aarch64-linux-gnu-g++作为编译器(交叉工具链可以yum install gcc-aarch64-linux-gnu),configure时指定-platform linux-aarch64-gnu-g++。要注意的是,交叉编译的Qt依赖库也必须是对应ARM架构的,不能用x86的libxcb、fontconfig,跨架构的二进制链接在一起必然出问题。
但说实话,如果不是必须,我不推荐ARM64上交叉编译。直接在目标机上原生编译,前期虽然慢,但后期所有依赖和运行时环境都会留在目标机器上,程序出错时的排查成本低非常多。交叉编译出来的程序,一旦遇到运行库不兼容的问题,调试难度翻倍。
6. 收尾:实测感受与个人经验
整个流程走完后,我的直观感受是:Kylin Server的ARM64环境并没有想象中那么"原生"地支持Qt,很多x86下简单忽略的问题,在这个平台上都会主动找上门。但正因为如此,把编译流程完整走一遍,你对Qt的构建机制、依赖关系和系统底层组件的理解会深一个层次。
最后再分享两个自己的使用习惯。第一,每次configure之后,把生成的config.summary文件保存一份,里面记录了所有特性的启用情况,排查问题时比看日志快得多。第二,编译完成之前,一定先保留好configure的原始参数——可以写进一个build.sh脚本留在源码目录里,下次回来看的时候不会一头雾水。当初我为了方便复查,随手在脚本开头加了一行日期注释,结果这个脚本后面帮我省了至少一个小时的回忆时间。
本文还有配套的精品资源,点击获取