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

Aspekte

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

Auf dieser Seite werden die Grundlagen und Vorteile der Verwendung von Aspekten beschrieben. Außerdem finden Sie hier einfache und erweiterte Beispiele.

Mit Aspekten können Sie Build-Abhängigkeitsdiagramme mit zusätzlichen Informationen und Maßnahmen erweitern. Hier sind einige typische Anwendungsfälle:

  • IDEs, die Istio einbinden, können Aspekte verwenden, um Informationen über das Projekt zu erfassen.
  • Mit Tools zur Codegenerierung können Sie Aspekte zur unabhängigen Ausführung Ihrer Eingaben nutzen. Beispiel: Mit BUILD-Dateien kann eine Hierarchie von Ю-Bibliotheksdefinitionen angegeben werden. Bei sprachspezifischen Regeln können Aktionen zum Anhängen von Aktionen verwendet werden, die Ю-Supportcode für eine bestimmte Sprache generieren.

Seitenverhältnis

BUILD-Dateien enthalten eine Beschreibung des Quellcodes eines Projekts: welche Quelldateien zum Projekt gehören, welche Artefakte (Ziele) aus diesen Dateien erstellt werden sollten, welche Abhängigkeiten zwischen diesen Dateien usw. sind erforderlich, um die Builds für die Erstellung der Artefakte (z. B. die Ausführung mit Compiler oder verlinken) zu ermitteln und diese Aktionen auszuführen. Dazu erstellt er eine Abhängigkeitsgrafik zwischen Zielen und ruft diese Grafik auf, um diese Aktionen zu erfassen.

Hier ein Beispiel für die Datei BUILD:

java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)

In dieser Datei BUILD ist ein Abhängigkeitsdiagramm definiert, das in der folgenden Abbildung dargestellt ist:

Grafik erstellen

Abbildung 1. Grafik zur BUILD-Dateiabhängigkeit.

Istio analysiert dieses Abhängigkeitsdiagramm, indem eine Implementierungsfunktion der entsprechenden Regel (in diesem Fall „java_library"“) für jedes Ziel im Beispiel oben aufgerufen wird. Funktionen zur Regelimplementierung generieren Aktionen, die Artefakte wie .jar-Dateien erstellen, und übergeben Informationen wie Standorte und Namen dieser Artefakte an die umgekehrten Abhängigkeiten dieser Ziele in Anbietern.

Facetten ähneln Regeln insofern, als sie eine Implementierungsfunktion haben, die Aktionen generiert und Anbieter zurückgibt. Die Grundlage für diese Methode bildet jedoch der Aufbau der Abhängigkeitsgrafik. Ein Aspekt hat eine Implementierung und eine Liste aller Attribute, die er weitergibt. Nehmen Sie einen Aspekt A an, der sich entlang von Attributen mit dem Namen „deps&det;“ verbreitet. Dieser Aspekt kann auf ein Ziel X angewendet werden, sodass ein Anwendungsknoten A(X) entsteht. Während seiner Anwendung wird Aspekt A rekursiv auf alle Ziele angewendet, auf die X sich im Attribut „deps“ bezieht (alle Attribute in der Verbreitungsliste von A&#39).

Aus diesem Grund wird ein einzelner Vorgang, bei dem Aspekte A auf ein Ziel X angewendet werden, zu einem Schattendiagramm des ursprünglichen Abhängigkeitsdiagramms der Ziele führt, wie in der folgenden Abbildung gezeigt:

Grafik mit Seitenverhältnis erstellen

Abbildung 2. Grafik mit Aspekten erstellen.

Die einzigen Kanten, die verdeckt werden, sind die Kanten entlang der Attribute im Umfang der Weitergabe. Daher ist runtime_deps in diesem Beispiel nicht verdeckt. Eine Funktion zur Facettenimplementierung wird dann für alle Knoten im Schattendiagramm aufgerufen, ähnlich wie bei Regelimplementierungen auf den Knoten des ursprünglichen Diagramms.

Einfaches Beispiel

In diesem Beispiel wird gezeigt, wie die Quelldateien rekursiv für eine Regel und alle zugehörigen Abhängigkeiten mit dem Attribut deps gedruckt werden. Sie sehen eine Implementierung des Attributs, eine Definition und den Aufruf von Seiten über die Istio-Befehlszeile.

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

Lassen Sie das Beispiel in mehrere Teile zerlegen und jedes einzeln prüfen.

Seitenverhältnisdefinition

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

Facettendefinitionen ähneln Regeldefinitionen und werden mit der Funktion aspect definiert.

Genau wie eine Regel hat ein Aspekt eine Implementierungsfunktion, in diesem Fall _print_aspect_impl.

attr_aspects ist eine Liste mit Regelattributen, über die der Aspekt weitergegeben wird. In diesem Fall wird der Aspekt im Attribut deps der Regeln angewendet, auf die er angewendet wird.

Ein weiteres allgemeines Argument für attr_aspects ist ['*']. Damit wird der Aspekt auf alle Attribute einer Regel angewendet.

Umsetzung von Aspekten

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

Die Funktionen zur Aspekte der Implementierung ähneln den Funktionen zur Regelimplementierung. Sie geben Anbieter zurück, können Aktionen generieren und zwei Argumente ausführen:

  • target: das Ziel, auf das der Aspekt angewendet wird.
  • ctx: ctx-Objekt, das für den Zugriff auf Attribute und zum Generieren von Ausgaben und Aktionen verwendet werden kann.

Die Implementierungsfunktion kann über ctx.rule.attr auf die Attribute der Zielregel zugreifen. Er kann Anbieter prüfen, die von dem Ziel bereitgestellt werden, auf das es angewendet wird (über das Argument target).

Es müssen bestimmte Aspekte zurückgegeben werden. In diesem Beispiel bietet der Aspekt nichts und gibt eine leere Liste zurück.

Aspekt über die Befehlszeile aufrufen

Die einfachste Möglichkeit, einen Aspekt anzuwenden, ist über die Befehlszeile mit dem Argument --aspects. Angenommen, der oben dargestellte Aspekt wurde in einer Datei mit dem Namen print.bzl definiert:

bazel build //MyExample:example --aspects print.bzl%print_aspect

wendet print_aspect auf das Ziel-example und alle Zielregeln an, die rekursiv über das Attribut deps zugänglich sind.

Das Flag --aspects verwendet ein Argument, das eine Spezifikation des Aspekts im Format <extension file label>%<aspect top-level name> ist.

Erweitertes Beispiel

Im folgenden Beispiel wird die Verwendung eines Aspekts aus einer Zielregel veranschaulicht, mit der Dateien in Zielen gezählt und möglicherweise nach Erweiterungen gefiltert werden. Sie erfahren, wie Sie einen Anbieter verwenden, um Werte zurückzugeben, mit einem Parameter ein Argument an eine Facettenimplementierung zu übergeben und einen Faktor aus einer Regel aufzurufen.

file_count.bzl-Datei:

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

BUILD.bazel-Datei:

load('//:file_count.bzl', 'file_count_rule')

cc_library(
    name = 'lib',
    srcs = [
        'lib.h',
        'lib.cc',
    ],
)

cc_binary(
    name = 'app',
    srcs = [
        'app.h',
        'app.cc',
        'main.cc',
    ],
    deps = ['lib'],
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

Seitenverhältnisdefinition

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

Dieses Beispiel zeigt, wie sich der Aspekt über das Attribut deps verbreitet.

attrs definiert eine Reihe von Attributen für einen Aspekt. Attribute des öffentlichen Formats haben den Typ string und werden als Parameter aufgerufen. Parameter müssen ein values-Attribut enthalten. In diesem Beispiel ist der Parameter extension enthalten, der '*', 'h' oder 'cc' als Wert enthalten darf.

Parameterwerte für den Aspekt werden dem String-Attribut mit dem Namen der Regel entnommen, die den Aspekt anfordert (siehe Definition von file_count_rule). Aspekte mit Parametern können nicht über die Befehlszeile verwendet werden, da es keine Syntax zum Definieren der Parameter gibt.

Attribute können auch private Attribute des Typs label oder label_list haben. Attribute von privaten Labels können verwendet werden, um Abhängigkeiten von Tools oder Bibliotheken anzugeben, die für Aktionen erforderlich sind, die durch bestimmte Aspekte generiert werden. In diesem Beispiel ist kein privates Attribut definiert. Im folgenden Code-Snippet wird jedoch gezeigt, wie ein Tool an einen Aspekt übergeben werden kann:

...
    attrs = {
        '_protoc' : attr.label(
            default = Label('//tools:protoc'),
            executable = True,
            cfg = "exec"
        )
    }
...

Umsetzung von Aspekten

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

Genau wie bei einer Regelimplementierungsfunktion gibt eine Facettenimplementierungsfunktion eine Struktur von Anbietern zurück, die auf ihre Abhängigkeiten zugreifen können.

In diesem Beispiel ist FileCountInfo als Anbieter mit einem Feld count definiert. Es empfiehlt sich, die Felder eines Anbieters mit dem Attribut fields explizit zu definieren.

Die Gruppe der Anbieter für die Facettenanwendung A(X) besteht aus einer Kombination von Anbietern, die sich aus der Implementierung einer Regel für Ziel X und aus der Implementierung von Aspekt A ergeben. Die Anbieter, die eine Regelimplementierung verbreitet, werden erstellt und eingefroren, bevor Aspekte angewendet werden. Sie können dann nicht mehr geändert werden. Ein Fehler tritt auf, wenn ein Ziel und ein Aspekt, die darauf angewendet werden, jeweils einen Anbieter desselben Typs bereitstellen. Die Ausnahmen sind OutputGroupInfo (der zusammengeführt wird, solange durch die Regel und der Aspekt unterschiedliche Ausgabegruppen angegeben werden) und InstrumentedFilesInfo (der vom Aspekt übernommen wird). Es kann also sein, dass für Implementierungen von Seiten nie DefaultInfo zurückgegeben wird.

Die Parameter und privaten Attribute werden in den Attributen des ctx übergeben. In diesem Beispiel wird auf den Parameter extension verwiesen und bestimmt, welche Dateien gezählt werden.

Bei wiederkehrenden Anbietern werden die Werte der Attribute, bei denen der Aspekte weitergegeben wird (aus der Liste attr_aspects), durch die Ergebnisse einer Anwendung für diesen Aspekt ersetzt. Wenn z. B. Ziel X auf Y und Z gesetzt ist, hat ctx.rule.attr.deps für A(X) den Wert [A(Y), A(Z)]. In diesem Beispiel sind ctx.rule.attr.deps Zielobjekte, die die Ergebnisse der Anwendung des Aspekts auf das Originalziel, auf das der Aspekt angewendet wurde, sind.

Im Beispiel wird der FileCountInfo aus den Abhängigkeiten des Ziels auf den FileCountInfo-Anbieter zugegriffen, um die Gesamtzahl der umgestellten Dateien zu ermitteln.

Faktor aus einer Regel aufrufen

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

Die Regelimplementierung zeigt, wie du über ctx.attr.deps auf FileCountInfo zugreifen kannst.

Die Regeldefinition zeigt, wie ein Parameter (extension) definiert und mit einem Standardwert (*) versehen wird. Beachten Sie, dass ein Standardwert, der nicht 'cc', 'h' oder '*' ist, aufgrund der Einschränkungen, die für den Parameter in der Seitenverhältnisdefinition gelten, ein Fehler ist.

Einen Faktor über eine Zielregel aufrufen

load('//:file_count.bzl', 'file_count_rule')

cc_binary(
    name = 'app',
...
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

Hier erfährst du, wie du den Parameter extension über die Regel an den Seitenverhältnis übergibst. Da der Parameter extension einen Standardwert in der Regelimplementierung hat, wird extension als optionaler Parameter betrachtet.

Beim Erstellen des Ziels file_count wird unser Aspekt selbst ausgewertet und alle Ziele werden rekursiv über deps erreicht.