Bazel 如何使用workspace\u status\u命令的输出构建自定义规则?
Bazel 如何使用workspace\u status\u命令的输出构建自定义规则?,bazel,Bazel,bazel build标志--workspace\u status\u命令支持调用脚本来检索例如存储库元数据,这也称为构建戳记,在java\u binary等规则中可用 我想使用此元数据创建一个自定义规则。 我想用它来实现一个通用的支持功能。它应该接收git版本和一些其他属性,并创建一个可用作依赖项的version.go输出文件 所以我开始了一段旅程,在各种bazel存储库中寻找规则 像Rules\u docker这样的规则支持在container\u image中使用stamp进行戳记,并允许
bazel build
标志--workspace\u status\u命令
支持调用脚本来检索例如存储库元数据,这也称为构建戳记,在java\u binary
等规则中可用
我想使用此元数据创建一个自定义规则。
我想用它来实现一个通用的支持功能。它应该接收git版本和一些其他属性,并创建一个可用作依赖项的version.go
输出文件
所以我开始了一段旅程,在各种bazel存储库中寻找规则
像Rules\u docker
这样的规则支持在container\u image
中使用stamp
进行戳记,并允许您在属性中引用状态输出
rules\u go
在go\u binary
的x\u defs
属性中支持它
这将是我的理想目标,我在
看起来我可以通过将中的条目用作替换的字典来获得所需的内容。但我不知道如何找到这些文件的字典。这两个文件似乎是“非官方的”,它们不是ctx
文档的一部分
在我已经发现的基础上:如何根据status命令输出获取dict
如果这是不可能的,那么访问自定义规则的workspace\u status\u命令
输出的最短/最简单的方法是什么?我一直在您所在的位置,我最终遵循您开始探索的路径。我生成了一个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_二进制文件。我很乐意了解一种更好的(更地道,更少的啤酒花)方法,但是(现在)我同意:它有效。对该方法和您的担忧很少有评论:
- 如果您无法读入(文件并在Skylark中执行操作)本身
- …说到这里,无论如何,保持转换(工具)和构建描述(bazel)的分离可能不是一件坏事
- 可以讨论什么构成官方文档,但参考手册中可能没有出现
ctx.info_文件,它记录在源代码树中。:)其他领域也是如此(我希望这不是因为这些接口被认为还没有提交)
为确保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\u 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])
基于Ondrej的答案,我现在使用这样的东西(在SO编辑器中改编,可能包含小错误):
tools/bazel.rc
:
build --workspace_status_command=tools/workspace_status.sh
tools/workspace\u status.sh
:
echo STABLE_GIT_REV $(git rev-parse HEAD)
version.bzl
:
_VERSION_TEMPLATE_SH = """
set -e -u -o pipefail
while read line; do
export "${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',
},
)
\u版本\u模板\u SH=”“”
设置-e-u-o管道故障
边读边做
导出“${line%*}”=“${line}”
完成我会发誓会有一条评论要求更详细一点。我已经清理了这些信息,保留了相关部分,并将其添加到了答案中。是的,对不起。有一条评论。但我删除了它,因为我最初没有接到本机Python的呼叫,并且没有正确地解决您的解决方案。由于缺乏编辑权限,我将其删除。我很抱歉目前正在根据您编写的内容试验genrule-感谢您的更新。如果我有进展,我将添加另一个答案。不用担心。我还添加了构建文件中的一个片段,该片段将python脚本公开给世界其他地方(标签用于上面规则的\u expand\u template
)。我更喜欢让bazel像python一样处理python。这是显而易见的。并且有助于移植构建环境。这些示例是一个更大的包中的一部分,用作特定项目/自定义构建结果打包的外部依赖项。因此,发布所有内容可能会更混乱,而不是更有用。我希望我的回答能回报您。Thanks的惊人信息!
_VERSION_TEMPLATE_SH = """
set -e -u -o pipefail
while read line; do
export "${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',
},
)