我一直在你所在的地方,我最终走上了你开始探索的道路。我生成了一个 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。我很乐意了解一种更好的(更本地化,更少跳数)的方法,但是(现在)我选择了:它有效。关于方法和您的担忧的几位 cmets:
- AFAIK 您无法读取(文件并在 Skylark 中执行操作)本身...
- ...说起来,无论如何保持转换(工具)和构建描述(bazel)分开可能不是一件坏事。
- 官方文档的构成可能存在争议,但
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 脚本暴露给 bazel 其余部分的 Python 脚本的方式。
py_binary(
name = "expand_template",
main = "expand_template.py",
srcs = ["expand_template.py"],
visibility = ["//visibility:public"],
)