Как написать тестовое правило Базеля, используя предоставленный инструмент, а не созданный правилом? - PullRequest
0 голосов
/ 06 октября 2019

У меня есть тестовый инструмент (грубо говоря, разностный инструмент), который принимает два входа и возвращает как выход (разница между двумя входами), так и код возврата (0, если два входа совпадают, 1 в противном случае),Он построен в Kotlin и доступен в моем репозитории на //java/fr/enoent/phosphorus.

Я хочу написать правило, которое проверяет, что сгенерированный чем-то файл идентичен эталонному файлу, уже присутствующему в репозитории. Я пробовал что-то с ctx.actions.run, проблема в том, что мое правило, с установленным test = True, должно возвращать исполняемый файл , построенный по этому правилу (поэтому инструмент не предоставлен правилу). Затем я попытался обернуть его в сценарий оболочки, следуя примеру , например:

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 - это просто макрос, генерирующий фактическую команду.)

Однако у этого подхода есть две проблемы:

  • Выходные данные не могут быть объявлены таким образом. Это не связано с какими-либо действиями (и Базель жалуется на это). Может быть, мне не нужно объявлять вывод для теста? Делает ли Базель что-либо доступное в папке с тестами, если тест не пройден?
  • Файлы запуска, необходимые для запуска инструмента, по-видимому, недоступны при выполнении теста: java/fr/enoent/phosphorus/phosphorus: line 359: /home/kernald/.cache/bazel/_bazel_kernald/58c025fbb926eac6827117ef80f7d2fa/sandbox/linux-sandbox/1979/execroot/fr_enoent/bazel-out/k8-fastbuild/bin/tools/phosphorus/tests/should_pass.runfiles/remotejdk11_linux/bin/java: No such file or directory

В целом, я чувствую, что использование сценария оболочки просто добавляет ненужную косвенность и теряет некоторый контекст (например, файлы запуска инструментов). В идеале я бы просто использовал ctx.actions.run и полагался на его код возврата, но это, кажется, не вариант, поскольку тесту, очевидно, нужно сгенерировать исполняемый файл. Каков будет правильный подход к написанию такого правила?

1 Ответ

0 голосов
/ 09 октября 2019

Оказывается, генерация скрипта - это правильный подход, (насколько я понял) невозможно вернуть какой-то указатель на ctx.actions.run. Правило теста требует для получения исполняемого файла.

Что касается выходного файла, который генерирует инструмент: его вообще не нужно объявлять. Мне просто нужно убедиться, что он сгенерирован в $TEST_UNDECLARED_OUTPUTS_DIR. Каждый отдельный файл в этом каталоге будет добавлен в архив под названием output.zip от Bazel. Это (частично) задокументировано здесь .

Что касается исполняемых файлов, у меня был двоичный файл инструмента, но не было его собственных исполняемых файлов. Вот фиксированное правило:

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,
)

Ключевой частью является .merge(ctx.attr._phosphorus_tool[DefaultInfo].default_runfiles) в возвращенном DefaultInfo.

Я также допустил небольшую ошибку в конфигурации, так как этот тест предназначендля запуска на целевой конфигурации, а не на хосте, это было исправлено соответствующим образом.

...