Esto se puede usar, por ejemplo, para una biblioteca multiplataforma que automáticamente elige la implementación adecuada para la arquitectura o para un objeto binario configurable por funciones que se puede personalizar en el momento de la compilación.
Ejemplo
# 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",
},
)
Esto declara un cc_binary que "elige" sus dependencias en función de las marcas de la
línea de comandos. En particular, deps se convierte en lo siguiente:
| 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() sirve como marcador de posición para un valor que se elegirá en función de las
condiciones de configuración, que son etiquetas que hacen referencia a los destinos config_setting. Cuando se usa select() en un atributo configurable, el atributo
adopta valores diferentes cuando se cumplen diferentes condiciones.
Las coincidencias deben ser inequívocas. Si coinciden varias condiciones, sucede lo siguiente:
- Todas se resuelven en el mismo valor. Por ejemplo, cuando se ejecuta en linux x86, esto es inequívoco
{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}porque ambas ramas se resuelven en "hello". - El
valuesde una es un superconjunto estricto de todas las demás. Por ejemplo,values = {"cpu": "x86", "compilation_mode": "dbg"}es una especialización inequívoca devalues = {"cpu": "x86"}.
La condición integrada //conditions:default coincide automáticamente cuando
no lo hace ninguna otra.
Si bien este ejemplo usa deps, select() funciona igual de bien en srcs,
resources, cmd y la mayoría de los demás atributos. Solo una pequeña cantidad de atributos
son no configurables, y estos están claramente anotados. Por ejemplo, el atributo propio de no es configurable.config_settingvalues
select() y dependencias
Ciertos atributos cambian los parámetros de compilación para todas las dependencias transitivas
en un destino. Por ejemplo, genrule's tools cambia --cpu a la CPU de
la máquina que ejecuta Bazel (que, gracias a la compilación cruzada, puede ser diferente
de la CPU para la que se compila el destino). Esto se conoce como una
transición de configuración.
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"],
}),
)
activo
$ bazel build //myapp:my_genrule --cpu=arm
en una máquina de desarrollador x86 vincula la compilación a g_arm.src, tool1 y
x86tool.cc. Ambos selects adjuntos a my_genrule usan los parámetros de compilación de my_genrule's, que incluyen --cpu=arm. El atributo tools cambia
--cpu a x86 para tool1 y sus dependencias transitivas. El select en
tool1 usa los parámetros de compilación de tool1, que incluyen --cpu=x86.
Condiciones de configuración
Cada clave de un atributo configurable es una referencia de etiqueta a un
config_setting o
constraint_value.
config_setting es solo una colección de
parámetros de configuración de marcas de línea de comandos esperados. Si los encapsulas en un destino, es
fácil mantener las condiciones "estándar" a las que los usuarios pueden hacer referencia desde varios lugares.
constraint_value proporciona compatibilidad con el comportamiento multiplataforma.
Marcas integradas
Las marcas como --cpu están integradas en Bazel: la herramienta de compilación las comprende de forma nativa
para todas las compilaciones en todos los proyectos. Se especifican con el atributo
values de
config_setting:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN es un nombre de marca (sin --, por lo que "cpu" en lugar de "--cpu"). valueN
es el valor esperado para esa marca. :meaningful_condition_name coincide si
cada entrada en values coincide. El ordenamiento es irrelevante.
valueN se analiza como si se hubiera establecido en la línea de comandos. Esto tiene las siguientes implicaciones:
values = { "compilation_mode": "opt" }coincide conbazel build -c opt.values = { "force_pic": "true" }coincide conbazel build --force_pic=1.values = { "force_pic": "0" }coincide conbazel build --noforce_pic.
config_setting solo admite marcas que afectan el comportamiento de destino. Por ejemplo,
--show_progress no está permitido porque
solo afecta la forma en que Bazel informa el progreso al usuario. Los destinos no pueden usar esa
marca para construir sus resultados. No se documenta el conjunto exacto de marcas compatibles. En la práctica, funcionan la mayoría de las marcas que "tienen sentido".
Marcas personalizadas
Puedes modelar tus propias marcas específicas del proyecto con la configuración de compilación de Starlark. A diferencia de las marcas integradas, estas se definen como destinos de compilación, por lo que Bazel hace referencia a ellas con etiquetas de destino.
Se activan con el
flag_values
atributo de config_setting's:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
El comportamiento es el mismo que para las marcas integradas. Consulta aquí un ejemplo práctico.
--define
es una sintaxis heredada alternativa para las marcas personalizadas (por ejemplo
--define foo=bar). Esto se puede expresar en el atributo
values
(values = {"define": "foo=bar"}) o en el atributo
define_values
(define_values = {"foo": "bar"}). --define solo es compatible con la retrocompatibilidad. Prefiere la configuración de compilación de Starlark siempre que sea posible.
values, flag_values y define_values se evalúan de forma independiente. El
config_setting coincide si todos los valores de todos ellos coinciden.
La condición predeterminada
La condición integrada //conditions:default coincide cuando no lo hace ninguna otra condición
coincide.
Debido a la regla de "exactamente una coincidencia", un atributo configurable sin coincidencia
y sin condición predeterminada emite un "no matching conditions" error. Esto puede
proteger contra fallas silenciosas de parámetros de configuración inesperados:
# 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 obtener errores aún más claros, puedes configurar mensajes personalizados con select()'s
no_match_error atributo.
Plataformas
Si bien la capacidad de especificar varias marcas en la línea de comandos proporciona flexibilidad, también puede ser engorroso configurar cada una de forma individual cada vez que deseas compilar un destino. Las plataformas te permiten consolidarlas en paquetes 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",
],
)
La plataforma se puede especificar en la línea de comandos. Activa los
config_settings que contienen un subconjunto de los constraint_values de la plataforma,
lo que permite que esos config_settings coincidan en las expresiones select().
Por ejemplo, para establecer el atributo srcs de my_rocks en calcite.sh,
puedes ejecutar
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
Sin plataformas, esto podría verse así:
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select() también puede leer constraint_values directamente:
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"],
}),
)
Esto evita la necesidad de usar config_settings de código estándar cuando solo necesitas
verificar valores únicos.
Las plataformas aún están en desarrollo. Consulta la documentación para obtener más detalles.
Combinación de select()s
select puede aparecer varias veces en el mismo 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 no puede aparecer dentro de otro select. Si necesitas anidar selects
y tu atributo toma otros destinos como valores, usa un destino intermedio:
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"],
...
}),
)
Si necesitas que un select coincida cuando coinciden varias condiciones, considera el encadenamiento AND.
Encadenamiento OR
Ten en cuenta lo siguiente:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": [":standard_lib"],
":config2": [":standard_lib"],
":config3": [":standard_lib"],
":config4": [":special_lib"],
}),
)
La mayoría de las condiciones se evalúan en la misma dependencia. Sin embargo, esta sintaxis es difícil de leer y
mantener. Sería bueno no tener que repetir [":standard_lib"] varias
veces.
Una opción es predefinir el valor como una variable 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"],
}),
)
Esto facilita la administración de la dependencia. Sin embargo, aún causa duplicación innecesaria.
Para obtener asistencia más directa, usa una de las siguientes opciones:
selects.with_or
La
with_or
macro en el módulo Skylib's
selects
admite condiciones OR directamente dentro de un 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
La macro config_setting_group en el módulo Skylib'sselects admite varios ORs config_settinging:
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"],
}),
)
A diferencia de selects.with_or, los diferentes destinos pueden compartir :config1_or_2 en
diferentes atributos.
Es un error que coincidan varias condiciones, a menos que una sea una "especialización" inequívoca de las demás o que todas se resuelvan en el mismo valor. Consulta aquí para obtener más detalles.
Encadenamiento AND
Si necesitas que una rama select coincida cuando coinciden varias condiciones, usa la
Skylib
config_setting_group de 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"],
}),
)
A diferencia del encadenamiento OR, los config_settings existentes no se pueden ANDed
directamente dentro de un select. Debes incluirlos de forma explícita en un config_setting_group.
Mensajes de error personalizados
De forma predeterminada, cuando no coincide ninguna condición, el destino al que se adjunta select()
falla con el siguiente error:
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
Esto se puede personalizar con el 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
Compatibilidad con reglas
Las implementaciones de reglas reciben los valores resueltos de los atributos configurables. Por ejemplo, dado lo siguiente:
# myapp/BUILD
some_rule(
name = "my_target",
some_attr = select({
":foo_mode": [":foo"],
":bar_mode": [":bar"],
}),
)
$ bazel build //myapp/my_target --define mode=foo
El código de implementación de la regla ve ctx.attr.some_attr como [":foo"].
Las macros pueden aceptar select() cláusulas y pasarlas a reglas nativas
Sin embargo, no pueden manipularlas directamente. Por ejemplo, no hay forma
de que una macro convierta
select({"foo": "val"}, ...)
a
select({"foo": "val_with_suffix"}, ...)
Esto se debe a dos motivos.
En primer lugar, las macros que necesitan saber qué ruta de acceso elegirá un select no pueden funcionar
porque se evalúan en la fase de carga de Bazel,
que se produce antes de que se conozcan los valores de las marcas.
Esta es una restricción de diseño principal de Bazel que es poco probable que cambie pronto.
En segundo lugar, las macros que solo necesitan iterar sobre todas select rutas de acceso, aunque
técnicamente factibles, carecen de una IU coherente. Se necesita más diseño para cambiar
esto.
Consulta de Bazel y cquery
Bazel query opera en la fase de carga de Bazel
.
Esto significa que no sabe qué marcas de línea de comandos usa un destino, ya que esas
marcas no se evalúan hasta más adelante en la compilación (en la
fase de análisis).
Por lo tanto, no puede determinar qué select() ramas se eligen.
Bazel cquery opera después de la fase de análisis de Bazel, por lo que tiene
toda esta información y puede resolver select()s con precisión.
Considera lo siguiente:
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 sobreaproxima las dependencias de :my_lib's:
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
mientras que cquery muestra sus dependencias exactas:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
Preguntas frecuentes
¿Por qué no funciona select() en las macros?
select() sí funciona en las reglas. Consulta Compatibilidad con reglas para obtener más detalles.
El problema clave que suele significar esta pregunta es que select() no funciona en macros. Son diferentes de las reglas. Consulta la documentación sobre reglas y macros para comprender la diferencia. A continuación, se muestra un ejemplo de extremo a extremo:
Define una regla y una 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)
Crea una instancia de la regla y la 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",
}),
)
La compilación falla porque sad_macro no puede procesar el 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.
La compilación se realiza correctamente cuando comentas 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.
Esto es imposible de cambiar porque por definición las macros se evalúan antes de que Bazel lea las marcas de línea de comandos de la compilación. Eso significa que no hay suficiente información para evaluar select().
Sin embargo, las macros pueden pasar select()s como blobs opacos a las reglas:
# 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 qué select() siempre muestra verdadero?
Debido a que las macros (pero no las reglas) por definición
no pueden evaluar select()s, cualquier intento de hacerlo
suele producir un error:
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().
Los booleanos son un caso especial que falla de forma silenciosa, por lo que debes estar especialmente atento a ellos:
$ 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.
Esto sucede porque las macros no comprenden el contenido de select().
Por lo tanto, lo que realmente evalúan es el objeto select() en sí. Según los
estándares de diseño
de Python, todos los objetos, excepto una pequeña cantidad de excepciones,
muestran automáticamente el valor verdadero.
¿Puedo leer select() como un diccionario?
Las macros no pueden evaluar select(s) porque se evalúan antes de que
Bazel sepa cuáles son los parámetros de línea de comandos de la compilación. ¿Pueden al menos leer
el select()'s diccionario para, por ejemplo, agregar un sufijo a cada valor?
Conceptualmente, esto es posible, pero aún no es una función de Bazel.
Lo que puedes hacer hoy es preparar un diccionario directo y, luego, ingresarlo en un
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
Si deseas admitir select() y tipos nativos, puedes hacer lo siguiente:
$ 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 qué select() no funciona con bind()?
En primer lugar, no uses bind(). Está obsoleta en favor de alias().
La respuesta técnica es que bind() es una regla de repositorio, no una regla BUILD.
Las reglas de repositorio no tienen una configuración específica y no se evalúan de
la misma manera que las reglas BUILD. Por lo tanto, un select() en un bind() no puede
evaluarse en ninguna rama específica.
En su lugar, debes usar alias(), con un select() en
el atributo actual, para realizar este tipo de determinación en el tiempo de ejecución. Esto
funciona correctamente, ya que alias() es una regla BUILD y se evalúa con una
configuración específica.
Incluso puedes hacer que un destino bind() apunte a un alias(), si es necesario.
$ cat WORKSPACE
workspace(name = "myapp")
bind(name = "openssl", actual = "//:ssl")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
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",
}),
)
Con esta configuración, puedes pasar --define ssl_library=alternative, y cualquier destino
que dependa de //:ssl o //external:ssl verá la alternativa
ubicada en @alternative//:ssl.
Pero, en realidad, deja de usar bind().
¿Por qué mi select() no elige lo que espero?
Si //myapp:foo tiene un select() que no elige la condición que esperas,
usa cquery y bazel config para depurar:
Si //myapp:foo es el destino de nivel superior que compilas, ejecuta lo siguiente:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
Si compilas algún otro destino //bar que dependa de
//myapp:foo en algún lugar de su subgrafo, ejecuta lo siguiente:
$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)
El (12e23b9a2b534a) junto a //myapp:foo es un hash de la
configuración que resuelve el select() de //myapp:foo. Puedes inspeccionar sus
valores con 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]
...
}
...
Luego, compara este resultado con la configuración esperada por cada config_setting.
//myapp:foo puede existir en diferentes configuraciones en la misma compilación. Consulta los
documentos de cquery para obtener orientación sobre el uso de somepath para obtener el correcto.
¿Por qué no funciona select() con las plataformas?
Bazel no admite atributos configurables que verifiquen si una plataforma determinada es la plataforma de destino porque la semántica no está clara.
Por ejemplo:
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": [],
}),
)
En este archivo BUILD, ¿qué select() se debe usar si la plataforma de destino tiene las restricciones
@platforms//cpu:x86 y @platforms//os:linux, pero no es la
:x86_linux_platform definida aquí? El autor del archivo BUILD y el usuario
que definió la plataforma separada pueden tener ideas diferentes.
¿Qué otra opción debería usar?
En su lugar, define un config_setting que coincida con cualquier plataforma con
estas restricciones:
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": [],
}),
)
Este proceso define una semántica específica, lo que hace que sea más claro para los usuarios qué plataformas cumplen con las condiciones deseadas.
¿Qué sucede si realmente quiero select en la plataforma?
Si los requisitos de compilación requieren específicamente verificar la plataforma, puedes invertir el valor de la marca --platforms en un 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": [],
}),
)
El equipo de Bazel no recomienda hacer esto; restringe demasiado tu compilación y confunde a los usuarios cuando la condición esperada no coincide.