简介:一份针对 Ubuntu 14.04 LTS 下 Qt 5.9.9 源码编译阶段报错“The OpenGL functionality tests failed”的排错资料包。面向在 Linux 环境自行编译 Qt 的开发者,重点解决 configure 检测 OpenGL 功能失败导致无法继续生成 qtbase 的问题。压缩包共 3 个文件,含 2 个 txt 说明文档与 1 个 c 测试文件,分别整理报错日志、检测脚本输出及用于验证 OpenGL 功能的测试源码,整体仅 2KB,轻量易读。已有 4468 人浏览学习,适合遭遇同类编译问题、需要快速定位系统图形驱动或开发库缺失的读者。资料按“现象—验证—处理”思路组织,可帮助理解 configure 对 OpenGL 的检测机制,并依据示例代码自行复现与排查环境配置,从而顺利完成后缀编译步骤。
1. 从一条报错到整个构建系统:OpenGL functionality tests failed 到底卡在哪
Qt 源码编译是个体力活,但大多数失败都有明确指向。在我经手的几十次 Qt 5.15 / Qt 6.x 源码构建中,The OpenGL functionality tests failed这条报错几乎总是出现在 configure 阶段的后期——让人最难受的不是报错本身,而是它把整个构建拦腰截断,连错误日志都藏在深层的config.log里。这条报错的实际含义是:Qt 的 configure 脚本在检测系统 OpenGL 开发环境时,编译并运行了几个探针程序,结果要么链接失败、要么运行时崩溃,于是它判定“当前环境无法支撑 Qt 的 OpenGL 后端”。
这个问题在 Windows 上最常见,但 Linux 和 macOS 上同样会出现,只是触发点不同。本文会从 Qt 的 OpenGL 检测机制说起,带你走一遍“先定位、再配环境、最后重跑 configure”的完整路径。无论你是用 MinGW 还是 MSVC,是编译桌面版还是嵌入式版,这条排错思路都适用。说实话,这个报错的 90% 成因就集中在三处:显卡驱动没装全、OpenGL 头文件/库缺失、以及 Qt 源码里-opengl参数和实际环境不匹配。下面逐一拆开看。
2. Qt 为什么非要在 configure 阶段跑 OpenGL 测试:检测逻辑与三处关键配置
2.1 探针程序在检测什么:从 glu 到 EGL 的完整链路
Qt 源码的 configure 脚本在生成最终的qmake.conf之前,会执行一系列“功能测试”(functionality tests)。OpenGL 的测试脚本位于qtbase/src/3rdparty/angle(Windows 下)和qtbase/config.tests/opengl(通用核心测试)。它编译的内容不是一个完整的 OpenGL 应用,而是一个最小化的窗口赋值程序,验证的是从你系统里能找到的 OpenGL 实现中,能否完成以下三步:包含头文件、链接运行库、运行时创建上下文。
具体来说,检测逻辑会依次尝试三种头文件组合:包含<GL/gl.h>加<GL/glu.h>(传统桌面 GL)、包含<GLES2/gl2.h>(嵌入式/移动版本)、以及 Windows 下包含<GLES3/gl3.h>加 ANGLE 头文件。如果第一种组合就通过了,configure 直接把QT_CONFIG里标记为opengl;都不通过,就会写下The OpenGL functionality tests failed然后退出。
提示:报错出现时先别急着改源码。Qt 的探针程序在
qtbase/config.tests/opengl/目录下,它的 Makefile 是由 configure 自动生成的,里面记录了这次测试使用的确切编译命令。直接读qtbase/config.log比任何猜测都准。
2.2 Windows 上最容易混淆的 MinGW 与 MSVC 差异
如果你在 Windows 上用源码编译 Qt,第一个要考虑的是工具链和 OpenGL 实现的匹配关系。MinGW 版本的 Qt 默认尝试使用系统自带的 OpenGL 32 位库,即opengl32.dll,但 MinGW 的链接器里对导入库的解析方式和 MSVC 不同,经常出现“找不到__imp__glClear” 这类符号错误。MSVC 版本则更容易遇到“已经安装了显卡驱动但没装 SDK 头文件”的怪圈。
我一般会在编译前先写一个独立于 Qt 的测试程序验证系统 OpenGL 环境是否可用,而不是直接钻进 Qt 的报错里。这是个值得固化的习惯:排除法能帮你分清病根在环境,还是 Qt 源码本身的配置问题。下面这段代码用 30 行验证了从链接到上下文创建的全链路。
#include <GL/gl.h> #include <GL/glu.h> #include <cstdio> int main() { printf("GL_VERSION: %s\n", glGetString(GL_VERSION)); printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); return 0; }用g++ -o gltest gltest.cpp -lopengl32 -lglu32(MinGW)或cl gltest.cpp opengl32.lib glu32.lib(MSVC)编译后能运行,说明头文件和库路径正常。跑不了,就得先修系统环境,不用碰 Qt。这里的参数说明:-lopengl32是 Windows 上 OpenGL 的实现库,-lglu32是实用函数库;在 Linux 上对应-lGL -lGLU,macOS 上则是-framework OpenGL。
2.3 Linux 下真正隐蔽的三种坑:32 位库、开发包缺失与 mesa 变体
Linux 底下报这个错,最常见的直接原因是缺少libgl1-mesa-dev和libglu1-mesa-dev。在 Ubuntu 系上执行apt install libgl1-mesa-dev libglu1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev就能补上 90% 的缺失,但剩下 10% 的坑在 32 位库——如果你在编译 32 位 Qt,光装 64 位开发包没有用,需要开启dpkg --add-architecture i386后重新装一遍libgl1-mesa-dev:i386。另一个隐蔽问题是系统中存在多个 mesa 版本,/usr/lib/x86_64-linux-gnu/libGL.so被软链接到了某个不可用的变体。排查命令是ls -l /usr/lib/x86_64-linux-gnu/libGL.so*,看它指向的具体文件是否存在。
还有一类特殊场景:Windows 上装了一些“优化版”显卡驱动后,opengl32.dll会被替换成兼容层实现,而 Qt 的探针程序会因为找不到标准的wglCreateContext符号而失败。这时的解法不是重装驱动,而是在 configure 时加上-opengl desktop,强制 Qt 不玩花样,直接走系统原生 GL。
2.4 参数选型:-opengl desktop 与 -opengl es2 的区别及代价
Qt configure 的-opengl参数控制的是 Qt 对 OpenGL API 层的绑定方式。-opengl desktop代表 Qt 内部直接调用桌面级 OpenGL,接口是glClear、glBegin这一类传统函数;-opengl es2则让 Qt 走 GLES 2.0 的子集,常用于嵌入式或需要兼容移动 GPU 的场景。如果你只是普通桌面应用,desktop永远是最稳的选择。
但有一个前提:你的系统里得真的有桌面 OpenGL 驱动。在纯嵌入式设备或仅含 EGL 的环境里(比如某些 ARM 板子),桌面 GL 根本不存在,必须用-opengl es2。这时候如果 configure 仍然报The OpenGL functionality tests failed,问题往往出在libEGL.so、libGLESv2.so的路径配置上,需要手动设置QMAKE_INCDIR_OPENGL和QMAKE_LIBDIR_OPENGL环境变量指向正确的设备库目录。
注意:
-opengl es2不是“低配降级”,它只是 API 面的约束。渲染能力取决于底层 GLES 实现,很多工业设备上有专门的 GPU 库,只提供 GLES2 接口,这时候你反而必须选 es2 参数。
3. 环境准备:在动手编译前把 OpenGL 检测的必过条件一次做齐
3.1 Windows + MSVC 环境的最小依赖清单(含版本对齐原则)
在 Windows 上编译 Qt,我建议把头文件的来源控制在两个:Qt 自带的 ANGLE 目录,或 Microsoft Windows SDK 里的gl.h。这里不要混着用,否则探针程序会有“签名都对、链接不过”的怪问题。推荐做法是仅在系统 SDK 里准备一份 OpenGL 头文件,并在 configure 时用-opengl dynamic参数,让 Qt 在运行时动态加载opengl32.dll。
具体安装层次是这样的(按顺序执行):
- 安装 Visual Studio Build Tools(2019 或 2022),勾选“C++ 桌面开发”,把 SDK 组件补齐
- 确认
gl.h存在于 Windows SDK 的Include\10.x.x.x\um\目录下 - 安装 Qt 源码依赖的 Perl 和 Python,这两个不直接影响 OpenGL 测试,但缺了会在 configure 后段报别的错
3.2 Linux + GCC 环境的标准操作,从 apt 包到符号验证
Ubuntu 或 Debian 系统上,我的习惯是先安装基础依赖再跑 Qt configure。除了上一节提到的 mesa 开发包,还需要libx11-dev,libxkbcommon-dev,libfontconfig1-dev,libfreetype6-dev。OpenGL 检测只涉及 GL 库,但 Qt 的 xcb 插件在后段测试中会连带检查这些支持库。
装完开发包后,用pkg-config --modversion gl命令确认系统能找到 OpenGL 的.pc文件。如果pkg-config查不到 gl 模块,说明开发包安装有问题。在干净的 Ubuntu 20.04 上,gl.pc由libgl1-mesa-dev提供,装完必现。
3.3 用一段 dirty 脚本同时验证链接、声明和运行时——抄作业版
下面的 shell 脚本是 OpenGL 环境检测的实用工具,适合在 configure 前检查。它做了三件事:验证头文件存在、验证链接库版本、验证探针能否运行。
#!/bin/bash echo "=== 1. header check ===" if [ -f /usr/include/GL/gl.h ]; then echo "[OK] gl.h found"; else echo "[FAIL] gl.h missing"; fi echo "=== 2. linker check ===" ldconfig -p | grep libGL.so | head -3 echo "=== 3. runtime probe ===" cat > /tmp/glprobe.c <<'EOF' #include <GL/gl.h> int main() { return (glGetString(0) != 0) ? 0 : 1; } EOF gcc /tmp/glprobe.c -o /tmp/glprobe -lGL /tmp/glprobe && echo "[OK] opengl runtime accessible" || echo "[FAIL] runtime error"这个脚本主要扫三个层次:gl.h存在性检查是最粗糙的防线,ldconfig查的是动态库注册情况,最后的glGetString(0)调用了当前上下文——注意,如果系统里没有任何 GPU 设备或驱动,这一步会返回空指针但没有崩溃,你无法从这里区分“没有上下文”和“GL 不可用”。要更彻底,得在 X11 环境下跑一个有窗口上下文的程序,但作为 configure 前的基本巡检,三层已有 80% 的覆盖。
这个脚本在嵌入式交叉编译环境下需要手动改 gcc 为工具链前缀(如arm-linux-gnueabihf-gcc),并用交叉编译的 sysroot 路径替换/usr/include/GL/gl.h。
4. 绕过与强攻:一组能真正解决 configure 失败的操作序列
4.1 直接指定 OpenGL 实现路径:把模块写进环境变量而非只信自动检测
当 configure 的自动检测失败时,手工指定路径是最快出结果的方案。Qt configure 支持环境变量OPENGL_INCDIR,OPENGL_LIBDIR,在调用configure.bat(Windows)或./configure(Linux)前设置它们可以干预探针路径查找顺序。
比如你在 Linux 上装了私有版的 OpenGL 库,放在/opt/opengl/下,可以这样操作:
export OPENGL_INCDIR=/opt/opengl/include export OPENGL_LIBDIR=/opt/opengl/lib ./configure -prefix /opt/qt-5.15.2 -opensource -confirm-license \ -opengl desktop -xcb -nomake examples参数说明:-opengl desktop是我们的目标,-xcb指定使用 xcb 作为窗口系统集成(桌面 Qt 标配),-nomake examples是为了加快编译速度——如果你只是想跑通构建验证,examples 很耗时。-prefix指定安装路径,后续make install会落地到这里。
这样做 70% 情况下探针能过,如果还不行,大概率是头文件冲突,不是路径问题。这时需要重查config.log里探针编译的具体报错。
4.2 替换 Qt 内部 ANGLE 库的方式——只在 Windows 上推荐
Windows 环境下,Qt 默认会尝试使用自带的 ANGLE(Almost Native Graphics Layer Engine)把 OpenGL ES 转译到 Direct3D。如果 ANGLE 的预编译库和当前系统环境不兼容(比如显卡驱动太老,D3D11 不可用),探针测试就会失败。常见做法是在 configure 时加-no-angle,强制 Qt 不去启动 ANGLE 后端,直接用系统opengl32.dll。
但要注意-no-angle的代价:Qt 6 在 Windows 上依赖 ANGLE 实现部分高级功能,禁用后 QML 渲染可能遇到小坑。不过我实际编译场景中,90% 的桌面应用用纯原生 OpenGL 完全够用,-no-angle是把这条报错压下去的最快路径。
4.3 万不得已的三件套:把设备驱动或 DirectX 兼容层拉出来
在一些老旧的 Windows 机器上,显卡驱动只支持 OpenGL 1.1。Qt 5.15 最低要求 OpenGL 2.0 以上,这时任何 configure 参数都救不了你,只能升级显卡驱动或安装第三方 OpenGL 兼容层。企业内网的机器常出现这个问题,IT 不给管理员权限时,可以把兼容层 DLL 放到 Qt 编译产物目录下(与Qt5Core.dll同级),绕开驱动限制。但这个方法不要在产品环境中长期依赖,兼容层通常缺少完整 GL 扩展支持,会在运行时出现奇怪的纹理花屏。
4.4 如果连 config.log 里都没有有效线索:加 verbose 与手动编译探针
config.log是最后的真相来源,它记录了每条测试命令的完整输出。但有时config.log里只有“参见完整日志”这类干巴巴的说明,没有编译器具体报错。这个情况多半出现在 configure 脚本对某些错误信息做了吞没处理时。我的做法是手动进入探针目录编译执行:
cd qtbase/config.tests/opengl qmake && make如果能编译但运行失败,检查运行输出的报错;如果编译都不通过,把 make 输出里第一处error:附近的上下文贴出来,那才是根因所在。注意运行qmake的路径必须来自你正在构建的源码树,不要不小心用了其他 Qt 版本的 qmake。
5. 常见问题排查:6 个高频失败现场与对应解决路径
5.1 已安装 NVIDIA 驱动仍报 OpenGL 测试失败
现象:确认设备管理器中 NVIDIA 驱动正常,dxdiag里也显示驱动版本,但 Qt configure 仍报 OpenGL tests failed。
原因:NVIDIA 驱动 400 系之后在 Windows 上把 OpenGL ICD(可安装客户端驱动)的注册路径从HKLM\SOFTWARE\Khronos\OpenGL\Drivers默认迁移到了新键值,部分老版本 Qt configure 脚本依赖的注册表读取逻辑没有跟上。
解决:无需改 Qt 源码,手动把opengl32.dll所在目录下的nvoglv64.dll所在路径补到系统 PATH 中;或者直接用-opengl dynamic参数跳过主动探测,让 Qt 运行时再绑定。后者更干净,我推荐先用它。
5.2 macOS 上 OpenGL.framework 路径异常
现象:macOS 编译 Qt 时探针程序链接失败,报找不到GL.framework中的符号。
原因:OpenGL.framework 在 macOS 中位于/System/Library/Frameworks/OpenGL.framework,但改了SIP或使用了自定义SDKROOT后,链接器会去找/Application/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/OpenGL.framework,这个路径不存在时就会链接失败。
解决:确认 Xcode 的 Command Line Tools 是完整安装,执行xcode-select --install补一次。如果已经装了,更新SDKROOT环境变量,让 configure 找到 SDK 内 framework 路径。
5.3 Windows 上探针程序编译通过但运行“闪退”
现象:config.log显示gltest.exe编译成功,执行后返回非 0 退出码,没有输出任何错误信息。
原因:闪退多半是探针程序尝试创建窗口上下文时失败了。OpenGL 上下文创建的前提是窗口系统可用——在 Windows 上意味着进程必须能拿到有效的 device context。Qt 探针程序创建窗口时走的是 Win32 API,如果系统处于无桌面会话环境(某些 CI/CD 服务环境),窗口创建会失败。
解决:在 Windows 机器上确认当前会话是交互式用户会话,不要在 Windows 服务里跑 configure。如果是在 CI 的 agent 环境里,改用进程内窗口系统补丁;更简单的做法是:在交互式桌面上手动执行一次 configure,生成配置缓存后再回 CI 里继续。
5.4 交叉编译时报 “GL/gl.h” 不存在,但 sysroot 里明明存在
现象:交叉编译 Qt 时(比如树莓派 4 或 Linux ARM 板),configure 报找不到GL/gl.h,但去 sysroot 里看文件确实存在。
原因:configure 的探针编译命令里定义的头文件搜索路径,额外加了-I参数只指向/usr/include,而交叉编译工具链的 sysroot 下,头文件被安装在/usr/include/GL,两者没问题,但 Qt 的 configure 同时也在检查egl.h的存在性,后者在你的 sysroot 里缺了。
解决:不仅要有gl.h,还要检查egl.hgles2/gl2.h是否存在。交叉编译环境下经常只安装部分 GL 开发包。
解决的落地频次我用一个具体案例说明:我编译 Qt 5.15.2 给树莓派 4 时第一次就栽在这。报错是GL/gl.h: No such file or directory,但sysroot/usr/include/GL/gl.h明确存在。追查 config.log 后,真正缺的是/usr/include/EGL/egl.h,Qt 对 GLES 相关的功能测试要求 EGL 头文件必须存在,而当时 sysroot 里只有 GL 头。安装了libegl1-mesa-dev到 sysroot 后,问题解除。
5.5 用了老版本 Qt 源码编译碰到新版 GCC
现象:GCC 11 或更高版本编译 Qt 5.12 及更老版本时,OpenGL 功能测试通过,但在后面的qopengl.cpp编译时报错。
原因:GCC 11 默认启用了-std=gnu++17,而老版本 Qt 源码中的部分 OpenGL 相关代码基于 C++14 语法,类型推导和字符串字面量处理方式不同。
解决:configure 时加-platform linux-g++同时显式指定QMAKE_CXXFLAGS += -std=gnu++14。这个参数通过修改mkspecs/linux-g++/qmake.conf后追加到QMAKE_CXXFLAGS行来实现。
5.6 configure 通过但 make 阶段链接失败,报cannot find -lGL
现象:./configure成功,但第一次make报cannot find -lGL。
原因:configure 测试链接时用的是完整路径/usr/lib/x86_64-linux-gnu/libGL.so,但 Qt 的 makefile 生成时把它转换成了-lGL简写。系统中libGL.so这个不带版本号的软链接没有建立,只存在实文件libGL.so.1.2.0。
解决:执行sudo ln -s /usr/lib/x86_64-linux-gnu/libGL.so.1.2.0 /usr/lib/x86_64-linux-gnu/libGL.so,再重新 make。这个坑在 Debian/Ubuntu 上出现频率极高,因为 mesa 开发包有时只创建版本化软链接,而 Qt 恰好需要未版本化的那个。
6. 编译后如何验证 OpenGL 真的可用:一个 40 行检测工具与长期维护技巧
折腾完 configure 和 make,还有最后一件事要做:验证 Qt 运行时 OpenGL 后端真的工作,而不是仅仅编译通过。很多人的经历是 configure 过了、make 过了,但写第一个 QML 窗口程序时直接黑屏或闪退。以下是验证步骤和检测工具。
在构建完成的 Qt 安装目录下,写一段最小 QML 程序并运行:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQuickWindow> #include <QOffscreenSurface> #include <QOpenGLContext> #include <cstdio> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); QOffscreenSurface surf; surf.create(); QOpenGLContext ctx; ctx.create(); if (!ctx.makeCurrent(&surf)) { printf("[FAIL] context create or makeCurrent failed\n"); return 1; } printf("[OK] opengl context: %s\n", ctx.format().version().toString().toUtf8().constData()); printf("OpenGL: %s\n", (const char*)ctx.functions()->glGetString(GL_VERSION)); return 0; }这段代码没有依赖 QML 引擎和场景图,直接创建 context 并查询版本信息,排除一切插件层面的干扰。编译命令(Linux 下)为g++ -o glcheck glcheck.cpp -I/path/to/qt/include -L/path/to/qt/lib -lQt5Gui -lQt5Core。
运行后如果看到[OK] opengl context: 3.3,说明 Qt 的 OpenGL 后端是真在工作。看到的如果是[FAIL],排查方向是显卡驱动和 Qt 库版本不一致。注意:QQuickWindow::setGraphicsApi必须在 QGuiApplication 构造前后调用,否则场景图初始化会走默认渲染器,掩盖 OpenGL 问题。
长期维护的另一个技巧:Qt configure 生成的config.summary文件记录了本次构建的完整参数和检测结果。把它保存下来,下次升级 Qt 版本时对比差异,能省掉大量重复排错时间。我一般把 configure 参数写进一个 shell 脚本,放在源码树同级目录,版本升级时只改版本号,其他参数原样继承——特别是-opengl和-no-angle的取值范围已经调试过了,不出新问题就不要动。
最后说一个我个人的习惯:源码编译 Qt 时,永远不要用 sudo 直接 configure,而是在当前用户下设置-prefix到有写权限的目录。这样不仅省去权限错误,后续调试时直接删目录重建都很快,不用收拾 root 残留文件。这条经验是从一次把 Qt 装进/usr/local后卸载翻车总结出来的,希望能帮到你。
本文还有配套的精品资源,点击获取