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

Mit externen Abhängigkeiten arbeiten

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

Bazel kann von Zielen aus anderen Projekten abhängen. Abhängigkeiten von diesen anderen Projekten werden als externe Abhängigkeiten bezeichnet.

Die Datei WORKSPACE (oder WORKSPACE.bazel-Datei) im Arbeitsbereichsverzeichnis teilt Bazel mit, wie andere Projekte abgerufen werden können. Diese anderen Projekte können eine oder mehrere BUILD-Dateien mit eigenen Zielen enthalten. BUILD-Dateien im Hauptprojekt können von diesen externen Zielen abhängen, indem sie ihren Namen aus der Datei WORKSPACE verwenden.

Beispiel: Auf einem System gibt es zwei Projekte:

/
  home/
    user/
      project1/
        WORKSPACE
        BUILD
        srcs/
          ...
      project2/
        WORKSPACE
        BUILD
        my-libs/

Wenn project1 von einem Ziel :foo, definiert in /home/user/project2/BUILD, abhängen würde, könnte es angeben, dass ein Repository namens project2 unter /home/user/project2 gefunden werden könnte. Dann können Ziele in /home/user/project1/BUILD von @project2//:foo abhängen.

Mit der Datei WORKSPACE können sich Nutzer auf Ziele aus anderen Teilen des Dateisystems verlassen oder aus dem Internet herunterladen. Sie verwendet dieselbe Syntax wie BUILD-Dateien, lässt jedoch einen anderen Satz von Regeln namens Repository-Regeln zu (manchmal auch als Arbeitsbereichregeln bezeichnet). Bazel bietet einige integrierte Repository-Regeln und eine Reihe von eingebetteten Starlark-Repository-Regeln. Nutzer können auch benutzerdefinierte Repository-Regeln schreiben, um ein komplexeres Verhalten zu erzielen.

Unterstützte Typen von externen Abhängigkeiten

Es gibt einige grundlegende Arten von externen Abhängigkeiten:

Abhängig von anderen Bazel-Projekten

Wenn Sie Ziele aus einem zweiten Bazel-Projekt verwenden möchten, können Sie diese mit local_repository, git_repository oder http_archive aus dem lokalen Dateisystem symlinken, auf ein Git-Repository verweisen oder das Projekt herunterladen.

Angenommen, Sie arbeiten an einem Projekt (my-project/) und möchten von den Zielen aus dem Projekt Ihres Kollegen coworkers-project/ abhängig sein. Bei beiden Projekten wird Bazel verwendet, sodass Sie das Projekt Ihres Kollegen als externe Abhängigkeit hinzufügen und dann Ziele festlegen können, die Ihr Kollege aus Ihren eigenen BUILD-Dateien definiert hat. In diesem Fall fügen Sie my_project/WORKSPACE Folgendes hinzu:

local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
)

Wenn Ihr Kollege das Ziel //foo:bar hat, kann es von Ihrem Projekt als @coworkers_project//foo:bar bezeichnet werden. Namen externer Projekte müssen gültige Arbeitsbereichsnamen sein.

Abhängig von Nicht-Bazel-Projekten

Mit Regeln mit dem Präfix new_, z. B. new_local_repository, können Sie Ziele aus Projekten erstellen, die Bazel nicht verwenden.

Angenommen, Sie arbeiten an einem Projekt (my-project/) und möchten sich auf das Projekt Ihres Kollegen coworkers-project/ beziehen. Für das Projekt Ihres Kollegen wird make zum Erstellen verwendet, aber Sie möchten sich auf eine der von ihm generierten SO-Dateien verlassen. Fügen Sie dazu Folgendes zu my_project/WORKSPACE hinzu:

new_local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
    build_file = "coworker.BUILD",
)

build_file gibt eine BUILD-Datei an, die auf das vorhandene Projekt angewendet werden soll. Beispiel:

cc_library(
    name = "some-lib",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

Sie können sich dann auf @coworkers_project//:some-lib aus den BUILD-Dateien Ihres Projekts verlassen.

Abhängig von externen Paketen

Maven-Artefakte und Repositories

Verwenden Sie den Regelsatz rules_jvm_external, um Artefakte aus Maven-Repositories herunterzuladen und als Java-Abhängigkeiten verfügbar zu machen.

Abhängigkeiten abrufen

Externe Abhängigkeiten werden während bazel build standardmäßig nach Bedarf abgerufen. Wenn Sie die für einen bestimmten Satz von Zielen erforderlichen Abhängigkeiten vorab abrufen möchten, verwenden Sie bazel fetch. Verwenden Sie bazel sync, um alle externen Abhängigkeiten bedingungslos abzurufen. Da abgerufene Repositories in der Ausgabebasis gespeichert werden, erfolgt der Abruf pro Arbeitsbereich.

Abhängigkeiten abschirmen

Wir empfehlen, in Ihrem Projekt nach Möglichkeit eine Richtlinie für eine einzelne Version zu verwenden. Dies ist für Abhängigkeiten erforderlich, die Sie kompilieren und in Ihrer endgültigen Binärdatei enden. In Fällen, in denen dies jedoch nicht der Fall ist, können Abhängigkeiten verdeckt werden. Stellen Sie sich folgendes Szenario vor:

meinprojekt/WORKSPACE

workspace(name = "myproject")

local_repository(
    name = "A",
    path = "../A",
)
local_repository(
    name = "B",
    path = "../B",
)

A/WORKSPACE

workspace(name = "A")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "...",
)

B/Arbeitsbereich

workspace(name = "B")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)

Beide Abhängigkeiten A und B hängen von testrunner ab, hängen jedoch von verschiedenen testrunner-Versionen ab. Es gibt keinen Grund, warum diese Testläufer nicht friedlich in myproject vorhanden sind. Sie treten jedoch aufeinander, da sie denselben Namen haben. Aktualisieren Sie myproject/WORKSPACE, um beide Abhängigkeiten zu deklarieren:

workspace(name = "myproject")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner-v1",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "..."
)
http_archive(
    name = "testrunner-v2",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)
local_repository(
    name = "A",
    path = "../A",
    repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
    name = "B",
    path = "../B",
    repo_mapping = {"@testrunner" : "@testrunner-v2"}
)

Dieser Mechanismus kann auch zum Verbinden von Diamanten verwendet werden. Wenn beispielsweise A und B dieselbe Abhängigkeit hatten, aber mit unterschiedlichen Namen aufrufen, können diese Abhängigkeiten in myproject/WORKSPACE zusammengeführt werden.

Repositories über die Befehlszeile überschreiben

Verwenden Sie das Flag --override_repository, um ein deklariertes Repository mit einem lokalen Repository über die Befehlszeile zu überschreiben. Mit diesem Flag wird der Inhalt externer Repositories geändert, ohne den Quellcode zu ändern.

Wenn Sie beispielsweise @foo durch das lokale Verzeichnis /path/to/local/foo überschreiben möchten, übergeben Sie das Flag --override_repository=foo=/path/to/local/foo.

Beispiele für Anwendungsfälle sind:

  • Fehlerbehebung Sie können beispielsweise ein http_archive-Repository in einem lokalen Verzeichnis überschreiben, in dem Sie Änderungen einfacher vornehmen können.
  • Anbieter. Wenn Sie sich in einer Umgebung befinden, in der Sie keine Netzwerkaufrufe durchführen können, überschreiben Sie die netzwerkbasierten Repository-Regeln so, dass sie stattdessen auf lokale Verzeichnisse verweisen.

Proxys verwenden

Bazel ruft Proxyadressen aus den Umgebungsvariablen HTTPS_PROXY und HTTP_PROXY ab und verwendet diese, um HTTP-/HTTPS-Dateien herunterzuladen (falls angegeben).

Unterstützung für IPv6

Auf reinen IPv6-Maschinen kann Bazel Abhängigkeiten ohne Änderungen herunterladen. Bei Dual-Stack-IPv4-/IPv6-Maschinen folgt Bazel jedoch derselben Konvention wie Java: Wenn IPv4 aktiviert ist, wird IPv4 bevorzugt. In einigen Situationen, z. B. wenn das IPv4-Netzwerk keine externen Adressen auflösen oder erreichen kann, kann das zu Network unreachable-Ausnahmen und Build-Fehlern führen. In diesen Fällen können Sie das Verhalten von Bazel überschreiben und IPv6 mit dem java.net.preferIPv6Addresses=true-Systemattribut bevorzugen. Insbesondere:

  • Verwenden Sie die Startoption --host_jvm_args=-Djava.net.preferIPv6Addresses=true, indem Sie beispielsweise die folgende Zeile in Ihre .bazelrc-Datei einfügen:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • Wenn du Java-Build-Ziele ausführst, die auch mit dem Internet verbunden werden müssen (Integrationstests brauchen das manchmal), verwende ebenfalls das Tool-Flag --jvmopt=-Djava.net.preferIPv6Addresses=true, z. B. mit der folgenden Zeile in deiner .bazelrc-Datei:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • Wenn Sie beispielsweise rules_jvm_external für die Abhängigkeit von Versionsauflösung verwenden, fügen Sie -Djava.net.preferIPv6Addresses=true auch in die Umgebungsvariable COURSIER_OPTS ein, um JVM-Optionen für Coursier bereitzustellen.

Transitive Abhängigkeiten

Bazel liest nur Abhängigkeiten, die in Ihrer WORKSPACE-Datei aufgeführt sind. Wenn Ihr Projekt (A) von einem anderen Projekt (B) abhängig ist, das eine Abhängigkeit von einem dritten Projekt (C) in seiner WORKSPACE-Datei auflistet, müssen Sie sowohl B als auch C zur WORKSPACE-Datei Ihres Projekts hinzufügen. Diese Anforderung kann die Dateigröße WORKSPACE erhöhen. Allerdings ist die Wahrscheinlichkeit begrenzt, dass eine Bibliothek C mit Version 1.0 und eine andere C mit 2.0 enthält.

Caching externer Abhängigkeiten

Standardmäßig lädt Bazel externe Abhängigkeiten nur noch einmal herunter, wenn sich ihre Definition ändert. Änderungen an Dateien, auf die in der Definition verwiesen wird, z. B. Patches oder BUILD-Dateien, werden auch von Bazel berücksichtigt.

Mit bazel sync können Sie ein erneutes Herunterladen erzwingen.

Layout

Externe Abhängigkeiten werden alle in ein Verzeichnis unter dem Unterverzeichnis external in der Ausgabebasis heruntergeladen. Bei einem lokalen Repository wird ein symlink statt eines neuen Verzeichnisses erstellt. Sie können das Verzeichnis external mit folgendem Befehl aufrufen:

ls $(bazel info output_base)/external

Durch die Ausführung von bazel clean wird das externe Verzeichnis nicht gelöscht. Verwenden Sie bazel clean --expunge, um alle externen Artefakte zu entfernen.

Offline-Builds

Manchmal ist es wünschenswert oder notwendig, einen Build offline auszuführen. Für einfache Anwendungsfälle wie das Reisen in einem Flugzeug kann ein Vorabruf der erforderlichen Repositories mit bazel fetch oder bazel sync ausreichen. Über die Option --nofetch kann außerdem das Abrufen weiterer Repositories während des Builds deaktiviert werden.

Bei echten Offline-Builds, bei denen die Bereitstellung der erforderlichen Dateien von einer anderen Entität als Bazel erfolgt, unterstützt Bazel die Option --distdir. Wenn eine Repository-Regel Bazel anfordert, eine Datei über ctx.download oder ctx.download_and_extract abzurufen, und eine Hash-Summe der erforderlichen Datei bereitstellt, prüft Bazel zuerst die von dieser Option angegebenen Verzeichnisse für eine Datei, die dem Basisnamen der angegebenen URL entspricht, und verwendet diese lokale Kopie, wenn der Hash übereinstimmt.

Bazel selbst verwendet diese Technik, um ein Bootstrap offline über das Distributionsartefakt durchzuführen. Dazu werden alle erforderlichen externen Abhängigkeiten in einem internen distdir_tar erfasst. Weitere Informationen

Mit Bazel können jedoch beliebige Befehle in Repository-Regeln ausgeführt werden, ohne zu wissen, ob sie das Netzwerk aufrufen. Bazel hat daher keine Option, um Builds vollständig offline zu erzwingen. Wenn also getestet werden soll, ob ein Build korrekt offline funktioniert, ist eine externe Blockierung des Netzwerks erforderlich, wie es beim Bootstrap-Test der Fall ist.

Best Practices

Repository-Regeln

In der Regel ist eine Repository-Regel für Folgendes verantwortlich:

  • Systemeinstellungen werden erkannt und in Dateien geschrieben.
  • Ressourcen an anderer Stelle im System finden
  • Ressourcen werden von URLs heruntergeladen.
  • BUILD-Dateien werden im externen Repository-Verzeichnis generiert oder symlinkiert.

Vermeiden Sie nach Möglichkeit repository_ctx.execute. Wenn Sie beispielsweise eine Nicht-Bazel-C++-Bibliothek mit einem Build mit Make verwenden, ist es besser, repository_ctx.download() zu verwenden und dann eine BUILD-Datei zu schreiben, die sie erstellt, anstatt ctx.execute(["make"]) auszuführen.

Ich bevorzuge http_archive zu git_repository und new_git_repository. Hierfür gibt es folgende Gründe:

  • Git-Repository-Regeln hängen vom System git(1) ab, während der HTTP-Downloader in Bazel eingebunden ist und keine Systemabhängigkeiten hat.
  • http_archive unterstützt eine Liste von urls als Spiegel und git_repository nur ein einzelnes remote.
  • http_archive funktioniert mit dem Repository-Cache, aber nicht mit git_repository. Weitere Informationen finden Sie unter #5116.

Nicht bind() verwenden. Unter Entfernen von Bind finden Sie ausführliche Informationen zu den Problemen und Alternativen.