在Analysis阶段会运行rule的实现函数,但此时所有文件还只是“声明”, 不能在这个阶段读取任何文件内容。
📌 为什么在 Analysis 阶段运行了你的 rule?
这是 Bazel 的设计哲学:
Bazel 想要构建是 声明式的、可追踪的、可并行的、可缓存的。
所以它把 构建动作(actions)和 运行动作(execution)严格分开!
📌 在Analysis阶段是做什么呢?
构建动作的图 (constuct action graph)。
For each target in post-order, run the rule implementation.
Rules CAN
declare output files and actions that produce them.
access attributes and file names from current target.
access "providers" (metadata) from attribute targets.
return providers for use by dependent targets.
Rules CANNOT
read or write files directly.
run commands directly.
Runs on the host machine, in parallel. Action graph cached in memory.
📌 想读取依赖文件内容该怎么办呢?
在 Bazel 中,如果你需要基于某个文件内容做某种转换或构建,可行的一个方法是:
✅ 在 Analysis 阶段 声明该文件为输入,
✅ 在 Execution 阶段 通过构建动作(action)中的命令 来读取它的内容、处理它。
📌 示例:你想要构建一个 target,它依赖一个 JSON 文件,提取里面的版本号并生成一个头文件。
🚫 错误方式(想在 rule 里读取 JSON 文件):
# 错误示意:rule implementation 想在 Analysis 阶段直接读取内容
with open(ctx.file.config_json.path) as f:
data = json.load(f)
version = data["version"]
这在 Bazel 中是 不允许的,因为:
-
Analysis 阶段 禁止访问文件内容;
-
.path
是未来文件路径,实际还没被复制或生成。
✅ 正确做法:声明为输入 + 写个脚本在 Execution 阶段读取
def _my_rule_impl(ctx):
output = ctx.actions.declare_file("version_header.h")
ctx.actions.run(
inputs = [ctx.file.config_json],
outputs = [output],
arguments = [ctx.file.config_json.path, output.path],
executable = ctx.executable._script,
)
return DefaultInfo(files = depset([output]))
my_rule = rule(
implementation = _my_rule_impl,
attrs = {
"config_json": attr.label(allow_single_file = True),
"_script": attr.label(
allow_single_file = True,
executable = True,
default = "//tools:extract_version_script", # 你写的脚本
),
},
)
🔧 脚本 extract_version_script(Python/POSIX shell):
比如 Python 实现:
# extract_version_script.py
import json
import sys
with open(sys.argv[1]) as f:
data = json.load(f)
version = data["version"]
with open(sys.argv[2], "w") as out:
out.write(f"#define VERSION \"{version}\"\n")
✅ 这样做的好处:
-
规则是 声明式的;
-
所有输入输出都在 Bazel 的控制之中;
-
文件内容读取只发生在执行阶段;
-
结果是可缓存的、可重现的。