Reserva la fecha: BazelCon 2023 se celebrará el 24 y 25 de octubre en Google Múnich. ¡Ya comenzó el registro! Más información

Atributos de compilación configurables

Informa un problema Ver código fuente

Atributos configurables, comúnmente conocidos como select(), es una función de Bazel que permite a los usuarios activar o desactivar los valores de los atributos de las reglas de compilación en la línea de comandos.

Esto se puede usar, por ejemplo, en una biblioteca multiplataforma que elige de forma automática la implementación adecuada para la arquitectura o en un objeto binario configurable de 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",
    },
)

De esta manera, se declara un cc_binary que "elige" sus dependencias según las marcas en 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() funciona como marcador de posición para un valor que se elegirá según 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 efectivamente valores diferentes cuando se aplican diferentes condiciones.

Las coincidencias deben ser ambiguas: Si coinciden varias condiciones, * todas se resuelven con el mismo valor. Por ejemplo, cuando se ejecuta en Linux x86, esta es una ambigüedad de {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}, ya que ambas ramas se resuelven en "hello". * El values de cada uno es un superconjunto estricto de todos los 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 en la mayoría de los otros atributos. Solo una pequeña cantidad de atributos no se pueden configurar y se anotan claramente. Por ejemplo, el 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 de un objetivo. Por ejemplo, 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 a la CPU para la que se compiló el destino. Esto se conoce como transición de configuración.

Dada

#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 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. 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 elemento config_setting o constraint_value.

config_setting es solo una colección de opciones de configuración de marcas de línea de comandos esperadas. Si se encapsulan 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 se compilan en Bazel: la herramienta de compilación los entiende de forma nativa para todas las compilaciones en 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 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 todas las entradas de values coinciden. El orden es irrelevante.

valueN se analiza como si estuviera configurado en la línea de comandos. Esto significa lo siguiente:

  • values = { "compilation_mode": "opt" } coincide con bazel build -c opt
  • values = { "force_pic": "true" } coincide con bazel build --force_pic=1
  • values = { "force_pic": "0" } coincide con bazel build --noforce_pic

config_setting solo admite marcas que afectan el comportamiento objetivo. Por ejemplo, no se permite --show_progress porque solo afecta el progreso de los informes de Bazel para el usuario. Los destinos no pueden usar esa marca para construir sus resultados. El conjunto exacto de marcas compatibles no está documentado. 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 objetivo.

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 para las marcas integradas. Consulta aquí un ejemplo funcional.

--define es una sintaxis heredada alternativa para marcas personalizadas (por ejemplo, --define foo=bar). Esto se puede expresar en el atributo valores (values = {"define": "foo=bar"}) o el atributo define_values (define_values = {"foo": "bar"}). --define solo es compatible con la retrocompatibilidad. Siempre que sea posible, se prefiere la configuración de compilación de Starlark.

values, flag_values y define_values se evalúan de forma independiente. El config_setting coincide si todos los valores coinciden.

La condición predeterminada

La condición integrada //conditions:default coincide cuando no coincide ninguna otra condición.

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 proteger 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 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 individualmente 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_setting que contienen un subconjunto del constraint_values de la plataforma, lo que permite que esos config_setting 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, se podría ver así:

bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic

select() también puede leer directamente 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 código estándar config_setting cuando solo necesitas realizar comparaciones con valores únicos.

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 Y encadenar.

O encadenamiento

Tenga 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. Aun así, provoca 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 de 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 ORing múltiples 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, diferentes destinos pueden compartir :config1_or_2 entre diferentes atributos.

Es un error que coincidan varias condiciones, a menos que una sea una "especialización" inequívoca de las demás o todas se resuelvan con el mismo valor. Obtén más detalles aquí.

Y encadenamiento

Si necesitas que una rama select coincida cuando coinciden varias condiciones, usa la 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"],
    }),
)

A diferencia del encadenamiento O, los config_setting existentes no se pueden AND directamente dentro de un select. Debes unirlas de forma explícita en una 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 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 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 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 la regla considera que ctx.attr.some_attr es [":foo"].

