【问题标题】:Verify step in bazel验证 bazel 中的步骤
【发布时间】:2017-03-28 18:30:42
【问题描述】:

我正在寻找在 Bazel 中运行“检查”或“验证”步骤的好方法,例如 go vetgofmtpylintcppcheck。这些步骤不会创建任何输出文件。唯一重要的是返回码(如测试)。

现在我正在使用以下配方:

sh_test(
    name = "verify-pylint",
    srcs = ["verify-pylint.sh"],
    data = ["//:all-srcs"],
)

verify-pylint.sh 看起来像这样:

find . -name '*.py' | xargs pylint

这有两个问题:

  • 验证逻辑在 shell 脚本和 BUILD 文件之间拆分。理想情况下,我希望将两者放在同一个地方(在 BUILD 文件中)
  • 只要源文件之一发生更改(在 //:all-srcs 中),bazel test verify-pylint 就会在每个文件上重新运行 pylint(这可能会很昂贵/很慢)。

在 bazel 中运行这些步骤的惯用方式是什么?

【问题讨论】:

    标签: bazel


    【解决方案1】:

    解决方案不止一种。

    最干净的方法是在构建时进行验证:为要验证的每个文件(或一批文件)创建一个genrule,如果验证成功,则 genrule 会输出一些内容,如果失败,则该规则不输出任何内容,这也会自动使构建失败。

    由于验证的成功取决于文件的内容,并且相同的输入应该产生相同的输出,所以 genrules 应该产生一个依赖于输入内容的输出文件。最方便的方法是验证成功时将文件的摘要写入输出,验证失败则不输出。

    要使验证器可重用,您可以创建一个Skylark macro 并在您的所有包中使用它。

    要将所有这些放在一起,您可以编写如下内容。

    //tools:py_verify_test.bzl的内容:

    def py_verify_test(name, srcs, visibility = None):
        rules = {"%s-file%d" % (name, hash(s)): s for s in srcs}
        for rulename, src in rules.items():
            native.genrule(
                name = rulename,
                srcs = [s],
                outs = ["%s.md5" % rulename],
                cmd = "$(location //tools:py_verifier) $< && md5sum $< > $@",
                tools = ["//tools:py_verifier"],
                visibility = ["//visibility:private"],
            )
    
        native.sh_test(
            name = name,
            srcs = ["//tools:build_test.sh"],
            data = rules.keys(),
            visibility = visibility,
        )
    

    //tools:build_test.sh的内容:

    #!/bin/true
    # If the test rule's dependencies could be built,
    # then all files were successfully verified at
    # build time, so this test can merely return true.
    

    //tools:BUILD的内容:

    # I just use sh_binary as an example, this could
    # be a more complicated rule of course.
    sh_binary(
        name = "py_verifier",
        srcs = ["py_verifier.sh"],
        visibility = ["//visibility:public"],
    )
    

    任何想要验证文件的包的内容:

    load("//tools:py_verify_test.bzl", "py_verify_test")
    
    py_verify_test(
        name = "verify",
        srcs = glob(["**/*.py"]),
    )
    

    【讨论】:

      【解决方案2】:

      一个简单的解决方案。

      在您的 BUILD 文件中:

      load(":gofmt.bzl", "gofmt_test")
      
      gofmt_test(
          name = "format_test",
          srcs = glob(["*.go"]),
      )
      

      gofmt.bzl:

      def gofmt_test(name, srcs):
        cmd = """
          export TMPDIR=.
          out=$$(gofmt -d $(SRCS))
      
          if [ -n "$$out" ]; then
            echo "gmfmt failed:"
            echo "$$out"
            exit 1
          fi
          touch $@
        """
        native.genrule(
            name = name,
            cmd = cmd,
            srcs = srcs,
            outs = [name + ".out"],
            tools = ["gofmt.sh"],
        )
      

      一些备注:

      • 如果您的包装脚本增长,您应该将它放在一个单独的 .sh 文件中。
      • 在 genrule 命令中,由于转义,我们需要 $$ 而不是 $(参见 documentation
      • gofmt_test 实际上不是测试,将与 bazel build :all 一起运行。如果您确实需要测试,请参阅 Laszlo 的示例并致电 sh_test
      • 我调用touch 来创建文件,因为genrule 需要输出才能成功。
      • export TMPDIR=. 是必需的,因为默认情况下沙盒会阻止写入其他目录。

      要缓存每个文件的结果(并避免重新检查未更改的文件),您需要创建多个操作。请参阅 Laszlo 的 for 循环。

      为了简化代码,我们可以提供一个通用规则。也许这是我们应该放在标准库中的东西。

      【讨论】:

        猜你喜欢
        • 2020-03-07
        • 2020-12-05
        • 1970-01-01
        • 2020-05-24
        • 2021-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多