Как написать правило теста Bazel, используя предоставленный инструмент, а не созданный на основе правил?
У меня есть тестовый инструмент (грубо говоря, инструмент сравнения), который принимает два входа и возвращает как результат (разницу между двумя входами), так и код возврата (0, если два входа совпадают, 1 в противном случае). Он построен в Котлине и доступен по адресу//java/fr/enoent/phosphorus
в моем репо.
Я хочу написать правило, которое проверяет, что файл, сгенерированный чем-либо, идентичен эталонному файлу, уже присутствующему в репозитории. Я пробовал что-то сctx.actions.run
, проблема в том, что мое правило, имея test = True
set, должен возвращать исполняемый файл, созданный этим правилом (а не инструмент, предоставленный правилу). Затем я попытался обернуть его в сценарий оболочки, следуя примеру, например:
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 ответ
Оказывается, генерация скрипта - правильный подход, (насколько я понял) невозможно вернуть какой-то указатель на ctx.actions.run
. Правило тестирования должно иметь исполняемый вывод.
Что касается выходного файла, который генерирует инструмент: его вообще не нужно объявлять. Мне просто нужно убедиться, что он сгенерирован в$TEST_UNDECLARED_OUTPUTS_DIR
. Каждый файл в этом каталоге будет добавлен в архив с именемoutput.zip
пользователя Базель. Это (частично) задокументировано здесь.
Что касается файлов запуска, у меня был двоичный файл инструмента, но не его собственные файлы запуска. Вот фиксированное правило:
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
.
Я также допустил небольшую ошибку в конфигурации, поскольку этот тест предназначен для запуска на целевой конфигурации, а не на хосте, он был исправлен соответствующим образом.