简介:面向 Linux 桌面应用开发者的一份 Qt 5.9.9 静态编译库压缩包,基于 CentOS 7.6 x64 与 GCC 4.8.5 构建,并启用 Qt XCB 平台插件,支持图形界面程序开发。使用该库编译出的可执行文件,通过 ldd 查看时不会出现 Qt 相关动态依赖,非常适合需要单文件分发、部署到无 Qt 运行环境的目标机器,能有效简化交付和运维成本。资源包体积约 197.4MB,共包含 2000 个文件,以 h 头文件为主(约 3746 个),另有 a 静态库、pri/prf 工程配置、cmake 配置、qm 翻译文件、png 图标等;头文件覆盖 QtCore、QtGui、QtWidgets、QtNetwork、QtSql 等主要模块,开发者可据此快速搭建静态链接工程。已有 569 人学习该资源。包内还保留原作者给出的 configure 配置参数,例如 -confirm-license -opensource -static -qt-xcb -no-opengl 等,既可直接使用现有库,也可参考参数自行复现或裁剪模块。下载后即可获得一套完整可用的 Qt 静态库目录,省去从零编译 Qt 的繁琐过程,快速产出免依赖的图形界面程序。
1. 这个压缩包解决什么问题:一台老机器上的 Qt 5.9.9 静态编译环境
第一次拿到 qt-5.9.9-gcc485-libc217-static-qt-xcb.tar.gz 时,我一度以为解压就能用,结果整整卡了一下午:程序反复报 xcb 平台插件加载失败,后来才搞清楚这套包是围绕“gcc 4.8.5 编译、glibc 2.17 时代系统”构建的静态 Qt,xcb 插件静态编进了库里,可运行时仍要依赖一批系统级 X11/XCB 动态库。Qt 5.9.9 是能跑在 gcc 4.8 上的最后一代 LTS,很多仍在稳定运行的老工作站、内网数据展示终端,以及做跨平台分发的静态 GUI 项目,都基于这套组合。装不好这个基础件,后面的工程会在 qmake 混用、xcb 启动报错里反复翻车。文章按实际安装顺序记录,覆盖环境匹配、目录规划、xcb 依赖核查和最终验证,适合需要把 Qt GUI 程序静态打包并迁到其他机器上运行的开发者。
2. 安装前环境检查:gcc 4.8.5、libc 2.17 与运行平台的匹配
2.1 先看编译环境:qmake 和静态库里锁定的工具链
包名里的 gcc485 和 libc217 直接暴露了它的编译环境:gcc 4.8.5,glibc 2.17。这套组合对应 Linux 7 系列发行版的时代,很多还在内网服役的老工作站就是这套底座。静态 Qt 的好处是程序编出来不依赖 libQt5Core.so 这类动态库,但缺点也在这:如果系统环境和编译环境差太远,链接阶段会冒出各种 ABI 类报错。所以动手前我一般先看三样东西,命令写在一起,逐个确认。
gcc --version | head -n 1 ldd --version | head -n 1 uname -m第一行确认系统默认 gcc 版本,第二行确认 glibc 版本,第三行确认架构。gcc 那行不要只盯着数字大小:包里的 Qt 库是用 gcc 4.8.5 编的,它本身不要求你在编译自己代码时也必须用 4.8.5,但要求最终链入的 libstdc++ 能和旧 ABI 对上。glibc 2.17 是下限概念,如果系统 glibc 高于 2.17,通常照样能跑,区别只在动态链接器加载系统库时的解析行为。架构必须严格一致,x86_64 的包硬塞进 aarch64 机器,qmake 第一步就会报错。
我一般还会顺手跑一条 ldconfig 检查,确认系统的 X11 相关动态库路径是否正常,这个问题在第 4 章会集中体现。先记一个结论:这个包最差也要跑在 libc 2.17 及以上、带完整 X11 运行库的 Linux 环境里,低于这个版本的系统基本不用考虑,高于的能兼容,但必须过一遍 xcb 依赖检查和 ABI 链接检查。
| 系统 glibc 版本 | 预期表现 | 需要做的处理 |
|---|---|---|
| glibc 2.17(同代系统) | 直接解压即可,兼容性最好 | 安装 xcb-util 系列运行库 |
| glibc 高于 2.17,低于 2.28 | 大概率可用 | 编译自己代码时注意 libstdc++ ABI 开关 |
| glibc 高于 2.28 | 能跑,但要多做验证 | 重点查 ldd 输出和 XCB 错误日志 |
2.2 再看包内容:解压前做一次体检
拿到任意一个编译好的 Qt 压缩包,我都不急着解压到目标目录,先花几秒钟看包里有什么,能省掉后面很多猜谜时间。tar.gz 可以不解压直接列内容,用下面两条命令就能把包的结构摸个大概。
tar -tzf qt-5.9.9-gcc485-libc217-static-qt-xcb.tar.gz | head -30 tar -tzf qt-5.9.9-gcc485-libc217-static-qt-xcb.tar.gz | grep -E "mkspecs|plugins/platforms|lib/libQt5"第一条看顶层目录结构,确认这个包是带一个顶层目录,还是直接把 bin、lib、mkspecs 铺在根上,这决定第 3 章解压时要不要加 --strip-components=1。第二条检查三样关键内容:mkspecs 目录是否完整,plugins/platforms 下有没有 libqxcb.a,lib 目录下静态库文件是否齐全。mkspecs 决定 qmake 生成 Makefile 时用哪套编译规则,缺了它项目连 configure 都过不了;libqxcb.a 是静态 xcb 平台插件的本体,缺了它程序启动时必然报 xcb 加载失败。
结构看完,再用 file 命令抽一个关键二进制做格式检查。从 tar 包里抽出一个文件到标准输出再交给 file 判读,是小技巧,不用整个解压。
tar -xOf qt-5.9.9-gcc485-libc217-static-qt-xcb.tar.gz <包内相对路径>/bin/qmake | file -看到输出里带“ELF 64-bit LSB executable, x86-64”基本就可以放心。如果看到 32 位或者 ARM 架构,直接放弃,包和平台不匹配。做完这一步,环境检查结束,可以开始布置安装目录。
3. 解压与路径规划:把 Qt 5.9.9 静态环境隔离到 /opt 目录
3.1 解压手法:保留顶层目录还是剥掉一层
这种预编译的 Qt 包,我习惯放到 /opt 下独立目录,不用 /usr/local。理由很实际:静态 Qt 环境经常需要整体迁移、整体备份,放到独立目录里 tar 一下就带走;而 /usr/local 下面可能已有其他 Qt 版本,一旦 PATH 被覆盖,系统原来的 Qt 程序也会遭殃。隔离安装能避免这类灾难。
sudo mkdir -p /opt/qt-5.9.9-static sudo tar -xzf qt-5.9.9-gcc485-libc217-static-qt-xcb.tar.gz \ -C /opt/qt-5.9.9-static --strip-components=1第二条命令里的 --strip-components=1 做的是“剥掉一层目录”的动作。如果 tar 包解压出来根目录是 qt-5.9.9-static 这样的顶层目录,加上这个参数后,里面内容会直接散到 /opt/qt-5.9.9-static 下,目录结构更扁平。如果第 2 章体检时看到包本来就把 bin、lib 平铺在根上,就不要加这个参数。判断错误也没关系,解压后多一层目录,把环境变量指过去就行,代价只是路径写长一点。
解压完别急着配环境,先看一眼 /opt/qt-5.9.9-static 下面有没有 lib64 和 lib。同一套 Qt 5.9.9 里,静态库可能分布在两个目录,qmake 生成 Makefile 时会通过 mkspecs 里的规则去对应位置找库,你手动配 LD_LIBRARY_PATH 时也要两个目录都照顾到。
ls /opt/qt-5.9.9-static ls /opt/qt-5.9.9-static/lib | head -20第一行看整体布局,第二行确认 libQt5Core.a、libQt5Gui.a、libQt5Widgets.a 这些核心静态库都在。看到 lib 目录下是 .a 后缀的库文件,说明这个包确实是静态版本而不是动态版本,后面编译出来的程序才能做到单文件迁移。
注意:静态 Qt 包编译出的程序,运行时依然需要系统提供 X11、xcb 库、字体配置和 OpenGL 驱动。所谓“静态”,指 Qt 自身不再作为动态依赖,但不代表程序完全不接触系统动态库。
3.2 项目级环境变量:隔离污染,按需加载
很多人在这一步直接把 export 写进 /etc/profile,这是我最不推荐的做法。全局变量会污染系统里其他 Qt 项目,这个坑后面专门讲。更稳的是写一个项目级环境脚本,哪个项目要用,source 一下即可,互不干扰。
cat > /opt/qt-5.9.9-static/qt-env.sh <<'EOF' export QTDIR=/opt/qt-5.9.9-static export PATH="$QTDIR/bin:$PATH" export LD_LIBRARY_PATH="$QTDIR/lib:$QTDIR/lib64:${LD_LIBRARY_PATH:-}" EOF这段脚本做了三件事:QTDIR 指向 Qt 安装根目录,很多 Qt 工具会读取它定位资源;PATH 把 qmake 和 moc、uic 这些工具放进来,保证命令行直接可用;LD_LIBRARY_PATH 把 Qt 的库目录加进动态库搜索路径,虽然静态 Qt 大多用不上,但有些辅助工具和私有插件仍可能需要。注意变量后面的${LD_LIBRARY_PATH:-},意思是原本系统里有这个变量就保留,没有就留空,不会覆盖系统原有配置。
3.3 验证安装前缀:qmake -query 输出对照
环境变量配好后,第一件事是确认系统此刻用的 qmake 是不是 /opt 下这个。长期维护多套 Qt 环境的机器上,qtchooser 这类工具会把 qmake 劫持到其他版本,不验证就会用错编译器规则。
source /opt/qt-5.9.9-static/qt-env.sh which qmake qmake -v qmake -query QT_INSTALL_PREFIXwhich 输出应该是 /opt/qt-5.9.9-static/bin/qmake,qmake -v 显示 Qt 版本 5.9.9,QT_INSTALL_PREFIX 输出 /opt/qt-5.9.9-static。三条都对上,说明环境已经干净指向新装的静态 Qt。若 QT_INSTALL_PREFIX 输出别的路径,说明 qmake 还是被其他工具劫持了,去检查 PATH 顺序或手动把 /opt/qt-5.9.9-static/bin 移到最前面。
注意:qmake -query 的输出决定了 Makefile 里所有编译和链接路径,只要 QT_INSTALL_PREFIX 不对,后面的编译必定出错。这一点是静态 Qt 环境安装里最容易被忽视的细节。
4. xcb 平台插件的静态化:缺哪个动态库、怎么补
4.1 静态 Qt 的“动态尾巴”
qt-xcb 这个后缀说明包编译时把 xcb 平台插件静态编了进去,程序不再需要单独加载 libqxcb.so。但这不代表 xcb 相关的系统库也能静态化——xcb 本身要跟 X server 通信,底层依赖 libxcb 系列动态库,这部分是系统提供的,Qt 包不会替你带上。
之前在某模拟项目X上踩过一次很深的坑:程序在开发机上跑得好好的,复制到另一台同版本系统上打开,直接弹 “Failed to load platform plugin xcb”,查了两小时才发现那台机器缺 libxcb-xinerama.so.0。原因就是这台精简安装的系统没装 xcb 的工具库,而开发机装了。所以装完静态 Qt 后,必须主动核查 xcb 相关依赖,不能指望目标机器恰好都有。
4.2 用 readelf 定位静态库依赖
libqxcb.a 是二进制归档文件,ldd 对它没用,因为它不是可执行文件。要查它引用了哪些系统符号,我一般用 readelf 看动态段引用,虽然 .a 里的 .o 文件还没链接成最终可执行文件,但能看出它对外部符号的引用方式。实际操作中更常见的做法是:先把程序编出来,再对最终可执行文件跑 ldd,那才是真实运行时依赖。
readelf -d /opt/qt-5.9.9-static/plugins/platforms/libqxcb.a 2>/dev/null | grep NEEDED这条命令可能输出不多,因为 .a 文件通常没有完整动态段。更推荐的做法是编译一个最简单的 Qt 程序,然后对编译产物执行 ldd,把 xcb 相关的依赖一次性看全。
ldd /path/to/your_qt_demo_binary | grep -E "xcb|X11|GL|font"输出的动态依赖列表里,libxcb-xinerama.so.0、libxcb-icccm.so.4、libxcb-image.so.0、libxcb-keysyms.so.1、libxcb-render-util.so.0 这几项常出现。任何一项缺失,程序启动时都会有对应报错,最常见的表现是 “error while loading shared libraries: libxcb-xinerama.so.0: cannot open shared object file”。
4.3 补装 X 依赖的常用命令
补依赖的方法因包管理器而异,但原则相同:把 xcb 工具库、X11 开发头文件、OpenGL 开发库、字体配置这四类装齐。以下命令是我在基于 yum 的系统上常用的一套。
sudo yum install -y \ libxcb xcb-util xcb-util-image xcb-util-keysyms xcb-util-renderutil xcb-util-wm \ xkbcommon-x11 libxkbcommon-x11-0 \ mesa-libGL mesa-libGL-devel fontconfig fontconfig-devel参数说明:libxcb 是基础连接库;xcb-util 系列是工具库,Qt 的 xcb 插件会直接调用其中符号;xkbcommon-x11 负责键盘映射,缺失时程序可能不响应按键;mesa-libGL 和 mesa-libGL-devel 解决 OpenGL 渲染和开发头文件缺失;fontconfig 处理 Qt 程序查找系统字体的流程,缺了会看到 Fontconfig error 的警告,界面文字变成方块。
装完依赖再对程序跑一次 ldd,直到 xcb 相关项全部显示 found,这个包的环境才算真正装完。注意这步不产生编译错误,只产生运行错误,很容易被跳过,实际部署时一定要做。
5. 避坑记录:xcb 报错、GL 头文件缺失与 ABI 翻车现场
5.1 报错:qt.qpa.plugin: Could not load the Qt platform plugin “xcb”
现象:程序编译通过,运行后打印 “could not load” 然后立刻退出,有时带着“Cannot load library”的后缀,同时提示 platform plugin 搜索路径。
原因:静态 Qt 虽然把 libqxcb.a 编进程序,但 xcb 插件运行时要动态加载 libxcb-xinerama.so.0 等系统库,目标机恰好缺少其中某一个时,整个插件加载被中止,Qt 就给出这个误导性很强的报错。开发机上程序能跑,不代表迁移目标机没问题。
解决:先对程序执行 ldd 看缺失项,再用包管理器补齐。我当时缺的是 libxcb-xinerama.so.0,执行sudo yum install -y xcb-util-xinerama后问题消失。从这个经历之后,我凡是拿到静态 Qt 包,会先编个空窗口程序跑一遍,确认平台插件加载成功再开始写业务代码。
5.2 报错:QXcbConnection: XCB error: 148 (Unknown)
现象:窗口能打开,但界面白屏、刷新卡死,终端持续刷 XCB error 148,系统响应越来越迟钝,最后只能强制杀进程。
原因:Qt 启动时会尝试创建带 OpenGL 的渲染上下文,但目标机的 X server 或显卡驱动不支持当前 GLX 扩展,导致渲染初始化失败后会陷入反复重试。很多远程桌面和老旧显卡场景下都会遇到,和 Qt 包本身无关,是渲染后端不匹配。
解决:强制 Qt 走软件渲染。设置环境变量 QT_XCB_GL_INTEGRATION=none,让 xcb 插件不启用 GL 集成,界面直接通过软件绘制。这是我在无独显的老工作站上试过最有效的手段,代价是部分 3D 效果缺失,普通 Widgets 程序完全够用。如果还需要 OpenGL 数据渲染,可以试 LIBGL_ALWAYS_SOFTWARE=1,把系统 GL 调用整体转成软渲染。
5.3 报错:GL/gl.h: No such file or directory
现象:编译自己的 Qt 程序时,编译阶段直接报缺少 GL/gl.h,后续还有一堆和 OpenGL 相关的头文件找不到,编译中断在 **qopenglfunctions.h 附近。
原因:静态 Qt 库在编译时引用了 OpenGL 头文件,但头文件不属于 Qt 包分发内容,要由系统提供。很多精简系统只装了运行库,没装开发包,所以编译时缺头文件。
解决:安装 mesa-libGL-devel 和 mesa-libGLU-devel,这两个包提供 GL/gl.h、GL/glu.h 以及链接时需要的 -lGL 库。装完重新执行 make,之前报错的位置应该能正常通过。这个坑在纯 Qt Widgets 项目里也会出现,因为 Qt 的 GUI 模块编译链接时统一走 OpenGL 检查逻辑。
5.4 报错:undefined reference to `std::__cxx11::basic_string’ 相关符号
现象:自己写的代码和静态 Qt 库链接时,链接器报出大量 undefined reference,符号里带 std::__cxx11 前缀,看起来像标准库内部符号缺失。
原因:gcc 5.1 以后引入双 ABI,新编译器默认开启 _GLIBCXX_USE_CXX11_ABI=1,生成的新版 std::string 符号和 gcc 4.8.5 编译出的旧符号不兼容。静态 Qt 库是旧 ABI 编出来的,你用新版 gcc 编自己的代码,两边符号对不上,链接器自然找不到定义。
解决:方法有二。一是编译时加-D_GLIBCXX_USE_CXX11_ABI=0,把当前编译单元切回旧 ABI,让 std::string 符号和 Qt 库对齐,这是收益最快的做法;二是干脆用 gcc 4.8.5 同代编译器来编项目代码,从源头避免 ABI 差异。能一并编就一并编,是这套环境下最稳妥的铁律。
5.5 翻车现场:qmake 被 qtchooser 劫持
现象:刚配置完环境,which qmake 指到正确路径,可一进入项目目录执行 qmake,项目却用了系统自带的另一个 Qt 版本,生成的 Makefile 里全是别的路径。
原因:某些发行版自带 qtchooser 工具,会在 PATH 里拦截 qmake 命令并转发到它配置的 Qt 版本。如果直接在项目终端里运行 qmake,没确认 PATH 是否真的生效,就很容易中招。
解决:进项目目录后先执行which qmake和qmake -query QT_INSTALL_PREFIX,两条都指向 /opt/qt-5.9.9-static 再继续。我后来把这两条写进项目构建脚本开头,每个新终端都自动校验,彻底杜绝了这类玄学问题。
6. 验证跑通:qmake 构建一个 Qt Widgets 程序并在 xcb 下运行
这套环境装没装好,不是看 qmake -v 顺不顺眼,而是看能不能编译出一个真实 GUI 程序并在 xcb 下跑起来。我每次都用同一个最小 Widgets 程序做验证,步骤固定,结果可复现。
先在项目目录建两个文件。main.cpp 只做一件事:显示一个窗口,标题栏打印 Qt 版本和当前平台插件名。demo.pro 用最简单的 qmake 工程格式。
// main.cpp #include <QApplication> #include <QLabel> #include <QVBoxLayout> #include <QWidget> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(QString("Qt %1 / QPA: %2").arg(qVersion()).arg(app.platformName())); QVBoxLayout layout; layout.addWidget(&label); QWidget w; w.setLayout(&layout); w.setWindowTitle("xcb_demo"); w.resize(320, 120); w.show(); return app.exec(); }# demo.pro QT += widgets TARGET = xcb_demo TEMPLATE = app SOURCES += main.cpp然后按以下顺序执行构建,每一步的输出要能对上预期。
source /opt/qt-5.9.9-static/qt-env.sh mkdir -p ~/build/qt-demo && cd ~/build/qt-demo qmake ~/src/qt-demo/demo.pro make -j4qmake 正常生成 Makefile 后,make 结束应看到可执行文件 xcb_demo。运行验证分两步:先看 xcb 插件加载日志,再确认最终动态依赖。
QT_DEBUG_PLUGINS=1 QT_QPA_PLATFORM=xcb ./xcb_demo & ldd ./xcb_demo | grep -E "Qt|xcb|GL|font"QT_DEBUG_PLUGINS=1 会打印插件的加载过程,看到 “Loaded QXcbIntegration” 字样说明 xcb 平台插件加载成功;ldd 输出里不应出现 libQt5Core.so 这类 Qt 动态库,因为这是静态构建,但可以出现 libxcb 开头的系统库。窗口标题显示 Qt 5.9.9 / QPA: xcb,程序能正常打开关闭,整个环境验证通过。从那以后,我每次拿到陌生静态 Qt 包都会强制走一遍这套流程,从环境变量校验到最小程序跑通,再去接真实项目代码。没跑通之前,任何其他优化都是空中楼阁。希望帮到你。
本文还有配套的精品资源,点击获取