在Bazel中编写含随机性的genrule及相关技术问题咨询
构建封闭性与种子传递方式的问题解答
背景
我们有一个接收随机种子作为输入的代码生成器:未指定种子时会随机生成种子,输出结果非确定;指定种子时输出一致。最初尝试用如下genrule生成代码:
genrule( name = "generated_code", srcs = [], outs = ["generated_code.h"], cmd = "my-code-gen -o $@", # Notice that seed not specified )
但担心这会破坏依赖该目标的构建封闭性(hermeticity),于是通过自定义规则及build_setting配置生成器种子,支持从CLI指定种子。现咨询两个问题:
- 上述未指定种子的genrule是否属于非封闭性构建?破坏封闭性会带来哪些问题?
- 对比build_setting,使用--action_env传递种子是否更适合当前场景?
问题1解答
- 属于非封闭性构建。Bazel的构建封闭性核心要求是相同输入必须产生完全相同的输出,这个genrule未指定固定种子,每次执行
my-code-gen都会生成随机种子,导致输出的generated_code.h内容无规律变化,直接违反了封闭性要求。 - 破坏封闭性的具体问题:
- 缓存失效:Bazel的本地/远程缓存依赖输入哈希判断是否复用结果,输出不稳定会让缓存命中率极低,每次构建都要重新执行该genrule,大幅拖慢构建速度。
- 构建结果不一致:不同环境、不同时间的构建产物可能不同,引发难以复现的bug——比如本地测试正常,但CI构建出的代码存在问题,排查难度极大。
- 干扰增量构建:即使没有任何输入变更,该目标也会被强制重新构建,破坏增量构建的正确性。
- 协作冲突:团队成员构建出的代码版本不一致,导致代码合并、测试环节出现不必要的矛盾。
问题2解答
对比build_setting,使用--action_env传递种子并不适合当前场景,原因如下:
- 构建可追溯性差异:
build_setting是显式的构建参数,会被纳入Bazel的构建哈希计算,能清晰体现种子对构建结果的影响;而--action_env传递的环境变量默认不会计入输入哈希,即使种子变化,Bazel可能仍复用旧缓存,导致构建结果不符合预期。 - 参数管理清晰度差异:
build_setting可以在BUILD文件中定义默认值,也支持通过CLI(如--//:seed=xxx)覆盖,参数作用范围明确;--action_env是全局环境变量,可能被其他无关构建动作意外读取,引发潜在冲突。 - 规则集成友好度差异:自定义规则结合
build_setting能自然地将种子作为生成器的输入参数,在规则逻辑中直接引用,代码可读性和维护性更强;而通过--action_env传递需要手动在命令行拼接环境变量,容易出错,也不利于规则封装。
内容的提问来源于stack exchange,提问作者Shaun Hsiao
相关产品推荐
相关产品推荐