Las macros pueden aceptar cláusulas select() y pasarlas a reglas nativas. Sin embargo, no pueden manipularlos directamente. Por ejemplo, no hay manera de que una macro genere conversiones

select({"foo": "val"}, ...)

a

select({"foo": "val_with_suffix"}, ...)

Esto se debe a dos motivos.

Primero, las macros que necesitan saber qué ruta elegirá un select no pueden funcionar porque las macros 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 en todas las rutas de acceso de select, aunque técnicamente son posibles, carecen de una IU coherente. Se necesita un diseño más detallado para cambiar esto.

Consulta y Bazel

Bazel query opera durante la fase de carga de Bazel. Esto significa que no sabe qué marcas de línea de comandos usa un objetivo, 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.

Bazel cquery opera después de la fase de análisis de Bazel, por lo que tiene toda esa información y puede resolver select() con precisión.

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 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 macros?

select() funciona en las reglas. Consulta Compatibilidad con reglas para obtener más detalles.

El problema clave que esta pregunta suele significar es que select() no funciona en macros. Estas difieren de las reglas. Consulta la documentación sobre las reglas y las macros para comprender la diferencia. Este es 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)

Cree 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 de forma correcta cuando haces un comentario en 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 las macros por definición 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 la función select().

Sin embargo, las macros pueden pasar select() 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 un valor verdadero?

Debido a que, por definición, las macros (pero no las reglas) no pueden evaluar los select(), cualquier intento de hacerlo suele generar 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 comprenden el contenido de select(). Por lo tanto, lo que están evaluando es el objeto select(). Según los estándares de diseño de Pythonic, todos los objetos, salvo una pequeña cantidad de excepciones, se muestran automáticamente como verdaderos.

¿Puedo leer select() como un diccionario?

Las macros no pueden evaluar selecciones porque las macros se evalúan antes de que Bazel sabe cuáles son los parámetros de la línea de comandos de la compilación. ¿Pueden leer, al menos, el diccionario del select(), por ejemplo, para agregar un sufijo a cada valor?

En teoría, esto es posible, pero aún no es un elemento de Bazel. Lo que puedes hacer hoy es preparar un diccionario directo y, luego, incorporarlo 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 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()?

Debido a que bind() es una regla de WORKSPACE, no una regla de BUILD.

Las reglas de Workspace 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 realmente ninguna rama específica.

En cambio, debes usar alias(), con un select() en el atributo actual, para realizar este tipo de determinación del tiempo de ejecución. Esto funciona de forma correcta, ya que alias() es una regla BUILD y se evalúa con una configuración específica.

Incluso puedes tener un punto de destino de bind() a 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é no selecciona select() 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 objetivo de nivel superior que compilas, ejecuta lo siguiente:

$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)

Si quieres compilar otra //bar de destino que depende 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 que espera cada config_setting.

//myapp:foo puede existir en diferentes configuraciones en la misma compilación. Consulta los documentos de consulta para obtener ayuda sobre cómo usar somepath a fin de obtener el correcto.

¿Por qué select() no funciona con plataformas?

Bazel no admite atributos configurables que verifiquen 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() se debe usar si la plataforma de destino tiene las restricciones @platforms//cpu:x86 y @platforms//os:linux, pero no está definida en :x86_linux_platform? El autor del archivo BUILD y el usuario que definió la plataforma independiente pueden tener ideas diferentes.

¿Qué otra opción debería usar?

En su lugar, define una config_setting que coincida con cualquier plataforma con las siguientes 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 aclara a los usuarios qué plataformas cumplen con las condiciones deseadas.

¿Qué sucede si realmente quiero select en la plataforma?

Si tus requisitos de compilación requieren que se verifique específicamente la plataforma, puedes girar el valor de la marca --platforms en una 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 esta práctica; restringe tu compilación en exceso y confunde a los usuarios cuando la condición esperada no coincide.