Bazel的repository_rule
一直使用Bazel内建的repository (git_repository, http_archive, etc),但实际上bazel提供了自定义的repository_rule给我们自定义repository的rule,以提高我们定义库的方式。
一个例子
假如在一个远端git上有一个c++的库A,它是一个预编译过的库。也就是说它包含一个lib目录,里面有.a和.so文件,但是在这个库A的BUILD文件里,它是这么写的:
cc_library(
name = "module_a",
hdrs = glob(["include/*.h"]),
srcs = glob(["lib/*.a"]),
)
很显然,这个库只提供了静态库,并不提供动态库。
突然有一天由于某些特殊原因,我们需要使用这个库里的so,而不是.a,我们会怎么做?
最直接的思路就是在库A里增加一个cc_library的rule “libmodule_a”,这个rule的srcs里只包含.so。然后把我们自己库里的所有依赖都改个遍,把依赖@A//:module_a 都换成@A//:libmodule_a。
但是非常不幸,对于库A我们没有修改权限,并且对于本地编译来说,我们既要提供依赖.a的编译方式,也需要提供.so的编译方式。
怎么办?可能另一个思路是,对于库A我们使用new_git_repository的rule,然后对其指定我们本地的有动态库rule的BUILD文件。然后我们通过config_setting指定编译参数,在本地进行依赖时通过config_setting选择相应的module_a的rule进行依赖。
这是一种思路,但是如果我们自己的库B的依赖库C,D,E也同时依赖了@A//:module_a,我们可以有权限更改自己的库B进行config_setting select deps rule,但是我们如何保证其他库的依赖也更改呢?
另一种思路:使用repository_rule对库A进行改造,让它根据我们传入的参数对库A的BUILD文件进行改变。例如我们在workspace.bzl里自定义一个module_a_repo的rule:
def _git_clone_or_update(ctx):
if ((ctx.attr.tag == "" and ctx.attr.commit == "") or
(ctx.attr.tag != "" and ctx.attr.commit != "")):
ctx.fail("Exactly one of commit or tag should be provided")
if ctx.attr.commit != "":
ref = ctx.attr.commit
else:
ref = "tags/" + ctx.attr.tag
st = ctx.execute(["bash", '-c', """
set -ex
( cd {working_dir} &&
if ! (cd '{dir}' && git rev-parse --git-dir) >/dev/null 2>&1; then
rm -rf '{dir}'
git clone '{remote}' '{dir}'
fi
cd '{dir}'
git reset --hard {ref} || (git fetch && git reset --hard {ref})
git clean -xdf)
""".format(
working_dir=ctx.path(".").dirname,
dir=ctx.path("."),
remote=ctx.attr.remote,
ref=ref,
)])
if st.return_code != 0:
fail("error cloning %s:\n%s" % (ctx.name, st.stderr))
def _module_a_repo_implement(ctx):
_git_clone_or_update(ctx)
BUILD_CONTENT="""
cc_library(
name = "module_a",
hdrs = ["include/*.h"],
srcs = ["lib/*.a"],
includes = ["include"],
visibility = [
"//visibility:public",
],
)
"""
if ctx.attr.mode == "dynamic":
BUILD_CONTENT = BUILD_CONTENT.replace("lib/*.a", 'lib/*.so')
ctx.file('BUILD', content=BUILD_CONTENT)
module_a_repo = repository_rule(
implementation = _module_a_repo_implement,
attrs = {
"remote": attr.string(mandatory=True),
"commit": attr.string(default=""),
"tag": attr.string(default=""),
"mode": attr.string(default="static")
}
)
那个在WORKSPACE文件里可以直接这样写:
load("//:workspace.bzl", "module_a_repo")
module_a_repo(
name="A",
remote="git@git.xxx.com:A/A.git",
commit="some_commit",
mode="static"
)
如果我们想要编译时依赖A的动态库,那么可以把mode="static"改成mode="dynamic", 这样的操作在shell脚本里就非常容易实现了。

本文介绍了如何利用Bazel的repository_rule功能自定义规则,解决在无法修改远程库源码的情况下,根据需求动态选择静态库或动态库的问题。通过示例展示了如何创建一个名为module_a_repo的规则,使得WORKSPACE文件能够根据传入参数动态改变库的依赖类型。
262

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



