在Bazel中,给定构建目标,脚本(在Bazel外部运行)如何获得生成文件的路径?
场景:我使用Bazel进行构建,然后在完成后,我想将结果复制到服务器。我只需要知道要复制哪些文件。我可以对文件列表进行硬编码,但我不想这样做。
一个简单的例子:这个Bazel脚本:
genrule(
name = "main",
srcs = ["main.in"],
outs = ["main.out"],
cmd = "cp $< $@",
)
如果您创建名为main.in
的文件,然后运行bazel build :main
,则bazel报告:
INFO: Found 1 target...
Target //:main up-to-date:
bazel-genfiles/main.out
INFO: Elapsed time: 6.427s, Critical Path: 0.40s
所以有:bazel-genfiles/main.out
。但是我可以使用什么机器可读技术来获得该路径? (我可以解析bazel build
的输出,但我们不鼓励这样做。)
我找到的最接近的是使用bazel query --output=xml :main
,它以XML格式转储有关:main
的信息。输出包括以下行:
<rule-output name="//:main.out"/>
这非常接近我想要的。但name
是Bazel的标签格式;我不知道如何将它作为一条道路。
我可以在name
字段上进行某种字符串替换,将其转换为bazel-genfiles/main.out
;但即便这样也不可靠。如果我的genrule
已包含output_to_bindir = 1
,则输出将为bazel-bin/main.out
。
此外,并非所有规则在XML输出中都有<rule-output>
字段。例如,如果我的BUILD
文件具有此代码来创建C库:
cc_library(
name = "mylib",
srcs = glob(["*.c"])
)
bazel query --output=xml :mylib
的输出不包含<rule-output>
或其他有用的内容:
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<query version="2">
<rule class="cc_library" location="/Users/mikemorearty/src/bazel/test1/BUILD:8:1" name="//:mylib">
<string name="name" value="mylib"/>
<list name="srcs">
<label value="//:foo.c"/>
</list>
<rule-input name="//:foo.c"/>
<rule-input name="//tools/defaults:crosstool"/>
<rule-input name="@bazel_tools//tools/cpp:stl"/>
</rule>
</query>
答案 0 :(得分:3)
在两次bazel
次运行之间,输出路径应该相同。也就是说,如果您构建//path/to:target
然后bazel clean
并再次构建,它应该生成相同的文件。由于此输出文件是常量,您可以运行
ls "$(bazel info bazel-genfiles)/main.out"
我相信这会为你提供一个构建发生后该文件创建位置的参考(它不会为你构建它)。
如果您希望从目标转到依赖于您正在运行的rules_*
的文件名。例如,在rules_go中,输出路径取决于go_library
目标的参数。 rules_go团队最近为他们的项目记录了this behavior。
二进制输出路径通常应该在不同版本之间保持稳定,并且您可以依赖它们并没有太大差异。但是,根据我的经验,这个问题通常表明你应该考虑将过程中以前的外部部分移动到Bazel作为一个规则或自定义规则。例如,我以前使用这个技巧来组装NPM包但现在我做了整个事情in Bazel并且有一个目标生成.tar,我有兴趣上传到NPM。也许您可以跟进一些关于您对此感兴趣的具体细节,并且我们可能能够通过一个不依赖外部系统理解Bazel构建路径的解决方案。
答案 1 :(得分:0)
您可以使用bazel aquery
来查询此信息
动作图。
这是一个稍微丰富一点的示例,其中有一个来自两个输出文件 风格:
$ ls
BUILD main.in WORKSPACE
$ cat WORKSPACE
$ cat BUILD
genrule(
name = "main",
srcs = ["main.in"],
outs = ["main.o1", "main.o2"],
cmd = "cp $< $(location main.o1); cp $< $(location main.o2)",
)
$ cat main.in
hello
使用bazel aquery //:main --output=textproto
来查询具有机器可读输出的操作图(原始数据为analysis.ActionGraphContainer
):
$ bazel aquery //:main --output=textproto >aquery_result 2>/dev/null
$ cat aquery_result
artifacts {
id: "0"
exec_path: "main.in"
}
artifacts {
id: "1"
exec_path: "external/bazel_tools/tools/genrule/genrule-setup.sh"
}
artifacts {
id: "2"
exec_path: "bazel-out/k8-fastbuild/genfiles/main.o1"
}
artifacts {
id: "3"
exec_path: "bazel-out/k8-fastbuild/genfiles/main.o2"
}
actions {
target_id: "0"
action_key: "dd7fd759bbecce118a399c6ce7b0c4aa"
mnemonic: "Genrule"
configuration_id: "0"
arguments: "/bin/bash"
arguments: "-c"
arguments: "source external/bazel_tools/tools/genrule/genrule-setup.sh; cp main.in bazel-out/k8-fastbuild/genfiles/main.o1; cp main.in bazel-out/k8-fastbuild/genfiles/main.o2"
input_dep_set_ids: "0"
output_ids: "2"
output_ids: "3"
}
targets {
id: "0"
label: "//:main"
rule_class_id: "0"
}
dep_set_of_files {
id: "0"
direct_artifact_ids: "0"
direct_artifact_ids: "1"
}
configuration {
id: "0"
mnemonic: "k8-fastbuild"
platform_name: "k8"
}
rule_classes {
id: "0"
name: "genrule"
}
数据并非完全集中在一处,但请注意:
2
和3
的工件对应于我们所需的两个工件
输出文件,并将这些工件的输出位置列出为
相对于工作空间根目录的磁盘上文件的路径; artifacts
的{{1}}条目与工件相关联
ID 0
和2
;和3
的{{1}}条目与targets
相关联
标签。鉴于这种简单的结构,我们可以轻松地将脚本打包在一起
列出与提供的标签相对应的所有输出文件。我找不到
直接取决于Bazel对"0"
或其定义的定义
来自外部存储库的语言绑定,因此您可以修补
将以下脚本放入//:main
存储库本身中:
工具/list_outputs/list_outputs.py
analysis.proto
工具/列表输出/已构建
bazelbuild/bazel
作为Git补丁,为了您的方便: https://gist.github.com/wchargin/5e6a43a203d6c95454aae2886c5b54e4
请注意,此代码尚未经过审核或验证 正确性我主要以它为例。如果有用的话 您,那么也许这个周末我可以为此编写一些测试并进行PR 针对Bazel本身。