验证bazel中的步骤

时间:2017-03-28 18:30:42

标签: bazel

我正在寻找在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执行这些步骤的惯用方法是什么?

2 个答案:

答案 0 :(得分:2)

有多种解决方案。

最简洁的方法是在构建时进行验证:为要验证的每个文件(或一批文件)创建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"]),
)

答案 1 :(得分:1)

一个简单的解决方案。

在您的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循环。

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