☰
从环境准备到产物验证:macOS编译Chromium 144全流程解析
2026/10/19 16:33:40 网站建设 项目流程

1. 编译启动前的最后检查:工具链与环境变量

到了这一步,你的 depot_tools 应该已经就位,源码目录也已经同步完成。很多第一次动手编译 Chromium 的朋友容易在这里犯一个错误——以为代码拉下来就可以直接敲autoninja,结果编译到一半才发现 Xcode 版本不对、SDK 路径没配好,或者 Python 环境被 Homebrew 的版本污染了。这一篇我们就从编译前的环境检查开始,把最容易翻车的几个点一次性揪出来。

1.1 macOS 工具链版本硬性要求

Chromium 144 这个版本的构建对 macOS 工具链要求非常明确:Xcode 15 或更高版本,对应的 macOS SDK 至少是 14.4。如果你还在用 Xcode 14,那么请先去 App Store 升级,否则在生成构建文件阶段就会直接报错,根本走不到编译那一步。

我建议在开始之前,先执行一条命令把当前工具链信息打出来,确认自己的环境到底处于什么状态:

xcode-select -p xcodebuild -showsdks clang --version python3 --version

正常情况下,xcode-select -p应该输出类似/Applications/Xcode.app/Contents/Developer的路径。如果你发现指向的是 Command Line Tools,或者干脆是别的地方,先执行sudo xcode-select -r重置一下。

还有一个小细节容易被忽略:Xcode 首次启动时的许可协议。如果你刚装完 Xcode 还没打开过,命令行工具会有可能报 "license not accepted" 的错误。这时候运行sudo xcodebuild -license accept就好。

1.2 环境变量配置与 depot_tools 的 PATH 优先级

env 里最关键的是DEPOT_TOOLS_UPDATE这个变量。很多老手在编译 144 这个版本时建议设置export DEPOT_TOOLS_UPDATE=0,为什么?因为 depot_tools 会在每次执行gclient或gn时自动尝试更新自身,而 Chromium 144 这个版本的源码树对 depot_tools 的版本有隐性依赖,一旦 depot_tools 更新到过新的版本,反而可能出现兼容性异常。

我踩过一次坑:某次编译到一半,gclient sync自动拉取了最新的 depot_tools,结果导致gn的生成结果和源码树里的构建假设不匹配,浪费了整整一个下午排查。从那时起,我固定在编译某个版本时,先写好环境变量的 export,再进编译流程。

建议把以下内容加到你的~/.zshrc里:

export DEPOT_TOOLS_UPDATE=0 export PATH="$HOME/chromium/depot_tools:$PATH"

注意 PATH 的顺序务必让 depot_tools 在最前面。macOS 自带的clang、python3路径如果排在 depot_tools 前面,gn在解析工具链时可能找到错误的工具版本。depot_tools 里面自带了一套与 Chromium 构建系统匹配的 Python 环境,必须保证解释器和脚本优先从 depot_tools 目录加载。

2. 生成构建文件:gn 参数的核心逻辑

环境没问题之后,下一步就是使用gn gen生成构建文件。这是整个编译流程中最需要动脑的一步,因为这里的参数直接决定了编译产物是什么样的、编译时长是多少、以及跑起来时候的内存占用。

2.1 out 目录与构建目录命名规范

Chromium 的构建产物放在 source 目录下的out/文件夹,同一个源码树可以同时维护多个构建目录。常见的命名是out/Default、out/Release、out/Components,但你想叫什么都行,gn不区分名字的魔法值。通常我会建议把构建类型和用途直接写进目录名里,比如out/arm64_Release、out/x64_Debug,避免隔了几个月回来忘记这个目录是什么配置。

构建目录在第一次执行gn gen时自动创建。比如创建一个 Release 版本的构建目录:

gn gen out/Release --args="is_debug=false is_official_build=true target_cpu=\"arm64\""

