第5章:构建系统(Build System)
一、导读
1.1 本章学习目标
第5章讲解 Google 的构建系统——单体仓库、构建缓存、依赖管理。
本章的核心学习目标包括:
- 理解单体仓库的优势和挑战
- 掌握构建缓存的原理
- 学会管理大规模依赖
1.2 为什么本章如此重要
构建系统影响每个工程师的日常效率。Google 的单体仓库包含数十亿行代码,构建系统必须足够快、足够可靠。
1.3 核心问题引导
单体仓库和多仓库各有什么优缺点?
构建缓存如何加速构建?
如何管理大规模依赖?
构建系统应该提供什么保证?
二、核心概念详解
2.1 单体仓库(Monorepo)
Google 的单体仓库:
- 所有代码在一个 Git 仓库
- 数十亿行代码
- 数万名工程师同时提交
优势:
1. 代码共享:直接引用,无需发布
2. 原子变更:跨项目修改一次提交
3. 统一工具:一套工具服务所有人
4. 代码可见:任何人可以查看任何代码
挑战:
1. 仓库大小:需要特殊工具处理
2. 构建速度:需要强大的缓存
3. 权限管理:细粒度的权限控制
4. 分支策略:几乎所有开发在主分支2.2 构建缓存
构建缓存原理:
1. 内容寻址缓存
- 用输入的哈希作为缓存键
- 相同输入 = 相同输出
- 缓存可以跨机器共享
2. 增量构建
- 只重新构建变化的部分
- 依赖图追踪变化传播
- 未变化的部分使用缓存
3. 远程缓存
- 构建结果存储在远程服务器
- 不同工程师共享缓存
- CI 和本地构建共享缓存
// Bazel 构建示例
// 只有 src/main.go 变化时,只重新编译这个目标
go_binary(
name = "server",
srcs = ["main.go"],
deps = ["//pkg/http", "//pkg/db"],
)2.3 依赖管理
Google 的依赖管理:
1. 内部依赖
- 直接引用源码
- 无需版本管理
- 修改一起提交
2. 外部依赖
- 集中管理(Vendoring)
- 所有项目使用相同版本
- 升级需要全仓库测试
3. 依赖升级策略
- 自动化升级工具
- 渐进式升级
- 兼容性测试
// 依赖升级流程:
// 1. 更新外部依赖版本
// 2. 运行全仓库测试
// 3. 修复不兼容的代码
// 4. 一次性提交三、实战案例
3.1 案例一:Bazel 构建配置
python
# BUILD 文件
load("@rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "order",
srcs = [
"order.go",
"order_line.go",
],
importpath = "example.com/order",
visibility = ["//visibility:public"],
deps = [
"//pkg/money",
"//pkg/event",
],
)
go_test(
name = "order_test",
srcs = ["order_test.go"],
embed = [":order"],
)四、本章小结
单体仓库:代码共享、原子变更、统一工具。
构建缓存:内容寻址、增量构建、远程缓存。
依赖管理:内部直接引用、外部集中管理。
五、思考与练习
单体仓库适合你的团队吗?
如何为你的项目实现构建缓存?
如何管理外部依赖的升级?