Los atributos configurables, conocidos como select()
, son una función de Bazel que les permite a los usuarios activar o desactivar los valores de los atributos de reglas de compilación en la línea de comandos.
Se puede usar, por ejemplo, para una biblioteca multiplataforma que elige automáticamente la implementación adecuada para la arquitectura, o bien para un objeto binario configurable de funciones que se puede personalizar durante el tiempo de 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",
},
)
De esta manera, se declara un cc_binary
que "elige" sus dependencias en función de las marcas de la línea de comandos. Específicamente, deps
se convierte en lo siguiente:
Comando | dependencias = |
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 objetivos config_setting
. Cuando se usa select()
en un atributo configurable, el atributo adopta de forma eficaz diferentes valores cuando se mantienen diferentes condiciones.
Las coincidencias deben ser ambiguas: debe coincidir exactamente una condición o, si coinciden varias condiciones, el values
de uno debe ser un superconjunto estricto de todas las demás. Por ejemplo, values = {"cpu": "x86", "compilation_mode": "dbg"}
es una especialización inequívoca de values = {"cpu": "x86"}
. La condición integrada //conditions:default
coincide automáticamente cuando nada más lo hace.
Si bien en este ejemplo se 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 no se pueden configurar y están anotados claramente. Por ejemplo, el propio atributo values
de config_setting
no se puede configurar.
select()
y dependencias
Ciertos atributos cambian los parámetros de compilación de todas las dependencias transitivas en un destino. Por ejemplo, el elemento tools
de genrule
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 compiló el destino). Esto se conoce como transición de configuración.
Otorgado
#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
, se vincula la compilación a g_arm.src
, tool1
y x86tool.cc
. Los dos select
adjuntos a my_genrule
usan los parámetros de compilación de my_genrule
, que incluyen --cpu=arm
. El atributo tools
cambia --cpu
a x86
para tool1
y sus dependencias transitivas. select
en tool1
usa los parámetros de compilación de tool1
, que incluyen --cpu=x86
.
Condiciones de configuración
Cada clave en un atributo configurable es una referencia de etiqueta a un config_setting
o constraint_value
.
config_setting
es solo una colección de las configuraciones esperadas de las marcas de línea de comandos. Si los encapsulas en un objetivo, es fácil mantener 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 de todos los proyectos. Estas se especifican con el atributo values
de config_setting
:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
es el nombre de una marca (sin --
, por lo que "cpu"
en lugar de "--cpu"
). valueN
es el valor esperado para esa marca. :meaningful_condition_name
coincide si todas las entradas de values
coinciden. El pedido es irrelevante.
valueN
se analiza como si se hubiera establecido en la línea de comandos. Esto significa lo siguiente:
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 objetivo. Por ejemplo, no se permite --show_progress
, ya que solo afecta la forma en que Bazel informa el progreso al usuario. Los objetivos no pueden usar esa marca para construir sus resultados. No se documenta el conjunto exacto de marcas compatibles. En la práctica, la mayoría de las marcas que "tienen sentido" funcionan.
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.
Estos se activan con el atributo flag_values
de config_setting
:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
El comportamiento es el mismo que el de las marcas integradas. Consulta aquí un ejemplo práctico.
--define
es una sintaxis heredada alternativa para 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. Opta por la configuración de compilación de Starlark siempre que sea posible.
values
, flag_values
y define_values
se evalúan de forma independiente. config_setting
coincidirá si todos los valores de todas ellas coinciden.
La condición predeterminada
La condición integrada //conditions:default
coincide cuando ninguna otra condición coincide.
Debido a la regla "exactamente una coincidencia", un atributo configurable sin coincidencia y sin condición predeterminada emite un error "no matching conditions"
. Esto puede brindar protección contra fallas silenciosas de configuraciones 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 obtener errores aún más claros, puedes configurar mensajes personalizados con el atributo no_match_error
de select()
.
Plataformas
Si bien la capacidad de especificar varias marcas en la línea de comandos proporciona flexibilidad, también puede ser difícil configurar cada una de manera individual cada vez que desees crear un destino. Las plataformas te permiten consolidar estos paquetes 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 las config_setting
que contienen un subconjunto de las constraint_values
de la plataforma, lo que permite que esos config_setting
coincidan en expresiones select()
.
Por ejemplo, para establecer el atributo srcs
de my_rocks
en calcite.sh
, simplemente 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 directamente objetos constraint_value
:
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 ahorra la necesidad de usar config_setting
estándar cuando solo necesitas verificar valores individuales.
Las plataformas aún están en desarrollo. Consulta la documentación para obtener más detalles.
Combina select()
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 objetivos como valores, usa un objetivo 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 una select
coincida cuando coinciden varias condiciones, considera usar el encadenamiento Y.
O encadenamiento
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, pero 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, todavía genera una duplicación innecesaria.
Para obtener asistencia más directa, usa una de las siguientes opciones:
selects.with_or
La macro with_or en el módulo selects
de Skylib admite condiciones OR
directamente dentro de una 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 selects
de Skylib admite OR
en varios config_setting
:
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 distintos objetivos pueden compartir :config1_or_2
entre diferentes atributos.
Es un error que varias condiciones coincidan, a menos que una sea una "especialización" no ambigua de las demás. Consulta aquí para obtener más información.
Encadenamiento con el operador AND
Si necesitas que una rama select
coincida cuando coinciden varias condiciones, usa la macro config_setting_group de la macro
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 de las cadenas OR, las config_setting
existentes no se pueden incluir directamente en AND
dentro de una select
. Debes unirlas de manera explícita en un config_setting_group
.
Mensajes de error personalizados
De forma predeterminada, cuando no coincide ninguna condición, el objetivo 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
Se puede personalizar con el atributo 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
Compatibilidad de las reglas
Las implementaciones de reglas reciben los valores resueltos de los atributos configurables. Por ejemplo:
# 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 reglas ve ctx.attr.some_attr
como [":foo"]
.
Las macros pueden aceptar cláusulas select()
y pasarlas a reglas nativas. Sin embargo, no pueden manipularlos directamente. Por ejemplo, no hay forma
de que una macro convierta
select({"foo": "val"}, ...)
para
select({"foo": "val_with_suffix"}, ...)
Esto se debe a dos motivos.
Primero, las macros que necesitan saber qué ruta de acceso elegirá un select
no pueden funcionar
porque las macros se evalúan en la fase de carga de Bazel,
que ocurre antes de que se conozcan los valores de la marca.
Esta es una restricción principal de diseño de Bazel que es poco probable que cambie pronto.
En segundo lugar, las macros que solo necesitan iterar en todas las rutas de acceso de select
, aunque técnicamente son posibles, carecen de una IU coherente. Se necesita un diseño adicional para cambiar esto.
Consulta y cquery de Bazel
Bazel query
opera a través de 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é ramas de select()
se eligen.
cquery
de Bazel funciona después de la fase de análisis de Bazel, por lo que tiene
toda esta información y puede resolver los select()
con precisión.
Considera:
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
sobrepasa las dependencias de :my_lib
:
$ 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é select() no funciona en las macros?
select() hace que funcione en las reglas. Consulta Compatibilidad de reglas para obtener más detalles.
El problema clave que suele indicar esta pregunta es que select() no funciona en macros. Estas son diferentes de las reglas. Consulta la documentación sobre reglas y macros para comprender la diferencia. A continuación, se incluye un ejemplo integral:
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({
"//tools/target_cpu:x86": "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({
"//tools/target_cpu:x86": "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 marcas sad_macro
como comentario:
# 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 las macros por definición se evalúan antes de Bazel lee las marcas de línea de comandos de la compilación. Eso significa que no hay suficiente información para evaluar select()s.
Sin embargo, las macros pueden pasar select()
como BLOB 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 un valor verdadero?
Debido a que las macros (pero no las reglas) no pueden evaluar select()
por definición, por lo general, cualquier intento de hacerlo produce 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 prestar especial atención 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({
"//tools/target_cpu:x86": 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 entienden el contenido de select()
.
Lo que realmente evalúa es el objeto select()
en sí. Según los estándares de diseño de Pythonic, todos los objetos, excepto un número muy pequeño de excepciones, se muestran automáticamente como verdaderos.
¿Puedo leer select() como un dict?
Las macros no pueden evaluar las selecciones porque se evalúan antes de que Bazel sepa cuáles son los parámetros de línea de comandos de la compilación. ¿Puede leer al menos el diccionario de select()
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 = {
"//tools/target_cpu:x86": "x86 mode",
},
)
$ bazel build //testapp:selecty --cpu=x86 && cat bazel-genfiles/testapp/selecty.out
x86 mode WITH SUFFIX
Si quieres admitir los tipos nativos y select()
, 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()?
Porque bind()
es una regla de WORKSPACE, no de BUILD.
Las reglas del lugar de trabajo no tienen una configuración específica y no se evalúan de la misma manera que las reglas de BUILD. Por lo tanto, una select()
en una bind()
no puede evaluar ninguna rama específica.
En su lugar, debes usar alias()
, con un select()
en el atributo actual
, para realizar este tipo de determinación del tiempo de ejecución. Esto
funciona correctamente, ya que alias()
es una regla de BUILD y se evalúa con una
configuración específica.
Incluso puedes tener un punto de destino bind()
para un alias()
, si es necesario.
$ 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",
}),
)
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
.
¿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 estás compilando, ejecuta lo siguiente:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
Si estás compilando otra //bar
de destino que depende de //myapp:foo en alguna parte 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 que espera cada config_setting
.
//myapp:foo
puede existir en diferentes configuraciones en la misma compilación. Consulta los documentos de cquery para obtener ayuda sobre cómo usar somepath
a fin de obtener el adecuado.
¿Por qué select()
no funciona con plataformas?
Bazel no admite atributos configurables que verifican si una plataforma determinada es la plataforma de destino porque la semántica no es 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()
debe usarse si la plataforma de destino tiene las restricciones @platforms//cpu:x86
y @platforms//os:linux
, pero no se define :x86_linux_platform
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 cambio, 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 deja en claro a los usuarios qué plataformas cumplen las condiciones deseadas.
¿Qué sucede si realmente quiero select
en la plataforma?
Si tus requisitos de compilación requieren que se verifique la plataforma, puedes cambiar 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 respalda este comportamiento, ya que restringe demasiado tu compilación y confunde a los usuarios cuando la condición esperada no coincide.