这里我提前说清楚:Chromium 144 在 Apple Silicon 机器上默认生成的是 arm64 架构的产物,如果你的 Mac 是 Intel 的,target_cpu 应设为x64。如果你在 Apple Silicon 的机器上强行指定x64,也不是不行,但需要 Rosetta 环境配合,而且 arm64 的原生编译效率会高很多,没必要主动降级。

2.2 关键 build args 详解:Debug 与 Release 的取舍

关于is_debug和is_official_build,这两个参数对编译行为和产物体积的影响巨大,值得花一分钟搞明白背后的逻辑。

先看is_debug=true的情况。这是 Chromium 源码默认的状态,所有符号不裁剪、优化级别低,方便用 LLDB 打断点调试。但代价是编译出来的二进制体积巨大——以我实测的数据,Debug 版 Chromium 的 dSYM 符号文件和主程序加起来经常超过 8GB。运行速度和系统浏览器比也有肉眼可见的差距,毕竟所有断言、调试检查都是开启状态。

再看 Release 模式,即is_debug=false。Release 模式下编译器启用 O2/O3 级别的优化,调试符号默认裁剪,产物体积小得多,运行效率接近线上版本。对于大部分只是想研究 Chromium 性能、跑自动化测试或者做二次开发的朋友来说,Release 是更合理的日常选择。

如果你做的是正式分发用的构建,还需要加is_official_build=true。这个参数会进一步收紧编译参数,比如启用-ffunction-sections、-fdata-sections和链接阶段的--gc-sections,把没用的代码段和函数直接剔除出最终二进文件。

2.3 component build:在编译速度与产物完整性之间做选择

这里我特别想谈谈is_component_build这个参数。如果是首次编译 Chromium,对编译时长有敏感要求,可以把is_component_build=true放到构建参数里。

Component Build 的核心思想是把 Chromium 拆成一组动态库(.dylib),而不是打成一个巨大的可执行文件。它的优势在于增量编译——改一个模块的代码,只需要重新链接对应的 dylib,而不需要像非组件构建那样,把几百个静态库重新塞到主二进制里。

话虽如此,component build 也有自己的问题。动态库拆分带来的性能开销是真实存在的,尤其是对浏览器内核这种处处讲究性能的软件,组件模式下的 Javascript 引擎和渲染管线会有约 10%~20% 的性能退化。如果你需要做性能测试,或者想拿编译出的 Chromium 当一个主力研究浏览器,我更推荐关闭组件构建,老老实实让链接器合并产出单一的执行文件。

就我个人的实践而言,我把 144 的第一次完整编译用了非组件模式,全量耗时约 3 小时 50 分钟。如果使用 component build,初次全量编译大约可以缩短到 2 个半小时左右,但后续调整代码的重编译速度差异才是关键——组件模式通常能把这部分耗时减少 80%。

2.4 针对 mac 的常用参数配置参考

直接给你一份经过验证的构建参数,适合在 macOS 上、Apple Silicon 芯片的机器编译 Chromium 144 Release 版:

gn gen out/Release --args=" is_debug=false is_official_build=true target_cpu=\"arm64\" symbol_level=0 enable_stripping=true "

symbol_level=0是很多人容易遗漏的参数。Chromium 的默认符号等级在不同配置下表现不一样,Release 模式下有时默认会带上部分 DWARF 符号,直接把符号完全关掉能明显减少链接负担和占用磁盘空间。而enable_stripping=true会让构建系统在链接完成后主动删除可执行文件中的符号表,这对于只是日常运行、不打算断点调试的场景非常合适。

如果你是 x64 的 Intel Mac,那么把target_cpu="x64"换上去就行,其余参数不用动。

3. 启动 Ninja 构建:第一次编译的全流程实战

gn gen成功之后,out 目录里会生成build.ninja文件,接下来就是整个流程里机器负载最高、耗时最长的一个环节——执行autoninja。

3.1 autoninja 与 ninja 的选择

Chromium 老指南里常见的命令是ninja -C out/Release chrome,但如果你用的是 depot_tools 里自带的autoninja,它会自动根据机器 CPU 核数和内存大小调整并发任务数。苹果 M 系列芯片的 CPU 核心分性能核和能效核,autoninja对这类异构核心架构做了适配,比直接用ninja盲目指定-j参数要稳妥得多。

