Configuraciones

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 forma en que se compila tu proyecto. Se incluye cómo definir parámetros de configuración de compilación y se proporcionan ejemplos.

Esto permite hacer lo siguiente:

  • definir marcas personalizadas para tu proyecto, lo que dejará de estar disponible la necesidad de --define
  • Escribir transiciones para configurar dependencias en configuraciones diferentes a las de sus superiores (como --compilation_mode=opt o --cpu=arm)
  • Integrar mejores valores predeterminados en reglas (como compilar automáticamente //my:android_app con un SDK especificado)

y mucho más, todo desde archivos .bzl (no se requiere un lanzamiento de Bazel). Consulta el repositorio de bazelbuild/examples para ver ejemplos.

Configuración de compilación definida por el usuario

Una configuración de compilación es una pieza única de información de configuración. Piensa en una configuración como un mapa de par clave-valor. Si configuras --cpu=ppc y --copt="-DFoo", se produce una configuración similar a {cpu: ppc, copt: "-DFoo"}. Cada entrada es una configuración de compilación.

Las marcas tradicionales, como cpu y copt, son configuraciones nativas: sus claves se definen y sus valores se establecen dentro de código nativo de Java 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, es necesario un lanzamiento de Bazel. La configuración de compilación definida por el usuario se define en archivos .bzl (y, por lo tanto, no 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, consulta más información a continuación), pero también se pueden configurar mediante transiciones definidas por el usuario.

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

Ejemplo de extremo a extremo

El parámetro build_setting rule()

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

# 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 la compilación. El tipo se limita a un conjunto de tipos básicos de Starlark, como bool y string. Consulta la documentación del módulo config para obtener más detalles. Se puede realizar una escritura más compleja en la función de implementación de la regla. Más adelante, se brinda más información sobre este tema.

Las funciones del módulo config toman un parámetro booleano opcional, flag, que se establece como falso de forma predeterminada. Si flag se establece como verdadero, los usuarios pueden establecer la configuración de compilación en la línea de comandos, así como internamente, los escritores de reglas a través de valores y transiciones predeterminados. Los usuarios no deben establecer todos los parámetros de configuración. Por ejemplo, si, como escritor de reglas, tienes algún modo de depuración que te gustaría activar dentro de las reglas de prueba, no quieres que los usuarios tengan la capacidad de activar indiscriminadamente esa función dentro de otras reglas que no sean de prueba.

Cómo usar ="{.build_setting_value

Como 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 mediante el método ctx.build_setting_value. Este método solo está disponible para objetos ctx de reglas de configuración de compilación. Estos métodos de implementación pueden reenviar directamente el valor de configuración de compilación o realizar trabajos adicionales en él, como la comprobación de tipo o la creación de structs más complejos. A continuación, te mostramos 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 strings de conjuntos múltiples

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

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

Cada configuración de la marca se trata como un único 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"].

Crea instancias de la configuración de compilación

Las reglas definidas con el parámetro build_setting tienen un atributo build_setting_default obligatorio implícito. Este atributo adquiere el mismo tipo que el declarado por 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/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)

Configuración predefinida

Ejemplo de extremo a extremo

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

Por ejemplo, para definir un parámetro de configuración que acepta un conjunto limitado de valores de cadena:

# 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 de configuración de compilación comunes.

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

Según la configuración de compilación

Si un destino quiere leer una parte de la información de configuración, puede depender directamente de la configuración de la compilación mediante 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 quieran crear un conjunto canónico de configuraciones de compilación del que dependan todas las reglas para ese idioma. Aunque el concepto nativo de fragments ya no existe como un objeto codificado en el mundo de la 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

Al igual que con la mayoría de las marcas nativas, puedes usar la línea de comandos para establecer parámetros de configuración de compilación que se marcan como marcas. El nombre de la configuración de compilación es su 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 una 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 la compilación para facilitar la lectura en la línea de comandos. Los alias funcionan de manera similar a las marcas nativas y también utilizan la sintaxis de la opción de guion doble.

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 hace que el más reciente tenga prioridad. Usa nombres de alias únicos para evitar resultados de análisis no intencionados.

Para usar el alias, escríbelo en lugar de la ruta de acceso objetivo de la configuración de la compilación. Con el ejemplo anterior de coffee configurado en el .bazelrc del usuario, ocurre lo siguiente:

$ 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 configurar alias en la línea de comandos, dejarlos en un .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 con 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 destino real para el que se establece la configuración de compilación. Las transiciones pueden leer o escribir label_flag y label_setting, y el usuario puede configurar label_flag como otras reglas build_setting. La única diferencia es que no pueden definirse de forma personalizada.

Con el tiempo, la configuración de las etiquetas reemplazará la funcionalidad de los valores predeterminados de límite tardío. Los atributos predeterminados que llegan tarde son atributos de tipo etiqueta cuyos valores finales pueden verse afectados por la configuración. En Starlark, esto reemplazará a 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 configurar los atributos en la configuración de la compilación mediante select(). Los objetivos de configuración de la 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 el tipo de 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 transición de configuración asigna la transformación de un destino configurado a otro dentro del 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"
  )

