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

Abhängigkeiten

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

Ein Ziel-A hängt von einem Ziel-B ab, wenn B bei der Build- oder Ausführungszeit von A benötigt wird. Die Abhängigkeitsbeziehung erzeugt einen gerichteten azyklischen Graphen (Directed Acyclic Graph, DAG) über Ziele und wird als Abhängigkeitsgrafik bezeichnet.

Die direkten Abhängigkeiten eines Ziels sind diejenigen, die über den Pfad der Länge 1 in der Abhängigkeitsgrafik erreichbar sind. Die transitiven Abhängigkeiten eines Ziels sind Ziele, auf denen es sich über einen Pfad beliebiger Länge durch das Diagramm auswirkt.

Im Kontext von Builds gibt es zwei Abhängigkeitsdiagramme, das Diagramm mit tatsächlichen Abhängigkeiten und das Diagramm mit deklarierten Abhängigkeiten. In den meisten Fällen sind die beiden Grafiken so ähnlich, dass diese Unterscheidung nicht nötig ist. Für die folgende Erläuterung ist sie jedoch nützlich.

Tatsächliche und deklarierte Abhängigkeiten

Ein Ziel-X ist tatsächlich abhängig von Ziel Y, wenn Y vorhanden sein muss, erstellt und auf dem neuesten Stand sein muss, damit X nicht richtig erstellt wurden. Gebaut kann das Generieren, Verarbeiten, Kompilieren, Verknüpfen, Archivieren, Komprimieren, Ausführen oder jede andere Art von Aufgaben darstellen, die während eines Build-Vorgangs regelmäßig ausgeführt werden.

Ein Ziel-X hat eine deklarierte Abhängigkeit von Ziel Y, wenn im Paket von X ein Abhängigkeitsrand von X zu Y vorhanden ist. aus.

Bei korrekten Builds muss die Grafik der tatsächlichen Abhängigkeiten A eine Teilgrafik des Diagramms der deklarierten Abhängigkeiten D sein. Das heißt, jedes Paar direkt verbundener Knoten x --> y in A muss auch direkt in D verbunden sein. Dabei kann man sagen, dass D eine Annäherung von A ist.

Dateiautoren vom Typ BUILD müssen für jede Regel explizit alle tatsächlichen direkten Abhängigkeiten für das Build-System deklarieren.

Das Nichtbeachten dieses Prinzips führt zu nicht definiertem Verhalten: Der Build kann fehlschlagen, aber schlimmer kann er von einigen vorherigen Vorgängen oder von vorübergehenden deklarierten Abhängigkeiten abhängen, die das Ziel hat. Bazel sucht nach fehlenden Abhängigkeiten und meldet Fehler, jedoch kann diese Überprüfung nicht in allen Fällen abgeschlossen werden.

Sie sollten nicht versuchen, alles indirekt importierte Listen aufzulisten, auch wenn sie von A bei der Ausführung erforderlich sind.

Bei einem Build des Ziels X prüft das Build-Tool die gesamte transitive Schließung der Abhängigkeiten von X, um sicherzustellen, dass alle Änderungen an diesen Zielen im Endergebnis widergespiegelt werden, sodass Zwischenversionen neu erstellt werden. aus.

Die transitive Natur von Abhängigkeiten führt zu einem häufigen Fehler. Es kann vorkommen, dass Code in einer Datei Code verwendet, der von einer indirekten Abhängigkeit bereitgestellt wird – ein transitiver, aber keiner direkten Edge-Vorgang in der deklarierten Abhängigkeitsgrafik. Indirekte Abhängigkeiten werden in der Datei BUILD nicht angezeigt. Da die Regel nicht direkt vom Anbieter abhängt, gibt es keine Möglichkeit, Änderungen zu verfolgen, wie in der folgenden Beispielzeitachse gezeigt:

1. Angegebene Abhängigkeiten stimmen mit tatsächlichen Abhängigkeiten überein

Zuerst funktioniert alles. Der Code im Paket a verwendet den Code im Paket b. Der Code im Paket b verwendet Code im Paket c. Daher ist a transitiv von c abhängig.

a/BUILD b/BUILD
rule(
    name = "a",
    srcs = "a.in",
    deps = "//b:b",
)
      
rule(
    name = "b",
    srcs = "b.in",
    deps = "//c:c",
)
      
a / a.in b / b.in
import b;
b.foo();
    
import c;
function foo() {
  c.bar();
}
      
Angegebene Abhängigkeitsgrafik mit Pfeilen, die a, b und c verbinden
Deklarierte Abhängigkeitsgrafik
Tatsächliche Abhängigkeitsgrafik, die der deklarierten Abhängigkeitsgrafik mit Pfeilen entspricht, die a, b und c verbinden
Tatsächliche Abhängigkeitsgrafik

Die deklarierten Abhängigkeiten entsprechen den tatsächlichen Abhängigkeiten. Alles in Ordnung.

2. Nicht angegebene Abhängigkeit hinzufügen

Eine latente Gefahr entsteht, wenn jemand Code zu a hinzufügt, der eine direkte tatsächliche Abhängigkeit von c erzeugt, aber vergessen, diese in der Build-Datei zu deklarieren.a/BUILD.

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
Angegebene Abhängigkeitsgrafik mit Pfeilen, die a, b und c verbinden
Deklarierte Abhängigkeitsgrafik
Diagramm der tatsächlichen Abhängigkeit mit Pfeilen, die a, b und c verbinden Ein Pfeil verbindet jetzt auch A mit C. Entspricht nicht der deklarierten Abhängigkeitsgrafik
Tatsächliche Abhängigkeitsgrafik

Die deklarierten Abhängigkeiten übersteigen die tatsächlichen Abhängigkeiten nicht mehr. Dies kann in Ordnung sein, da die transitiven Schließungen der beiden Grafiken gleich sind, jedoch ein Problem maskiert: a hat eine tatsächliche, aber nicht deklarierte Abhängigkeit von c.

3. Divergenz zwischen deklarierten und tatsächlichen Abhängigkeitsdiagrammen

Die Gefahr wird entdeckt, wenn jemand b refaktoriert, sodass er nicht mehr von c abhängt, wodurch a versehentlich und ohne eigenes Verschulden durchbrochen wird.

  b/BUILD
 
rule(
    name = "b",
    srcs = "b.in",
    deps = "//d:d",
)
      
  b / b.in
 
      import d;
      function foo() {
        d.baz();
      }
      
Angegebene Abhängigkeitsgrafik mit Pfeilen, die a und b verbinden.
                  b verbindet sich nicht mehr mit c, wodurch eine a mit c getrennt wird
Deklarierte Abhängigkeitsgrafik
Tatsächliche Abhängigkeitsgrafik, die eine Verbindung zu b und c zeigt, aber b stellt keine Verbindung zu c mehr her
Tatsächliche Abhängigkeitsgrafik

Die deklarierte Abhängigkeitsgrafik stellt jetzt eine Annäherung an die tatsächlichen Abhängigkeiten dar, auch wenn sie vorübergehend geschlossen ist. schlägt der Build wahrscheinlich fehl.

Das Problem könnte dadurch verhindert werden, dass die in Schritt 2 eingeführte tatsächliche Abhängigkeit von a zu c in der BUILD-Datei richtig deklariert wurde.

Arten von Abhängigkeiten

Die meisten Build-Regeln haben drei Attribute, um unterschiedliche Arten von allgemeinen Abhängigkeiten anzugeben: srcs, deps und data. Diese werden unten erläutert. Weitere Informationen finden Sie unter Attribute für alle Regeln.

Viele Regeln haben auch zusätzliche Attribute für regelspezifische Arten von Abhängigkeiten, z. B. compiler oder resources. Diese werden in der Build-Enzyklopädie ausführlich beschrieben.

srcs Abhängigkeiten

Dateien, die direkt von den Regeln ausgegeben werden, die Quelldateien ausgeben.

deps Abhängigkeiten

Regel, die auf separat kompilierte Module verweist, die Header-Dateien, Symbole, Bibliotheken, Daten usw. bereitstellen

data Abhängigkeiten

Ein Build-Ziel benötigt möglicherweise einige Datendateien, um korrekt ausgeführt zu werden. Diese Datendateien sind kein Quellcode und haben keinen Einfluss auf die Erstellung des Ziels. Ein Einheitentest kann beispielsweise die Ausgabe einer Funktion mit dem Inhalt einer Datei vergleichen. Wenn Sie den Einheitentest erstellen, benötigen Sie die Datei nicht. Sie benötigen sie aber, wenn Sie den Test ausführen. Dasselbe gilt für Tools, die während der Ausführung gestartet werden.

Das Build-System führt Tests in einem isolierten Verzeichnis aus, in dem nur Dateien als data verfügbar sind. Wenn also eine Binärdatei, eine Bibliothek oder ein Test bestimmte Dateien ausführen muss, geben Sie sie (oder eine Build-Regel, die sie enthält) in data an. Beispiel:

# I need a config file from a directory named env:
java_binary(
    name = "setenv",
    ...
    data = [":env/default_env.txt"],
)

# I need test data from another directory
sh_test(
    name = "regtest",
    srcs = ["regtest.sh"],
    data = [
        "//data:file1.txt",
        "//data:file2.txt",
        ...
    ],
)

Diese Dateien sind über den relativen Pfad path/to/data/file verfügbar. In Tests können Sie auf diese Dateien verweisen, indem Sie die Pfade des Quellverzeichnisses und des relativen Pfads des Arbeitsbereichs zusammenführen, z. B. ${TEST_SRCDIR}/workspace/path/to/data/file.

Mit Labels auf Referenzverzeichnisse verweisen

Wenn Sie sich unsere BUILD-Dateien ansehen, werden Sie möglicherweise feststellen, dass sich einige data-Labels auf Verzeichnisse beziehen. Diese Labels enden mit /. oder / wie die folgenden, die Sie nicht verwenden sollten:

Nicht empfohlen: data = ["//data/regression:unittest/."]

Nicht empfohlen: data = ["testdata/."]

Nicht empfohlen: data = ["testdata/"]

Dies scheint praktisch zu sein, insbesondere für Tests, da ein Test alle Datendateien im Verzeichnis verwenden kann.

Aber probieren Sie das nicht. Das Build-System muss den vollständigen Satz von Dateien kennen, die Eingaben (oder Tests) enthalten, um sicherzustellen, dass die inkrementellen Neuerstellungen (und die erneute Ausführung von Tests) nach einer Änderung korrekt sind. Wenn Sie ein Verzeichnis angeben, führt das Build-System eine Neuerstellung nur aus, wenn sich das Verzeichnis selbst ändert (durch das Hinzufügen oder Löschen von Dateien), es kann jedoch keine Änderungen an einzelnen Dateien erkennen als haben diese Änderungen keine Auswirkungen auf das einschließende Verzeichnis. Anstatt Verzeichnisse als Eingaben für das Build-System anzugeben, sollten Sie die darin enthaltenen Dateien entweder explizit oder mithilfe der Funktion glob() auflisten. (Erzwingen Sie mit **, dass glob() rekursiv ist.)

Empfohlendata = glob(["testdata/**"])

Leider gibt es einige Szenarien, in denen Verzeichnislabels verwendet werden müssen. Wenn das Verzeichnis testdata beispielsweise Dateien enthält, deren Namen nicht der Labelsyntax entsprechen, dann wird die Datei explizit aufgelistet oder die Verwendung der glob()-Funktion erzeugt einen ungültigen Labelfehler. In diesem Fall müssen Sie Verzeichnislabels verwenden. Beachten Sie jedoch das Risiko fehlerhafter Neuerstellungen wie oben beschrieben.

Wenn Sie Verzeichnislabels verwenden müssen, können Sie nicht mit einem relativen ../-Pfad auf das übergeordnete Paket verweisen. Verwenden Sie stattdessen einen absoluten Pfad wie //data/regression:unittest/..

Alle externen Regeln, z. B. ein Test, die mehrere Dateien verwenden müssen, müssen explizit die Abhängigkeit von allen Dateien deklarieren. Sie können filegroup() verwenden, um Dateien in der Datei BUILD zu gruppieren:

filegroup(
        name = 'my_data',
        srcs = glob(['my_unittest_data/*'])
)

Anschließend können Sie auf das Label my_data als Datenabhängigkeit in Ihrem Test verweisen.

BUILD-Dateien Sichtbarkeit