☰
Telegram Desktop 源码编译实战:QT 5.3.1 与 MSVC 工具链全解析
2026/9/29 13:16:46 网站建设 项目流程

简介:这是一份Telegram开源IM在Windows环境下的编译实战记录,主要面向有一定C++基础、尝试自行编译Telegram桌面版或QT5.3.1的开发者。作者基于VS2013和tdesktop官方MSVC指南,完整记录了从环境准备、OpenSSL/LZMA/zlib/libexif等依赖搭建,到Telegram源码编译中各类典型错误的定位与处理,并单独整理了QT5.3.1的编译过程,包括Perl/Python/Ruby环境配置、ICU可选安装、VS2013开发人员命令提示下的configure与nmake参数等。整份资源为1个doc文档,压缩包仅29KB,内容高度浓缩,适合作为编译Telegram/QT时快速查阅的排错手册。目前已有569人学习下载。文档中对openssl头文件路径错误、Qt5Widgets.lib缺失、libeay32MT.lib链接失败等高频问题给出了具体修改方法和命令示例,能帮助读者少走弯路。

1. 从源码编译 Telegram Desktop:为什么非要碰 QT 5.3.1

Telegram Desktop 的官方预编译包能满足日常使用,但当你需要定制客户端、修改 UI 逻辑、或者想在离线内网环境部署时,从源码编译就成了绕不开的路径。更麻烦的是,Telegram Desktop 的历史版本和 QT 5.3.1 绑定得很死——新版 Qt 的接口变化会让老代码直接编译失败。我拆这个项目时踩了十几个坑,从 MSVC 工具链到 qscintilla 静态库,每一步都有值得记录的细节。这篇笔记直接给你可复现的完整流程,包括 configure 参数、jom 并行编译设置、以及cannot find -lpublic这类高频报错的定位思路。如果你正准备在自己的机器上完整编译一遍 Telegram Desktop,尤其是用 QT 5.3.1 这个老版本,这篇就是给你写的。

2. 环境准备与工具链选型:先把三种“编译器”的账算清

2.1 MSVC vs MinGW:QT 5.3.1 的兼容性真相

Telegram Desktop 官方推荐的编译环境是 MSVC(Microsoft Visual C++),而不是很多人习惯的 MinGW。原因很直接:QT 5.3.1 时代的 Windows 版本对 MinGW 的支持不完整,Telegram 源码里大量使用了windows.h底层 API 和 MSVC 特有的#pragma指令,MinGW 的 GCC 解释不了这些代码。我见过有人硬用 MinGW 编 Telegram,最后在链接阶段遇到几百个未解析符号,那个排查过程非常痛苦。

正确做法是安装 Visual Studio 2013 或 2015 的社区版——注意版本的匹配。Telegram Desktop 在 QT 5.3.1 分支时,官方测试环境是 VS2013,用 VS2015 也能编,但 VS2017 以上的工具链会对老代码里的某些类型转换报错,因为std::auto_ptr被彻底移除了。

如果你只有 VS2019 或 VS2022,也不是完全没救,需要额外做一件事:安装“单个组件”里的Windows 7 SDK和MSVC v120 工具集(VS2013 的编译器核心)。没有这个,QT 5.3.1 的qmake生成的 Makefile 里的-GL-参数会直接报错。

提示:MSVC 工具链和 MinGW 的区别不只在编译速度,最重要的是 ABI(应用二进制接口)不同。QT 库如果用的是 MSVC 编译的,你的业务代码也必须用 MSVC,混着链接会出LNK2038这类 mismatch 错误。

2.2 源码获取:Telegram 仓库的完整克隆方式

Telegram Desktop 的源码在 GitHub 上,仓库地址是telegramdesktop/tdesktop。因为项目体积很大(超过 2GB),加上子模块众多,直接git clone经常会中断。常见做法是先用--depth=1做浅克隆,再拉子模块。

git clone --depth=1 --branch v1.0.0 https://github.com/telegramdesktop/tdesktop.git cd tdesktop git submodule init git submodule update --depth=1

这段命令的逻辑是:--depth=1只拉最新的一次提交,大幅减少传输量;--branch v1.0.0指定了你要编译的分支——如果你要编 QT 5.3.1 版本,需要切到对应历史分支,比如v0.9.6或v1.0.0。git submodule update会把 Telegram 依赖的第三方库(如libtgvoip、ffmpeg、qscintilla等)拉下来。

