
在Windows 10上用Visual Studio 2019编译WebRTC这件事看着就四步装工具、拉代码、配参数、跑ninja但真正动手的人都知道每一步都能卡半天。网上教程不少可大多要么基于老版本要么默认你已经知道depot_tools是什么结果照着敲几行命令就开始报错最后都不知道该怪编译器还是怪自己。这篇博文就基于Win10 VS2019这套组合把从零到一跑通的全过程、走过的弯路、踩过的坑按顺序记录下来。适合三类人准备做WebRTC二次开发、想读源码但希望先在本地看到编译产物、以及单纯想验证自己机器能不能扛住这个巨型项目的人。我先说结论WebRTC不是一个普通开源库把它理解成“一整个实时通信框架被单独抽出”更准确源码树里塞满了abseil、protobuf、boringssl、FFmpeg相关模块和各种平台抽象层。正因如此编译它考验的并不是你多会写C而是你对构建系统、环境配置和排错流程有多熟悉。下面按步骤来。1. 编译前的硬件与软件配置这些前置条件缺一不可1.1 磁盘、内存与CPU先做好烧盘的准备很多人第一步就栽在磁盘上。第一次拉取WebRTC源码仓库.git目录就有二十到四十GB这还只是开始编译过程中产生的中间文件、obj文件、PDB符号文件动辄再来几十GB。所以我给的最低建议是给这个项目单独预留至少100GB空闲空间并且放在SSD上。ninja会并行创建和读写成千上万个很小的文件机械硬盘在大量随机小文件读写下会慢到让你怀疑人生甚至直接把IO拖死。内存方面16GB是舒适线8GB理论上能编译但链接阶段非常容易内存耗尽。编译时每个并行的cl.exe进程大约要吃几百MB到1GB内存最后链接时还要一次性载入几十个obj文件所以内存紧张的话别硬上默认并行度。CPU核心数当然是越多越好但核心多也意味着内存占用高这两个指标要一起看。我见过8核32G的机器跑得飞快也见过16核16G的机器在链接阶段被系统杀掉进程这种事全看内存够不够。1.2 VS2019工作负载与Windows 10 SDK安装选项别乱勾VS2019装Community版就完全够用关键是安装时一定要勾选“使用C的桌面开发”。这个工作负载会带上MSVC v142编译器和Windows 10 SDK。很多人图省事只装“C生成工具”那个轻量包结果后面gclient的钩子脚本默认按完整VS的安装路径去找工具链找不到就报错。所以别绕弯直接装完整IDE占用大点但省心。SDK版本这个问题比较微妙。gn在检查Visual Studio实例时会尝试匹配它认可的SDK版本范围并不是越新越好。我的建议是安装VS2019时保持默认勾选的Windows 10 SDK版本别为了追求新版把老的SDK组件反选掉。有些教程会让你额外装某个特定SDK其实没必要默认配置反而是gn最容易识别的组合。1.3 depot_tools的落地与环境变量设置depot_tools是Chromium系项目的构建工具集WebRTC用的是同一套里面包含gclient、gn、ninja以及配套的Python环境。下载后建议解压到短路径比如C:\depot_tools千万别放在带空格或中文的目录里。然后把它加到系统PATH的最前面。这一步很关键因为如果你的PATH里同时存在多个git或pythongclient可能选到错误版本后面会冒出一堆莫名其妙的问题。接下来设置两个环境变量比任何教程里的高级参数都重要[Environment]::SetEnvironmentVariable(DEPOT_TOOLS_WIN_TOOLCHAIN, 0, User) [Environment]::SetEnvironmentVariable(DEPOT_TOOLS_UPDATE, 0, User)第一个变量DEPOT_TOOLS_WIN_TOOLCHAIN0的含义是强制告诉构建系统使用本机已经安装的VS2019工具链而不是尝试去下载一个专用toolchain。不设置这个变量时gn默认会去拉一个体积很大的工具链包下载成功率感人很多人在这一步卡了一整天设成0之后就会老老实实用你装好的Visual Studio。第二个变量DEPOT_TOOLS_UPDATE0关掉depot_tools的自动更新。depot_tools每次调用都会先尝试自更新到最新版本如果你的网络环境和官方更新源连接不稳定整个命令会卡在“Updating depot_tools”这一步很久才继续看起来像死机。关掉它之后就没这个烦恼了以后想更新手动执行一次即可。2. depot_tools的首次启动与WebRTC源码获取2.1 gclient的自我引导第一次运行慢是正常的装好环境变量后随便打开一个新的终端敲一个gclient命令depot_tools会开始第一次自我引导。它会下载自己管理的Python运行时、cipd客户端和一些辅助工具。这个过程会打印一大串输出速度不快耐心等就好。很多教程轻描淡写一句“运行gclient一行命令即可”完全没提第一次跑可能要等十几分钟甚至更久。怎么判断它到底是真在干活还是卡死了打开任务管理器看有没有python、cipd这些进程在占用CPU或者看C:\depot_tools目录的大小是否在持续增长。只要有变化就是在正常引导。如果啧了半天一点动静都没有且打印停在更新相关提示很可能就是自更新卡住了按上一节说的设了DEPOT_TOOLS_UPDATE0再重跑即可。2.2 fetch webrtc与gclient sync的分工先建一个干净的工作目录我习惯用C:\webrtcmkdir C:\webrtc cd C:\webrtc fetch --nohooks webrtcfetch --nohooks webrtc会创建src目录拉取WebRTC主仓库的.git数据同时生成一个.gclient配置文件。这里的--nohooks意思是暂时不执行后面的钩子脚本等手动sync时统一处理这样整个流程更可控。fetch结束之后紧接着跑gclient syncgclient sync按照.gclient里的依赖清单把WebRTC关联的几十个子仓库拉齐并执行钩子脚本。这一步是整个过程中最耗时的阶段之一。整个src目录加上.git轻轻松松超过五十GB。别自作聪明去搞什么浅克隆WebRTC的依赖版本是环环相扣的浅克隆省下的时间后面全会在莫名其妙的编译错误里还回去。2.3 路径规范与安全防护软件排除避免“灵异错误”的底子之前说了路径不能有中文和空格这里再把影响说透构建系统里很多脚本会进行路径拼接和哈希计算非ASCII字符和空格会直接导致脚本解析异常而且报错信息往往跟真正的原因南辕北辙排查起来十分痛苦。另一个常被忽视的点是安全防护软件。ninja在编译时会生成大量临时文件和obj文件安全防护软件如果开启实时扫描会逐个检查这些文件速度被拖慢不说偶尔还会把正在写入的中间文件当作可疑对象锁住导致“cannot open output file”这类诡异错误。建议把C:\webrtc和C:\depot_tools都加入排除列表。这一步做完编译时的文件IO负担会明显下降。3. GN参数配置决定编译速度和产物形态的关键一步3.1 最常用的参数组合与含义进入src目录后首次生成构建文件我推荐用这套debug参数cd C:\webrtc\src gn gen out/Default --argsis_debugtrue target_cpu\x64\ is_component_buildtrue symbol_level1 rtc_include_testsfalse逐个解释这些参数的含义is_debugtrue生成调试版本优化级别低附带调试信息迭代开发阶段用这个。target_cpux64目标架构Windows平台没什么悬念就是x64想编译32位版本改成x86。is_component_buildtrue组件构建模式这个参数对后续开发体验影响巨大下面单独说。symbol_level1符号级别直接影响PDB文件的大小和编译链接速度。rtc_include_testsfalse不生成test相关target。WebRTC里测试代码量非常庞大这一项能让target列表大幅缩水编译时间明显缩短。如果你想要release版本可以再建一个目录gn gen out/Release --argsis_debugfalse is_component_buildfalse target_cpu\x64\ symbol_level0 rtc_include_testsfalse rtc_build_examplesfalse这里is_debugfalse关闭调试信息symbol_level0完全不生成PDB编译和链接都会快很多。rtc_build_examplesfalse连示例程序也跳过进一步压缩构建范围。不同的out目录对应不同构建配置互不干扰想切换配置只需要gn gen到另一个目录别反复覆盖同一个out目录。3.2 is_component_build的选择逻辑别小看这个开关is_component_build是我最想强调的一个参数。它决定WebRTC构建结果是动态库DLL还是静态库LIB。对于大多数人来说调试阶段一定要开组件构建。组件构建会把庞大的WebRTC拆成一组DLL文件这样链接速度非常快。你改了源码里一个文件重新编译时只需要重链对应的那个模块DLL而不必把整个WebRTC全部重链一遍。静态库模式下改动任何一个小文件都可能触发对巨型LIB的重新链接速度慢到让人抓狂。两种模式各有适用场景我整理了一个对比维度组件构建is_component_buildtrue静态构建is_component_buildfalse链接速度快适合日常迭代慢改动后全量重链很痛苦运行时依赖需要附带一组DLL单个LIB链接进exe即可调试体验模块边界清楚断点定位直观符号集中也能调试但链路长产物分发需要把多个DLL一起带上更干净适合交付所以我的建议是日常开发和看源码用组件构建做最终发布或交付单个可执行文件时再切到静态构建重新gn gen一个release目录慢慢编一次。3.3 symbol_level与编译耗时的实际影响symbol_level取值范围是0、1、2。默认情况下可能是2也就是完整符号PDB文件巨大编译链接都要额外花大量时间磁盘占用也高。很多人第一次编WebRTC时按默认配置来编了五六个小时还没结束其实很大一部分时间都耗在生成这些完整的符号文件上。日常开发把symbol_level1就够用了可以正常断点调试PDB体积和生成时间都控制得不错。如果只是打算编译出来看看产物、跑跑示例symbol_level0更快。这个参数非常值得在调试版本里手动指定千万不要用默认值硬顶。4. ninja编译中的典型失败场景与完整排查链路4.1 链接器内存耗尽与LNK1120的处理编译过程中最常见的崩溃场景出现在链接阶段。现象是终端打出fatal error LNK1120、LNK1102或者干脆cl.exe/link.exe进程直接消失最后只留下一句“编译失败”。这种问题基本不是代码错误而是并行度太高导致内存耗尽。第一次遇到时我第一反应是去查代码还怀疑是不是某个模块本身有问题。后来看任务管理器才发现编译时十几个cl.exe同时跑每个占几百MB内存最后链接还要一次性载入大量obj文件内存瞬间见底。解决办法很直接降低并行度。ninja -C out/Default -j 4 webrtc把并行数压到4链接压力会小很多。8GB内存的机器建议从一开始就跑-j 4别硬撑默认并行度。另外把symbol_level从2降到1也能明显减少链接阶段的内存占用。ninja是增量构建失败任务下一次会重新执行不需要重新拉代码。4.2 文件占用与“cannot open output file”背后的原因还有一种很让人恼火的报错LNK1104: cannot open file out\Default\webrtc.dll或者某个obj文件无法写入。这种问题九成以上是文件被别的东西吃住了。完整的排查顺序我总结如下打开任务管理器搜一遍cl.exe、link.exe、ninja.exe把残留的进程全部结束。Windows上经常出现编译崩溃后子进程没退干净文件锁还没释放的情况。回忆自己刚才有没有运行过out目录里生成的exe或dll。如果跑过演示程序没关窗口文件会被占用必须关掉相关进程。检查安全防护软件是否正在扫描这个目录把整个out目录加进排除列表再重新编译。按这个顺序走大多数报错到第二步就能解决。如果这三步都做了还不行再考虑是不是杀毒软件把obj文件误隔离了去隔离区翻一下一目了然。4.3 “卡在第一步”的假死定位depot_tools自更新问题另一个高频问题不是编译时报错而是sync和gclient命令卡住不动。有个非常典型的场景执行gclient sync后终端一直停在那光标闪但进度不推看着就像断网假死。定位这类问题有个很有效的思路先看终端最上面打印的内容是什么。如果显示的是Updating depot_tools那问题几乎可以确定出在depot_tools自更新上。解决方法是终止当前进程确认DEPOT_TOOLS_UPDATE0已设置然后再重新跑。如果打印显示Syncing projects那就是真的在传输大型仓库数据这一阶段要的是耐心。可以通过资源监视器查看磁盘和网卡占用率只要能确认数据在流动就不要反复CtrlC中断。我曾经因为手贱中断了好几次每次都留下一个不完整的仓库状态后续修复反而更花时间。4.4 编译中断后如何续跑别踩增量构建的暗坑ninja设计得非常友好中断之后直接重新执行同一条ninja命令已经编译完成的部分不会重来它会从断点继续。但这里有个大坑如果你中途改了GN参数或者拉了新代码却不重新gn gen直接跑ninja那个害人的报错通常是“构建文件与源码状态不一致”或者干脆出现一堆看似无关的编译错误。正确的流程永远是这个顺序先重新执行gn gen把生成文件写好再跑ninja。有人会图省事直接编辑out/Default/args.gn这个文件但gn不重新生成ninja文件的话改动根本不生效白白浪费时间。5. 编译产物与后续集成的合理姿势5.1 编译完成后out目录里到底生成了什么编译结束后先别急着到处翻文件先想清楚自己用的是哪种构建模式。组件构建模式下out/Default下能看到webrtc.dll以及对应的导入库webrtc.lib同时还有一批依赖模块的DLL比如absl、protobuf这些。静态构建模式下out/Default/obj下会生成webrtc.lib和一堆第三方库的lib文件。知道这个区别很重要很多人拿着静态库的路径去链接动态库模式的代码或者反过来折腾半天也不知道哪里出了问题。5.2 静态库与动态库两种使用方式的取舍刚才在GN参数部分提过组件构建和静态构建的区别这里再补充一点实际使用时的感受。如果你打算把WebRTC集成进自己的独立工程组件构建的体验会好很多链接时只需要指到webrtc.lib这个导入库运行时把webrtc.dll和相关的DLL一起拷到exe旁边就行。静态构建模式下你的工程链接的是webrtc.lib真身但事情没这么简单——WebRTC依赖了一堆系统库和第三方库包括ws2_32.lib、winmm.lib、secur32.lib、iphlpapi.lib、crypt32.lib等等。如果你用GN管理自己的项目构建系统会自动把依赖串好但如果你手工建个VS工程去链接这些依赖库必须自己一个个补上缺一个就冒出来一堆unresolved external symbol。5.3 用源码自带的示例验证编译结果与其编译完立刻手工建一个空项目跟那一堆库搏斗不如先把官方示例编出来验证工具链是完整的。WebRTC源码的src/examples目录下带了不少示例程序最典型的是peerconnection相关的demo。直接在src目录下执行ninja -C out/Default peerconnection_server ninja -C out/Default peerconnection_client如果这两条命令顺利完成运行服务端和客户端能看到一个简单的点对点演示界面。这个示例能跑起来意味着你的WebRTC库整体是完整可用的问题至少不在构建环节。之后再做自己的集成开发心里就有底了。5.4 在自己的工程里正确引用WebRTC头文件与库真正到自己写代码时头文件路径和组织方式容易劝退新手。WebRTC的头文件分散在src/api、src/pc、src/rtc_base、src/modules这些目录下并没有一个统一的一个“include”目录。用的时候得把这些路径逐个加到项目的include search path里。链接方面前面说过的系统库依赖要补全否则会出现一堆WSAStartup、getaddrinfo符号找不到的链接错误。这里再分享一个我踩过坑后的经验如果目标只是“在Windows上做WebRTC二次开发”强烈建议把你的代码直接放进src源码树里作为WebRTC构建系统的一部分去管理哪怕只是个简单的rtc::Thread调用demo。这样能自动获得正确的include路径、依赖库顺序和链接参数远比自己手工搭一个VS工程去对抗构建系统省力得多。编译WebRTC这件事我最大的体会是技术难点从来不在C本身而是整个构建链路上那些环环相扣的环境问题、参数问题和文件问题。现在每次动GN参数前我都会顺手把终端里上次的ninja进程确认一遍然后gn gen、再ninja这个习惯帮我避开了很多“报错—清理—重新编译”的死循环。希望这篇偏实战的记录能帮你在Win10 VS2019上少绕几个弯一次把WebRTC跑起来。