在 Bazel 中,明确地定义依赖关系是构建系统成功的关键,这也正是它高效、可重复、可缓存的原因之一。Bazel 永远不会猜测构建顺序,而是根据你定义的依赖图来调度每一个构建动作。 可以把 Bazel 理解为一个大型的 “声明式有向图构建器”,每一个节点(rule)都要告诉 Bazel:“我需要哪些前置节点”,它才能高效调度和增量构建。
下面系统性地总结一下Bazel中定义依赖关系的方式,理解这些依赖的写法,基本就掌握了Bazel构建图的构造方式。
Bazel 中的依赖关系类型
可以从 3 个层次来看:
1. 规则(Rule)中内建属性来定义依赖
-
适用于常见规则如
cc_binary,py_library,genrule,filegroup等。 -
常用字段有:
| 字段名 | 作用 | 用途示例 |
|---|---|---|
srcs | 源文件或源规则 | 通常指源码或生成文件的依赖 |
deps | 其他规则的依赖(编译时链接) | 通常用于编译依赖 |
data | 运行时需要的文件 | 用于测试或 runtime 资源 |
hdrs | C/C++ 的头文件依赖 | 指定需要包含的头文件 |
tools | genrule 或 action 的工具依赖 | 用于生成过程调用的工具 |
outs | 声明的输出文件 | 其他规则可以依赖这些输出 |
✅ 示例 1:cc_binary 依赖 cc_library
cc_library(
name = "util_lib",
srcs = ["util.cc"],
hdrs = ["util.h"],
)
cc_binary(
name = "main_app",
srcs = ["main.cc"],
deps = [":util_lib"],
)
👉 main_app 编译时会用到 util_lib 提供的源码和头文件。
✅ 示例 2:使用 genrule 生成文件并被其他规则依赖
genrule(
name = "generate_config",
outs = ["config.txt"],
cmd = "echo 'config=1' > $@",
)
filegroup(
name = "generated_files",
srcs = [":generate_config"],
)
sh_binary(
name = "my_script",
srcs = ["main.sh"],
data = [":generated_files"], # 运行时依赖
)
👉 my_script 在运行时能访问 config.txt。
✅ 示例 3:使用 tools 字段
genrule(
name = "generate_data",
outs = ["data.txt"],
tools = ["//tools:my_tool"],
cmd = "$(location //tools:my_tool) > $@",
)
👉 Bazel 会自动确保 my_tool 被构建出来,并传递其路径到 $@。
2. 在 macro 中连接规则依赖
宏(macro)是生成规则的函数,要手动连接规则之间的依赖。
✅ 示例 4:Macro 中手动指定依赖顺序
def my_pipeline(name):
native.genrule(
name = name + "_step1",
outs = ["out1.txt"],
cmd = "echo step1 > $@",
)
native.genrule(
name = name + "_step2",
srcs = [":" + name + "_step1"],
outs = ["out2.txt"],
cmd = "cat $(SRCS) > $@ && echo step2 >> $@",
)
使用:
load("//build_defs:my_macros.bzl", "my_pipeline")
my_pipeline(name = "build_data")
👉 step2 明确依赖 step1 的输出。
3. Starlark 自定义规则中显式声明依赖(高级用法)
如果你自定义 rule(),你需要在 attrs 里声明依赖字段,在 implementation 中使用依赖内容。
✅ 示例 5:自定义 rule 使用 deps
def _my_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep.label)
return []
my_rule = rule(
implementation = _my_rule_impl,
attrs = {
"deps": attr.label_list(),
}
)
BUILD 文件中用法:
my_rule(
name = "hello",
deps = ["//lib:foo", "//lib:bar"],
)
👉 这允许你在规则实现中读取依赖规则的信息(如文件、标签等)。
📦 额外的依赖类型和技巧
✅ label_list / label 的其他字段定义依赖
例如你可以在自定义规则中定义:
attrs = {
"srcs": attr.label_list(allow_files = True),
"hdrs": attr.label_list(allow_files = True),
"tools": attr.label_list(cfg = "exec"),
}
-
allow_files = True表示可以接受文件依赖 -
cfg = "exec"表示运行在执行环境(比如用作生成工具)
✅ filegroup 用于聚合文件或规则
filegroup(
name = "all_headers",
srcs = glob(["*.h"]),
)
cc_library(
name = "lib_with_headers",
hdrs = [":all_headers"],
srcs = ["main.cc"],
)
总结:Bazel 中定义依赖的方式
| 场景 | 写法 | 用途说明 |
|---|---|---|
| 编译依赖 | deps = [":other_rule"] | C/C++、Python、Java 等代码依赖 |
| 文件依赖 | srcs = [...], hdrs = [...] | 源文件或头文件 |
| 运行时依赖 | data = [...] | 测试或脚本运行依赖文件 |
| 工具依赖 | tools = [...] | 生成器工具,如代码生成器 |
| 宏中依赖 | srcs = [":prev_rule"] | 宏中手动指定依赖顺序 |
| 自定义规则中依赖 | attrs = { "deps": attr.label_list() } | 高级场景:Starlark rule |
1214

被折叠的 条评论
为什么被折叠?



