Bazel中通过宏复用多子项目pip依赖配置逻辑的问题
我明白你遇到的问题了——Bazel宏里确实不能直接写load语句,因为load必须放在文件的顶层,这就导致你没法把pip_import+load+pip_install的完整流程直接封装到宏里。不过我们可以用两种思路来实现复用,不用重复粘贴配置,而且不需要额外的bzl文件。
方案1:利用WORKSPACE循环(Bazel 6+)
如果你的Bazel版本是6或以上,WORKSPACE支持for循环,这是最简洁的方式。我们可以把重复的pip_import逻辑封装成宏,然后用循环批量处理所有子项目,再批量加载并执行pip_install。
首先在你的pyrules.bzl里写封装pip_import的宏:
# pyrules.bzl load("@io_bazel_rules_python//:python/pip.bzl", "pip_import") def setup_pip_deps(subproject_name, pip_repo_name): # 封装重复的pip_import配置 pip_import( name = pip_repo_name, requirements = "@%s//:requirements.txt" % subproject_name, # 这里可以添加其他通用参数,比如extra_pip_args等 )
然后在父WORKSPACE里,用循环批量处理所有子项目:
# WORKSPACE # 先加载我们的宏 load("//:pyrules.bzl", "setup_pip_deps") # 定义所有子项目的配置:(子项目仓库名, pip依赖仓库名) SUBPROJECT_PIP_CONFIGS = [ ("subproject1", "foo1"), ("subproject2", "foo2"), ("subproject3", "foo3"), ] # 第一步:批量创建pip_import仓库 for subproject, pip_repo in SUBPROJECT_PIP_CONFIGS: setup_pip_deps(subproject_name = subproject, pip_repo_name = pip_repo) # 第二步:批量加载每个pip仓库的requirements.bzl并执行pip_install for subproject, pip_repo in SUBPROJECT_PIP_CONFIGS: load("@%s//:requirements.bzl" % pip_repo, "pip_install") pip_install()
这样你只需要维护SUBPROJECT_PIP_CONFIGS这个列表,新增子项目时只需要加一行即可,完全避免了重复粘贴配置。
方案2:改用pip_parse(更现代的方式)
如果你可以切换到pip_parse(Bazel推荐的替代pip_import的方式,需要锁定版本的requirements.txt),可以进一步简化流程,因为pip_parse不需要额外的pip_install调用,直接生成可引用的依赖仓库。
修改pyrules.bzl:
# pyrules.bzl load("@io_bazel_rules_python//:python/pip.bzl", "pip_parse") def setup_pip_deps(subproject_name, pip_repo_name): pip_parse( name = pip_repo_name, requirements_lock = "@%s//:requirements.txt" % subproject_name, # 通用参数可以在这里统一配置 )
然后WORKSPACE里的循环更简单:
# WORKSPACE load("//:pyrules.bzl", "setup_pip_deps") SUBPROJECT_PIP_CONFIGS = [ ("subproject1", "foo1"), ("subproject2", "foo2"), ] for subproject, pip_repo in SUBPROJECT_PIP_CONFIGS: setup_pip_deps(subproject_name = subproject, pip_repo_name = pip_repo)
之后你就可以在BUILD文件里直接用@foo1//:numpy这样的方式引用依赖,不需要额外的pip_install步骤。
为什么之前的宏方案失败?
Bazel的load语句有严格的位置限制:必须放在.bzl文件的顶层(不能在函数、宏或规则内部),所以你没法把load("@foo//:requirements.bzl", "pip_install")写到宏里。上面的方案通过把load移到WORKSPACE的循环中,绕开了这个限制,同时实现了复用。
内容的提问来源于stack exchange,提问作者0x00

