Configuraciones

Informar un problema Ver código fuente Nightly · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

En esta página, se describen los beneficios y el uso básico de las configuraciones de Starlark, la API de Bazel para personalizar la compilación de tu proyecto. Incluye cómo definir la configuración de compilación y proporciona ejemplos.

Esto permite hacer lo siguiente:

  • definir marcas personalizadas para tu proyecto, lo que deja obsoleta la necesidad de --define
  • escritura Transitions para configurar las dependencias parámetros de configuración diferentes a los de sus superiores (como --compilation_mode=opt o --cpu=arm)
  • incorporar mejores valores predeterminados en las reglas (como compilar automáticamente //my:android_app con un SDK especificado)

y mucho más, todo desde archivos .bzl (no se requiere la versión de Bazel). Consulta la bazelbuild/examples repositorio de ejemplos.

Configuración de compilación definida por el usuario

Un parámetro de configuración de compilación es una sola pieza de información de configuración. Piensa en una configuración como un mapa de par clave-valor. La configuración de --cpu=ppc y --copt="-DFoo" produce una configuración que se ve como {cpu: ppc, copt: "-DFoo"}. Cada entrada es un parámetro de configuración de compilación.

Las marcas tradicionales, como cpu y copt, son parámetros de configuración nativos, es decir, sus claves se definen y sus valores se establecen dentro del código Java nativo de Bazel. Los usuarios de Bazel solo pueden leerlos y escribirlos a través de la línea de comandos y otras APIs que se mantienen de forma nativa. Para cambiar las marcas nativas y las APIs que las exponen, se requiere una versión de Bazel. Compilación definida por el usuario parámetros de configuración se definen en los archivos .bzl (por lo que no se necesita una versión de Bazel para registrar cambios). También se pueden configurar a través de la línea de comandos (si se designan como flags, obtén más información a continuación), pero también se pueden configurar a través de transiciones definidas por el usuario.

Cómo definir la configuración de compilación

Ejemplo de extremo a extremo

El parámetro rule() build_setting

Los parámetros de configuración de compilación son reglas como cualquier otra regla y se diferencian con el build_setting de la función rule() de Starlark atributo.

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

El atributo build_setting toma una función que designa el tipo de configuración de compilación. El tipo se limita a un conjunto de tipos básicos de Starlark, como bool y string. Consulta el módulo config documentación para obtener más detalles. Se pueden escribir líneas más complejas en la función de implementación de la regla. Sigue leyendo para obtener más información.

Las funciones del módulo config toman un parámetro booleano opcional, flag, que se establece en falso de forma predeterminada. Si flag se establece en verdadero, los usuarios pueden configurar la configuración de compilación en la línea de comandos, así como los creadores de reglas de forma interna a través de valores predeterminados y transiciones. Los usuarios no deben poder configurar todos los parámetros. Por ejemplo, si como escritor de reglas tienes un modo de depuración que deseas activar dentro de las reglas de prueba, no quieres darles a los usuarios la capacidad de activar esa función de forma indiscriminada dentro de otras reglas que no son de prueba.

Cómo usar ctx.build_setting_value

Al igual que todas las reglas, las reglas de configuración de compilación tienen funciones de implementación. Se puede acceder al valor básico de tipo Starlark de la configuración de compilación a través del método ctx.build_setting_value. Este método solo está disponible para los objetos ctx de las reglas de configuración de compilación. Estos métodos de implementación pueden reenviar directamente el valor de la configuración de compilación o realizar tareas adicionales en él, como la verificación de tipos o la creación de estructuras más complejas. A continuación, te indicamos cómo implementar una configuración de compilación de tipo enum:

# example/buildsettings/build_settings.bzl
TemperatureProvider = provider(fields = ['type'])

temperatures = ["HOT", "LUKEWARM", "ICED"]

def _impl(ctx):
    raw_temperature = ctx.build_setting_value
    if raw_temperature not in temperatures:
        fail(str(ctx.label) + " build setting allowed to take values {"
             + ", ".join(temperatures) + "} but was set to unallowed value "
             + raw_temperature)
    return TemperatureProvider(type = raw_temperature)

temperature = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

Define marcas de cadena de varios conjuntos

La configuración de cadenas tiene un parámetro allow_multiple adicional que permite a las que se establezca varias veces en la línea de comandos o en Bazelrcs. Su valor predeterminado aún se establece con un atributo de tipo de cadena:

# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
    name = "roasts",
    build_setting_default = "medium"
)

