
简介这是一份在 Linux 下完成静态编译的 Qt 5.9.9 开发库编译环境为 CentOS 7.6 x64、GCC 4.8.5、libc 2.17并启用了 -qt-xcb 图形平台插件。它主要面向需要把 Qt 图形界面程序部署到不带 Qt 运行库的 Linux 目标机、希望以单个可执行文件分发的 C 开发者静态链接后通过 ldd 检查可确认已无 Qt 相关动态依赖从而显著降低现场部署时的依赖缺失与版本冲突风险。压缩包整体约 197.4MB共 2000 个文件以 .h 头文件、.a 静态库、.prl/.prf/.pri 工程描述文件、.cmake 配置脚本和 .qm 翻译文件为主体涵盖 Core、Gui、Widgets、Network、Qml 等常用模块并带有必要的 xcb 平台插件及辅助工具解压后可直接供 qmake、CMake 或 Qt Creator 工程引用省去从源码自行编译的冗长过程。该包已有 567 人学习下载除可直接配置进工程使用的库文件与清晰目录外也可作为进一步定制 Qt 模块的基线版本如需自行编译还可参考作者给出的 configure 静态编译参数。 这个压缩包名字初看又长又怪但只要在Linux下折腾过大一点的项目一眼就能读出里面的信息量qt-5.9.9-gcc485-libc217-static-qt-xcb.tar.gz。这串字符组合在一起摆明了是有人或者某个CI系统在一个特定的旧环境里把Qt 5.9.9编译成了静态链接版本并且特地带上xcb平台插件。它的价值不在于“能跑”而在于“在老系统上能免掉一堆乱七八糟的依赖直接跑”。如果你正在CentOS 7、RHEL 7这类老系统上做Qt开发或者手里有一个二进制包需要部署到各种目标机上被xcb插件折磨过这篇文章就是给你准备的。我会从文件名拆解开始逐步讲清楚这个版本的适用范围、编译思路、xcb插件的部署要点以及我实际踩过和排查过的问题。内容偏实操不堆概念希望对正在被Qt部署问题卡住的人有帮助。1. 文件名拆解每一个字段都不是白写的1.1 Qt版本号5.9.9最后的实用主义者Qt 5.9.9是5.9 LTS系列的最后一个小版本。5.9本身是Qt比较经典的长期支持版本官方对它的维护一直持续到2020年。这个版本最大的优势是——对老旧编译器和老旧系统非常友好。它不需要C14/17的特性整体构建要求低而且对glibc的版本要求没有后面5.12、5.15那么激进。如果你是在一个GCC版本停在4.8.5的环境里做开发Qt 5.9.9基本就是你能舒服使用的最上限。5.12开始官方虽然还支持GCC 4.8但实际上很多场景下会遇到配置检测问题5.15干脆要求GCC 5.3以上。碰到这种环境与其折腾升级GCC连带影响系统库稳定性不如老老实实选5.9.9这也是为什么到现在还能在各大下载站看到这个版本的预编译包。1.2 gcc485与libc217系统环境的真实写照gcc485对应的是GCC 4.8.5libc217对应glibc 2.17这两个数字放到一起几乎可以直接锁定是CentOS 7.x或者RHEL 7.x。这套组合拳意味着你面对的是一个“古老但稳定”的生产环境。老并不可怕可怕的是这个环境中编译出来的程序如果动态链接了Qt库放到稍微新一点的系统上可能因为glibc版本差异跑不起来反过来在太新的系统上编译的Qt程序拿到这里会直接报GLIBC_2.18 not found。所以这个包的命名方式实际上是在告诉使用者一个关键信息它是在glibc 2.17下编译的动态链接时对glibc的依赖被约束到了最低要求。如果你把它拿到CentOS 7、Ubuntu 14.04等老系统上跑基本不用担心找不到符号的问题。而开发者选择static而不是动态编译则是更进一步把其余系统依赖的坑也填上。1.3 static和qt-xcb一个解决依赖一个解决显示static字段表示Qt库本身是静态编译的。意思是你的最终可执行文件会把QtCore、QtGui、QtWidgets这些库直接揉进二进制里运行时不再需要找libQt5Core.so.5这些文件。qt-xcb则是平台插件。Qt在Linux下默认通过xcb插件来连接X Window系统所有基于X11的桌面环境GNOME、KDE、XFCE等老环境都得靠它。如果这个插件缺失或不匹配最常见的报错就是qt.qpa.plugin: Could not load the Qt platform plugin xcb in even though it was found然后窗口直接起不来。把这个字段写进包名说明编译者专门处理了这个问题不是随手打了个静态库就完事。2. 为什么需要静态编译的Qt搞清这个很多人会想岔2.1 动态链接的“甜蜜陷阱”很多人一开始做Linux Qt开发都是直接apt或者yum装一个qtbase然后动态编译。开发机上一切正常等到拷到别的机器上就开始上演“缺库大作战”。动态链接的好处是节省磁盘和内存可以共享库文件但代价是部署时的依赖地狱。目标机器上少了任何一个libQt5Xxx.so.5程序就起不来而且报错往往是分割成好几段出现的——先报缺少libQt5Widgets.so.5你拷过去又报缺少libQt5Gui.so.5再拷过去又报缺libqxcb.so……这种没完没了的追查过程我相信每个部署过的人都有心理阴影。2.2 静态编译解决了什么不解决什么静态编译把Qt自身相关的一大堆.so全部揉进可执行文件里部署的时候你只需要带一个二进制文件跑起来就会自己把Qt各部分初始化好。这是它最核心的价值——省心。但要注意静态编译不是银弹。Qt仍然依赖一些系统级别的第三方库尤其是xcb插件绕不开X11协议相关的库。比如libxcb、libxcb-xinerama、libxcb-xkb这些扩展库它不会全部静态包含进去。如果目标系统缺这些你在启动程序时照样会看到xcb加载失败。所以静态Qt包在部署时往往还需要配合一个“系统依赖检查脚本”或者带上相应的.rpm/.deb包一起分发。2.3 这个组合的实际应用场景常见于三类需求场景内网离线环境部署目标机器不能联网不能直接从yum源装依赖只能拷贝一个绿色包直接跑。跨多版本Linux发布希望同一个二进制在CentOS 7、Ubuntu 14.04、Debian 8等不同发行版上都能运行动态链接很难做到这一点而基于老glibc静态编译的包兼容性显著提升。嵌入式或特种终端系统裁剪过库不全又没法随意改系统只能把应用做到“自带一切”。3. 从零到一如何构建这样一个静态Qt包这部分我更想以“如果你需要自己编译一个完全相同的包”为前提来讲。毕竟直接拿到现成包固然好但能搞明白它的来路遇到问题你才知道往哪儿查。3.1 环境准备的优先级编译环境本身最好就是目标环境的上限。比如想兼容CentOS 7最好就在CentOS 7上编译这样glibc版本自然就是2.17。如果你的开发机是Ubuntu 20.04又非要在那上面编理论上也能通过加一些兼容参数实现但坑会非常多不如直接用Docker拉一个centos:7镜像干净。建议在编译前安装基础依赖yum groupinstall Development Tools yum install -y libxcb-devel xcb-util-devel xcb-util-image-devel xcb-util-keysyms-devel xcb-util-renderutil-devel xcb-util-wm-devel yum install -y mesa-libGL-devel libX11-devel libXext-devel libXi-devel libXrandr-devel libXfixes-devel fontconfig-devel freetype-devel这些开发包对应运行时目标机器所需要的动态库。其中libxcb-devel尤其关键因为xcb插件编译时依赖它。缺了它你后面就算配置了-qt-xcb也编不出完整的插件。3.2 核心configure参数解析解压Qt源码后新建一个build目录以保持源码干净mkdir build cd build ../qt-everywhere-opensource-src-5.9.9/configure \ -static \ -release \ -opensource \ -confirm-license \ -prefix /opt/qt-5.9.9-static \ -qt-xcb \ -qt-xkbcommon \ -no-opengl \ -no-gtk \ -fontconfig \ -system-freetype \ -nomake examples \ -nomake tests这里逐项拆解一下-static编译静态Qt库。-qt-xcb把xcb相关的库以自带方式编译而不是完全依赖系统全局版本。这种方式会增加兼容性避免因为系统xcb库版本差异导致运行时行为不同。-no-opengl很多老机器没有OpenGL支持干脆禁用。如果你的应用需要硬件加速可以改用-opengl desktop但需要确保目标机器有libGL.so。-fontconfig -system-freetype使用系统字体配置和后端。Qt界面要正常显示中文这俩基本是标配不要在自己实现字体引擎上走弯路。-nomake examples -nomake tests只编译必要模块省时间。configure结束之后分两步编译make -j4 make install如果你的机器内存足够-j8也行。编译时间大概在30分钟到2小时不等取决于机器性能。装完之后在/opt/qt-5.9.9-static/lib/下看到的库基本是.a结尾的静态库。3.3 使用静态Qt编译应用的特殊处理当你用这个Qt编译自己的项目时需要额外注意两个“坑中坑”。第一个是qt.conf文件的处理。即使做了静态编译Qt在运行时还会找qt.conf来定位插件目录和翻译文件等路径。如果你希望程序在任意目录都能运行需要在可执行文件旁边放一个qt.conf里面写[Paths] Prefix . Libraries . Plugins plugins这个文件告诉Qt“我的运行路径就在我所在的位置不需要去系统路径找。” 如果没有它有些版本可能会默认去/opt/qt-5.9.9-static找插件而目标机器上根本没有这个目录结果就是程序秒退。第二个是编译时链接参数的顺序问题。静态链接对库顺序非常敏感必须把更基础的库放在后面否则会出现“undefined reference”错误。用qmake时可能出现这类问题解决办法是检查LIBS变量的顺序通常多试几次就能定位到到底缺的是哪个库。通过pkg-config引入xcb的话尽量把$(pkg-config --libs xcb xcb-xinerama xcb-icccm xcb-keysyms xcb-xkb)放在最后面用系统库方式解决顺序冲突。4. xcb插件的灵魂拷问为什么静态Qt还是报xcb错误4.1 问题现象与根因分析最常见的错误信息长这样qt.qpa.plugin: Could not load the Qt platform plugin xcb in even though it was found.后面往往跟着一行Failed to load platform plugin xcb. Available platforms are: offscreen xcb minimal很多人一看到“static”就以为程序里已经什么东西都有了看到这个报错直接从椅子上跳起来“为什么我都用静态库了还说找不到xcb” 其实原因很简单在静态编译模式下xcb插件是无法在运行时从外部plugins/platforms/libqxcb.so动态加载的——它通常已经被静态编译进去。但xcb插件本身仍然是一个QPluginLoader的产物它需要在上层补丁中注册成功而这个注册过程依赖的不是动态库文件而是Qt自身的Q_IMPORT_PLUGIN(qxcb)这个宏。如果你是在源码里自己构建Qt的应用需要在项目的.pro文件或CMakeLists里加入对Q_IMPORT_PLUGIN(qxcb)的引用或者在main函数之前加入#include QtPlugin Q_IMPORT_PLUGIN(qxcb)这样编译器才会把静态的qxcb插件符号链接进最终可执行文件。很多基于Qt自定义编译项目的人忘记这一步就卡在这里。4.2 运行时依赖库不完全问题排除插件注册问题后如果你仍然遇到xcb启动崩溃则多半是因为xcb插件最终生成的可执行文件仍然动态链接了几个系统级的X11相关库。在一台干净的CentOS 7最小安装上运行时容易缺的库包括libxcb-xinerama.so.0libxcb-xkb.so.1libxcb-icccm.so.4libxcb-keysyms.so.1libxcb-render-util.so.0libxcb-shape.so.0你可以在编译机器上通过ldd your_app查看依赖只要看到这类libxcb-*.so出现就说明它们还是动态依赖。这意味着部署时要么在目标机器上一并安装这些运行时库要么在编译时利用libxcb的静态版本把这条依赖链也断掉。但如果要彻底断掉技术上比较麻烦因为Qt的-qt-xcb选项通常不足以把整个libxcb全系列静态化。实际项目里更常见的做法是保留这些运行库的动态依赖然后在部署包中附上这些.so的副本或者写一个部署脚本用ldd自动收集依赖并打包。4.3 使用 ldd 打包依赖的自动化技巧这里分享一个我常用的部署脚本片段bash用于在编译机上提取所有动态依赖#!/bin/bash DEPLOY_DIR./app_pkg mkdir -p $DEPLOY_DIR/plugins cp -a your_app $DEPLOY_DIR/ ldd $DEPLOY_DIR/your_app | grep / | awk {print $3} | xargs -I {} cp -L {} $DEPLOY_DIR/跑完之后把$DEPLOY_DIR整体拷到目标机器上运行即可。这个方法并不完美因为某些共享库的依赖可能来自版本不同的路径但对于大多数xcb相关的系统库这个做法有效。5. 常见运行问题与排查技巧实录这里我按实际项目中遇到的高频问题整理成表方便查询症状可能原因处理方式程序启动后无窗口终端无输出没有写明qt.conf或插件路径不正确确保可执行文件旁有qt.conf并设置正确的Plugins路径报libxcb-xinerama.so.0: cannot open shared object file目标系统缺xcb拓展库在目标机器上安装libxcb-xinerama或用ldd收集后一并分发报could not find a Qt platform plugin xcb后无法启动Q_IMPORT_PLUGIN缺失或静态插件未链接在main中加Q_IMPORT_PLUGIN(qxcb)重新编译报This application failed to start because no Qt platform plugin could be initialized.环境变量QT_QPA_PLATFORM设置错误或platform库不匹配检查QT_QPA_PLATFORM尝试export QT_QPA_PLATFORMxcb程序能起但界面上中文字体变方块字体文件缺失或fontconfig配置未生效确认目标机器有中文字体并安装fontconfig依赖使用静态OpenGL相关代码时段错误编译时用了OpenGL但目标环境无GPU支持改用-no-opengl拉低渲染到CPU路径或使用QPainter程序秒退但要到strace才能看出端倪xcb连接失败通常是远程X服务不允许访问export DISPLAY:0或检查XAUTHORITY5.1 无界面环境下的判断方法如果程序起的不是GUI版本只是想测试xcb加载是否成功可以临时写一个极简demo#include QApplication #include QLabel int main(int argc, char** argv) { QApplication app(argc, argv); QLabel label(hello); label.show(); return app.exec(); }然后到目标机器上跑。如果demo能弹窗就说明整套Qt环境没问题应用本身问题出在别处。如果demo都弹不出来就用strace跟踪看卡在哪里strace -f -e traceopen,openat,execve ./your_app重点看返回ENOENT的文件路径基本上缺失的库一目了然。5.2 关于旧的虚拟机和远程X11转发的场景还有一个常见场景是在Windows上用XShellXming或者MobaXterm跑远程Qt程序。这种情况下xcb插件即使没问题也容易因为X服务对某些扩展特性的支持不足而报xcb_connection_has_error()之类的错误。如果目标只是远程测试可以在程序启动前设置export QT_X11_NO_MITSHM1屏蔽特定的共享内存扩展大部分情况下能绕过去。如果依然不行就升级X服务端或者改用VNC方式跑。5.3 静态包体积和启动速度的取舍一个静态Qt 5.9.9的最小GUI程序打包后二进制体积通常在8MB~20MB之间加上全部插件可能到30MB以上。这比动态链接要多不少。但考虑到免部署带来的无脑体验这点体积多数场景下是可以接受的。启动速度方面静态编译并不必然比动态链接慢。动态链接阶段本身也有成本反而是静态链接后省去动态加载器解析库符号的时间在某些环境下肉眼看起来启动更跟手。有些人觉得静态Qt启动慢多半是因为运行库路径配置有误、Qt在瞎找插件。写在最后的拉力经验我自己曾经维护过一套基于CentOS 7 Qt 5.9.9的工业控制界面程序。最初就是动态编译每次升级现场版本都要带一堆.so还经常因为现场机器某个库版本不对导致界面起不来。后来我花了两天时间搭了一个干净环境做静态编译用的是这个文件名一模一样的版本组合。从那以后部署就变成了拷一个文件往里一放改改配置文件系统服务起起来就完事再没在Qt库依赖上翻过车。如果你手头已经有这个现成的qt-5.9.9-gcc485-libc217-static-qt-xcb.tar.gz恭喜你可以直接跳到第4节和第5节验证自己的应用程序。如果你刚准备折腾这套组合建议编译之前最好把GCC、libxcb-devel这些环境一次性装齐毕竟configure失败的配角体验我替你试过了。如果再遇到奇奇怪怪的崩溃优先怀疑的不是Qt而是系统的X11环境。把DISPLAY变量、Xauthority权限先捋清楚往往比重新编译快得多。希望这篇东西能帮你少踩几个坑。本文还有配套的精品资源点击获取