- Buck2, unser neues Open-Source-Build-System im großen Maßstabist jetzt auf GitHub verfügbar.
- Buck2 ist ein erweiterbares und leistungsstarkes Build-System, das in Rust geschrieben wurde und darauf ausgelegt ist, Ihr Build-Erlebnis schneller und effizienter zu machen.
- Bei unseren internen Tests bei Meta haben wir festgestellt, dass Buck2 Builds doppelt so schnell abschloss wie Buck1.
Buck2, das Open-Source-Großbausystem von Meta, ist jetzt über das öffentlich verfügbar Buck2-Website Und der Buck2 GitHub-Repository. Während es einige Gemeinsamkeiten mit anderen Build-Systemen (wie Buck1 und Bazel) aufweist, handelt es sich bei Buck2 um eine Neufassung von Grund auf. Buck2 bietet eine vollständige Trennung der Kern- und sprachspezifischen Regeln mit erhöhter Parallelität, Integration mit Remote-Ausführung und virtuellen Dateisystemen sowie eine neu gestaltete Konsolenausgabe. Alle diese Änderungen zielen darauf ab, Ingenieuren und Entwicklern zu helfen, weniger Zeit mit Warten zu verbringen und mehr Zeit mit der Iteration ihres Codes zu verbringen.
Tausende Entwickler bei Meta verwenden bereits Buck2 und führen täglich Millionen von Builds durch, wobei Builds doppelt so schnell abgeschlossen werden wie mit Buck1. Unsere eigene interne Analyse hat gezeigt, dass Ingenieure deutlich mehr Code produzieren konnten, wenn ihre Builds von Buck2 ausgeführt wurden, und wir hoffen, dass auch die gesamte Branche davon profitieren wird.
Warum Buck neu aufbauen?
Build-Systeme stehen zwischen einem Programmierer und der Ausführung seines Codes. Daher wirkt sich alles, was wir tun können, um die Erfahrung schneller oder produktiver zu machen, direkt darauf aus, wie effektiv ein Entwickler sein kann. Das Ziel von Buck2 bestand darin, das beizubehalten, was uns an Buck1 gefallen hat (die Kernkonzepte und Arbeitsabläufe), und uns von Innovationen nach Buck1 inspirieren zu lassen (einschließlich Bazel, AdaptonUnd Shake) und konzentrieren Sie sich auf Geschwindigkeit und die Ermöglichung neuer Erfahrungen.
Das Design von Buck2 basiert auf den folgenden Prinzipien:
- Das Kern-Build-System kennt keine sprachspezifischen Regeln. Die Trennung der Regeln vom Kern bedeutet, dass die Regeln leichter zu ändern und zu verstehen sind. Der Kern von Buck2 ist in Rust geschrieben und seine Sprachregeln (z. B. wie man C++ erstellt) sind darin geschrieben Sternenlerche. Diese Trennung steht im Gegensatz zu Buck1 (wo alle Regeln im Kern geschrieben sind) und Bazel (wo C++/Java im Kern geschrieben ist).
- Das Build-System wird von a angetrieben einzelnes inkrementelles Abhängigkeitsdiagramm, wobei jegliche Phasen vermieden werden (im Gegensatz zu Buck1 oder Bazel). Diese Entscheidung beseitigt viele Arten von Fehlern und erhöht die Parallelität.
- Die Regel-API ist so konzipiert, dass sie erweiterte Funktionen für die Leistung enthält, zusammen mit dynamischen (oder monadischen) Abhängigkeitsmerkmalen für die Ausdrucksfähigkeit. Gleichzeitig werden diese Funktionen sorgfältig eingeschränkt, um sicherzustellen, dass andere Eigenschaften (z. B. schnelle Abfragen oder Hermetik) nicht beeinträchtigt werden.
- Die Open-Source-Version ist nahezu identisch mit unserer internen Version. Die einzigen Teile, die ausgetauscht wurden, sind die Toolchains (die auf die internen Kopien unserer Compiler verweisen) und die Remote-Ausführung (die auf unsere internen Server verweist). — Beide verfügen über Open-Source-Alternativen. Darüber hinaus veröffentlichen wir alle Regeln genau so, wie sie intern verwendet werden. Darüber hinaus haben wir einige der logischen Komponenten in separate Kisten aufgeteilt (z. B Sternenlerche, Superkonsole, Allokativ, Pavillon), sodass sie außerhalb von Buck2 verwendet werden können.
- Buck2 ist für die Integration mit der Remote-Ausführung geschrieben, mit der Möglichkeit, Aktionen auf Remote-Computern auszuführen. Wir verwenden das Gleiche API als Bazelund haben die Remote-Ausführung mit getestet Bauscheune Und EngFlow. Obwohl dies nicht erforderlich ist (und für Leute, die mit der Open-Source-Version beginnen, auch nicht wirklich erwartet wird), sind wir in der Lage, rekursive Digests effizient zu berechnen und sie effizient an die Remote-Ausführung zu senden.
- Buck2 ist für die Integration in virtuelle Dateisysteme geschrieben, wobei nicht das gesamte Repository vollständig ausgecheckt, sondern bei Bedarf abgerufen wird, wenn auf die Dateien zugegriffen wird. Insbesondere unterstützen wir Sapling-basierte Dateisysteme. Für eine gute Integration achten wir auf Dateibenachrichtigungen (mit Wächter) und fordern sowohl Dateien als auch Dateiauszüge ohne direkte Dateioperationen an. Der Vorteil besteht darin, dass wir virtuelle Dateisysteme so schnell wie ein vollständiges Auschecken erstellen können, jedoch mit den Vorteilen eines viel schnelleren Auscheckens und einer viel geringeren Festplattennutzung.
Die wichtigste Erkenntnis aus all diesen Verbesserungen ist, dass wir Buck2 so konzipiert haben, dass es schnell ist. Im realen Einsatz ist Buck2 je nach Build deutlich schneller als Buck1. Wenn keine Änderungen am Quellcode vorgenommen werden, ist Buck2 bei nachfolgenden Builds fast sofort verfügbar. Wenn viel Arbeit zu erledigen ist, beginnt Buck2 mit der schnelleren Ausführung und weist eine größere Parallelität auf. Diese Geschwindigkeitssteigerung ist sowohl eine Folge vieler der oben genannten Faktoren als auch der Sorgfalt und Aufmerksamkeit.
Die Benutzeransicht
Für Endbenutzer funktioniert Buck2 größtenteils genauso wie Buck1 (das in erster Näherung Bazel ziemlich ähnlich ist). Ein Benutzer definiert Ziele in einem BOCK Datei
Ein Benutzer kann dann damit bauen buck2-Build //:my_binary. Der Wert main.rs ist eine Quelldatei und :meine Bibliothek ist eine darin definierte Abhängigkeit BOCK Datei. Es ist erwähnenswert, dass Buck2 größtenteils mit dem kompatibel ist BOCK Dateien von Buck1.
Neben der Geschwindigkeitssteigerung gibt es im Vergleich zu Buck1 zwei weitere, für den Benutzer sichtbare, wesentliche Unterschiede.
Erstens wurde die Konsolenausgabe darüber hinaus neu gestaltet Superkonsolenbibliothek, das wir speziell für Buck2 entwickelt haben. Die Konsole zeigt ein paar weitere Details und fühlt sich viel angenehmer an:
Zweitens gibt es einen persistenten Daemon, der ein einzelnes Abhängigkeitsdiagramm verwaltet. Wenn Sie a ändern BOCK Wenn Sie eine Datei, eine Abhängigkeit oder eine Quelldatei erstellen, machen wir die entsprechenden Dinge im Abhängigkeitsdiagramm ungültig und fordern dann die Ausgabeartefakte über die Befehlszeile an. In Buck1 gibt es mehrere unterschiedliche Abhängigkeitsdiagramme, die zu Phasen wie der Zieldiagrammkonstruktion, der Aktionsdiagrammkonstruktion und der anschließenden Aktionsdiagrammausführung führen. Es gibt auch einige Operationen, die nicht im Diagramm ausgeführt werden. Wenn sich in Buck1 bestimmte Dinge ändern, werden ganze Diagramme verworfen und nicht nur die minimalen Teile ungültig. Mit einem einzigen Abhängigkeitsdiagramm ist Buck2 einfacher, vermeidet mehr redundante Arbeit und vermeidet explizite Phasen. Alles im Abhängigkeitsdiagramm hat einen Schlüssel (wie er identifiziert wird) und einen Wert sowie eine Funktion zur Berechnung des Werts aus dem Schlüssel und anderen zugehörigen Schlüsseln (nach dem Modell im Artikel: „„Systeme à la carte bauen“).
Die Ansicht des Regelautors
Während sich das Benutzermodell sehr stark an Buck1 orientiert, ist der Ansatz für Regeln völlig anders. In Buck zum Beispiel gibt es viele Regeln rust_binary oben verwendet. Während eine Regel in Buck1 eine in Buck1 integrierte Java-Klasse war, ist eine Regel in Buck2 vollständig entkoppelt. Buck2 wird außerdem mit einem „Vorspiel“ von Regeln geliefert, die die meisten Buck1-Regeln implementieren.
Die Buck1-Regeln wurden im Laufe der Zeit optimiert, verfügten über zahlreiche Leistungsoptimierungen und leistungsstarke Funktionen wie das Durchlaufen von Graphen, aber von diesen Regeln wurde auch erwartet, dass sie vielen komplexen Invarianten gehorchen—manchmal gegen diese Regeln verstoßen. Für Buck2 ist die Regel-API vollständig in Starlark, was uns dazu zwang, diese Funktionen als generisch wiederverwendbare APIs zu abstrahieren, um sie sicher, ausdrucksstark und leistungsfähig zu machen—eine schwierige Balance. Wir werden zwei solcher Beispiele ansprechen.
OCaml-Abhängigkeiten
Die Abhängigkeitsstruktur der OCaml-Bibliothek lässt sich in Buck1 nur schwer ausdrücken. Eine OCaml-Bibliothek besteht aus einer Reihe von OCaml-Dateien. Diese müssen in Abhängigkeitsreihenfolge kompiliert werden—also wenn A.ml Verwendet B.mlmüssen Sie kompilieren B.ml Erste. Bazel erfordert die Abhängigkeit von A.ml An B.ml explizit in die geschrieben werden BOCK Datei. Sowohl Buck1 als auch Buck2 lassen diese interne Abhängigkeit implizit und führen das Tool aus ocamldep um darauf zu schließen, was weniger Wartung erfordert, wenn sich die Struktur ändert. Buck1 ist gelaufen ocamldep kurz nach dem Parsen der BOCK Datei, was nicht wirklich erlaubt war, und Abhängigkeiten wurden nicht verfolgt. Wenn Sie also die Importe zu sehr änderten, verursachte Buck1 falsche Kompilierungsfehler. Mit Buck2 können wir das verwenden neues Primitiv dynamische_Ausgabemit dem Sie einen Befehl ausführen, die Ausgabe der Datei lesen und dann den Rest des Diagramms vernetzen können—Einfügen der richtigen Abhängigkeiten zwischen den .ml Dateien automatisch.
C++-Linkabhängigkeiten
Betrachten Sie das C++-Verknüpfungsmodell: Um eine Bibliothek zu erstellen, müssen Sie normalerweise ihre Build-Ausgabe zusammen mit dem transitiven Abschluss der Build-Ausgabe ihrer Abhängigkeiten verknüpfen. Wenn Sie einfach den Satz von Abhängigkeiten auf jeder Ebene duplizieren, während Sie sich im Diagramm nach oben bewegen, erhalten Sie Folgendes: An2) Speichernutzung. In Buck1 gab es in vielen Regeln benutzerdefinierten Code, um dieses Muster zu erfassen, der auf der Fähigkeit beruhte, Java-Werte im Speicher gemeinsam zu nutzen und die Abhängigkeiten an Ort und Stelle innerhalb der Regelstruktur darzustellen (da es kein verifiziertes Abhängigkeitsdiagramm gab). In Buck2 gibt es viel stärkere Abstraktionsgrenzen, daher muss eine solche Wiederverwendung expliziter gemacht werden. Deshalb, wir eingeführte transitive Mengen (tsets) um dieses Muster von Mengen zu erfassen, die einen transitiven Abschluss darstellen. Indem wir Tsets abstrakter gestalteten, konnten wir die Tsets auch direkt mit dem zugrunde liegenden Abhängigkeitsgraphen verknüpfen, was bedeutet, dass diese Darstellung sowohl hinsichtlich des Speichers als auch der Rechenzeit effizient ist.
Probieren Sie Buck2 jetzt aus
Wir freuen uns darauf, dass die Leute Buck2 ausprobieren und würden uns über jedes Feedback freuen (GitHub-Probleme sind der beste Weg). Wir gehen davon aus, dass Buck2 für mittelgroße mehrsprachige Projekte am interessantesten sein wird. Besuche den Buck2-Erste-Startseite für mehr Informationen.