Cada parámetro de configuración de la marca se trata como un solo valor:

$ bazel build //my/target --//example:roasts=blonde \
    --//example:roasts=medium,dark

Lo anterior se analiza como {"//example:roasts": ["blonde", "medium,dark"]} y ctx.build_setting_value muestra la lista ["blonde", "medium,dark"].

Cómo crear instancias de la configuración de compilación

Las reglas definidas con el parámetro build_setting tienen un valor obligatorio implícito build_setting_default. Este atributo tiene el mismo tipo que el que declara el parámetro build_setting.

# example/buildsettings/build_settings.bzl
FlavorProvider = provider(fields = ['type'])

def _impl(ctx):
    return FlavorProvider(type = ctx.build_setting_value)

flavor = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)

Configuración predefinida

Ejemplo de extremo a extremo

El Skylib incluye un conjunto de parámetros de configuración predefinidos de los que puedes crear una instancia sin tener para escribir Starlark personalizado.

Por ejemplo, para definir un parámetro de configuración que acepte un conjunto limitado de valores de cadena, haz lo siguiente:

# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
    name = "myflag",
    values = ["a", "b", "c"],
    build_setting_default = "a",
)

Para obtener una lista completa, consulta Reglas comunes de configuración de compilación.

Cómo usar la configuración de compilación

Según la configuración de compilación

Si un destino desea leer información de configuración, puede depender directamente de la configuración de compilación a través de una dependencia de atributo normal.

# example/rules.bzl
load("//example/buildsettings:build_settings.bzl", "FlavorProvider")
def _rule_impl(ctx):
    if ctx.attr.flavor[FlavorProvider].type == "ORANGE":
        ...

drink_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "flavor": attr.label()
    }
)
# example/BUILD
load("//example:rules.bzl", "drink_rule")
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)
drink_rule(
    name = "my_drink",
    flavor = ":favorite_flavor",
)

Es posible que los idiomas deseen crear un conjunto canónico de parámetros de configuración de compilación del que dependen todas las reglas de ese idioma. Aunque el concepto nativo de fragments ya no existe como un objeto codificado en el mundo de configuración de Starlark, una forma de traducir este concepto sería usar conjuntos de atributos implícitos comunes. Por ejemplo:

# kotlin/rules.bzl
_KOTLIN_CONFIG = {
    "_compiler": attr.label(default = "//kotlin/config:compiler-flag"),
    "_mode": attr.label(default = "//kotlin/config:mode-flag"),
    ...
}

...

kotlin_library = rule(
    implementation = _rule_impl,
    attrs = dicts.add({
        "library-attr": attr.string()
    }, _KOTLIN_CONFIG)
)

