BazelCon 2022 findet vom 16. bis 17. November in New York und online statt.
Jetzt anmelden

Konfigurationen

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Auf dieser Seite werden die Vorteile und die grundlegende Verwendung der Starlark-Konfigurationen, der Bayel API für die Anpassung Ihres Projekts, beschrieben. Sie enthält Informationen zum Definieren von Build-Einstellungen und Beispiele.

Dadurch können Sie:

  • Benutzerdefinierte Flags für Ihr Projekt definieren, wodurch --define nicht mehr benötigt wird
  • Schreiben von Übergängen, um Konfigurationen in anderen Konfigurationen als ihre übergeordneten Konfigurationen zu konfigurieren (z. B. --compilation_mode=opt oder --cpu=arm)
  • Vereinfachte Standardeinstellungen (z. B. automatisch //my:android_app mit einem angegebenen SDK erstellen)

und mehr, komplett über .bzl-Dateien (kein {5/}-Release erforderlich). Beispiele findest du im Repository bazelbuild/examples.

Benutzerdefinierte Build-Einstellungen

Eine Build-Einstellung ist eine einzelne Konfigurationsinformation. Stellen Sie sich eine Konfiguration wie eine Schlüssel/Wert-Zuordnung vor. Wenn Sie --cpu=ppc und --copt="-DFoo" festlegen, wird die Konfiguration {cpu: ppc, copt: "-DFoo"} erstellt. Jeder Eintrag ist eine Build-Einstellung.

Traditionelle Flags wie cpu und copt sind native Einstellungen. Die Schlüssel werden definiert und ihre Werte werden im nativen Bay Java-Code festgelegt. Istio-Nutzer können sie nur über die Befehlszeile und andere APIs nativ lesen und schreiben. Zum Ändern nativer Flags und der APIs, zu denen sie verfügbar sind, ist ein Bazel-Release erforderlich. Benutzerdefinierte Build-Einstellungen werden in .bzl-Dateien definiert. Daher sind zur Nutzung von Änderungen keine aktuellen Release-Einstellungen erforderlich. Sie können auch über die Befehlszeile festgelegt werden, wenn sie als flags gekennzeichnet sind (siehe unten), aber auch über benutzerdefinierte Übergänge.

Build-Einstellungen definieren

Beispiel für ein Ende zum Ende

Der Parameter build_setting rule()

Build-Einstellungen sind Regeln wie alle anderen Regeln und unterscheiden sich mit dem Star-Attribut rule()-Attribut build_setting.

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

Das Attribut build_setting verwendet eine Funktion, die den Typ der Build-Einstellung angibt. Der Typ ist auf eine Reihe von grundlegenden Starlark-Typen wie bool und string beschränkt. Weitere Informationen findest du in der Dokumentation zum Modul config. Komplexere Eingaben sind in der Implementierungsfunktion der Regel möglich. Weitere Informationen finden Sie unten.

Die Funktionen des config-Moduls verwenden den optionalen booleschen Parameter flag, der standardmäßig auf „false“ gesetzt ist. Wenn flag auf „true“ gesetzt ist, kann die Build-Einstellung von Nutzern in der Befehlszeile sowie intern von Regelautoren über Standardwerte und Übergänge festgelegt werden. Nicht alle Einstellungen sollten von Nutzern konfiguriert werden. Wenn Sie beispielsweise als Regelautor einen Debug-Modus haben, den Sie in Testregeln aktivieren möchten, möchten Sie den Nutzern die Möglichkeit geben, diese Funktion ungewollt in anderen Nicht-Testregeln zu aktivieren.

ctx.build_setting_value verwenden

Wie alle Regeln haben auch Build-Einstellungsregeln Implementierungsfunktionen. Der grundlegende Wert des Starlark-Typs der Build-Einstellungen kann mit der Methode ctx.build_setting_value aufgerufen werden. Diese Methode ist nur für ctx-Objekte von Build-Einstellungsregeln verfügbar. Diese Implementierungsmethoden können den Wert der Build-Einstellungen direkt weiterleiten oder zusätzliche Arbeitsschritte wie die Typprüfung oder die Erstellung komplexer Strukturen ausführen. So implementieren Sie eine Build-Einstellung vom Typ 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)
)