建议完整命令如下:

autoninja -C out/Release chrome

chrome是目标名,表示构建完整的浏览器可执行文件。如果只想快速验证某个模块,可以考虑只构建对应的单元测试目标。但既然走到了编译全流程这一步,直接上主目标就好——只有完整构建过一遍,之后做增量编译才有参照基准。

3.2 初次编译的耗时评估与硬件影响

初次编译的综合耗时取决于机器配置。我手上的 M 系列芯片的机器,32GB 内存起步,编译 144 的 Release 非组件版,全量时长大约在 3 到 4 小时之间。如果你的机器内存只有 16GB,那么 ninja 为了控制内存占用,会自动降低并行度,实际耗时可能拉到 6 小时以上——这不是配置有问题,而是构建系统做的自我保护。

内存不足时,最典型的症状是链接阶段突然报出 "clang: error: unable to execute command: Killed" 之类的信息。这个Killed是系统层面的 OOM 杀进程导致的,不是 clang 本身的编译错误。遇到这种情况,别急着翻代码,先用memory_pressure命令看看系统内存水位,同时检查是不是同时开了太多 IDE 和浏览器窗口。

如果你预算许可,我想给一个具体建议:把内存升到 32GB 以上,会是最值得的一笔投资。编译 Chromium 的时候,32GB 内存的机型可以跑满并行度,16GB 只能在 60% 左右的速度徘徊,这种差距不是靠软件调优能抹平的。

3.3 构建过程的实时状态解读

执行autoninja后,终端会开启类似仪表盘的实时界面。刚开始会有几秒钟的静止,然后出现大量[0/XXXX]进度的闪烁和不断变化的编译命令。这里的XXXX是当前构建系统根据源文件数量计算出的总任务数,对 Chromium 144 来说,全量任务数通常在 40000 这个数量级。

第一次看到 40000 多个任务时不要慌,这数字看着吓人,但每个任务对应的是单个源码文件的编译或链接操作。构建过程中有一批文件会先完成,进度跳跃的幅度会逐渐变大,因为后面的阶段并行度会稳定下来。

需要特别提醒:如果在终端里看到某个文件反复编译失败又自动重试,不要一直挂着等。Ninja 在编译失败后默认会停止整个构建链。长时间卡住通常意味着某个前置任务在等待资源,或者某个编译参数产生了冲突。果断中断构建(Ctrl+C),根据报错信息排查后再重来。

4. 链接阶段与产物验证:从构建成功到可运行

跳过那些进展顺利的部分,如果你最终看到了像这样的输出,那就说明编译真正完成了:

[42000/42000] LINK ./Chromium

是否真的万事大吉?还不是。链接成功只代表这一步技术上通过了,还需要做几个产物验证步骤。

4.1 可执行文件的位置与结构

编译成功后,产物路径是out/Release/Chromium.app。注意文件名的大小写,macOS 上构建出来的应用包名是Chromium.app,而不是大多数人直觉中的chrome.app。

如果你的is_official_build=true,整个Chromium.app内部结构是相当紧凑的。主可执行文件在Chromium.app/Contents/MacOS/Chromium,绝大多数内嵌资源(如默认扩展组、组件清单、ICU 数据)也会被一并打包进Contents/Frameworks或Contents/Resources目录。相比之下非 official 版本会有更多框架零散放置在同一个 bundle 中,行为上类似但不完全一致。

4.2 首次运行的常见失败与原因定位

拿到Chromium.app之后,直接双击或者在终端里执行:

./out/Release/Chromium.app/Contents/MacOS/Chromium

如果秒退,最常见的原因是macOS 的 Gatekeeper 拦截。本地构建的应用没有签名,首次运行可能被拒绝。用sudo xattr -dr com.apple.quarantine ./out/Release/Chromium.app移除隔离属性,一般就能解决。

如果你在命令行运行发现输出了一堆类似FATAL:root:...的日志,那大概率是运行时需要的某些资源文件没有在预期路径下找到。请检查你是否用--user-data-dir指定了一个独立的用户数据目录:

./out/Release/Chromium.app/Contents/MacOS/Chromium --user-data-dir=/tmp/chromium_tmp_profile

这个参数强烈建议首次运行时都带上。默认用户目录里可能残留旧版本 Chromium 的配置,格式和 144 的偏好系统不兼容,轻则页面排版异常,重则直接闪退。

5. 增量编译与日常迭代:真正的效率提升手段

全量编译完成后,我们遇到的大部分场景其实是改一段代码,然后看效果。这意味着了解增量编译的机制能极大改善体验。

5.1 ninja 增量编译的基本逻辑

Ninja 的增量构建核心依赖文件时间戳和内容哈希。每次执行autoninja -C out/Release chrome时,ninja 会根据build.ninja里定义的依赖关系,比对当前磁盘上源文件的修改时间和上次构建时的记录。

只有发生变化的源文件才会被重新编译,再重新链接。不过有个连锁反应要注意:如果改的是一个头文件,而头文件被几百个 cc 文件引用,那么这些 cc 文件会一起重编——这就是#include依赖树带来的"头文件地震"。

5.2 减少无用重编译的两个关键技巧

第一个技巧是不要把构建目录和源码目录混放。Chromium 的构建脚本会把编译产物输出到 out 目录,运行时动态下载的资源在源码树之外的 cache 目录。如果你做了任何改源码的操作,不要顺手修改 out 目录下的文件,那样会污染构建系统对文件状态的判断。

第二个技巧是善用 gn 的依赖分析。在改代码之前,先跑一下:

gn check out/Release

这个命令会扫描 include 依赖关系,提前把缺失头文件、循环依赖等结构性问题暴露出来,避免编译跑到一半才被报错中断。

5.3 实测:一次小改动引起的重编译规模

拿我最近一次实践举例。我在 Chromium 144 源码里改动了content/browser下某个 worker 生命周期管理相关的源文件,顺手改了一个被十余个文件共用的头文件。触发增量编译后,ninja 显示需要重新编译的文件数是 314 个,链接阶段重新执行一次。整个流程耗时约 4 分 20 秒——和全量编译相比已经非常轻量了。

如果你的增量编译耗时超过 15 分钟,大概率是改动触碰到了 core 级别的公共头文件,比如base或third_party/blink/public下面的头文件,这类文件一旦变化,波及范围能到几千个源文件。

6. 编译期内存管理与拖慢构建的隐形杀手

除开硬件水平和编译参数的差异,有几个隐形因素会明显拖慢编译速度。

6.1 后台驻留程序对 ninja 并行度的负面影响

编译 Chromium 时,ninja 会按照 CPU 调度器看到的可用核心数来决定并行编译任务数。如果此时你同时开着 Docker、多个 JetBrains IDE、浏览器几十个标签页、甚至剪辑软件在后台跑渲染,这些程序会抢占 CPU 时间片,并导致 macOS 的内存压力升高。

我在一次实测中发现,后台开着常规开发环境时,ninja 实际编译时间比干净的终端环境多了约 27%。编译期间强烈建议把无关 App 退干净,至少是暂时退出。

6.2 使用 iconv/spotlight 导致的异常 CPU 占用的排查

有另一个隐蔽的问题:macOS 的 Spotlight 索引在首次生成大量新文件时(比如首次全量编译创建了几十万个 .o 文件),会触发mds_store进程疯狂占用 CPU。这时候你以为在编译,实际系统一半算力都被搜索服务抢走了。

可以在系统设置里暂时把 Chromium 源码目录加入 Spotlight 的排除列表。如果在编译期间明显感受到系统卡顿,打开活动监视器确认mdworker、mds的 CPU 占用率是不是飙升,如果是,那就手动踢一脚索引:

sudo mdutil -i off /path/to/chromium/src

编译完成、不再频繁生成新文件后,再sudo mdutil -i on恢复索引。

6.3 温度墙与降频:长时间高负载下的硬件特性

还有一个必须直面的事实——长时间编译会让 Mac 的温度持续飙升。Apple Silicon 芯片的散热设计偏向轻薄本,高负载下温度墙触发后,CPU 频率会明显下降,导致编译速度不升反降。这属于硬件特性,不是配置失误。

解决方法是给笔记本垫高、加强通风,或者使用散热底座。台式机的 Mac 相对好一些,但也别把机器放在密闭柜子里编译。如果你用的是带风扇的 Mac,编译时能听到风扇呼啸属于正常现象;如果听不到声音但不卡顿,说明系统在通过降频方式控制温度,表面上安静了,编译时长却可能拉长。

7. 针对常见报错场景的排查手册

这个章节把我在编译 Chromium 144 过程中亲身踩过的报错和解决方案整理出来,按症状分类。

7.1 常见错误速查表

报错关键字出现阶段可能原因处理方式
depot_tools update failedgclient / fetch网络无法访问 depot_tools 更新源设置DEPOT_TOOLS_UPDATE=0
Xcode version too oldgn gen系统 Xcode 低于构建要求升级 Xcode 到 15+
SDK not foundgn gen命令行工具未选到完整 Xcodesudo xcode-select -r重置路径
unable to execute command: Killed编译阶段内存不足,OOM减少并行度或升级内存
ld: library not found for -lc++abi链接阶段SDK 路径异常或 SDK 版本过新检查xcrun --show-sdk-path
ninja: error: unknown target 'chrome'autoninjaout 目录的 gn 配置未完成先执行 gn gen
Failed to locate installed Mac OS X SDKsgn genXcode 未完全安装完成打开一次 Xcode 完成初始化

7.2 链接阶段的 lld 崩溃问题

如果你在链接阶段碰到clang-17: error: linker command failed with exit code 1,先不要怀疑代码写错了。Chromium 144 的链接过程对内存和临时目录空间同时有要求。

链接器在合并大量静态库时,需要在/tmp或构建目录生成中间文件,如果磁盘剩余空间小于 20GB,就会在这里失败。先执行df -h检查可用空间,清理掉 Xcode DerivedData 的缓存和各类旧的编译产物后重试。

7.3 Xcode 路径错乱导致的诡异问题

还有一类偶发问题是Xcode 路径错乱。你也许之前装过 Command Line Tools,后面又装了完整版 Xcode,但系统里仍残留了旧工具的符号链接。这时xcrun --show-sdk-path看到的路径可能指向/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk。Chromium 的构建认为这个 SDK 版本与工具链不匹配,就会出现各种莫名其妙的编译失败。

解决办法是一套三连:

sudo xcode-select -r xcode-select --install sudo xcodebuild -license accept

然后再检查xcrun --show-sdk-path,确认它输出的是 Xcode.app 内部的 SDK 路径。这个细节虽然简单,但经常能解决那些日志看起来完全无解的问题。

8. 编译结束之后的进阶:自定义 GN 参数与性能调试

如果全流程你已经能流畅跑通了,那就可以考虑通过这些进阶参数把编译出的 Chromium 真正变成你专属的版本。

8.1 针对性能调试开启符号与优化共存

日常开发如果想调试性能问题,通常希望保留部分符号但开启高优化,这种需求可以用symbol_level=1实现。这个等级只保留函数名等基础符号,不带源码行号信息,体积比symbol_level=2小得多,又能让sample工具分析调用栈时看到函数名,不至于全是十六进制地址。

8.2 自定义编译参数带来的功能差异

你可以按需加入若干开关。比如:

gn gen out/Profile --args=" is_debug=false is_component_build=true symbol_level=1 enable_nacl=false "

enable_nacl=false会跳过 NaCl 相关模块的构建,能省几分钟编译时间。如果你明知用不到这个功能,在源码头文件里也会减少很多没必要的依赖文件重编。

另一个实用的开关是enable_remoting=false,它会裁掉 Chromium 内置的远程桌面客户端相关代码,后续增量编译时,涉及这个模块的文件都不会被纳入任务队列。说老实话,这些小开关积累下来,对后续每次增量编译的提速效果相当可观。