这里有个重点:子模块必须拉全,否则编译到一半会报fatal error: 'libtgvoip/Media.h' file not found之类的头文件缺失错误。检查子模块是否完整的命令是:

ls telgui/ThirdParty/

正常情况下你应该看到libtgvoip、ffmpeg、qscintilla、catch等目录都在。缺哪个就单独初始化哪个,比如缺 qscintilla 就执行git submodule update --init --recursive telgui/ThirdParty/qscintilla。

2.3 依赖库清单:QT 5.3.1 之外还需要什么

Telegram Desktop 的依赖库比想象中多,QT 只是其中一块。核心依赖包括:

依赖库用途获取方式
QT 5.3.1主界面框架、网络、事件循环官方源码编译或预编译包
qscintilla代码编辑器控件(用于 Telegram 的代码高亮)源码编译,注意必须与 QT 同版本
libtgvoipVoIP 语音通话子模块拉取
ffmpeg音视频编解码子模块拉取,如果不需要语音和视频可以禁用
OpenSSL加密协议系统自带或另行编译

其中最容易出问题的是 qscintilla。它本身是个独立的 QT 控件库,Telegram 的TelegramMessenger里用它实现消息代码块的语法高亮。qscintilla 的编译必须和 QT 5.3.1 严格同版本,否则会出现 slots 信号连接失败——这个报错在运行时才暴露,编译期是看不出来的。

另外,如果你不需要语音通话,编译时可以加TDESKTOP_DISABLE_TGVOIP=1这个开关,省掉 libtgvoip 的编译时间。我一般会禁用,因为语音通话模块依赖的 WebRTC 编译复杂度很高,动不动就要 40 分钟起步。

3. QT 5.3.1 完整编译:configure 参数与 qscintilla 联动

3.1 configure 参数怎么给:静态链接的关键配置

QT 5.3.1 的编译是整个流程里耗时最长的环节,用默认参数编一次大约 2 小时,所以 configure 参数必须一次给对。我实际使用的参数组合是这样的:

cd qt-5.3.1 configure.bat -prefix D:\Qt\5.3.1\msvc2013 -opensource -confirm-license -debug-and-release -static -opengl desktop -no-openssl -no-icu -no-angle -nomake tests -nomake examples -mp

逐个拆解这些参数的含义:

  • -prefix指定安装路径,务必放在空间充足的盘,QT 编译后占用约 5GB。
  • -debug-and-release同时生成调试版和发布版。Telegram 的 qmake 文件里默认两个版本都会用,省掉后面再次配置的麻烦。
  • -static是关键——Telegram Desktop 默认要求静态链接 QT,因为官方发布的 Windows 版就是这样打包的,动态链接反而需要额外拷贝大量 dll。
  • -no-openssl在 configure 阶段跳过 OpenSSL 检测,Telegram 会自己处理加密库,这能避免 QT 的 OpenSSL 版本和系统版本冲突。
  • -no-angle很重要。ANGLE 是 QT 用来把 OpenGL ES 翻译成 DirectX 的库,Telegram 不需要它,而 ANGLE 编译极慢,去掉能省 20 分钟。
  • -mp开启多处理器编译,配合后面的jom使用,否则单线程编译 QT 是灾难。

configure 执行完后会生成Makefile,然后跑编译:

jom -j8 jom install

第一条命令里的-j8表示 8 个并行任务。你要根据 CPU 核心数调整,一般-j4到-j16之间。如果内存只有 8GB,建议-j4,因为并行编译每个任务会吃 700MB 左右内存,8 个任务可能直接爆内存导致编译进程被系统杀掉。

3.2 编译失败时怎么看:jom 输出与日志定位

jom 编译失败是常态,关键是学会快读日志。QT 编译报错的常见形式有两种:

  • Error 1 (for counter) : cl开头——这是真正的 MSVC 编译错误,说明某个 C++ 源文件编译不过。
  • NMAKE : fatal error U1077—— 这是依赖错误,通常是因为上一个文件没有正确生成,导致链接阶段缺文件。

处理策略就一条:先修第一个报错,不要往下翻。因为后续错误大概率是同一个原因导致的连锁反应。比如qtbase编译时报cannot open input file 'qtmain.lib',这时候要检查是不是qtmain这个子项目还没 build 完——顺序乱了。

QT 5.3.1 在 Windows 上编译时的“黄金顺序”是:configure生成后,手动构建qtbase这个子模块,因为它是其他所有模块(如qtscript、qtsvg)的基础。命令行单独构建:

cd qtbase jom -j8 cd ..

如果qtbase顺利通过,剩下的模块基本不会有大问题。

3.3 qscintilla 下载与编译:必须手动干预的一步

qscintilla 是个独立的库,不跟随 QT 的 configure 流程。Telegram 的源码里虽然包含了 qscintilla 子模块,但它的 build 文件需要单独执行。这个库的编译极其挑剔,网上很多人在这里翻车。

先看源码目录结构:

cd telgui/ThirdParty/qscintilla

这个目录下有Qt4Qt5子目录,里面有qscintilla.pro文件。进入这个目录执行:

cd Qt4Qt5 D:\Qt\5.3.1\msvc2013\bin\qmake.exe qscintilla.pro -spec win32-msvc2013 jom -j8

如果你用的是 VS2015 工具链,把-spec win32-msvc2013换成win32-msvc2015。这一步非常容易出错的地方是:qmake 中间不能有空格或者中文路径。D:\Qt\5.3.1\msvc2013这个路径中如果出现中文,qscintilla 的.vcxproj文件生成会失败,报一堆Cannot find file的诡异错误。

编译完成后,把生成的qscintilla2.lib和头文件复制到 QT 的 lib 和 include 目录:

copy Qt4Qt5\release\qscintilla2.lib D:\Qt\5.3.1\msvc2013\lib\ copy Qt4Qt5\Qsci\qsciscintilla.h D:\Qt\5.3.1\msvc2013\include\

注意:qscintilla 是 LGPL 协议,静态链接进 Telegram 后,如果你不打算开源自己的改动,需要仔细确认一下合规要求。这里只提编译技术问题,不展开法律细节。

4. Telegram Desktop 主程序编译:qmake、jom 与生成目录

4.1 生成主工程 Makefile:两个关键文件缺一不可

QT 编译完毕、qscintilla 就位之后,才轮到 Telegram 主工程。项目根目录下的Telegram文件夹里有Telegram.pro文件,需要先手动指定 qmake 路径:

cd Telegram D:\Qt\5.3.1\msvc2013\bin\qmake.exe Telegram.pro -spec win32-msvc2013 CONFIG+=debug_and_release

这一步的坑在于CONFIG+=debug_and_release必须加,不加的话后面编译会报debug\...obj : fatal error LNK1181: cannot open input file 'release\...obj'——它试图链接一个不存在的 release 目录下的文件。

qmake 成功执行后,会生成Makefile和Makefile.Release两个文件。老手习惯直接打开Makefile.Release看编译选项——如果里面DEFINES那行没有TDESKTOP_DISABLE_CRASH_REPORTS和TDESKTOP_DISABLE_AUTOUPDATE,建议手动加上。这两个宏分别关闭崩溃上报和自动更新,纯本地编译时开着它们会额外引入网络请求逻辑,反而让程序启动变慢。

4.2 主程序编译:jom 并行与内存调优

Telegram 主工程的编译同样用 jom,但这个项目的并发度不能贪高。Telegram 的代码里包含大量模板类(特别是Streaming模块),单个编译单元内存消耗极大,-j8在 Telegram 主工程上实测容易把 16GB 内存吃满后触发 OOM。

jom -j4

如果内存足够(32GB 以上),可以尝试-j6;内存 8GB 的机器老老实实-j2,虽然慢,但至少不会中途崩。编译时间上,-j4环境下大概 1 小时左右跑完。

编译过程中最常见的一道坎是fatal error C1060: compiler is out of heap space,这是 MSVC 编译器自己内存不足的表现,不是你系统内存不足。解决方法是把cl.exe的/Zm参数调大,在 qmake 的QMAKE_CXXFLAGS里追加/Zm800。这个参数是给编译器预留更多预编译头文件内存的,800 是一个比较安全的数值。

4.3 编译产物路径与验证:拿到 Telegram.exe 只是第一步

编译完成后,可执行文件在Telegram\Release\Telegram.exe。但别急着双击运行——这个 exe 还缺Telegram\Resources目录下的资源文件(图标、语言包、字体等)。如果不拷贝资源目录,程序启动后界面是空白的,或者直接闪退。

复制资源的命令:

xcopy /E /I Telegram\Resources Telegram\Release\Resources

