Atributos configuráveis, conhecidos como select(), são um recurso do Bazel que permite aos usuários alternar os valores
dos atributos da regra de build na linha de comando.
Isso pode ser usado, por exemplo, para uma biblioteca multiplataforma que escolhe automaticamente a implementação adequada para a arquitetura ou para um binário configurável por recursos que pode ser personalizado no momento do build.
Exemplo
# myapp/BUILD
cc_binary(
name = "mybinary",
srcs = ["main.cc"],
deps = select({
":arm_build": [":arm_lib"],
":x86_debug_build": [":x86_dev_lib"],
"//conditions:default": [":generic_lib"],
}),
)
config_setting(
name = "arm_build",
values = {"cpu": "arm"},
)
config_setting(
name = "x86_debug_build",
values = {
"cpu": "x86",
"compilation_mode": "dbg",
},
)
Isso declara um cc_binary que "escolhe" as dependências com base nas flags na
linha de comando. Especificamente, deps se torna:
| Comando | deps = |
bazel build //myapp:mybinary --cpu=arm |
[":arm_lib"] |
bazel build //myapp:mybinary -c dbg --cpu=x86 |
[":x86_dev_lib"] |
bazel build //myapp:mybinary --cpu=ppc |
[":generic_lib"] |
bazel build //myapp:mybinary -c dbg --cpu=ppc |
[":generic_lib"] |
select() serve como um marcador de posição para um valor que será escolhido com base nas
condições de configuração, que são rótulos que referenciam config_setting
destinos. Ao usar select() em um atributo configurável, o atributo
adota valores diferentes quando condições diferentes são válidas.
As correspondências precisam ser inequívocas: se várias condições corresponderem, então:
- Todos eles são resolvidos para o mesmo valor. Por exemplo, ao executar no Linux x86, isso é inequívoco
{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}porque ambas as ramificações são resolvidas como "hello". - Um dos
valuesé um superconjunto estrito de todos os outros. Por exemplo,values = {"cpu": "x86", "compilation_mode": "dbg"}é uma especialização inequívoca devalues = {"cpu": "x86"}.
A condição integrada //conditions:default corresponde automaticamente quando
nada mais corresponde.
Embora este exemplo use deps, select() funciona tão bem em srcs,
resources, cmd e na maioria dos outros atributos. Apenas um pequeno número de atributos
não é configurável, e eles são claramente anotados. Por exemplo, o próprio atributo
config_setting's own
values não é configurável.
select() e dependências
Alguns atributos mudam os parâmetros de build para todas as dependências transitivas
em um destino. Por exemplo, genrule's tools muda --cpu para a CPU da
máquina que executa o Bazel (que, graças à compilação cruzada, pode ser diferente
da CPU para a qual o destino é criado). Isso é conhecido como uma
transição de configuração.
Considerando
#myapp/BUILD
config_setting(
name = "arm_cpu",
values = {"cpu": "arm"},
)
config_setting(
name = "x86_cpu",
values = {"cpu": "x86"},
)
genrule(
name = "my_genrule",
srcs = select({
":arm_cpu": ["g_arm.src"],
":x86_cpu": ["g_x86.src"],
}),
tools = select({
":arm_cpu": [":tool1"],
":x86_cpu": [":tool2"],
}),
)
cc_binary(
name = "tool1",
srcs = select({
":arm_cpu": ["armtool.cc"],
":x86_cpu": ["x86tool.cc"],
}),
)
em execução
$ bazel build //myapp:my_genrule --cpu=arm
em uma máquina de desenvolvedor x86 vincula o build a g_arm.src, tool1 e
x86tool.cc. Os dois selects anexados a my_genrule usam os parâmetros de build de my_genrule's, que incluem --cpu=arm. O atributo tools muda
--cpu para x86 para tool1 e suas dependências transitivas. O select em
tool1 usa tool1's parâmetros de build, que incluem --cpu=x86.
Condições de configuração
Cada chave em um atributo configurável é uma referência de rótulo a um
config_setting ou
constraint_value.
config_setting é apenas uma coleção de
configurações de flag de linha de comando esperadas. Ao encapsular essas configurações em um destino, é
fácil manter as condições "padrão" que os usuários podem referenciar em vários lugares.
constraint_value oferece suporte ao comportamento multiplataforma.
Flags integradas
Flags como --cpu são integradas ao Bazel: a ferramenta de build as entende nativamente
para todos os builds em todos os projetos. Elas são especificadas com
config_setting's
values atributo:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN é um nome de flag (sem --, então "cpu" em vez de "--cpu"). valueN
é o valor esperado para essa flag. :meaningful_condition_name corresponde se
cada entrada em values corresponder. A ordem é irrelevante.
valueN é analisado como se tivesse sido definido na linha de comando. Isso significa:
values = { "compilation_mode": "opt" }corresponde abazel build -c optvalues = { "force_pic": "true" }corresponde abazel build --force_pic=1values = { "force_pic": "0" }corresponde abazel build --noforce_pic
config_setting oferece suporte apenas a flags que afetam o comportamento do destino. Por exemplo,
--show_progress não é permitido porque
afeta apenas a forma como o Bazel informa o progresso ao usuário. Os destinos não podem usar essa
flag para construir os resultados. O conjunto exato de flags compatíveis não está
documentado. Na prática, a maioria das flags que "fazem sentido" funciona.
Flags personalizadas
É possível modelar suas próprias flags específicas do projeto com as configurações de build do Starlark. Ao contrário das flags integradas, elas são definidas como destinos de build, então o Bazel as referencia com rótulos de destino.
Elas são acionadas com o atributo
flag_values
de config_setting:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
O comportamento é o mesmo das flags integradas. Consulte aqui para ver um exemplo funcional.
--define
é uma sintaxe legada alternativa para flags personalizadas (por exemplo,
--define foo=bar). Isso pode ser expresso no atributo de
valores
(values = {"define": "foo=bar"}) ou no atributo
define_values
(define_values = {"foo": "bar"}). --define só é compatível com versões anteriores. Prefira as configurações de build do Starlark sempre que possível.
values, flag_values e define_values são avaliados de forma independente. O
config_setting corresponde se todos os valores em todos eles corresponderem.
A condição padrão
A condição integrada //conditions:default corresponde quando nenhuma outra condição
corresponde.
Devido à regra "exatamente uma correspondência", um atributo configurável sem correspondência
e sem condição padrão emite um "no matching conditions" erro. Isso pode
proteger contra falhas silenciosas de configurações inesperadas:
# myapp/BUILD
config_setting(
name = "x86_cpu",
values = {"cpu": "x86"},
)
cc_library(
name = "x86_only_lib",
srcs = select({
":x86_cpu": ["lib.cc"],
}),
)
$ bazel build //myapp:x86_only_lib --cpu=arm
ERROR: Configurable attribute "srcs" doesn't match this configuration (would
a default condition help?).
Conditions checked:
//myapp:x86_cpu
Para erros ainda mais claros, é possível definir mensagens personalizadas com o atributo select()'s
no_match_error.
Plataformas
Embora a capacidade de especificar várias flags na linha de comando ofereça flexibilidade, também pode ser difícil definir cada uma delas individualmente sempre que você quiser criar um destino. As plataformas permitem consolidar essas flags em pacotes simples.
# myapp/BUILD
sh_binary(
name = "my_rocks",
srcs = select({
":basalt": ["pyroxene.sh"],
":marble": ["calcite.sh"],
"//conditions:default": ["feldspar.sh"],
}),
)
config_setting(
name = "basalt",
constraint_values = [
":black",
":igneous",
],
)
config_setting(
name = "marble",
constraint_values = [
":white",
":metamorphic",
],
)
# constraint_setting acts as an enum type, and constraint_value as an enum value.
constraint_setting(name = "color")
constraint_value(name = "black", constraint_setting = "color")
constraint_value(name = "white", constraint_setting = "color")
constraint_setting(name = "texture")
constraint_value(name = "smooth", constraint_setting = "texture")
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
platform(
name = "basalt_platform",
constraint_values = [
":black",
":igneous",
],
)
platform(
name = "marble_platform",
constraint_values = [
":white",
":smooth",
":metamorphic",
],
)
A plataforma pode ser especificada na linha de comando. Ela ativa os
config_settings que contêm um subconjunto dos constraint_values da plataforma,
permitindo que esses config_settings correspondam em select() expressões.
Por exemplo, para definir o atributo srcs de my_rocks como calcite.sh,
basta executar
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
Sem plataformas, isso pode ser algo como
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select() também pode ler constraint_values diretamente:
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
sh_binary(
name = "my_rocks",
srcs = select({
":igneous": ["igneous.sh"],
":metamorphic" ["metamorphic.sh"],
}),
)
Isso evita a necessidade de boilerplate config_settings quando você só precisa
verificar valores únicos.
As plataformas ainda estão em desenvolvimento. Consulte a documentação para mais detalhes.
Como combinar select()s
select pode aparecer várias vezes no mesmo atributo:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"] +
select({
":armeabi_mode": ["armeabi_src.sh"],
":x86_mode": ["x86_src.sh"],
}) +
select({
":opt_mode": ["opt_extras.sh"],
":dbg_mode": ["dbg_extras.sh"],
}),
)
select não pode aparecer dentro de outro select. Se você precisar aninhar selects
e seu atributo aceitar outros destinos como valores, use um destino intermediário:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":armeabi_mode": [":armeabi_lib"],
...
}),
)
sh_library(
name = "armeabi_lib",
srcs = select({
":opt_mode": ["armeabi_with_opt.sh"],
...
}),
)
Se você precisar de um select para corresponder quando várias condições corresponderem, considere o encadeamento AND.
Encadeamento OR
Considere o seguinte:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": [":standard_lib"],
":config2": [":standard_lib"],
":config3": [":standard_lib"],
":config4": [":special_lib"],
}),
)
A maioria das condições é avaliada como a mesma dependência. No entanto, essa sintaxe é difícil de ler e
manter. Seria bom não ter que repetir [":standard_lib"] várias
vezes.
Uma opção é predefinir o valor como uma variável BUILD:
STANDARD_DEP = [":standard_lib"]
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": STANDARD_DEP,
":config2": STANDARD_DEP,
":config3": STANDARD_DEP,
":config4": [":special_lib"],
}),
)
Isso facilita o gerenciamento da dependência. No entanto, ainda causa duplicação desnecessária.
Para mais suporte direto, use uma das seguintes opções:
selects.with_or
A macro
with_or
no módulo Skylib's
selects
oferece suporte a condições OR diretamente dentro de um select:
load("@bazel_skylib//lib:selects.bzl", "selects")
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = selects.with_or({
(":config1", ":config2", ":config3"): [":standard_lib"],
":config4": [":special_lib"],
}),
)
selects.config_setting_group
A macro
config_setting_group
no módulo Skylib's
selects
oferece suporte a ORing vários config_settings:
load("@bazel_skylib//lib:selects.bzl", "selects")
config_setting(
name = "config1",
values = {"cpu": "arm"},
)
config_setting(
name = "config2",
values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
name = "config1_or_2",
match_any = [":config1", ":config2"],
)
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1_or_2": [":standard_lib"],
"//conditions:default": [":other_lib"],
}),
)
Ao contrário de selects.with_or, destinos diferentes podem compartilhar :config1_or_2 em
atributos diferentes.
É um erro que várias condições correspondam, a menos que uma seja uma "especialização" inequívoca das outras ou que todas sejam resolvidas para o mesmo valor. Consulte este link para mais detalhes.
Encadeamento AND
Se você precisar que uma ramificação select corresponda quando várias condições corresponderem, use a
Skylib
config_setting_group do Skylib:
config_setting(
name = "config1",
values = {"cpu": "arm"},
)
config_setting(
name = "config2",
values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
name = "config1_and_2",
match_all = [":config1", ":config2"],
)
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1_and_2": [":standard_lib"],
"//conditions:default": [":other_lib"],
}),
)
Ao contrário do encadeamento OR, os config_settings atuais não podem ser ANDed
diretamente dentro de um select. É necessário envolvê-los explicitamente em um config_setting_group.
Mensagens de erro personalizadas
Por padrão, quando nenhuma condição corresponde, o destino ao qual o select() está anexado
falha com o erro:
ERROR: Configurable attribute "deps" doesn't match this configuration (would
a default condition help?).
Conditions checked:
//tools/cc_target_os:darwin
//tools/cc_target_os:android
Isso pode ser personalizado com o no_match_error
atributo:
cc_library(
name = "my_lib",
deps = select(
{
"//tools/cc_target_os:android": [":android_deps"],
"//tools/cc_target_os:windows": [":windows_deps"],
},
no_match_error = "Please build with an Android or Windows toolchain",
),
)
$ bazel build //myapp:my_lib
ERROR: Configurable attribute "deps" doesn't match this configuration: Please
build with an Android or Windows toolchain
Compatibilidade de regras
As implementações de regras recebem os valores resolvidos de atributos configuráveis. Por exemplo, considerando:
# myapp/BUILD
some_rule(
name = "my_target",
some_attr = select({
":foo_mode": [":foo"],
":bar_mode": [":bar"],
}),
)
$ bazel build //myapp/my_target --define mode=foo
O código de implementação da regra considera ctx.attr.some_attr como [":foo"].
As macros podem aceitar select() cláusulas e transmiti-las para regras nativas. No entanto, elas não podem manipulá-las diretamente. Por exemplo, não há como uma macro converter
select({"foo": "val"}, ...)
a
select({"foo": "val_with_suffix"}, ...)
Isso ocorre por dois motivos.
Primeiro, as macros que precisam saber qual caminho um select vai escolher não podem funcionar
porque as macros são avaliadas na fase de carregamento do Bazel,
que ocorre antes que os valores da flag sejam conhecidos.
Essa é uma restrição de design principal do Bazel que provavelmente não vai mudar tão cedo.
Em segundo lugar, as macros que só precisam iterar em todos select caminhos, embora
tecnicamente viáveis, não têm uma interface coerente. Mais design é necessário para mudar
isso.
Consulta e cquery do Bazel
A query do Bazel opera na fase de carregamento
do Bazel.
Isso significa que ela não sabe quais flags de linha de comando um destino usa, já que essas
flags não são avaliadas até mais tarde no build (na
fase de análise).
Portanto, não é possível determinar quais ramificações select() são escolhidas.
A cquery do Bazel opera após a fase de análise do Bazel. Portanto, ela tem
todas essas informações e pode resolver select()s com precisão.
Considere:
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
# myapp/BUILD
string_flag(
name = "dog_type",
build_setting_default = "cat"
)
cc_library(
name = "my_lib",
deps = select({
":long": [":foo_dep"],
":short": [":bar_dep"],
}),
)
config_setting(
name = "long",
flag_values = {":dog_type": "dachshund"},
)
config_setting(
name = "short",
flag_values = {":dog_type": "pug"},
)
query superestima as dependências de :my_lib's:
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
enquanto cquery mostra as dependências exatas:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
Perguntas frequentes
Por que select() não funciona em macros?
select() funciona em regras. Consulte Compatibilidade de regras para mais detalhes.
O principal problema que essa pergunta geralmente significa é que select() não funciona em macros. Elas são diferentes das regras. Consulte a documentação sobre regras e macros para entender a diferença. Confira um exemplo de ponta a ponta:
Defina uma regra e uma macro:
# myapp/defs.bzl
# Rule implementation: when an attribute is read, all select()s have already
# been resolved. So it looks like a plain old attribute just like any other.
def _impl(ctx):
name = ctx.attr.name
allcaps = ctx.attr.my_config_string.upper() # This works fine on all values.
print("My name is " + name + " with custom message: " + allcaps)
# Rule declaration:
my_custom_bazel_rule = rule(
implementation = _impl,
attrs = {"my_config_string": attr.string()},
)
# Macro declaration:
def my_custom_bazel_macro(name, my_config_string):
allcaps = my_config_string.upper() # This line won't work with select(s).
print("My name is " + name + " with custom message: " + allcaps)
Instancie a regra e a macro:
# myapp/BUILD
load("//myapp:defs.bzl", "my_custom_bazel_rule")
load("//myapp:defs.bzl", "my_custom_bazel_macro")
my_custom_bazel_rule(
name = "happy_rule",
my_config_string = select({
"//third_party/bazel_platforms/cpu:x86_32": "first string",
"//third_party/bazel_platforms/cpu:ppc": "second string",
}),
)
my_custom_bazel_macro(
name = "happy_macro",
my_config_string = "fixed string",
)
my_custom_bazel_macro(
name = "sad_macro",
my_config_string = select({
"//third_party/bazel_platforms/cpu:x86_32": "first string",
"//third_party/bazel_platforms/cpu:ppc": "other string",
}),
)
O build falha porque sad_macro não pode processar o select():
$ bazel build //myapp:all
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
(most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
ERROR: error loading package 'myapp': Package 'myapp' contains errors.
O build é bem-sucedido quando você comenta sad_macro:
# Comment out sad_macro so it doesn't mess up the build.
$ bazel build //myapp:all
DEBUG: /myworkspace/myapp/defs.bzl:5:3: My name is happy_macro with custom message: FIXED STRING.
DEBUG: /myworkspace/myapp/hi.bzl:15:3: My name is happy_rule with custom message: FIRST STRING.
Isso é impossível de mudar porque por definição as macros são avaliadas antes que o Bazel leia as flags de linha de comando do build. Isso significa que não há informações suficientes para avaliar select()s.
No entanto, as macros podem transmitir select()s como blobs opacos para regras:
# myapp/defs.bzl
def my_custom_bazel_macro(name, my_config_string):
print("Invoking macro " + name)
my_custom_bazel_rule(
name = name + "_as_target",
my_config_string = my_config_string,
)
$ bazel build //myapp:sad_macro_less_sad
DEBUG: /myworkspace/myapp/defs.bzl:23:3: Invoking macro sad_macro_less_sad.
DEBUG: /myworkspace/myapp/defs.bzl:15:3: My name is sad_macro_less_sad with custom message: FIRST STRING.
Por que select() sempre retorna verdadeiro?
Como as macros (mas não as regras) por definição
não podem avaliar select()s, qualquer tentativa de fazer isso
geralmente produz um erro:
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
(most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
Os booleanos são um caso especial que falham silenciosamente. Portanto, você precisa ser particularmente vigilante com eles:
$ cat myapp/defs.bzl
def my_boolean_macro(boolval):
print("TRUE" if boolval else "FALSE")
$ cat myapp/BUILD
load("//myapp:defs.bzl", "my_boolean_macro")
my_boolean_macro(
boolval = select({
"//third_party/bazel_platforms/cpu:x86_32": True,
"//third_party/bazel_platforms/cpu:ppc": False,
}),
)
$ bazel build //myapp:all --cpu=x86
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
$ bazel build //mypro:all --cpu=ppc
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
Isso acontece porque as macros não entendem o conteúdo de select().
Portanto, o que elas realmente avaliam é o próprio objeto select(). De acordo com
Pythonic padrões de design
, todos os objetos, exceto um número muito pequeno de exceções,
retornam automaticamente o valor verdadeiro.
Posso ler select() como um dicionário?
As macros não podem avaliar select(s) porque as macros são avaliadas antes que o
Bazel saiba quais são os parâmetros de linha de comando do build. Elas podem pelo menos ler
o select()'s dictionary para, por exemplo, adicionar um sufixo a cada valor?
Conceitualmente, isso é possível, mas ainda não é um recurso do Bazel.
O que você pode fazer hoje é preparar um dicionário direto e, em seguida, transmiti-lo para um
select():
$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
for key in select_cmd.keys():
select_cmd[key] += " WITH SUFFIX"
native.genrule(
name = name,
outs = [name + ".out"],
srcs = [],
cmd = "echo " + select(select_cmd + {"//conditions:default": "default"})
+ " > $@"
)
$ cat myapp/BUILD
selecty_genrule(
name = "selecty",
select_cmd = {
"//third_party/bazel_platforms/cpu:x86_32": "x86 mode",
},
)
$ bazel build //testapp:selecty --cpu=x86 && cat bazel-genfiles/testapp/selecty.out
x86 mode WITH SUFFIX
Se você quiser oferecer suporte a select() e tipos nativos, faça o seguinte:
$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
cmd_suffix = ""
if type(select_cmd) == "string":
cmd_suffix = select_cmd + " WITH SUFFIX"
elif type(select_cmd) == "dict":
for key in select_cmd.keys():
select_cmd[key] += " WITH SUFFIX"
cmd_suffix = select(select_cmd + {"//conditions:default": "default"})
native.genrule(
name = name,
outs = [name + ".out"],
srcs = [],
cmd = "echo " + cmd_suffix + "> $@",
)
Por que select() não funciona com bind()?
Primeiro, não use bind(). Ele foi descontinuado em favor de alias().
A resposta técnica é que bind() é uma regra de repositório, não uma regra de BUILD.
As regras de repositório não têm uma configuração específica e não são avaliadas da mesma forma que
as regras de BUILD. Portanto, um select() em um bind() não pode
ser avaliado como uma ramificação específica.
Em vez disso, use alias(), com um select() no
atributo actual, para realizar esse tipo de determinação de tempo de execução. Isso
funciona corretamente, já que alias() é uma regra de BUILD e é avaliada com uma
configuração específica.
Você pode até mesmo ter um bind() destino apontando para um alias(), se necessário.
$ cat WORKSPACE
workspace(name = "myapp")
bind(name = "openssl", actual = "//:ssl")
http_archive(name = "alternative", ...)
http_archive(name = "boringssl", ...)
$ cat BUILD
config_setting(
name = "alt_ssl",
define_values = {
"ssl_library": "alternative",
},
)
alias(
name = "ssl",
actual = select({
"//:alt_ssl": "@alternative//:ssl",
"//conditions:default": "@boringssl//:ssl",
}),
)
Com essa configuração, é possível transmitir --define ssl_library=alternative, e qualquer destino
que dependa de //:ssl ou //external:ssl vai ver a alternativa
localizada em @alternative//:ssl.
Mas, na verdade, pare de usar bind().
Por que meu select() não escolhe o que eu espero?
Se //myapp:foo tiver um select() que não escolha a condição esperada,
use cquery e bazel config para depurar:
Se //myapp:foo for o destino de nível superior que você está criando, execute:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
Se você estiver criando algum outro destino //bar que dependa de
//myapp:foo em algum lugar do subgrafo, execute:
$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)
O (12e23b9a2b534a) ao lado de //myapp:foo é um hash da
configuração que resolve o select() de //myapp:foo. É possível inspecionar os
valores dele com bazel config:
$ bazel config 12e23b9a2b534a
BuildConfigurationValue 12e23b9a2b534a
Fragment com.google.devtools.build.lib.analysis.config.CoreOptions {
cpu: darwin
compilation_mode: fastbuild
...
}
Fragment com.google.devtools.build.lib.rules.cpp.CppOptions {
linkopt: [-Dfoo=bar]
...
}
...
Em seguida, compare essa saída com as configurações esperadas por cada config_setting.
//myapp:foo pode existir em configurações diferentes no mesmo build. Consulte os
documentos do cquery para orientações sobre como usar somepath para conseguir o correto.
Por que select() não funciona com plataformas?
O Bazel não oferece suporte a atributos configuráveis que verificam se uma determinada plataforma é a plataforma de destino porque a semântica não é clara.
Exemplo:
platform(
name = "x86_linux_platform",
constraint_values = [
"@platforms//cpu:x86",
"@platforms//os:linux",
],
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":x86_linux_platform": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
Nesse arquivo BUILD, qual select() deve ser usado se a plataforma de destino tiver as restrições
@platforms//cpu:x86 e @platforms//os:linux, mas não for a
:x86_linux_platform definida aqui? O autor do arquivo BUILD e o usuário
que definiu a plataforma separada podem ter ideias diferentes.
O que devo fazer?
Em vez disso, defina um config_setting que corresponda a qualquer plataforma com
essas restrições:
config_setting(
name = "is_x86_linux",
constraint_values = [
"@platforms//cpu:x86",
"@platforms//os:linux",
],
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":is_x86_linux": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
Esse processo define uma semântica específica, tornando mais claro para os usuários quais plataformas atendem às condições desejadas.
E se eu realmente quiser select na plataforma?
Se os requisitos de build exigirem especificamente a verificação da plataforma, você
poderá inverter o valor da flag --platforms em um config_setting:
config_setting(
name = "is_specific_x86_linux_platform",
values = {
"platforms": ["//package:x86_linux_platform"],
},
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":is_specific_x86_linux_platform": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
A equipe do Bazel não recomenda fazer isso. Isso restringe demais o build e confunde os usuários quando a condição esperada não corresponde.