Bazel用于构建大型的项目, 无论是本地运行,还是跑在CI上,对于编译效率的提升是巨大的。我们之前的项目是用CMake构建的,后面转用Bazel,编译和运行CI的效率会提升十倍。
Bazel 的核心知识点很多,但有一些是 必须掌握的基础和关键概念,包括 Bazel 构建阶段、依赖管理、构建规则、缓存机制等。以下是一个 系统化的基础知识清单,帮助你理解 Bazel 的核心原理。
1. Bazel 的三大构建阶段
Bazel 运行时分为 三个关键阶段:
① 加载阶段(Loading Phase)
- 解析
WORKSPACE
和BUILD
文件。 - 解析
BUILD
文件中的 构建规则(cc_library、cc_binary、py_binary等)。 - 计算 目标依赖关系(Dependency Graph)。
② 分析阶段(Analysis Phase)
- 解析 依赖关系,生成 构建执行计划(Action Graph)。
- 处理 规则扩展(如
select()
、config_settings()
)。
③ 执行阶段(Execution Phase)
- 实际执行构建命令(编译、链接、生成二进制文件)。
- 使用 缓存和并行执行 来加速构建。
2. Bazel 关键文件
① WORKSPACE
文件
- 定义 Bazel 项目的根目录。
- 导入外部依赖(如
http_archive()
、local_repository()
)。 - 每个 Bazel 项目只能有一个
WORKSPACE
文件。
② BUILD
文件
- 定义 构建目标(如
cc_binary
、py_library
)。 - 类似 Makefile,但声明式(声明依赖关系,Bazel 处理构建逻辑)。
- 关键构建规则:
cc_library( name = "mylib", srcs = ["mylib.cc"], hdrs = ["mylib.h"], deps = ["//utils:helpers"], # 依赖 utils 目录下的 helpers 库 ) cc_binary( name = "myapp", srcs = ["main.cc"], deps = [":mylib"], # 依赖当前 BUILD 文件中的 mylib )
③ .bazelrc
配置文件
- 定义 默认构建选项,如
--copt
(编译优化)、--compilation_mode
等。 - 示例:
build --copt=-O2 # 默认使用 -O2 进行优化 build --compilation_mode=opt # 默认使用 opt 模式 test --test_output=errors # 仅在测试失败时显示日志
3. Bazel 重要概念
3.1. bazel中的 repository, workspace, package, target 的概念:
1. Repository(代码仓库)
- Bazel 中的 repository 指的是一个 独立的代码仓库,可以是本地目录,也可以是远程仓库。
- 每个 Bazel 项目都会有一个主 repository(
@
代表当前 repo),可以通过WORKSPACE
文件定义外部 repository。 - 例如:
这里http_archive( name = "rules_cc", urls = ["https://github.com/bazelbuild/rules_cc/archive/main.zip"], strip_prefix = "rules_cc-main", )
rules_cc
就是一个外部 repository。
2. Workspace(工作空间)
- Bazel 项目的根目录,包含
WORKSPACE
文件。 WORKSPACE
文件定义了 外部依赖,并告诉 Bazel 这是一个独立的 Bazel 项目。- 示例:
workspace(name = "my_project") http_archive( name = "googletest", urls = ["https://github.com/google/googletest/archive/release-1.11.0.zip"], strip_prefix = "googletest-release-1.11.0", )
- 特点:
- 运行
bazel info workspace
可以查看当前 workspace 的路径。 - 每个 Bazel 构建都会在 workspace 目录内进行。
- 运行
3. Package(包)
- 每个包含
BUILD
文件的目录都是一个 package。 - package 名称 由
WORKSPACE
目录的相对路径决定。 - 示例:
my_project/ # WORKSPACE 根目录 ├── WORKSPACE ├── src/ │ ├── BUILD # 这个目录是一个 package │ ├── main.cc │ ├── utils/ │ │ ├── BUILD # utils 也是一个 package │ │ ├── math.cc
src/
是一个 package,因为它有BUILD
文件。src/utils/
也是一个 package,因为它有自己的BUILD
文件。
4. Target(构建目标)
- BUILD 文件中的每个可构建单元 , 比如是自己定义的 rule 或者是 macro,如
cc_library
、cc_binary
、py_binary
,都被称为一个 target。 - target 的唯一标识:
例如://package:target_name
cc_library( name = "math_lib", srcs = ["math.cc"], )
//src/utils:math_lib
就是math_lib
这个 target 的完整路径。
3.2. Bazel 依赖管理
//package:target
语法- 例:
//src:my_lib
代表src/BUILD
文件中的my_lib
目标。
- 例:
- 外部依赖
- 通过
WORKSPACE
文件定义,如:http_archive( name = "googletest", urls = ["https://github.com/google/googletest/archive/release-1.11.0.zip"], strip_prefix = "googletest-release-1.11.0", )
- 通过
- 跨目录依赖
- 依赖
//utils:math_lib
:deps = ["//utils:math_lib"]
- 依赖
3.3. bazel 的主要构建规则
Bazel 提供了一系列内置规则来支持不同类型的构建目标,主要分为 C++、Python、Java、Shell、测试 等类别。以下是常见的规则:
1. C++ 规则(C++ 编译 & 链接)
- cc_library:定义一个 C++ 静态或共享库。
- cc_binary:定义一个可执行的 C++ 二进制文件。
- cc_test:定义一个 C++ 测试目标。
示例:
cc_library(
name = "math_lib",
srcs = ["math.cc"],
hdrs = ["math.h"],
deps = ["//utils:helpers"],
)
cc_binary(
name = "main_app",
srcs = ["main.cc"],
deps = [":math_lib"],
)
cc_test(
name = "math_test",
srcs = ["math_test.cc"],
deps = [":math_lib"],
)
2. Python 规则(Python 脚本 & 库)
- py_library:定义一个 Python 库。
- py_binary:定义一个 Python 可执行文件。
- py_test:定义一个 Python 测试。
示例:
py_library(
name = "utils",
srcs = ["utils.py"],
)
py_binary(
name = "app",
srcs = ["app.py"],
deps = [":utils"],
)
py_test(
name = "utils_test",
srcs = ["test_utils.py"],
deps = [":utils"],
)
3. Java 规则(Java 构建 & 运行)
- java_library:定义 Java 代码库。
- java_binary:定义 Java 可执行文件(JAR)。
- java_test:定义 Java 测试。
示例:
java_library(
name = "utils",
srcs = ["Utils.java"],
)
java_binary(
name = "app",
main_class = "com.example.Main",
deps = [":utils"],
)
java_test(
name = "utils_test",
srcs = ["UtilsTest.java"],
deps = [":utils"],
)
4. Shell 脚本规则
- sh_library:定义 Shell 脚本库。
- sh_binary:定义 Shell 可执行脚本。
- sh_test:定义 Shell 测试脚本。
示例:
sh_binary(
name = "deploy_script",
srcs = ["deploy.sh"],
)
5. 测试规则
Bazel 内置 test 规则,支持 C++、Python、Java、Shell 等语言的测试目标。
- cc_test、py_test、java_test、sh_test
- 运行测试:
bazel test //src:math_test
6. 通用规则
- filegroup:定义一组文件,方便管理。
- genrule:自定义 Shell 命令生成文件(适用于代码生成)。
示例:
genrule(
name = "generate_version",
outs = ["version.txt"],
cmd = "echo '1.0.0' > $@",
)
3.4. 构建模式
- fastbuild(默认):最快编译,不优化。
- opt:用于 生产环境(相当于
-O2
)。 - dbg:用于 调试(带调试符号)。
切换编译模式:
bazel build --compilation_mode=dbg //src:app
3.5. Bazel 的缓存机制
- Sandbox(沙盒隔离):每个构建任务在独立的环境中运行,避免污染。
- Remote Cache(远程缓存):可以存储已构建的对象,加速 CI/CD 构建。
- Incremental Build(增量构建):Bazel 只重新编译 变更部分,提高效率。
4. Bazel 关键命令
命令 | 作用 |
---|---|
bazel build //target | 构建目标 |
bazel run //target | 运行目标 |
bazel test //target | 运行测试 |
bazel clean | 清理构建缓存 |
bazel query | 查询依赖关系 |
bazel fetch //target | 预下载外部依赖 |
bazel shutdown | 停止 Bazel 服务器 |
5. 进阶知识
① select()
实现条件依赖
select()
允许 基于不同条件选择依赖:cc_library( name = "mylib", srcs = ["mylib.cc"], hdrs = ["mylib.h"], deps = select({ "//conditions:default": [], "//config:use_advanced": ["//advanced:lib"], }), )
bazel build --define use_advanced=1
时使用advanced:lib
,否则为空。
② config_setting()
结合 select()
config_setting()
用于定义条件:config_setting( name = "enable_advanced", values = {"define": "use_advanced=1"}, )
③ bazel query
分析依赖
- 查询某个 target 的所有依赖:
bazel query "deps(//src:app)"
- 仅查询直接依赖:
bazel query "kind(cc_library, deps(//src:app))"
6. 重点总结
关键知识 | 说明 |
---|---|
Bazel 三阶段 | 加载(解析 BUILD )、分析(生成 DAG)、执行(编译 & 运行) |
BUILD 文件 | 声明目标、依赖 |
WORKSPACE 文件 | 定义 Bazel 项目,导入外部依赖 |
编译模式 | fastbuild (默认)、opt (优化)、dbg (调试) |
缓存机制 | 沙盒、远程缓存、增量构建 |
关键命令 |
|
Bazel 官方文档: https://bazel.build/extending
--------------------------------------------------------------------------------------------------------------------------------
若有新的知识, 会逐渐添加...........