kotlin_binary = rule(
    implementation = _binary_impl,
    attrs = dicts.add({
        "binary-attr": attr.label()
    }, _KOTLIN_CONFIG)

Cómo usar la configuración de compilación en la línea de comandos

De manera similar a la mayoría de las marcas nativas, puedes usar la línea de comandos para establecer configuraciones de compilación. marcadas como marcas. La compilación el nombre de la configuración es la ruta de destino completa con la sintaxis name=value:

$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed

Se admite la sintaxis booleana especial:

$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag

Usa alias de configuración de compilación

Puedes establecer un alias para la ruta de destino de la configuración de compilación para que sea más fácil de leer en la línea de comandos. Los alias funcionan de manera similar a las marcas nativas y también usan la sintaxis de la opción de dos guiones.

Para establecer un alias, agrega --flag_alias=ALIAS_NAME=TARGET_PATH a tu .bazelrc. Por ejemplo, para establecer un alias en coffee, haz lo siguiente:

# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp

Práctica recomendada: Configurar un alias varias veces da como resultado la versión más reciente que tenga prioridad. Usa nombres de alias únicos para evitar resultados de análisis no deseados.

Para usar el alias, escríbelo en lugar de la ruta de acceso de destino de la configuración de la compilación. Con el ejemplo anterior de coffee establecido en el .bazelrc del usuario:

$ bazel build //my/target --coffee=ICED

en lugar de

$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED

Práctica recomendada: Si bien es posible establecer alias en la línea de comandos, dejarlos en una .bazelrc reduce el desorden en la línea de comandos.

Configuración de compilación con tipo de etiqueta

Ejemplo de extremo a extremo

A diferencia de otros parámetros de configuración de compilación, los parámetros de configuración de tipo de etiqueta no se pueden definir con el parámetro de regla build_setting. En su lugar, Bazel tiene dos reglas integradas: label_flag y label_setting. Estas reglas reenvían los proveedores del objetivo real al que se establece la configuración de compilación. label_flag y label_setting se pueden leer o escribir mediante transiciones, y el usuario puede configurar label_flag como lo hacen otras reglas de build_setting. La única diferencia es que no se pueden definir de forma personalizada.

Con el tiempo, la configuración de tipo de etiqueta reemplazará la funcionalidad de límite tardío. los valores predeterminados. Los atributos predeterminados con límite tardío son atributos de tipo etiqueta cuyos los valores finales pueden verse afectados por la configuración. En Starlark, esto reemplazará la API de configuration_field.

# example/rules.bzl
MyProvider = provider(fields = ["my_field"])

def _dep_impl(ctx):
    return MyProvider(my_field = "yeehaw")

dep_rule = rule(
    implementation = _dep_impl
)

def _parent_impl(ctx):
    if ctx.attr.my_field_provider[MyProvider].my_field == "cowabunga":
        ...

parent_rule = rule(
    implementation = _parent_impl,
    attrs = { "my_field_provider": attr.label() }
)

# example/BUILD
load("//example:rules.bzl", "dep_rule", "parent_rule")

dep_rule(name = "dep")

parent_rule(name = "parent", my_field_provider = ":my_field_provider")

label_flag(
    name = "my_field_provider",
    build_setting_default = ":dep"
)

Configuración de compilación y select()

Ejemplo de extremo a extremo

Los usuarios pueden definir atributos en la configuración de compilación con select() Los objetivos de configuración de compilación se pueden pasar al atributo flag_values de config_setting El valor que debe coincidir con la configuración se pasa como un String y, luego, se analiza en el tipo de la configuración de compilación para la coincidencia.

config_setting(
    name = "my_config",
    flag_values = {
        "//example:favorite_flavor": "MANGO"
    }
)

Transiciones definidas por el usuario

Una configuración transición asigna la transformación de un destino configurado a otro gráfico de compilación.

Las reglas que los establecen deben incluir un atributo especial:

  "_allowlist_function_transition": attr.label(
      default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
  )

Si agregas transiciones, puedes expandir fácilmente el tamaño de tu gráfico de compilación. De esta forma, se establece una lista de entidades permitidas en los paquetes crear objetivos de esta regla. El valor predeterminado en el bloque de código anterior incluye todo en la lista de entidades permitidas. Pero si desea restringir quién utiliza su regla, puedes configurar ese atributo para que apunte a tu propia lista de entidades permitidas personalizada. Si necesitas asesoramiento o asistencia entender cómo las transiciones pueden afectar el rendimiento de tu compilación.

Definición

Las transiciones definen los cambios de configuración entre las reglas. Por ejemplo, una solicitud como "compilar mi dependencia para una CPU diferente a la de su superior" es manejado por un transición.

De forma formal, una transición es una función de una configuración de entrada a una o más configuraciones de salida. La mayoría de las transiciones son de 1:1, como "anular la configuración de entrada con --cpu=ppc". También pueden existir transiciones de 1:2 o más, pero tienen restricciones especiales.

En Starlark, las transiciones se definen como reglas, con una definición transition() función y una función de implementación.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//example:favorite_flavor" : "MINT"}

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

La función transition() toma una función de implementación, un conjunto de parámetros de configuración de compilación para leer (inputs) y un conjunto de parámetros de configuración de compilación para escribir (outputs). La función de implementación tiene dos parámetros, settings y attr. settings es un diccionario {String:Object} de todas las opciones de configuración declaradas en el parámetro inputs a transition().

attr es un diccionario de atributos y valores de la regla a la que se adjunta la transición. Si se adjunta como un transición perimetral saliente, los valores de estas son la resolución post-select() configurada. Cuando se adjunta como una transición de borde entrante, attr no incluye ningún atributo que use un selector para resolver su valor. Si un La transición perimetral entrante en --foo lee el atributo bar y, luego, también selecciona --foo para establecer el atributo bar, existe la posibilidad de que el valor transición perimetral entrante para leer el valor incorrecto de bar en la transición.

La función de implementación debe mostrar un diccionario (o una lista de diccionarios, en el caso de transiciones con varias configuraciones de salida) de los valores de configuración de compilación nuevos que se aplicarán. Los conjuntos de claves del diccionario que se muestran deben contienen exactamente el conjunto de configuraciones de compilación que se pasaron a outputs parámetro de la función de transición. Esto se aplica incluso si se configura no se haya modificado en el transcurso de la transición, su valor original se debe se pasen explícitamente en el diccionario devuelto.

Cómo definir transiciones de 1:2 o más

Ejemplo de extremo a extremo

La transición perimetral de salida puede asignar una sola entrada de salida en dos o más configuraciones de salida. Esto es útil para definir reglas que agrupan código multiarquitectónico.

Las transiciones 1:2+ se definen al devolver una lista de diccionarios en el de implementación de transición.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return [
        {"//example:favorite_flavor" : "LATTE"},
        {"//example:favorite_flavor" : "MOCHA"},
    ]

coffee_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

También pueden establecer claves personalizadas que la función de implementación de reglas puede usar para leer dependencias individuales:

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

Cómo adjuntar transiciones

Ejemplo de extremo a extremo

Las transiciones se pueden adjuntar en dos lugares: los bordes entrantes y los salientes. Esto significa que las reglas pueden realizar la transición de su propia configuración (entrante transición perimetral) y la transición de sus dependencias de configuración (salientes transición perimetral).

NOTA: Actualmente, no hay forma de adjuntar transiciones de Starlark a reglas nativas. Si necesitas hacerlo, comunícate con bazel-discuss@googlegroups.com para obtener ayuda con las soluciones alternativas.

Transiciones perimetrales entrantes

Las transiciones perimetrales entrantes se activan cuando se adjunta un objeto transition (creado por transition()) al parámetro cfg de rule():

# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
    implementation = _impl,
    cfg = hot_chocolate_transition,
    ...

Las transiciones de borde entrantes deben ser de 1:1.

Transiciones de borde salientes

Para activar las transiciones de borde salientes, se adjunta un objeto transition (creado por transition()) al parámetro cfg de un atributo:

# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
    implementation = _impl,
    attrs = { "dep": attr.label(cfg = coffee_transition)}
    ...

Las transiciones de borde salientes pueden ser 1:1 o 1:2+.

Consulta Cómo acceder a los atributos con transiciones sobre cómo leer estas claves.

Transiciones en opciones nativas

Ejemplo de extremo a extremo

Las transiciones de Starlark también pueden declarar operaciones de lectura y escritura en la compilación nativa opciones de configuración con un prefijo especial en el nombre de la opción.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//command_line_option:cpu": "k8"}

cpu_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]

Opciones nativas no admitidas

Bazel no admite la transición en --define con "//command_line_option:define" En su lugar, usa una configuración de compilación personalizada. En general, no se recomienda usar --define en favor de la configuración de compilación.

Bazel no admite la transición en --config. Esto se debe a que --config es una “expansión” que se expande a otras marcas.

Es fundamental que --config pueda incluir marcas que no afecten la configuración de compilación, como --spawn_strategy. Por su diseño, Bazel no puede vincular esas marcas a destinos individuales. Esto significa no hay una manera coherente de aplicarlos en las transiciones.

Como solución alternativa, puedes detallar explícitamente las marcas que son parte de la configuración en tu transición. Esto requiere mantener los --config's en dos lugares, lo que es una mancha conocida de la IU.

Las transiciones activadas permiten varias configuraciones de compilación.

Cuando configures la configuración de compilación que permite varios valores, el valor de la configuración se debe establecer con una lista.

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "string_flag")
string_flag(name = "roasts", build_setting_default = "medium")
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    # Using a value of just "dark" here will throw an error
    return {"//example:roasts" : ["dark"]},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:roasts"]
)

Transiciones no-ops

Si una transición muestra {}, [] o None, es un atajo para mantener todos los parámetros de configuración en sus valores originales. Esto puede ser más conveniente que explícitamente configurando cada salida para sí misma.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (attr)
    if settings["//example:already_chosen"] is True:
      return {}
    return {
      "//example:favorite_flavor": "dark chocolate",
      "//example:include_marshmallows": "yes",
      "//example:desired_temperature": "38C",
    }

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = ["//example:already_chosen"],
    outputs = [
        "//example:favorite_flavor",
        "//example:include_marshmallows",
        "//example:desired_temperature",
    ]
)