Cuando agregas transiciones, puedes aumentar fácilmente el tamaño de tu gráfico de compilación. Esto establece una lista de entidades permitidas para los paquetes en los que puedes crear destinos de esta regla. El valor predeterminado en el bloque de código anterior incluye todo en la lista de entidades permitidas. Sin embargo, si quieres restringir quién usa tu regla, puedes configurar ese atributo para que apunte a tu propia lista de entidades permitidas personalizada. Comunícate con bazel-discuss@googlegroups.com si necesitas asesoramiento o ayuda para comprender cómo las transiciones pueden afectar el rendimiento de tu compilación.

Definición

Las transiciones definen cambios de configuración entre reglas. Por ejemplo, una solicitud como "compilar mi dependencia para una CPU diferente a la de su superior" se maneja mediante una transición.

De manera 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 1:1, como "anular la configuración de entrada con --cpu=ppc". También pueden existir transiciones de 1:2 o superiores, pero tienen restricciones especiales.

En Starlark, las transiciones se definen como las reglas, con una función transition() que define 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 todos los parámetros de configuración declarados en transition() en el parámetro inputs.

attr es un diccionario de atributos y valores de la regla a la que se adjunta la transición. Cuando se adjunta como una transición perimetral saliente, los valores de estos atributos se configuran con una resolución posterior a la selección(). Cuando se adjunta como una transición perimetral entrante, attr no incluye ningún atributo que use un selector para resolver su valor. Si una transición perimetral entrante en --foo lee el atributo bar y, luego, también selecciona --foo para establecer el atributo bar, es posible que la transición perimetral entrante lea 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 nuevos valores de configuración de compilación que se aplicarán. Los conjuntos de claves del diccionario que se muestran deben contener exactamente el conjunto de parámetros de configuración de compilación que se pasaron al parámetro outputs de la función de transición. Esto se aplica incluso si una configuración de compilación no se modifica en realidad durante el transcurso de la transición: su valor original se debe pasar de forma explícita en el diccionario que se muestra.

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 configuración de entrada a dos o más configuraciones de salida. Esto es útil para definir reglas que agrupen el código multiarquitectónico.

Las transiciones de 1:2 o más se definen cuando se muestra una lista de diccionarios en la función de implementación de la 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 configurar 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 conectar en dos lugares: los bordes entrantes y los salientes. En efecto, esto significa que las reglas pueden hacer la transición de su propia configuración (transición perimetral entrante) y realizar la transición de las configuraciones de sus dependencias (transición perimetral saliente).

NOTA: Por el momento, no hay forma de adjuntar transiciones de Starlark a reglas nativas. Si necesitas hacerlo, comunícate con bazel-discuss@googlegroups.com para obtener ayuda para encontrar 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 perimetrales entrantes deben ser transiciones 1:1.

Transiciones del borde saliente

Para activar las transiciones de bordes salientes, debes adjuntar 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 del borde saliente pueden ser de 1:1 o 1:2+.

Consulta Accede a los atributos con transiciones para obtener información 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 las opciones de configuración de compilación nativas mediante 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 el uso nuevo de --define para priorizar la configuración de compilación.

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

Cabe destacar que --config puede incluir marcas que no afectan la configuración de compilación, como --spawn_strategy. Por diseño, Bazel no puede vincular esas marcas a destinos individuales. Esto significa que no hay una manera coherente de aplicarlas 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 la expansión de --config en dos lugares, lo que es una mancha de IU conocida.

Transiciones para permitir múltiples configuraciones de compilación

Cuando estableces ajustes de compilación que permiten varios valores, el valor de la configuración debe establecerse 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, esta es una abreviatura para mantener toda la configuración en sus valores originales. Esto puede ser más conveniente que configurar cada resultado de forma explícita.

# 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 se adjunta una transición a un perímetro saliente (independientemente de si es una transición de 1:1 o de 1:2+), ctx.attr se verá forzado a ser una lista, si aún no lo es. No se especifica el orden de los elementos en esta lista.

# 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 dependencias individuales de 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 en plataformas y cadenas de herramientas

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

Consideraciones de memoria y 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 consideres usar transiciones en tus reglas de compilación. A continuación, se muestra un ejemplo de cómo una transición puede crear un crecimiento exponencial de tu gráfico de compilación.

Compilaciones con comportamiento inadecuado: un caso de éxito

Gráfico de escalabilidad

Figura 1. Gráfico de escalabilidad en el que se 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 único objetivo, //pkg:dep.

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

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

Imagina que implement una marca, --//foo:owner=<STRING> y //pkg:i_b aplican.

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

En otras palabras, //pkg:i_b agrega b al valor anterior de --owner para todas 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\) destinos 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 de destino, con las correspondientes consecuencias de memoria y rendimiento.

PENDIENTE: Agregar estrategias para la medición y mitigación de estos problemas

Lecturas adicionales

Si deseas obtener más detalles para modificar las configuraciones de compilación, consulta lo siguiente: