Os atributos configuráveis, mais conhecidos como select()
, são um recurso do Bazel que permite aos usuários alternar os valores.
de atributos de regras de build na linha de comando.
Isso pode ser usado, por exemplo, para uma biblioteca multiplataforma que automaticamente escolhe a implementação adequada para a arquitetura ou para uma Binário configurável por recursos que pode ser personalizado no momento da compilação.
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 uma cc_binary
que "escolhe" as dependências com base nas flags
linha de comando. Especificamente, deps
se torna:
Comando | dependências = |
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 para um valor que será escolhido com base no
condições de configuração, que são rótulos que se referem a config_setting
de destino. Ao usar select()
em um atributo configurável, o atributo
adota efetivamente valores diferentes quando condições diferentes são mantidas.
As correspondências precisam ser inequívocas: se houver várias condições,
* Todos eles têm o mesmo valor. Por exemplo, ao executar no Linux x86, isso não é ambíguo
{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}
porque as duas ramificações são resolvidas para "hello".
* O values
de um é um superconjunto rigoroso de todos os outros. Por exemplo, values = {"cpu": "x86", "compilation_mode": "dbg"}
.
é uma especialização inequívoca do values = {"cpu": "x86"}
.
A condição integrada //conditions:default
corresponde automaticamente quando
nada mais faz.
Embora este exemplo use deps
, select()
também funciona bem em srcs
.
resources
, cmd
e a maioria dos outros atributos. Apenas um pequeno número de atributos
são não configuráveis e têm anotações claras. Por exemplo:
Do config_setting
O atributo values
não é configurável.
select()
e dependências
Alguns atributos mudam os parâmetros de build de todas as dependências transitivas
abaixo de uma meta. Por exemplo, o tools
de genrule
muda --cpu
para a CPU de
a máquina que executa o Bazel (que, graças à compilação cruzada, pode ser diferente
que a CPU para a qual o destino foi criado). Isso é conhecido como
transição de configuração.
Dado
#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 desenvolvimento x86
vincula o build a g_arm.src
, tool1
e
x86tool.cc
. As duas select
s anexadas a my_genrule
usam a my_genrule
parâmetros de build, que incluem --cpu=arm
. O atributo tools
muda
--cpu
para x86
para tool1
e as respectivas dependências transitivas. O select
em
O tool1
usa os parâmetros de build do tool1
, 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
as configurações esperadas de flag de linha de comando. Ao encapsular esses elementos em um destino,
fácil de manter como "padrão" condições que os usuários podem consultar em vários lugares.
constraint_value
fornece suporte para comportamento de várias plataformas.
Sinalizações integradas
Sinalizações como --cpu
são integradas ao Bazel: a ferramenta de build entende nativamente
para todos os builds em todos os projetos. Eles são especificados com
De config_setting
Atributo values
:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
é um nome de sinalização (sem --
, portanto, "cpu"
em vez de "--cpu"
). valueN
é o valor esperado para a sinalização. :meaningful_condition_name
corresponde se
todas as entradas em values
são correspondentes. A ordem é irrelevante.
valueN
é analisado como se estivesse definido na linha de comando. Isso significa que:
values = { "compilation_mode": "opt" }
corresponde abazel build -c opt
values = { "force_pic": "true" }
corresponde abazel build --force_pic=1
values = { "force_pic": "0" }
corresponde abazel build --noforce_pic
config_setting
só oferece suporte a sinalizações que afetam o comportamento de destino. Por exemplo:
--show_progress
não é permitido porque
ela afeta apenas a forma como o Bazel informa o progresso ao usuário. Os destinos não podem usar isso
para criar os resultados. O conjunto exato de sinalizações com suporte não é
documentadas. Na prática, a maioria das sinalizações que "faz sentido" funcionam.
Sinalizações personalizadas
É possível modelar suas próprias sinalizações específicas do projeto com Configurações de build do Starlark. Ao contrário das sinalizações incorporadas, elas são definidos como destinos de compilação. Por isso, o Bazel faz referência a eles com rótulos de destino.
Eles são acionados com o config_setting
flag_values
:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
O comportamento é o mesmo das sinalizações integradas. Confira aqui para um exemplo funcional.
--define
é uma sintaxe legada alternativa para sinalizações personalizadas (por exemplo,
--define foo=bar
). Isso pode ser expresso no campo
Atributo values
(values = {"define": "foo=bar"}
) ou o
Atributo define_values
(define_values = {"foo": "bar"}
). --define
só é compatível com versões anteriores
compatibilidade. Prefira as configurações de build do Starlark sempre que possível.
values
, flag_values
e define_values
são avaliados de forma independente. A
config_setting
faz a correspondência se todos os valores em todos eles forem correspondentes.
A condição padrão
A condição integrada //conditions:default
corresponde quando nenhuma outra condição
correspondências.
Por causa da "exatamente uma correspondência" um atributo configurável sem correspondência
e nenhuma condição padrão emite um erro "no matching conditions"
. 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, você pode definir mensagens personalizadas com o select()
no_match_error
.
Plataformas
Embora a capacidade de especificar várias sinalizações na linha de comando forneça ter flexibilidade, também pode ser trabalhoso definir cada um individualmente você quer criar um destino. Plataformas permitem consolidá-los 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. Isso ativa
config_setting
s que contêm um subconjunto dos constraint_values
da plataforma,
permitindo que esses config_setting
s correspondam a expressões select()
.
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 parecer
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select()
também pode ler constraint_value
s 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 economiza a necessidade de config_setting
s boilerplate quando você só precisa
verificar valores únicos.
As plataformas ainda estão em desenvolvimento. Consulte a documentação para mais detalhes.
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 usar 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 uma select
para corresponder quando várias condições corresponderem, considere AND
encadeamento.
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 avalia a mesma dependência. Mas essa sintaxe é difícil de ler
manter. Seria bom não ter que repetir [":standard_lib"]
vários
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. Mas ainda faz com que seja desnecessário duplicação.
Para receber mais suporte direto, use uma das seguintes opções:
selects.with_or
A
with_or
no Skylib
selects
oferece suporte a condições OR
diretamente em 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
config_setting_group
no Skylib
selects
oferece suporte a OR
vários config_setting
s:
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
entre
atributos diferentes.
É um erro quando várias condições correspondem, a menos que uma delas seja inequívoca "especialização" dos outros ou todos eles têm o mesmo valor. Clique aqui para mais detalhes.
Encadeamento AND
Se você precisar que uma ramificação select
corresponda a quando várias condições corresponderem, use o
Macro Skylib
config_setting_group:
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_setting
s já existentes não podem ser diretamente AND
em 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 a que select()
é 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
:
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 regra recebem os valores resolvidos de variáveis configuráveis atributos. 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 de regras vê ctx.attr.some_attr
como [":foo"]
.
As macros podem aceitar cláusulas select()
e transmiti-las para o ambiente nativo
regras de firewall. No entanto, eles não podem ser manipulados diretamente. Por exemplo, não há como
para que uma macro converta
select({"foo": "val"}, ...)
a
select({"foo": "val_with_suffix"}, ...)
Isso se deve a dois motivos.
Primeiro, as macros que precisam saber qual caminho um select
escolher não funcionam.
porque as macros são avaliadas na fase de carregamento do Bazel,
que ocorre antes de os valores das sinalizações serem conhecidos.
Essa é uma restrição principal do design do Bazel que provavelmente não vai mudar.
Segundo, as macros que só precisam iterar em todos os caminhos de select
, enquanto
são tecnicamente viável, não têm uma interface coerente. Um design adicional é necessário para mudar
isso.
Consulta do Bazel e cquery
O Bazel query
opera na
fase de carregamento.
Isso significa que ele não sabe quais sinalizações de linha de comando um alvo usa, pois essas
as flags não são avaliadas até mais tarde no build (na
fase de análise).
Por isso, não é possível determinar quais ramificações select()
são escolhidas.
O cquery
do Bazel opera após a fase de análise dele. Portanto, ele tem
todas essas informações e pode resolver com precisão select()
s.
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
superaproxima as dependências de :my_lib
:
$ 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 detalhes.
O principal problema que essa pergunta geralmente significa é que select() não funciona em macros. Eles são diferentes das regras. Consulte a documentação sobre regras e macros para entender a diferença. Confira um exemplo completo:
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({
"//tools/target_cpu:x86": "first string",
"//tools/target_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({
"//tools/target_cpu:x86": "first string",
"//tools/target_cpu:ppc": "other string",
}),
)
A criação falha porque o 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.
A criação é bem-sucedida 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.
É impossível mudar isso porque as macros por definição são avaliadas antes O Bazel lê as flags de linha de comando do build. Isso significa que não há para avaliar select()s.
No entanto, as macros podem transmitir select()
s como blobs opacos para as 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?
Porque macros (mas não regras) por definição
não é possível 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().
Booleanos são um caso especial que falha silenciosamente, portanto, você deve ser particularmente preste atenção a elas:
$ 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({
"//tools/target_cpu:x86": True,
"//tools/target_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()
.
Então, o que eles estão realmente avaliando é o próprio objeto select()
. De acordo com
Design Python
padrão, todos os objetos com exceção de um número muito pequeno de exceções
retornam "true" automaticamente.
Posso ler select() como um dict?
As macros não podem avaliar seleções porque elas são avaliadas antes
O Bazel sabe quais são os parâmetros da linha de comando do build. Eles pelo menos conseguem ler
dicionário do select()
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 inseri-lo em 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 = {
"//tools/target_cpu:x86": "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()?
Porque bind()
é uma regra do ESPAÇO DE TRABALHO, não uma regra BUILD.
As regras do espaço de trabalho não têm uma configuração específica e não são avaliadas
da mesma forma que as regras BUILD. Portanto, um select()
em um bind()
não pode
avaliar para qualquer ramificação específica.
Em vez disso, use alias()
, com um select()
no
o atributo actual
, para realizar esse tipo de determinação do ambiente de execução. Isso
funciona corretamente, já que alias()
é uma regra BUILD e é avaliado com uma
uma configuração específica.
Você pode até mesmo ter um ponto de destino bind()
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, você pode transmitir --define ssl_library=alternative
e qualquer destino
que depende de //:ssl
ou //external:ssl
verá a alternativa
localizado em @alternative//:ssl
.
Por que meu select() não escolhe o que eu espero?
Se //myapp:foo
tiver uma 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 outro //bar
de destino que dependa
//myapp:foo em algum lugar no subgráfico, 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
que resolve o select()
do //myapp:foo
. É possível inspecionar
valores 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 a
documentos do cquery (em inglês) para orientações sobre como usar somepath
e
um.
Por que o 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": [],
}),
)
Neste arquivo BUILD
, que select()
precisa ser usado se a plataforma de destino tiver o
@platforms//cpu:x86
e @platforms//os:linux
, mas não é o
:x86_linux_platform
definido 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
estas 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, deixando mais claro para os usuários o que plataformas atendam às condições desejadas.
E se eu realmente quiser select
na plataforma?
Caso seus requisitos de build exijam especificamente a verificação da plataforma,
pode 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. restringe excessivamente seu build e Confunde os usuários quando a condição esperada não é correspondente.