如何让Bazel正确缓存自行构建依赖?跨发行版缓存冲突处理
问题背景
我针对基于configure/make的项目(如xmlsec1)编写了Bazel规则,使用rules_foreign_cc的configure_make,相关定义如下:
xmlsec1.BUILD
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") filegroup(name="all_srcs", srcs=glob(["**"])) configure_make( name="xmlsec1", lib_name="xmlsec1", lib_source=":all_srcs", configure_command="configure", configure_in_place=True, out_binaries=["xmlsec1"], targets=["install"], )
xmlsec1.bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") def xmlsec1(): http_archive( name = "xmlsec1", url = "https://www.aleksey.com/xmlsec/download/xmlsec1-1.2.37.tar.gz", sha256 = "5f8dfbcb6d1e56bddd0b5ec2e00a3d0ca5342a9f57c24dffde5c796b2be2871c", build_file = "@//:xmlsec1.BUILD", )
正常构建无问题,但启用Bazel远程缓存并在不同Linux发行版间构建时出现冲突。我尝试通过bazel build --action_env=SYSTEM_DIGEST="$(cat /etc/os-release)"避免缓存冲突,该方式对xmlsec1的工件有效,但foreign_cc相关工具(如make)的构建未应用这些action_env变量。
在Ubuntu 22.04构建xmlsec1后,在CentOS-8上从远程缓存拉取构建时出现GLIBC版本错误:
+ /home/me/.cache/bazel/_bazel_me/8f6a55c898f3ec22f87d9cee5890b9e5/sandbox/processwrapper-sandbox/5/execroot/my_project_packages/bazel-out/k8-opt-exec-2B5CBBC6/bin/external/rules_foreign_cc/toolchains/make/bin/make install /home/me/.cache/bazel/_bazel_me/8f6a55c898f3ec22f87d9cee5890b9e5/sandbox/processwrapper-sandbox/5/execroot/my_project_packages/bazel-out/k8-opt-exec-2B5CBBC6/bin/external/rules_foreign_cc/toolchains/make/bin/make: /lib64/libc.so.6: version `GLIBC_2.34' not found (required by /home/me/.cache/bazel/_bazel_me/8f6a55c898f3ec22f87d9cee5890b9e5/sandbox/processwrapper-sandbox/5/execroot/my_project_packages/bazel-out/k8-opt-exec-2B5CBBC6/bin/external/rules_foreign_cc/toolchains/make/bin/make) /home/me/.cache/bazel/_bazel_me/8f6a55c898f3ec22f87d9cee5890b9e5/sandbox/processwrapper-sandbox/5/execroot/my_project_packages/bazel-out/k8-opt-exec-2B5CBBC6/bin/external/rules_foreign_cc/toolchains/make/bin/make: /lib64/libc.so.6: version `GLIBC_2.33' not found (required by /home/me/.cache/bazel/_bazel_me/8f6a55c898f3ec22f87d9cee5890b9e5/sandbox/processwrapper-sandbox/5/execroot/my_project_packages/bazel-out/k8-opt-exec-2B5CBBC6/bin/external/rules_foreign_cc/toolchains/make/bin/make)
推测原因:foreign_cc的make工具构建时未纳入--action_env变量生成的缓存键,导致高版本GLIBC链接的make被存入远程缓存;旧发行版拉取后因本地GLIBC版本不足报错。
问题解答
1. 这是预期行为吗?为何--action_env对Bazel隐式构建的部分无效,对显式定义的工件有效?
这是预期行为,核心原因在于构建目标的归属逻辑:
- 你显式定义的
xmlsec1属于用户工作区目标,Bazel默认会将--action_env指定的变量纳入这类目标的缓存键计算,因为这些变量可能直接影响编译结果(如系统库版本、编译参数)。 rules_foreign_cc的make工具属于工具链依赖,这类依赖由Bazel的工具链系统独立管理,缓存键仅基于工具链自身的定义(如源码、编译配置),不会自动继承用户通过--action_env传入的变量,所以跨发行版构建时会命中不兼容的缓存工件。
2. 能否将该规则应用到Bazel自身的依赖?
可以,但不能通过--action_env直接实现,需要通过工具链自定义完成:
- 为
rules_foreign_cc的工具链(如make)添加系统属性依赖,比如将/etc/os-release的内容、GLIBC版本作为工具链的输入参数。 - 具体做法是自定义工具链规则,把发行版标识、系统库版本等属性嵌入工具链的定义中,这样Bazel会将这些属性纳入工具链构建的缓存键,确保不同发行版生成的工具链缓存完全分离。
3. 是否有更好的方式定义系统属性以影响所有构建?
推荐以下几种更可靠的方案:
- 自定义Bazel平台:
定义包含发行版、GLIBC版本等核心属性的自定义平台,通过--host_platform参数指定。Bazel会自动将平台属性纳入所有构建目标(包括工具链)的缓存键计算,从根源上隔离不同系统环境的缓存。 - 全局配置系统环境变量:
在项目根目录的.bazelrc中添加全局构建配置:
注意此配置仅对用户工作区目标生效,若要覆盖工具链,仍需结合工具链自定义。build --action_env=SYSTEM_DIGEST="$(cat /etc/os-release)" - 预编译工具链替代:
直接指定适配目标发行版的预编译make工具链,避免Bazel隐式构建工具链,彻底消除跨发行版的工具兼容性问题。
内容的提问来源于stack exchange,提问作者frans
相关产品推荐
相关产品推荐