String-Flags mit mehreren Sätzen definieren

Stringeinstellungen haben einen zusätzlichen allow_multiple-Parameter, mit dem das Flag mehrmals in der Befehlszeile oder in Bayerischen Zeichen festgelegt werden kann. Der Standardwert wird weiterhin mit einem Stringtyp festgelegt:

# 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"
)

Jede Einstellung des Flags wird als ein Wert behandelt:

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

Das obige Beispiel wird in {"//example:roasts": ["blonde", "medium,dark"]} geparst und ctx.build_setting_value gibt die Liste ["blonde", "medium,dark"] zurück.

Build-Einstellungen instanziieren

Regeln, die mit dem Parameter build_setting definiert sind, haben ein implizites erforderliches Attribut build_setting_default. Dieses Attribut verwendet den gleichen Typ wie der Parameter 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"
)

Vordefinierte Einstellungen

Beispiel für ein Ende zum Ende

Die Bibliothek Skylib enthält eine Reihe vordefinierter Einstellungen, die Sie instanziieren können, ohne benutzerdefinierte Starlark-Daten schreiben zu müssen.

So definieren Sie beispielsweise eine Einstellung, die eine begrenzte Anzahl von Stringwerten akzeptiert:

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

Eine vollständige Liste finden Sie unter Allgemeine Build-Einstellungsregeln.

Build-Einstellungen verwenden

Abhängig von den Build-Einstellungen

Wenn ein Ziel bestimmte Konfigurationsinformationen lesen möchte, kann es direkt von der Build-Einstellung über eine regelmäßige Attributabhängigkeit abhängen.

# 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",
)

Für Sprachen kann es sinnvoll sein, eine kanonische Gruppe von Build-Einstellungen zu erstellen, auf der alle Regeln für diese Sprache basieren. Obwohl das native Konzept von fragments in der Starlark-Konfigurationswelt nicht mehr als hartcodiertes Objekt existiert, könnten Sie Gruppen von gängigen impliziten Attributen in dieses Konzept übersetzen. Beispiel:

# 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)

Build-Einstellungen in der Befehlszeile verwenden

Ähnlich wie bei den meisten nativen Flags können Sie über die Befehlszeile Build-Einstellungen festlegen, die als Flags markiert sind. Der Name der Build-Einstellung ist der vollständige Zielpfad mit der Syntax name=value:

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

Spezielle boolesche Syntax wird unterstützt:

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

Aliasse für Build-Einstellungen verwenden

Sie können einen Alias für den Build-Zielzielpfad festlegen, um das Lesen in der Befehlszeile zu erleichtern. Aliasse funktionieren ähnlich wie native Flags und nutzen auch die Syntax von Doppelstrichen.

Erstelle einen Alias, indem du --flag_alias=ALIAS_NAME=TARGET_PATH zu .bazelrc hinzufügst. So legen Sie einen Alias auf coffee fest:

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

Best Practice: Wenn Sie einen Alias mehrmals festlegen, hat der letzte Vorrang. Verwenden Sie eindeutige Alias-Namen, um unbeabsichtigte Parsing-Ergebnisse zu vermeiden.

Wenn Sie den Alias verwenden möchten, geben Sie ihn anstelle des Zielpfads der Build-Einstellung ein. Mit dem obigen Beispiel für coffee, das in den .bazelrc des Nutzers festgelegt ist, geschieht Folgendes:

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

anstelle von

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

Best Practice: Du kannst Aliasse zwar in der Befehlszeile festlegen, aber wenn sie in einem .bazelrc stehen, lässt sich die Befehlszeile besser organisieren.

Build-Einstellungen mit Labels

Beispiel für ein Ende zum Ende

Im Gegensatz zu anderen Build-Einstellungen können Einstellungen mit Labeltypen nicht mit dem Regelparameter build_setting definiert werden. Stattdessen hat Bazel zwei integrierte Regeln: label_flag und label_setting. Mit diesen Regeln werden die Anbieter des tatsächlichen Ziels weitergeleitet, auf das die Build-Einstellung festgelegt ist. label_flag und label_setting können durch Übergänge gelesen und geschrieben werden und label_flag kann wie andere build_setting-Regeln vom Nutzer festgelegt werden. Der einzige Unterschied besteht darin, dass sie nicht benutzerdefiniert definiert werden können.

Einstellungen vom Typ „Label“ ersetzen die Funktionen von Standardeinstellungen für späte Zeiten. Zu spät gebundene Standardattribute sind Attribute vom Typ Label, deren finale Werte von der Konfiguration beeinflusst werden können. In Starlark wird die API configuration_field ersetzt.

# 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"
)

Build-Einstellungen und select()

Beispiel für ein Ende zum Ende

Nutzer können Attribute in den Build-Einstellungen mit select() konfigurieren. Build-Zielziele können an das flag_values-Attribut von config_setting übergeben werden. Der Wert, der der Konfiguration entspricht, wird als String übergeben und dann in den Typ der Build-Einstellung für den Abgleich geparst.

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

Benutzerdefinierte Umstellungen

Durch einen Umstellung wird die Transformation innerhalb des Build-Grafik von einem konfigurierten Ziel zu einem anderen zugeordnet.

Regeln, mit denen sie festgelegt werden, müssen ein spezielles Attribut enthalten:

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

Durch das Hinzufügen von Übergängen kann die Größe Ihres Build-Diagramms ziemlich stark erweitert werden. Dadurch wird eine Zulassungsliste für die Pakete festgelegt, in denen Sie Ziele dieser Regel erstellen können. Mit dem Standardwert im Codeblock oben wird alles zugelassen. Wenn Sie jedoch einschränken möchten, wer Ihre Regel verwendet, können Sie dieses Attribut so einstellen, dass es auf Ihre eigene benutzerdefinierte Zulassungsliste verweist. Wenn Sie wissen möchten, wie sich Übergänge auf die Build-Leistung auswirken können, wenden Sie sich an bazel-Diskussion@googlegroups.com.

Wird definiert

Mit Übergangen werden Konfigurationsänderungen zwischen Regeln definiert. Beispielsweise wird bei einer Anfrage wie der Kompilierung meiner Abhängigkeit für eine andere CPU als ihre übergeordnete CPU ein Übergang verarbeitet.

Ein Übergang ist eine Funktion von einer Eingabekonfiguration zu einer oder mehreren Ausgabekonfigurationen. Die meisten Übergänge sind 1:1, wie „"die Eingabekonfiguration mit --cpu=ppc überschreiben“. Es können auch Übergänge von 1:2 vorhanden sein, allerdings mit speziellen Einschränkungen.

In Starlark werden Übergänge ähnlich wie Regeln definiert. Es definiert eine transition() -Funktion und eine Implementierungsfunktion.

# 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"]
)

Die Funktion transition() verwendet eine Implementierungsfunktion, eine Reihe von Build-Einstellungen, die gelesen werden sollen (inputs), und eine Reihe von Build-Einstellungen, die geschrieben werden sollen (outputs). Die Implementierungsfunktion umfasst die beiden Parameter settings und attr. settings ist ein Wörterbuch {String:Object} aller Einstellungen, die im Parameter inputs für transition() deklariert sind.

attr ist ein Wörterbuch mit Attributen und Werten der Regel, an die der Übergang angehängt ist. Wenn diese als Edge-Edge-Übergang angehängt werden, werden die Werte dieser Attribute nach der Auflösung von „select-select()“ konfiguriert. Wenn dieser als eingehender Edge-Übergang angehängt wird, enthält attr keine Attribute, die einen Selektor verwenden, um ihren Wert aufzulösen. Wenn ein eingehender Edge-Übergang bei --foo das Attribut bar liest und dann zum Auswählen des Attributs bar auch --foo auswählt, kann der eingehende Edge-Übergang den falschen Wert von bar während des Übergangs lesen.

Die Implementierungsfunktion muss ein Wörterbuch (oder eine Liste von Wörterbüchern im Fall von Übergängen mit mehreren Ausgabekonfigurationen) mit den neuen Build-Einstellungswerten zurückgeben. Die zurückgegebenen Schlüsselwörtersätze müssen zurückgegeben werden, sodass sie genau die Build-Einstellungen enthalten, die an den Parameter outputs der Übergangsfunktion übergeben werden. Dies gilt auch dann, wenn eine Build-Einstellung im Verlauf der Umstellung nicht geändert wurde. Der ursprüngliche Wert muss explizit im zurückgegebenen Wörterbuch übergeben werden.

1:2+-Übergänge definieren

Beispiel für ein Ende zum Ende

Mit einem Edge-Edge-Übergang kann eine einzelne Eingabekonfiguration zwei oder mehr Ausgabekonfigurationen zugeordnet werden. Das ist hilfreich, wenn Sie Regeln definieren, die Code für mehrere Architekturen enthalten.

1:2-Übergänge werden definiert, indem in der Übergangsfunktion eine Liste von Wörterbüchern zurückgegeben wird.

# 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"]
)

Außerdem können sie benutzerdefinierte Schlüssel festlegen, mit denen die Funktion zur Regelimplementierung einzelne Abhängigkeiten zum Lesen lesen kann:

# 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"]
)

Übergänge anhängen

Beispiel für ein Ende zum Ende

Übergänge können an zwei Stellen befestigt werden: eingehende Kanten und ausgehende Kanten. Das bedeutet, dass Regeln ihre eigene Konfiguration (eingehender Edge-Übergang) und ihre Abhängigkeiten (#-Umstellung) durchführen können (ausgehender Edge-Übergang).

HINWEIS: Es ist derzeit nicht möglich, Starlark-Übergänge an native Regeln anzuhängen. Wenden Sie sich dazu an bazel-Diskussion@googlegroups.com, um Problemumgehungen zu finden.

Eingehende Edge-Übergänge

Eingehende Edge-Übergänge werden aktiviert, indem ein transition-Objekt, das von transition() erstellt wurde, an den Parameter rule() cfg angehängt wird:

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

Eingehende Edge-Übergänge müssen 1:1-Übergänge sein.

Kantenübergänge

Um ausgehende Edge-Übergänge zu aktivieren, hängen Sie ein von transition() erstelltes transition-Objekt an einen cfg-Parameter an:

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

Für ausgehende Edge-Übergänge kann 1:1 oder 1:2 erfolgen.

Informationen zum Lesen dieser Schlüssel finden Sie unter Attribute mit Übergängen aufrufen.

Übergänge für native Optionen

Beispiel für ein Ende zum Ende

Starlark-Übergänge können auch über ein spezielles Präfix des Optionsnamens Lese- und Schreibvorgänge in nativen Build-Konfigurationsoptionen deklarieren.

# 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"]

Nicht unterstützte native Optionen

Istio unterstützt die Umstellung auf --define mit "//command_line_option:define" nicht. Verwenden Sie stattdessen eine benutzerdefinierte Build-Einstellung. Im Allgemeinen empfehlen wir, keine neuen Nutzungen von --define zu verwenden, sondern stattdessen Build-Einstellungen zu nutzen.

Istio unterstützt die Umstellung auf --config nicht. Der Grund dafür ist, dass --config ein &Erweiterungs-Flag ist, das auf andere Flags erweitert wird.

Wichtig ist, dass --config Flags enthalten kann, die sich nicht auf die Build-Konfiguration auswirken, z. B. --spawn_strategy. Istio kann standardmäßig diese Flags an einzelne Ziele binden. Das bedeutet, dass sie nicht einheitlich umgestellt werden können.

Als Problemumgehung können Sie die Flags, die gegenüberder Konfiguration Ihrer Umstellung gehören, explizit auflisten. Hierfür muss die --configErweiterung an zwei Stellen beibehalten werden. Das ist eine bekannte Benutzeroberfläche.

Übergänge für das Zulassen mehrerer Build-Einstellungen

Wenn Sie Build-Einstellungen festlegen, die mehrere Werte zulassen, muss der Wert der Einstellung mit einer Liste festgelegt werden.

# 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"]
)

Null-Übergänge

Wenn ein Übergang {}, [] oder None zurückgibt, ist dies eine Abkürzung, um alle Einstellungen auf ihre ursprünglichen Werte beizubehalten. Dies kann praktischer sein, als jede Ausgabe explizit auf sich selbst zu setzen.

# 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",
    ]
)

Auf Attribute mit Übergängen zugreifen

Beispiel für ein Ende zum Ende

Beim Anhängen eines Übergangs an eine ausgehende Kante (unabhängig davon, ob der Übergang ein 1:1- oder 1:2+-Übergang ist) wird ctx.attr als Liste erzwungen, wenn sie bereits vorhanden ist. Die Reihenfolge der Elemente in dieser Liste ist nicht angegeben.

# 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)
    })

Wenn der Übergang 1:2+ ist und benutzerdefinierte Schlüssel festlegt, kann ctx.split_attr verwendet werden, um einzelne Deps für jeden Schlüssel zu lesen:

# 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)
    })

Vollständiges Beispiel

Einbindung in Plattformen und Toolchains

Viele native Flags wie --cpu und --crosstool_top beziehen sich auf die Auflösung der Toolchain. Zukünftig werden explizite Übergänge für diese Arten von Flags wahrscheinlich durch die Umstellung auf der Zielplattform ersetzt.

Hinweise zu Arbeitsspeicher und Leistung

Das Hinzufügen von Übergängen und daher neuen Konfigurationen zu Ihrem Build ist mit Kosten verbunden: größere Build-Grafiken, weniger verständliche Build-Grafiken und langsamere Builds. Es lohnt sich, diese Kosten bei der Verwendung von Übergängen in Ihren Build-Regeln zu berücksichtigen. Unten sehen Sie ein Beispiel dafür, wie ein Übergang zu einem exponentiellen Wachstum Ihrer Build-Grafik führen kann.

Builds mit schlechtem Verhalten: Fallstudie

Skalierbarkeitsdiagramm

Abbildung 1. Skalierbarkeitsdiagramm mit einem Ziel der obersten Ebene und den zugehörigen Abhängigkeiten

Diese Grafik zeigt ein Top-Level-Ziel: //pkg:app, das von zwei Zielen abhängt: //pkg:1_0 und //pkg:1_1. Beide Ziele hängen von zwei Zielen ab: //pkg:2_0 und //pkg:2_1. Beide Ziele hängen von zwei Zielen ab: //pkg:3_0 und //pkg:3_1. Dies setzt sich bis //pkg:n_0 und //pkg:n_1 fort, die beide auf einem einzelnen Ziel basieren, //pkg:dep.

Zum Erstellen von //pkg:app sind \(2n+2\) Ziele erforderlich:

  • //pkg:app
  • //pkg:dep
  • //pkg:i_0 und //pkg:i_1 für \(i\) in \([1..n]\)

Angenommen, Sie implementieren ein Flag, --//foo:owner=<STRING> und //pkg:i_b wird angewendet.

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

Mit anderen Worten: //pkg:i_b hängt b an den alten Wert von --owner für alle seine Dep.

Dabei werden die folgenden konfigurierten Ziele erzeugt:

//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 erzeugt \(2^n\) konfigurierte Ziele: config.owner= \(b_0b_1...b_n\)für alle \(b_i\) in \(\{0,1\}\).

Die Build-Grafik ist daher exponentiell größer als die Zielgrafik, was entsprechende Speicher- und Leistungsprobleme zur Folge hat.

TODO: Strategien zur Messung und Behebung dieser Probleme hinzufügen.

Weitere Informationen

Weitere Informationen zum Ändern von Build-Konfigurationen finden Sie unter: