1. 工作区(Workspace)
工作区(Workspace)是 Bazel 构建系统的最顶层组织单元,它包含了所有需要构建的源文件、依赖和构建规则。
1.1 工作区的定义
一个目录只要包含名为 WORKSPACE 或 WORKSPACE.bazel 的文件,就被 Bazel 视为一个工作区。该文件所在的目录即为工作区根目录。
# WORKSPACE 文件示例(最简形式,可以为空)workspace(name = "my_project")
注意:
workspace()函数在 Bazel 8+ 中已弃用。如果你使用 Bzlmod(推荐),WORKSPACE文件可以为空甚至不存在,模块名称在MODULE.bazel中定义。
1.2 工作区根目录下的特殊文件
WORKSPACEWORKSPACE.bazel | ||
MODULE.bazel | ||
.bazelrc | ||
.bazelversion | ||
.bazelignore | ||
BUILDBUILD.bazel | ||
tools/ | ||
third_party/ |
1.3 仓库(Repository)
工作区由一个主仓库和零个或多个外部仓库组成:
工作区(Workspace)├── 主仓库(Main Repository) ← 你的项目代码│ ├── WORKSPACE│ ├── src/│ └── lib/└── 外部仓库(External Repos)├── @com_google_absl ← 通过 http_archive 引入├── @com_github_gtest ← 通过 http_archive 引入└── @local_config_cc ← C++ 工具链配置
外部仓库在 WORKSPACE 或 MODULE.bazel 中声明,Bazel 会在首次构建时自动下载。
2. 包(Package)
包(Package)是工作区内的基本组织单元,由包含 BUILD 或 BUILD.bazel 文件的目录定义。
2.1 包的定义规则
每个包含 BUILD文件的目录构成一个包包的边界由 BUILD文件确定:从BUILD所在目录到下一个BUILD所在目录之间的所有文件属于当前包包的名称是其 BUILD文件相对于工作区根目录的路径
my-project/ ← 工作区根├── WORKSPACE├── BUILD ← 根包 ""(空字符串)├── src/│ ├── BUILD ← 包 "src"│ ├── main.cc│ └── util.cc├── src/io/│ ├── BUILD ← 包 "src/io"│ ├── reader.cc│ └── writer.cc└── lib/├── BUILD ← 包 "lib"├── math.cc└── string.cc
注意:src/io/ 有自己的 BUILD 文件,所以 src/io 是一个独立的包,不属于 src 包。
2.2 包的最佳实践
✅ 推荐 ❌ 避免───────────────────────── ─────────────────────────每个有独立职责的目录放一个 BUILD 在根目录放一个巨大的 BUILD包的粒度适中(5-20个目标) 单个包包含上百个目标按功能/模块划分包 按文件类型划分包保持包之间的依赖关系清晰 循环依赖
3. 目标(Target)
目标(Target)是包内构建的基本单元,定义在 BUILD 文件中。C++ 项目中常用的目标类型包括:
3.1 C++ 相关规则
cc_binary — 可执行程序
cc_binary(name = "hello_world",srcs = ["hello.cc"],deps = ["//lib:greeting",],)
name | ||
srcs | ||
hdrs | ||
deps | ||
defines | ||
copts | ||
linkopts |
cc_library — 库
cc_library(name = "greeting",srcs = ["greeting.cc"],hdrs = ["greeting.h"],deps = ["//lib:string_utils",],)
name | ||
srcs | ||
hdrs | ||
deps | ||
defines | ||
copts | ||
linkstatic | ||
alwayslink | ||
interface_hdrs | ||
implementation_deps | ||
visibility |
关键概念:
hdrsvssrcs中的头文件
hdrs中的头文件是公共 API,可以被其他包通过 deps依赖后直接#include仅在 srcs中的头文件是私有的,只能在当前库的源文件中使用这种区分是 Bazel 保证依赖正确性和增量构建的基础
cc_test — 测试
cc_test(name = "greeting_test",srcs = ["greeting_test.cc"],deps = ["//src:greeting","@com_github_gtest//:gtest_main",],)
name | ||
srcs | ||
deps | ||
copts | ||
linkstatic | ||
args | ||
data | ||
size | ||
timeout | ||
flaky |
cc_proto_library — Protocol Buffers(需要 rules_proto)
cc_proto_library(name = "person_proto_cc",deps = [":person_proto"],)proto_library(name = "person_proto",srcs = ["person.proto"],)
3.2 其他常用规则
filegroup | |
genrule | |
alias | |
config_setting | |
constraint_settingconstraint_value |
4. 标签(Label)
标签(Label)是 Bazel 中引用目标的唯一标识符,格式如下:
@repository//package:target4.1 标签的组成部分
@com_google_absl//absl/strings:str_format│ │ ││ │ └── 目标名 (target)│ └── 包路径 (package)└── 仓库名 (repository)
@repository | ||
// | ||
package | ||
:target | :target |
4.2 标签缩写规则
# 完整形式@com_google_absl//absl/strings:str_format# 主仓库中//lib/math:math_utils # 完整//lib/math # 等价于 //lib/math:math(包名=目标名时)# 同包内的简写(最常用):math_utils # 当前包的 math_utils 目标math_utils # 同上(省略冒号,但不推荐)# 根包//:hello # 根包的 hello 目标hello # 同上(仅根包适用)
4.3 标签的最佳实践
# ✅ 推荐:跨包依赖使用完整标签deps = ["//lib/math:math_utils"]# ✅ 推荐:同包依赖使用冒号前缀deps = [":greeting"]# ❌ 避免:省略冒号(歧义,不推荐)deps = ["greeting"]# ❌ 避免:使用绝对路径引用本地文件# Bazel 不使用文件系统路径,始终使用标签
5. 依赖图(Dependency Graph)
Bazel 构建的核心是依赖图——一个有向无环图(DAG),描述了目标之间的依赖关系。
5.1 依赖的类型
cc_library(name = "app",srcs = ["app.cc"], # srcs 中的文件是 app 的依赖hdrs = ["app.h"],deps = [ # deps 声明对其他目标的依赖":core","//lib:utils",],)
C++ 规则中的依赖传播:
依赖类型 传播方向 说明───────── ──────── ──────srcs 不传播 仅当前目标使用hdrs 传播给依赖者 公共头文件,依赖者可 #includedeps 传播给依赖者 传递依赖(A→B→C,则 A 可用 C)implementation_deps 不传播给依赖者 仅当前库实现使用,不暴露给上层data 不传播 运行时数据文件
5.2 依赖图示例
cc_binary //:app├── cc_library //:core│ ├── cc_library //lib:utils│ │ └── cc_library //lib:base│ └── @com_google_absl//absl/strings└── cc_library //:io└── cc_library //lib:utils
对应的 BUILD 文件:
# 根目录 BUILDcc_binary(name = "app",srcs = ["app.cc"],deps = [":core",":io",],)cc_library(name = "core",srcs = ["core.cc"],hdrs = ["core.h"],deps = ["//lib:utils","@com_google_absl//absl/strings",],)cc_library(name = "io",srcs = ["io.cc"],hdrs = ["io.h"],deps = ["//lib:utils"],)
# lib/BUILDcc_library(name = "utils",srcs = ["utils.cc"],hdrs = ["utils.h"],deps = [":base"],)cc_library(name = "base",srcs = ["base.cc"],hdrs = ["base.h"],)
5.3 查看依赖图
Bazel 提供了强大的工具来查询和分析依赖图:
# 查询目标的所有依赖bazel query "deps(//:app)"# 查询依赖某个目标的所有目标bazel query "rdeps(//..., //lib:base)"# 生成依赖图(需要 graphviz)bazel query "deps(//:app)" --output=graph | dot -Tpng -o deps.png# 查询目标的直接依赖bazel query "deps(//:app, 1)"# 查找两个目标之间的依赖路径bazel query "somepath(//:app, //lib:base)"# 查看包中的所有目标bazel query "//lib:*"
6. 可见性(Visibility)
默认情况下,目标只对同一个包内的其他目标可见。通过 visibility 属性控制目标的访问范围。
6.1 可见性规则
cc_library(name = "internal_util",srcs = ["internal_util.cc"],hdrs = ["internal_util.h"],visibility = ["//visibility:private"], # 仅同包可见(默认))cc_library(name = "public_api",srcs = ["public_api.cc"],hdrs = ["public_api.h"],visibility = ["//visibility:public"], # 对所有包可见)cc_library(name = "team_api",srcs = ["team_api.cc"],hdrs = ["team_api.h"],visibility = ["//src:__pkg__", # 仅 src 包可见"//tests:__pkg__", # 仅 tests 包可见"//lib/...:__pkg__", # lib 下所有包可见],)
6.2 可见性标签说明
//visibility:public | |
//visibility:private | |
//some/package:__pkg__ | |
//some/package/...:__pkg__ |
7. 配置(Configuration)
Bazel 使用配置来控制构建行为。同一个目标可以在不同配置下构建多次。
7.1 构建配置的组成
配置 = 选项组合 + 平台 + 工具链常见的配置维度:
- 目标平台
(Target Platform):运行构建产物的平台 - 执行平台
(Execution Platform):执行构建动作的平台 - 编译模式
(Compiling Mode): fastbuild、dbg、opt - 特性
(Features):C++ 工具链的特性开关
7.2 编译模式
# 快速构建(默认)—— 不优化,带调试信息,构建速度快bazel build //:app -c fastbuild# 调试模式 —— 不优化,完整调试信息bazel build //:app -c dbg# 优化模式 —— 开启优化,适合发布bazel build //:app -c opt
7.3 使用 select() 实现条件配置
select() 是 Bazel 中实现条件配置的核心机制:
cc_library(name = "platform_lib",srcs = ["platform_lib.cc"],hdrs = ["platform_lib.h"],copts = select({"//:windows": ["/O2", "/W4"], # Windows MSVC 选项"//:linux": ["-O2", "-Wall"], # Linux GCC 选项"//:macos": ["-O2", "-Wall", "-stdlib=libc++"],"//conditions:default": ["-O2"], # 默认选项}),deps = select({"//:use_absl": ["@com_google_absl//absl/strings"],"//conditions:default": [],}),)
对应的配置定义:
# 根 BUILD 文件config_setting(name = "windows",constraint_values = ["@platforms//os:windows",],)config_setting(name = "linux",constraint_values = ["@platforms//os:linux",],)config_setting(name = "use_absl",define_values = {"use_absl": "true",},)
使用时通过 --define 激活:
bazel build //:platform_lib --define use_absl=true8. Bazel 命令行常用操作
8.1 核心命令
# 构建bazel build //:target # 构建指定目标bazel build //... # 构建工作区所有目标bazel build //src:all # 构建指定包所有目标# 运行bazel run //:target # 构建并运行bazel run //:target -- --arg1 --arg2 # 传递参数(用 -- 分隔)# 测试bazel test //:all # 运行所有测试bazel test //tests:math_test # 运行指定测试bazel test //... --test_output=all # 显示所有测试输出# 清理bazel clean # 清理构建输出bazel clean --expunge # 完全清理(包括缓存)# 查询bazel query //... # 列出所有目标bazel query "kind(cc_library, //...)" # 列出所有 cc_librarybazel query "deps(//:app)" # 查询依赖
8.2 常用选项
# 并发控制bazel build //:app --jobs=8 # 使用 8 个并发任务# 输出控制bazel build //:app --verbose_failures # 失败时显示完整命令bazel build //:app --action_env=VAR=val # 设置环境变量# 配置bazel build //:app -c opt # 优化模式bazel build //:app --config=release # 使用 .bazelrc 中定义的配置# 远程缓存bazel build //:app --remote_cache=grpc://cache.example.com:9092
9. 小结
本篇深入介绍了 Bazel 的核心概念:
✅ 工作区(Workspace)和仓库(Repository) ✅ 包(Package)的组织与划分 ✅ 目标(Target)的类型与属性,特别是 C++ 三大规则 ✅ 标签(Label)的语法与缩写规则 ✅ 依赖图(Dependency Graph)与依赖传播 ✅ 可见性(Visibility)控制 ✅ 配置(Configuration)与 select()条件配置✅ 常用命令行操作
夜雨聆风