
1. 项目概述与核心价值如果你正在用C开发桌面应用并且需要嵌入一个功能完整、性能现代的浏览器内核来显示网页内容那么绕不开的一个选择就是CEF。全称Chromium Embedded Framework顾名思义它就是把谷歌Chrome浏览器背后的Chromium引擎打包成一个库让你能像搭积木一样在自己的C程序里“嵌入”一个浏览器。这听起来很酷对吧但当你兴冲冲地去官网下载准备大干一场时很可能会被一盆冷水浇醒官方只提供预编译的二进制分发版而如果你想深度定制、或者需要链接到自己的项目进行调试又或者你用的编译器版本比较新老预编译的库很可能不兼容。这时候从源码开始编译CEF就成了必须跨过去的一道坎。我经历过好几次CEF的编译从早期的CEF1到现在的CEF3踩过的坑能写满好几页。网上教程不少但要么步骤缺失要么环境描述不清导致跟着做总卡在某个诡异的错误上。今天这篇我就把编译官方CEF3的完整流程、背后的原理以及那些容易让人抓狂的“坑点”彻底讲透。这不是一个简单的命令罗列我会告诉你每一步为什么要这么做遇到问题该怎么想、怎么查。目标很简单让你能在一台干净的Windows系统上成功编译出属于你自己的、可调试的CEF3库为后续的C客户端开发铺平道路。2. 编译环境深度解析与准备编译CEF3本质上是在编译一个定制化的Chromium。Chromium的构建系统以其复杂和严苛的环境要求而“闻名”。因此环境准备是成功的第一步也是最容易出错的一步。2.1 系统与硬件基础要求首先你的机器必须满足一些硬性指标。官方推荐使用64位系统Windows 10或11至少8GB内存但我强烈建议16GB或以上。编译Chromium是个内存吞噬兽在链接Linking阶段内存占用轻松突破10GB。硬盘方面你需要预留至少100GB的可用空间。这听起来夸张但当你下载完所有源码和工具链后你会发现80-90GB的占用是常态。CPU核心越多越好能显著缩短编译时间。我曾在8核16线程的机器上编译用了将近6个小时而在4核的笔记本上则花了超过12小时。所以耐心和一块大容量SSD是必备的。2.2 核心工具链的安装与配置这是重中之重版本必须严格匹配。Visual Studio这是Windows平台编译的绝对核心。CEF/Chromium对VS版本有明确要求。例如对应Chromium M80分支的CEF通常需要Visual Studio 2019版本16.0以上。你不能用VS 2022去编译一个要求VS 2019的版本否则会在生成项目文件gn或gclient阶段就报错。去微软官网下载Visual Studio Community版即可安装时务必勾选“使用C的桌面开发”工作负载确保MSVC编译器、Windows SDK和调试工具都被安装。注意安装路径不要有中文或空格使用默认路径C:\Program Files (x86)\Microsoft Visual Studio\2019\Community是最稳妥的。Windows 10 SDK在VS安装器中找到“单个组件”选项卡搜索并安装合适版本的Windows 10 SDK如10.0.19041.0。Chromium构建脚本会依赖特定版本的SDK。Depot Tools这是谷歌为Chromium系列项目包括CEF量身定做的源码管理工具集。它不是一个单一的软件而是一个包含gclient、fetch、gn、ninja等命令行工具的文件夹。下载直接从Chromium官方仓库获取。配置将depot_tools文件夹的路径例如D:\dev\depot_tools添加到系统环境变量PATH的最前面。这是关键必须放在其他可能包含python、git的路径之前因为它自带特定版本的Python和Git构建系统依赖这些特定版本。初始化打开一个新的管理员权限的命令提示符cmd或PowerShell运行一次gclient。它会自动下载和更新所需的工具如python、git。首次运行可能会提示你安装Windows Driver Kit按提示操作即可。2.3 源码获取与目录结构剖析CEF的源码管理方式比较特别它通过一个“主仓库”来管理对Chromium源码的依赖。创建并进入工作目录在你空间充足的盘符下如D:\创建一个干净的文件夹例如D:\cef_source。所有操作都在这里进行。下载CEF源码脚本在cef_source目录下打开配置好depot_tools的命令行执行curl -kLO https://bitbucket.org/chromiumembedded/cef/raw/master/tools/automate/automate-git.py这个automate-git.py是CEF官方提供的自动化构建脚本是我们整个流程的“指挥官”。理解分支与版本在运行脚本前你需要决定编译哪个版本。CEF版本号通常和Chromium版本号绑定如CEF 80.1.4 对应 Chromium 80。你可以去CEF官网查看分支信息。对于新手我建议选择一个相对稳定且文档较多的版本比如branchXXXXXXXX代表Chromium主版本号。在脚本中我们通过--branch参数指定。运行自动化脚本下载源码这是最耗时的一步它会下载CEF自身代码和庞大的Chromium源码。python automate-git.py --download-dirD:\cef_source --branchXXXX --no-distrib --no-build--download-dir指定源码下载目录。--branch指定CEF分支例如--branch4638对应Chromium 91。--no-distrib告诉脚本先不要生成二进制分发包。--no-build告诉脚本先不要编译只下载代码。 这个命令会运行很久需要下载几十GB的数据请保持网络通畅。它会自动调用depot_tools中的gclient来同步Chromium代码。完成后你的目录结构大致如下D:\cef_source\ ├── automate-git.py ├── chromium_git\ # Chromium主源码体积最大 │ └── src\ ├── cef_git\ # CEF自身的源码 │ └── cef/ └── depot_tools\ # 如果你把它放在这里的话理解这个结构很重要chromium_git\src是Chromium的家cef_git\cef是CEF对Chromium的“补丁”和封装层它们最终会通过符号链接等方式整合在一起。3. 编译配置生成与关键参数解读源码就位后我们不能直接打开Visual Studio就编译。Chromium使用一套自研的构建系统我们需要先用GNGenerate Ninja工具生成构建配置文件。3.1 进入Chromium源码目录所有构建操作都在Chromium的src目录下进行。cd D:\cef_source\chromium_git\src3.2 创建并切换构建目录Chromium支持多个并行的构建配置例如Debug版、Release版、不同目标CPU。我们通常创建一个独立的目录来存放特定配置。mkdir -p out\cef_build cd out\cef_build这里cef_build是我自定义的目录名你可以用Debug或Release来命名更直观。3.3 生成GN构建参数文件在cef_build目录下创建一个名为args.gn的文本文件。这个文件里的每一行配置都决定了最终编译出的库的特性。下面是一个典型的Debug版本配置# 设置构建目标操作系统和CPU架构 target_os “win” target_cpu “x64” # 设置构建类型debug表示调试版本包含符号可调试 is_debug true # 启用编译器优化即使在Debug版下也能获得可接受的性能 is_component_build false # 静态链接运行时库生成单个大文件适合分发 is_official_build false # 官方构建会开启更多优化但禁用断言调试时设为false # CEF相关的关键配置 enable_nacl false # 禁用Native Client通常不需要 use_sysroot false # 不使用系统根目录在Windows上通常为false # 符号级别2包含完整调试信息便于在VS中调试 symbol_level 2 # 关闭不必要的功能以加速编译可选 enable_widevine false enable_mse_mpeg2ts_stream_parser false对于Release版本你可以创建另一个目录如out\cef_release并使用如下配置target_os “win” target_cpu “x64” is_debug false is_component_build false is_official_build true # Release版可以开启以获得最佳性能 symbol_level 0 # 不包含调试符号体积小 enable_nacl false use_sysroot false3.4 运行GN生成Ninja文件保存好args.gn后在src目录下执行gn gen out\cef_buildgn会读取out\cef_build\args.gn中的配置并在out\cef_build目录下生成一整套ninja构建文件。如果配置有语法错误会在这里报错。成功的话会输出类似“Done. Made 1043 targets from 168 files…”的信息。4. 核心编译过程与实战操作生成Ninja文件后真正的编译才开始。我们使用ninja这个极速构建工具来驱动编译。4.1 开始编译CEF目标在src目录下运行以下命令ninja -C out\cef_build cef-C参数指定构建目录。cef是我们要构建的目标名称。这个目标定义在CEF的构建文件中它会编译出libcef.lib静态库、libcef.dll动态库、cef_sandbox.lib等所有CEF核心组件。接下来就是漫长的等待。你的CPU风扇会开始咆哮内存占用会飙升。这个过程完全是自动的它会编译Chromium和CEF的成千上万个源文件。在输出中你会看到大量的[CXX]、[LINK]提示。编译时间取决于你的机器性能从几小时到十几小时不等。4.2 编译配套工具与示例仅仅编译出libcef.dll还不够我们还需要一些配套的工具和资源文件。在cef目标编译完成后继续编译以下目标ninja -C out\cef_build cef_sandbox ninja -C out\cef_build cef_unittestscef_sandbox沙盒库对于需要严格安全隔离的浏览器进程是必须的。cef_unittests单元测试可执行文件编译它们可以验证你的构建是否基本正常。4.3 生成二进制分发包这是最后一步将编译好的所有文件库、头文件、资源、示例打包成一个规整的目录结构方便我们后续在项目中引用。 回到cef_source目录运行之前下载的自动化脚本但这次指定构建和打包cd D:\cef_source python automate-git.py --download-dirD:\cef_source --branchXXXX --force-build --build-targetcefclient --no-debug-build --x64-build--force-build强制重新构建如果你之前编译过可以去掉此参数尝试增量构建。--build-targetcefclient除了核心库还编译CEF自带的示例客户端cefclient这是一个功能丰富的浏览器demo对学习CEF API非常有帮助。--no-debug-build如果你要生成Release分发包。--x64-build指定64位架构。脚本执行完毕后你会在cef_source目录下找到一个以时间戳命名的文件夹例如chromium\src\out\Release_GN_x64或者直接在cef_source下出现一个build文件夹。里面就是你梦寐以求的成果build\ ├── cef_binary_XXXX_windows_x64\ # 这就是分发包 │ ├── Debug/ # Debug版本库和资源 │ ├── Release/ # Release版本库和资源 │ ├── Resources/ # 本地化资源、pak文件 │ ├── include/ # 所有C/C头文件 │ ├── cefclient.exe # 示例程序 │ ├── cef_sandbox.lib │ ├── libcef.lib │ └── libcef.dll └── ... (其他日志文件)这个cef_binary_XXXX_windows_x64文件夹就是你可以直接拿来集成到自己Visual Studio项目中的“SDK”。5. 疑难杂症排查与经验实录编译过程几乎不可能一帆风顺。下面是我总结的几个最常见的问题和解决方法。5.1 网络问题与源码同步失败问题在automate-git.py下载或gclient sync同步代码时卡住、断开或报错。原因Chromium源码仓库在境外网络不稳定是最大元凶。解决使用可靠的HTTP/HTTPS代理在命令行中设置代理环境变量注意这里指的是用于命令行工具下载的普通网络代理绝非任何违规工具。set http_proxyhttp://your_proxy:port set https_proxyhttp://your_proxy:port重试与继续如果中断可以重新运行相同的automate-git.py命令它通常会尝试继续之前的工作。对于gclient sync也可以多次执行。手动下载depot_tools如果curl下载automate-git.py失败可以用浏览器手动下载然后放到对应目录。5.2 编译工具链错误问题gn gen或ninja编译过程中报错提示找不到编译器、链接器失败LNKxxxx错误、或Windows SDK版本不对。原因环境变量PATH顺序不对或者VS版本、SDK版本不匹配。解决确认PATH在命令行输入where python和where git确保第一个结果都来自depot_tools目录。启动VS开发者命令行有时最笨的方法最有效。从开始菜单找到“Developer Command Prompt for VS 2019”并以此启动命令行它会自动设置好所有编译环境变量然后再在此环境中运行gn和ninja。检查SDK版本对照CEF官方论坛或代码仓库中的CHROMIUM_BUILD_COMPATIBILITY.txt文件确认所需的Windows SDK和VS的确切版本。5.3 内存不足与编译卡死问题编译到后期特别是链接chrome.dll或cefclient时系统卡死、ninja报错fatal error: C1060: compiler is out of heap space。原因链接阶段需要将成千上万个对象文件合并内存消耗巨大。解决关闭所有无关程序释放尽可能多的物理内存。增加系统虚拟内存将页面文件大小设置为系统管理或手动设置一个较大的值如32GB-64GB放在SSD上。使用ninja的-j参数限制并发数默认ninja会使用尽可能多的CPU核心并行编译这也会导致内存峰值过高。可以尝试减少并发任务数ninja -C out\cef_build cef -j 4 # 只使用4个并行任务分段编译先编译核心库cef再单独编译cefclient。5.4 生成的库文件无法使用问题编译成功生成了libcef.dll和libcef.lib但在自己的项目中链接时出错或者运行时崩溃。原因最常见的原因是运行时库CRT不匹配。你的项目使用的运行时库如/MDd,/MTd必须与编译CEF时使用的完全一致。解决检查CEF的编译配置回顾你args.gn中的is_component_build。false表示静态链接CRT类似/MTtrue表示动态链接CRT类似/MD。你必须在自己的项目属性中设置相同的“运行时库”选项。使用配套的头文件和库确保你的项目包含的是你刚编译出来的include头文件链接的是对应配置Debug/Release的.lib文件并且运行时libcef.dll在可执行文件的路径下。依赖文件缺失CEF运行需要一系列辅助文件如*.pak资源文件、locales文件夹、swiftshader目录等。在分发你的应用时需要将Release目录下的这些资源文件一同拷贝到你的exe同级目录。cefclient示例的目录结构就是最好的参考。6. 从编译成果到实际项目集成成功编译出CEF二进制包只是第一步把它用起来才是目的。这里简述一下关键集成步骤。6.1 创建或配置你的C项目在Visual Studio中创建一个新的“Windows桌面应用程序”项目。在项目属性中进行如下关键设置C/C - 常规 - 附加包含目录添加你编译好的CEF分发包中的include文件夹路径。链接器 - 常规 - 附加库目录添加Debug或Release文件夹的路径包含libcef.lib的目录。链接器 - 输入 - 附加依赖项添加libcef.libcef_sandbox.liblibcef_dll_wrapper.lib这个库在libcef_dll_wrapper项目里需要单独编译通常分发包里会提供VS的解决方案文件.sln打开它编译即可得到。C/C - 代码生成 - 运行时库必须与编译CEF时的一致查看args.gn中的is_component_build。6.2 初始化与简单窗口创建CEF的初始化有一套固定流程主要围绕CefApp和CefClient两个核心接口。你需要实现一个CefApp的子类用于处理进程相关的回调如命令行参数、沙盒设置。实现一个CefClient的子类如我们开篇API文档中看到的它返回各种事件处理器Handler的实例。例如实现CefLifeSpanHandler来管理浏览器窗口的生命周期实现CefLoadHandler来监听页面加载状态。在main函数或WinMain函数中调用CefInitialize进行初始化传递你的CefApp实例和设置。调用CefBrowserHost::CreateBrowserSync创建一个浏览器窗口将其父窗口句柄绑定到你自己的UI窗口上。进入CEF的消息循环CefRunMessageLoop或在自己的消息循环中定期调用CefDoMessageLoopWork。在程序退出前调用CefShutdown。这个过程涉及大量C回调接口的实现强烈建议从编译生成的cefclient示例项目开始学习。把它导入VS阅读它的代码是理解CEF编程模型最快的方式。6.3 资源文件与发布你的应用程序在发布时不能只携带一个libcef.dll。必须将整个Release文件夹下的内容除了.lib和.pdb都拷贝到你的exe所在目录保持其相对路径结构。尤其是Resources和locales文件夹缺少它们浏览器将无法正常显示网页。编译CEF3是一次对耐心、细心和系统知识的考验。它流程固定但细节繁多任何一个环节的疏漏都可能导致失败。但一旦你亲手走通了这个流程你对CEF乃至Chromium的理解会深刻得多。后续遇到任何链接错误、运行时崩溃你都能更有底气地去排查因为你知道这一切是如何从源码构建起来的。这份掌控感正是从源码编译的最大价值。