8.3 基于编译产物的二次开发环境准备

如果你的目标不只是获得一个浏览器,而是基于 Chromium 做二次开发,那么最值得花时间的事情是配置 IDE 的代码跳转环境。Chromium 144 的源码树接近 2GB,各种符号几千万个,没有合适的索引系统,浏览代码就是一场灾难。

建议生成一个compile_commands.json编译数据库,供 IDE 使用:

gn gen out/Release --args="..." --export-compile-commands

这个命令会在 out 目录下生成compile_commands.json,里面记录了你全部源文件的精确编译命令、include 路径和宏定义。无论你是用 Visual Studio Code 的 clangd 插件,还是用 CLion,都能直接读这个文件实现精准的跳转和补全。

实际使用中,我最明显的感受是:没有这个文件的代码浏览基本靠猜,有了之后,从一个render_frame_host跳到它的实现,整个过程一秒内完成,极大提升日常改代码的效率。

9. 一次编译过程的复盘与经验沉淀

前前后后把 144 的编译流程走完了,我想把这次实践中积累的一些体会尽量坦诚地分享出来。

9.1 时间规划上的建议

全量编译期间,最好不要盯着终端一直看。每 5 分钟瞄一眼确认没有卡死就行,人可以对编译流程完全放手。把编译安排在工作日晚间,挂机运行,第二天早上直接获得产物,是最高效的方式。如果你需要连续做多次实验性修改,可以考虑给测试机上配好组件构建模式,节省每次改动后的等待时间。

9.2 内存资源的硬性预算

说句实在话,不切换构建参数的前提下,编译 Chromium 144 的过程中,内存占用峰值通常在 20GB 到 28GB 之间。如果机器只有 16GB 内存,即便设置了低并行度,也会在链接阶段频繁触碰内存上限,导致系统响应变缓甚至编译被中止。预算充足的话,不要犹豫,升级到 32GB 是性价比最高的选择。

9.3 保持源码文件夹的干净

还有一个看似微小但实际影响很大的习惯:不要把 Chromium 的源码目录放到 iCloud Drive、OneDrive 这类自动同步的网盘目录下,也不要用 Time Machine 对源码目录做频繁的完整备份。这类连续大量文件变化的操作会让文件系统陷入频繁快照状态,macOS 的 APFS 虽然对小文件快照有优化,但几十万个目标文件的增量快照还是会造成显著的磁盘 I/O 开销,直接拉低编译时文件读取的速度。

根据个人经验,用一块本地 SSD 存放源码和构建产物,构建速度通常比存储在同步盘上要快 30% 左右。很值得专门准备一块外置 SSD(接口余量充足的前提下)专门放编译工程。

9.4 把这套知识沉淀成脚本

每次都手动 export 环境变量、写 gn 参数、启动 autoninja,很容易犯错。建议在src目录之外放一个build_env.sh脚本,把环境变量和编译参数写死,然后一键执行:

#!/bin/bash export DEPOT_TOOLS_UPDATE=0 export PATH="$HOME/chromium/depot_tools:$PATH" cd ~/chromium/src gn gen out/Release --args="is_debug=false is_official_build=true target_cpu=\"arm64\" symbol_level=0" autoninja -C out/Release chrome

这样即使隔了两三个月再回来,看到脚本也能立刻回忆起完整的构建流程。这也让我在想复现 Chromium 144 的特定行为时,可以快速进入状态,不用重新翻阅文档。

最后再说一个小技巧:如果你打算长期从事 Chromium 相关的研究,建议在第一次编译成功之后,立刻对 out 目录做一个文件列表快照,记录下所有产物的尺寸和哈希。以后遇到构建产物异常、二进制文件被改动等问题时,这个基线快照能帮助你快速定位问题范围。编译流程是重复性劳动不假,但很多"玄学问题"其实都出在基础环境不干净、参数理解不到位这些可控因素上。把这些可控因素逐一排除,Chromium 144 的编译之路就顺了。

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

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

立即咨询