bazel build
标记--workspace_status_command
支持调用脚本来检索,例如存储库元数据,这也称为构建标记,可在java_binary
等规则中使用。
我想使用此元数据创建自定义规则。
我想将它用于一个通用的支持功能。它应该接收git版本和一些其他属性,并创建一个version.go
输出文件,可用作依赖项。
所以我开始了解各种bazel存储库中的规则。
rules_docker
之类的规则支持在stamp
中使用container_image
标记,并允许您在属性中引用状态输出。
rules_go
在x_defs
的{{1}}属性中支持它。
这对我的目的来说是理想的,我挖了......
看起来我可以使用ctx.actions.expand_template
中的条目作为go_binary
的字典,通过ctx.info_file
or ctx.version_file
得到我想要的内容。但我还没弄清楚如何获取这些文件的字典。这两个文件似乎是"非官方的",它们不属于substitutions
文档。
基于我已经发现的东西:如何根据状态命令输出获得ctx
?
如果不可能,从自定义规则访问dict
输出的最短/最简单方法是什么?
答案 0 :(得分:0)
我一直都在你的位置,我最终走上了你开始探索的道路。我生成一个JSON描述,其中还包括从git收集的信息以及包含结果的包,我最终做了类似这样的事情:
def _build_mft_impl(ctx):
args = ctx.actions.args()
args.add('-f')
args.add(ctx.info_file)
args.add('-i')
args.add(ctx.files.src)
args.add('-o')
args.add(ctx.outputs.out)
ctx.actions.run(
outputs = [ctx.outputs.out],
inputs = ctx.files.src + [ctx.info_file],
arguments = [args],
progress_message = "Generating manifest: " + ctx.label.name,
executable = ctx.executable._expand_template,
)
def _get_mft_outputs(src):
return {"out": src.name[:-len(".tmpl")]}
build_manifest = rule(
implementation = _build_mft_impl,
attrs = {
"src": attr.label(mandatory=True,
allow_single_file=[".json.tmpl", ".json_tmpl"]),
"_expand_template": attr.label(default=Label("//:expand_template"),
executable=True,
cfg="host"),
},
outputs = _get_mft_outputs,
)
//:expand_template
在我的案例中是一个标签,指向执行转化的py_binary
。我很乐意了解更好(更本土,更少跳跃)的方式,但是(现在)我选择了:它有效。关于方法和您的顾虑的评论很少:
ctx.info_file
可能不会出现在参考手册中,它会在源代码树中记录。 :)对于其他领域也是如此(我希望这不是因为那些接口被认为还没有提交)。为了src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleContextApi.java
中的完整性,有:
@SkylarkCallable(
name = "info_file",
structField = true,
documented = false,
doc =
"Returns the file that is used to hold the non-volatile workspace status for the "
+ "current build request."
)
public FileApi getStableWorkspaceStatus() throws InterruptedException, EvalException;
编辑:评论中提到的额外细节。
在我的workspace_status.sh
中我会得到以下一行:
echo STABLE_GIT_REF $(git log -1 --pretty=format:%H)
在我的.json.tmpl
文件中,我会:
"ref": "${STABLE_GIT_REF}",
我选择了像文本符号这样的shell来替换,因为它对很多用户来说很直观,而且很容易匹配。
至于替换,实际代码的相关部分(CLI保留在此部分之外)将是:
def get_map(val_file):
"""
Return dictionary of key/value pairs from ``val_file`.
"""
value_map = {}
for line in val_file:
(key, value) = line.split(' ', 1)
value_map.update(((key, value.rstrip('\n')),))
return value_map
def expand_template(val_file, in_file, out_file):
"""
Read each line from ``in_file`` and write it to ``out_file`` replacing all
${KEY} references with values from ``val_file``.
"""
def _substitue_variable(mobj):
return value_map[mobj.group('var')]
re_pat = re.compile(r'\${(?P<var>[^} ]+)}')
value_map = get_map(val_file)
for line in in_file:
out_file.write(re_pat.subn(_substitue_variable, line)[0])
EDIT2:这就是Python脚本如何将python脚本暴露给bazel的其余部分。
py_binary(
name = "expand_template",
main = "expand_template.py",
srcs = ["expand_template.py"],
visibility = ["//visibility:public"],
)
答案 1 :(得分:0)
根据Ondrej的回答,我现在使用这样的东西(在SO编辑器中修改,可能包含小错误):
tools/bazel.rc
:
build --workspace_status_command=tools/workspace_status.sh
tools/workspace_status.sh
:
echo STABLE_GIT_REV $(git rev-parse HEAD)
version.bzl
:
_VERSION_TEMPLATE_SH = """
set -e -u -o pipefail
while read line; do
declare "${line% *}"="${line#* }"
done <"$INFILE" \
&& cat <<EOF >"$OUTFILE"
{ "ref": "${STABLE_GIT_REF}"
, "service": "${SERVICE_NAME}"
}
EOF
"""
def _commit_info_impl(ctx):
ctx.actions.run_shell(
outputs = [ctx.outputs.outfile],
inputs = [ctx.info_file],
progress_message = "Generating version file: " + ctx.label.name,
command = _VERSION_TEMPLATE_SH,
env = {
'INFILE': ctx.info_file.path,
'OUTFILE': ctx.outputs.version_go.path,
'SERVICE_NAME': ctx.attr.service,
},
)
commit_info = rule(
implementation = _commit_info_impl,
attrs = {
'service': attr.string(
mandatory = True,
doc = 'name of versioned service',
),
},
outputs = {
'outfile': 'manifest.json',
},
)