Что вызывает дублирование сборок одной и той же цели в Bazel?
Мы видим дубликаты сборок одной и той же цели в Bazel и задаемся вопросом, что может быть причиной этого.
Вот пример вывода:
[52,715 / 55,135] 12 action running
Bazel package: some-pkg - Target: a_target - Generating files at bazel-out/host/bin/some-pkg/a_target_generate [for host]; 264s remote-cache, processwrapper-sandbox
Bazel package: some-pkg - Target: a_target - Generating files at bazel-out/k8-fastbuild/bin/some-pkg/a_target_generate; 264s remote-cache, processwrapper-sandbox
...
Нам не удалось выявить проблему. Похоже, это происходит только в Linux, но не на Mac.
Цель
a_target
это
custom_rule
цель. Он должен быть независимым от платформы.
custom_rule = rule(
attrs = dict(
...
_custom_rule_java_binary = attr.label(
cfg = "host",
default = Label("//tools/bazel/build/rules/custom-rule:custom_rule_bin"),
executable = True,
),
_singlejar = attr.label(
cfg = "host",
default = Label("@bazel_tools//tools/jdk:singlejar"),
executable = True,
allow_files = True,
),
),
implementation = ...,
)
custom_rule_bin
определяется следующим образом:
java_library(
name = "custom_rule",
srcs = glob(["src/main/java/**/*.java"]),
deps = [
...,
],
)
java_binary(
name = "custom_rule_bin",
visibility = ["//visibility:public"],
main_class = "...",
runtime_deps = [
"@org_slf4j_simple",
":custom_rule",
"//some-pkg:some_pkg", # same some-pkg where a_target is built twice
],
)
Разница в том, что один говорит « для хоста », а другой нет. Кто-нибудь знает, что такое дополнительная сборка « для хоста »?
У меня есть ощущение, что это как-то связано с
cfg
атрибут пользовательского правила. Вероятно, это происходит из какого-то примера кода. Мы используем одно и то же значение для всех наших правил, которые генерируют код. Это настраиваемое правило является особенным, поскольку для запуска и создания дополнительного кода требуется код из приложения, создаваемого Bazel.
Любые идеи ценятся, почему
host
будет неправильным и каково будет правильное значение.
Любые идеи/советы, как это отлаживать?
1 ответ
Во-первых, одно замечание заключается в том, что конфигурация хоста в основном устарела, а «exec» обычно предпочтительнее. Некоторая информация об этом находится здесь: https://bazel.build/rules/rules#configurations.
Происходит то, что эта цель зависит от нескольких конфигураций, и поэтому bazel создаст эту цель в каждой конфигурации. Вы можете использовать cquery, чтобы выяснить, что происходит
В качестве очень простого примера:
genrule(
name = "gen_bin",
outs = ["bin"],
srcs = [":gen_lib"],
exec_tools = [":gen_tool"],
cmd = "touch $@",
)
genrule(
name = "gen_tool",
outs = ["tool"],
srcs = [":gen_lib"],
cmd = "touch $@",
)
genrule(
name = "gen_lib",
outs = ["lib"],
cmd = "touch $@; sleep 10",
)
Строительство
bin
, bazel запускает правило gen_lib дважды (параллельно):
$ bazel build bin
INFO: Analyzed target //:bin (5 packages loaded, 16 targets configured).
INFO: Found 1 target...
[1 / 5] 2 actions running
Executing genrule //:gen_lib; 1s linux-sandbox
Executing genrule //:gen_lib; 1s linux-sandbox
bazel config
дает конфигурации, которые в данный момент находятся в графе сборки в памяти:
$ bazel config
Available configurations:
5b39bc31deb1f1d37f1f858e7eec3964394eacce5bede4456dd59d417af4a6e9 (exec)
723da02ae6d0c5577e98242c8f06ca1bd1c6d7b295c97345ac31b844bfe8f79c
8960923b9e7dc13418be101268efd8e57d80283213d18174705345598b699c6b
fd805cc1de357c04c7abac1b40bae600e3d9ee56a8d17af0c28b5031ca09bfb9 (host)
тогда
cquery
:
$ bazel cquery "rdeps(//..., gen_lib)"
INFO: Analyzed 3 targets (0 packages loaded, 1 target configured).
INFO: Found 3 targets...
//:gen_lib (5b39bc3)
//:gen_lib (8960923)
//:gen_tool (5b39bc3)
//:gen_bin (8960923)
//:gen_tool (8960923)
INFO: Elapsed time: 0.052s
INFO: 0 processes.
INFO: Build completed successfully, 0 total actions
(cquery дает первые 7 цифр хэша конфигурации)
--output=graph
дает точечный график, который немного полезнее:
$ bazel cquery "rdeps(//..., gen_lib)" --output=graph > /tmp/graph
$ xdot /tmp/graph
Таким образом, gen_bin находится в целевой конфигурации (8960923) и зависит от gen_lib, поэтому gen_lib также будет встроен в целевую конфигурацию.
gen_bin также зависит от gen_tool через атрибут exec_tools, а exec_tools строит все в конфигурации exec (5b39bc3).
gen_tool также зависит от gen_lib, а поскольку gen_tool находится в конфигурации exec, версия gen_lib создается в конфигурации exec.
(Есть также другая версия gen_tool в целевой конфигурации в выходных данных, и это артефакт использования в аргументе «universe» rdeps(), поскольку
//...
захватит каждую цель. Точно так же, делая
bazel build //...
вызовет двойную сборку gen_tool.)