Bazel:你如何获得生成文件的路径?

时间:2017-12-17 21:25:53

标签: bazel

在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>

2 个答案:

答案 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"
}

数据并非完全集中在一处,但请注意:

  • ID为23的工件对应于我们所需的两个工件 输出文件,并将这些工件的输出位置列出为 相对于工作空间根目录的磁盘上文件的路径;
  • 目标ID为artifacts的{​​{1}}条目与工件相关联 ID 02;和
  • ID为3的{​​{1}}条目与targets相关联 标签。

鉴于这种简单的结构,我们可以轻松地将脚本打包在一起 列出与提供的标签相对应的所有输出文件。我找不到 直接取决于Bazel对"0"或其定义的定义 来自外部存储库的语言绑定,因此您可以修补 将以下脚本放入//:main存储库本身中:

工具/list_outputs/list_outputs.py

analysis.proto

工具/列表输出/已构建

bazelbuild/bazel

作为Git补丁,为了您的方便: https://gist.github.com/wchargin/5e6a43a203d6c95454aae2886c5b54e4

请注意,此代码尚未经过审核或验证 正确性我主要以它为例。如果有用的话 您,那么也许这个周末我可以为此编写一些测试并进行PR 针对Bazel本身。