Cómo acceder a los atributos con transiciones

Ejemplo de extremo a extremo

Cuando conectas una transición a un borde saliente (independientemente de si la transición es de 1:1 o de 1:2 o más), ctx.attr se fuerza a ser una lista. si todavía no lo está. El orden de los elementos de esta lista no se especifica.

# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    return {"//example:favorite_flavor" : "LATTE"},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

def _rule_impl(ctx):
    # Note: List access even though "dep" is not declared as list
    transitioned_dep = ctx.attr.dep[0]

    # Note: Access doesn't change, other_deps was already a list
    for other_dep in ctx.attr.other_deps:
      # ...


coffee_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = coffee_transition)
        "other_deps": attr.label_list(cfg = coffee_transition)
    })

Si la transición es 1:2+ y establece claves personalizadas, se puede usar ctx.split_attr para leer las dependencias individuales para cada clave:

# example/transitions/rules.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

def _rule_impl(ctx):
    apple_dep = ctx.split_attr.dep["Apple deps"]
    linux_dep = ctx.split_attr.dep["Linux deps"]
    # ctx.attr has a list of all deps for all keys. Order is not guaranteed.
    all_deps = ctx.attr.dep

multi_arch_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = multi_arch_transition)
    })

Consulta el ejemplo completo aquí.

Integración con plataformas y cadenas de herramientas

Muchas banderas nativas hoy en día, como --cpu y --crosstool_top, están relacionadas con resolución de la cadena de herramientas. En el futuro, las transiciones explícitas a estos tipos de es probable que las marcas se reemplacen con una transición en el plataforma de destino.

Consideraciones sobre la memoria y el rendimiento

Agregar transiciones y, por lo tanto, configuraciones nuevas a tu compilación tiene un costo: gráficos de compilación más grandes, gráficos de compilación menos comprensibles y compilaciones más lentas. Vale la pena considerar estos costos cuando se piensa en usar transiciones en las reglas de compilación. A continuación, se muestra un ejemplo de cómo una transición podría crear un crecimiento exponencial en tu gráfico de compilación.

Compilaciones con comportamiento inadecuado: un caso de éxito

Gráfico de escalabilidad

Figura 1: Grafo de escalabilidad que muestra un objetivo de nivel superior y sus dependencias.

En este gráfico, se muestra un objetivo de nivel superior, //pkg:app, que depende de dos objetivos, //pkg:1_0 y //pkg:1_1. Ambos objetivos dependen de dos objetivos, //pkg:2_0 y //pkg:2_1. Ambos objetivos dependen de dos objetivos, //pkg:3_0 y //pkg:3_1. Esto continúa hasta //pkg:n_0 y //pkg:n_1, que dependen de un solo objetivo, //pkg:dep.

La compilación de //pkg:app requiere \(2n+2\) destinos:

  • //pkg:app
  • //pkg:dep
  • //pkg:i_0 y //pkg:i_1 para \(i\) en \([1..n]\)

Imagina que implementas una marca --//foo:owner=<STRING> y se aplica //pkg:i_b.

depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"

En otras palabras, //pkg:i_b agrega b al valor anterior de --owner para todos. sus dependencias.

Esto produce los siguientes destinos configurados:

//pkg:app                              //foo:owner=""
//pkg:1_0                              //foo:owner=""
//pkg:1_1                              //foo:owner=""
//pkg:2_0 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_0 (via //pkg:1_1)              //foo:owner="1"
//pkg:2_1 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_1 (via //pkg:1_1)              //foo:owner="1"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_0)  //foo:owner="00"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_1)  //foo:owner="01"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_0)  //foo:owner="10"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_1)  //foo:owner="11"
...

//pkg:dep produce \(2^n\) objetivos configurados: config.owner= “\(b_0b_1...b_n\)” para todos \(b_i\) en \(\{0,1\}\).

Esto hace que el gráfico de compilación sea exponencialmente más grande que el gráfico objetivo, con las consecuencias correspondientes de memoria y rendimiento.

TODO: Agrega estrategias para la medición y mitigación de estos problemas.

Lecturas adicionales

Para obtener más detalles sobre cómo modificar configuraciones de compilación, consulta: