如何使用提供的工具而不是规则构建的工具来编写 Bazel 测试规则?

How to write a Bazel test rule using a provided tool rather than a rule-built one?

我有一个测试工具(粗略地说,一个 diffing 工具),它接受两个输入,return 既是一个输出(两个输入之间的差异),也是一个 return 代码(如果两个输入匹配则为 0,否则为 1)。它是用 Kotlin 构建的,可在我的存储库 //java/fr/enoent/phosphorus 中找到。

我想编写一个规则来测试由某物生成的文件是否与存储库中已存在的参考文件相同。我用 ctx.actions.run 尝试了一些东西,问题是我的规则,设置了 test = True,需要 return 一个由该规则 构建的可执行文件 (所以不是提供给规则的工具)。然后我尝试将它包装在 example 之后的 shell 脚本中,如下所示:

def _phosphorus_test_impl(ctx):
    output = ctx.actions.declare_file("{name}.phs".format(name = ctx.label.name))
    script = phosphorus_compare(
        ctx,
        reference = ctx.file.reference,
        comparison = ctx.file.comparison,
        out = output,
    )

    ctx.actions.write(
        output = ctx.outputs.executable,
        content = script,
    )

    runfiles = ctx.runfiles(files = [ctx.executable._phosphorus_tool, ctx.file.reference, ctx.file.comparison])
    return [DefaultInfo(runfiles = runfiles)]

phosphorus_test = rule(
    _phosphorus_test_impl,
    attrs = {
        "comparison": attr.label(
            allow_single_file = [".phs"],
            doc = "File to compare to the reference",
            mandatory = True,
        ),
        "reference": attr.label(
            allow_single_file = [".phs"],
            doc = "Reference file",
            mandatory = True,
        ),
        "_phosphorus_tool": attr.label(
            default = "//java/fr/enoent/phosphorus",
            executable = True,
            cfg = "host",
        ),
    },
    doc = "Compares two files, and fails if they are different.",
    test = True,
)

phosphorus_compare 只是生成实际命令的宏。)

但是,这种方法有两个问题:

总的来说,我觉得使用 shell 脚本只是添加了不必要的间接访问,并丢失了一些上下文(例如工具的 运行 文件)。理想情况下,我只使用 ctx.actions.run 并依赖其 return 代码,但它似乎不是一个选项,因为测试显然需要生成可执行文件。编写这样一条规则的正确方法是什么?

事实证明,生成脚本是正确的方法,(据我所知)不可能 return 某种指向 ctx.actions.run 的指针。测试规则需要 具有可执行输出。

关于工具生成的输出文件:根本不需要声明它。我只需要确保它是在 $TEST_UNDECLARED_OUTPUTS_DIR 中生成的。 Bazel 会将此目录中的每个文件添加到名为 output.zip 的存档中。这是(部分)记录 here.

关于 运行 文件,嗯,我有该工具的二进制文件,但没有它自己的 运行 文件。这是固定规则:

def _phosphorus_test_impl(ctx):
    script = phosphorus_compare(
        ctx,
        reference = ctx.file.reference,
        comparison = ctx.file.comparison,
        out = "%s.phs" % ctx.label.name,
    )

    ctx.actions.write(
        output = ctx.outputs.executable,
        content = script,
    )

    return [
        DefaultInfo(
            runfiles = ctx.runfiles(
                files = [
                    ctx.executable._phosphorus_tool,
                    ctx.file.reference,
                    ctx.file.comparison,
                ],
            ).merge(ctx.attr._phosphorus_tool[DefaultInfo].default_runfiles),
            executable = ctx.outputs.executable,
        ),
    ]

def phosphorus_test(size = "small", **kwargs):
    _phosphorus_test(size = size, **kwargs)

_phosphorus_test = rule(
    _phosphorus_test_impl,
    attrs = {
        "comparison": attr.label(
            allow_single_file = [".phs"],
            doc = "File to compare to the reference",
            mandatory = True,
        ),
        "reference": attr.label(
            allow_single_file = [".phs"],
            doc = "Reference file",
            mandatory = True,
        ),
        "_phosphorus_tool": attr.label(
            default = "//java/fr/enoent/phosphorus",
            executable = True,
            cfg = "target",
        ),
    },
    doc = "Compares two files, and fails if they are different.",
    test = True,
)

returned DefaultInfo 中的关键部分是 .merge(ctx.attr._phosphorus_tool[DefaultInfo].default_runfiles)

我在配置方面也犯了一个小错误,因为此测试旨在 运行 在目标配置上,而不是在主机上,它已相应修复。