拷完之后,运行 Telegram.exe,如果一切正常会弹出登录界面。这时候注意一个细节:Telegram 首次启动会向官方服务器请求配置,如果你的机器在国内网络环境下,这个请求可能会超时。这不是你编译的问题,是网络环境问题,换个方式处理即可。

另外,Release 目录下除了 Telegram.exe,还会看到Telegram.obj和一堆.pdb文件。.pdb是调试符号文件,如果是自己本地编译,建议保留;但如果你要分发这个 exe,.pdb会暴露源码路径信息,不介意的话可以删掉。

4.4 快速验证编译配置是否生效

编译完之后,验证你的自定义配置是否真的编进去了,可以用这个命令:

strings Telegram.exe | findstr "TDESKTOP_DISABLE_CRASH_REPORTS"

如果输出里有1或者宏名,说明编译配置生效了。注意strings是 GNU 工具,Windows 上可以装个sysinternals的替代品,或者直接用find命令搜二进制里的文本。这个方法在排查“我明明改了配置,为什么编译出的 exe 没变化”这种问题时非常实用。

5. 编译避坑指南:五条高频报错的定位与修复

5.1 报错cannot find -lpublic:QT 库路径没配对

现象:链接阶段报LINK : fatal error LNK1181: cannot open input file 'public.lib'或者 GCC 系的cannot find -lpublic。

原因:qmake 生成的 Makefile 中LIBS变量引用了名为public的库(源自代码里的QT += public或类似的错误拼写),但你的 QT 库里根本没有public.lib。这个问题的本质是 Telegram 的老代码里有一处对QtPlatformHeaders的错误引用。

解决:修改Telegram.pro文件,把QT += public删除或者改成QT += gui。改完后重新执行 qmake,不要只重新跑 jom——因为 Makefile 不会自动感知.pro文件的变化。一定要先删掉旧的 Makefile 再重新 qmake:

del Makefile Makefile.Release qmake.exe Telegram.pro -spec win32-msvc2013 jom -j4

5.2 qscintilla 编译后链接报错:版本不一致的无声失败

现象:Telegram 主程序链接时,报unresolved external symbol "public: virtual struct QMetaObject const * __thiscall QsciScintilla::metaObject(void)const"之类的错误。

原因:qscintilla 编译时用的 QT 版本和 Telegram 主程序编译时用的 QT 版本不一致。最常见的情况是你机器上原来装过另一个版本的 QT(比如 5.12),qmake 时误用了旧版本的qmake.exe,导致 qscintilla 的 moc 文件基于 5.12 生成,但链接时用的 QT 5.3.1 的头文件布局对不上。

解决:检查 qscintilla 的 Makefile 里QTDIR变量指向哪里。确保D:\Qt\5.3.1\msvc2013\bin\qmake.exe在你的 PATH 最前面。再彻底一点的做法是清掉 qscintilla 的 build 缓存重编:

cd telgui/ThirdParty/qscintilla/Qt4Qt5 del Makefile* *debug *release /s /q

然后重新 qmake、重新 jom。这件事没有捷径,只能重来。

5.3 编译中内存不足C1060:玄学但可解的编译器堆问题

现象:fatal error C1060: compiler is out of heap space,而且每次报错的代码文件都不一样,看起来像是随机崩溃。

原因:MSVC 编译器在编译大量使用模板的现代 C++ 代码时,预编译头(PCH)缓存占用会膨胀。这跟系统内存大小没有直接关系,更像编译器内部堆的分配策略问题。

解决:在.pro文件里添加QMAKE_CXXFLAGS += /Zm800,这个参数把编译器的预编译头内存上限从默认值调高。如果还不行,再把/MP(多进程编译)关掉——/MP会启动多个 cl.exe 实例,每个实例各自维护堆,总内存消耗成倍增加。

QMAKE_CXXFLAGS += /Zm800 /MP1

/MP1是强制单进程编译,虽然慢一点,但稳。我用这个方法解决了十几台机器上的相同报错。

5.4 编译产物能跑但界面空白:资源文件缺失的迷局

现象:Telegram.exe能启动,进程也在后台,但窗口一片白色,没有任何响应。

原因:缺少Resources目录下的样式表(.tdesktop文件)和图标资源。Telegram 的程序架构里,所有 UI 资源都是运行时从Resources目录加载的,而不是编译进 exe。这个设计初看很蠢,但它是为了让设计资源能独立更新。

解决:从源码目录的Telegram\Resources完整拷贝到Release目录,注意Resources下还有一个qrc子目录,里面是字体文件,不能漏拷贝。完成后目录结构应该是:

Release/ Telegram.exe Resources/ default.tdesktop icon.ico qrc/ font.ttf

5.5LNK2038 mismatch detected for RuntimeLibrary:MT 和 MD 之争

现象:编译到最后一个链接阶段,报LNK2038: mismatch detected for 'RuntimeLibrary': value 'MD_DynamicRelease' doesn't match value 'MT_StaticRelease'。

原因:你的某个依赖库(比如 qscintillarelease版本)是用动态运行时(/MD)编译的,但 Telegram 主工程用静态运行时(/MT)编译。Windows 不允许混合链接这两种运行时的目标文件。

解决:明确统一策略。如果主工程用/MT(静态运行时),则 qscintilla 也必须用/MT重新编。qscintilla 的qscintilla.pro里追加一行:

QMAKE_CXXFLAGS_RELEASE += /MT

然后重新编译 qscintilla。反过来如果主工程用/MD,那在 Telegram.pro 里设QMAKE_CXXFLAGS_RELEASE += /MD。关键是全项目一个标准,不能混。

6. 进阶验证与产物检查:编译结果到底能不能直接分发

6.1 用 dumpbin 检查 DLL 依赖链

本地编译的Telegram.exe能不能在其他机器上运行,关键看依赖链是否完整。用 MSVC 自带的 dumpbin 工具检查:

dumpbin /dependents Telegram.exe

输出里会出现一长串 DLL 列表。其中值得关注的是Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll这几项——如果你用的是-static编译的 QT,这些 DLL 不应该出现在依赖列表里。如果出现了,说明你的静态编译配置没生效(通常是-static参数没被 qmake 读到),这个 exe 拷到别的机器就会因缺 DLL 跑不起来。

另外注意看有没有libgcc_s_seh-1.dll或libstdc++-6.dll——出现这两个基本上可以判定你混入了 MinGW 编译的目标文件,这个 exe 在纯净 Windows 环境一定缺 DLL。

6.2 版本号与数字签名的修改

编译出来的 Telegram.exe 默认版本号是源码分支的版本(比如 1.0.0)。如果你准备分发到公司内部或者自定义品牌,需要改.rc资源文件里的版本信息。Telegram 的版本号定义在Telegram/Resources/telegram.rc里:

#define VERSION_MAJOR 1 #define VERSION_MINOR 0 #define VERSION_PATCH 0

修改后需要重新编译才生效,因为.rc文件是编译期嵌入 exe 的资源。这一步注意编码格式——.rc文件必须是 UTF-16 LE 编码,否则rc.exe会报资源解析错误。

数字签名这块,如果只是内部使用,可以不签。但要跨机器分发,Windows SmartScreen 会因为无签名弹红窗警告。条件够的话用自签名证书签一下也行,代码:

signtool sign /fd SHA256 /f mycert.pfx /p yourpassword Telegram.exe

注意自签名证书依然会触发 SmartScreen 警告,只是少一点威胁提示。

6.3 环境变量隔离:避免老 QT 残留的坑

编译后的 Telegram.exe 启动时,会先找同目录下的Qt5Core.dll(如果用的是动态链接)。但 Windows DLL 搜索顺序是优先系统目录的,如果另一套 QT(比如 5.12)的 DLL 路径在你的系统 PATH 里,可能出现“加载了错误的 Qt5Core.dll”导致运行崩溃。

一个简单的验证方法:设置环境变量QT_DEBUG_PLUGINS=1后启动 Telegram.exe,观察输出内容。

set QT_DEBUG_PLUGINS=1 Telegram.exe

输出里会打印实际加载的 QT 插件路径。如果指向的不是你的编译目录(D:\Qt\5.3.1\msvc2013\plugins),说明 PATH 污染。这时候把 Telegram.exe 所在的目录拷贝到一个干净的临时目录,单独运行,问题通常就消失了。

6.4 关于低配机器编译的最后提醒

如果你用了-j8编译时内存吃满直接黑屏死机,不要以为是硬件问题。这是 jom 并行任务把内存条榨干了,Windows 直接触发 OOM killer。从那以后我每次编译 Telegram 都会做三件事:打开任务管理器看内存占用、把 jom 并发数压到内存允许的下限、再设置页面文件至少 16GB。这个习惯救了我至少三次,每次都能避免编译到 70% 时系统崩溃前功尽弃。希望这篇编译笔记帮到你,也祝你在踩坑路上少走点弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询