Warum ein Build-System?

Auf dieser Seite wird erläutert, was Build-Systeme sind, deren Funktionsweise, warum Sie ein Build-System verwenden sollten und warum Compiler und Build-Skripts nicht die beste Wahl sind, wenn Ihr Unternehmen mit der Skalierung beginnt. Sie ist für Entwickler gedacht, die nicht viel Erfahrung mit einem Build-System haben.

Was ist ein Build-System?

Grundsätzlich haben alle Build-Systeme einen einfachen Zweck: Sie wandeln den von Entwicklern geschriebenen Quellcode in ausführbare Binärdateien um, die von Maschinen gelesen werden können. Build-Systeme sind nicht nur für von Menschen erstellten Code gedacht. Außerdem können Maschinen mit diesen Builds automatisch Builds erstellen, unabhängig davon, ob sie in der Produktion oder in Releases getestet werden. In einer Organisation mit Tausenden von Entwicklern werden die meisten Builds automatisch und nicht direkt von Technikern ausgelöst.

Kann ich nicht einfach einen Compiler verwenden?

Der Bedarf an einem Build-System ist möglicherweise nicht sofort offensichtlich. Die meisten Entwickler verwenden beim Programmieren kein Build-System: Die meisten beginnen mit dem Aufruf von Tools wie gcc oder javac direkt über die Befehlszeile oder dem entsprechenden Tool in einer integrierten Lösung. Entwicklungsumgebung (IDE) Solange sich der gesamte Quellcode im selben Verzeichnis befindet, funktioniert ein solcher Befehl problemlos:

javac *.java

Damit wird der Java-Compiler angewiesen, jede Java-Quelldatei im aktuellen Verzeichnis in eine binäre Klassendatei zu konvertieren. Im einfachsten Fall ist dies alles, was Sie benötigen.

Sobald der Code jedoch erweitert wird, beginnen die Komplikationen. javac ist intelligent genug, um in Unterverzeichnissen des aktuellen Verzeichnisses nach Code zu suchen, der importiert werden soll. Es ist jedoch nicht möglich, Code zu finden, der in anderen Teilen des Dateisystems gespeichert ist (z. B. in einer von mehreren Projekten gemeinsam genutzten Bibliothek). Außerdem werden nur Java-Codes erstellt. Große Systeme setzen häufig unterschiedliche Teile ein, die in einer Vielzahl von Programmiersprachen mit Webabhängigkeiten geschrieben werden, sodass kein Compiler für eine einzelne Sprache möglicherweise das gesamte System aufbauen kann.

Sobald Sie mit Code aus mehreren Sprachen oder mehreren Kompilierungseinheiten arbeiten, ist das Erstellen von Code kein einzelner Schritt mehr. Jetzt musst du auswerten, von welchen Code dein Code abhängt, und diese Teile in der richtigen Reihenfolge erstellen, wobei du vielleicht verschiedene Tools für jeden Teil verwendest. Wenn sich Abhängigkeiten ändern, müssen Sie diesen Vorgang wiederholen, um die Verwendung von veralteten Binärdateien zu vermeiden. Bei einer Codebasis mit gleichmäßiger Größe wird dieser Prozess schnell mühsam und fehleranfällig.

Der Compiler weiß auch nichts über den Umgang mit externen Abhängigkeiten, wie z. B. Drittanbieter-JAR-Dateien in Java. Ohne ein Build-System könnten Sie dies verwalten, indem Sie die Abhängigkeit aus dem Internet herunterladen, sie in einem Ordner lib auf der Festplatte speichern und den Compiler so konfigurieren, dass Bibliotheken aus diesem Verzeichnis gelesen werden. aus. Im Laufe der Zeit ist es schwierig, die Updates, Versionen und Quellen dieser externen Abhängigkeiten zu verwalten.

Was ist mit Shell-Skripts?

Angenommen, Ihr Hobbyprojekt beginnt so einfach, dass Sie es nur mit einem Compiler erstellen können. Dabei stoßen Sie jedoch auf einige der zuvor beschriebenen Probleme. Vielleicht halten Sie dennoch nicht davon aus, dass Sie ein Build-System benötigen, und können diese mühsamen Aufgaben mit einfachen Shell-Skripts automatisieren, um die Aufgaben in der richtigen Reihenfolge zu erstellen. Dies hilft zwar eine Weile, doch schon bald treten noch weitere Probleme auf:

  • Es wird mühsam. Wenn Ihr System komplexer wird, verbringen Sie fast so viel Zeit mit Ihren Build-Skripts wie mit echtem Code. Das Debugging von Shell-Skripts ist mühsam, da immer mehr Hacks übereinanderliegen.

  • Es ist langsam. Um sicherzustellen, dass Sie sich nicht versehentlich auf veraltete Bibliotheken verlassen, erstellen Sie Ihr Build-Skript jede Abhängigkeit in der entsprechenden Reihenfolge, wenn Sie es ausführen. Sie denken darüber nach, eine Logik hinzuzufügen, um zu erkennen, welche Teile neu erstellt werden müssen. Dies klingt jedoch kompliziert und fehleranfällig für ein Skript. Oder Sie überlegen, welche Teile jedes Mal neu erstellt werden müssen, aber dann kehren Sie zu Quadrat eins zurück.

  • Gute Neuigkeiten: Zeit für eine Veröffentlichung! Finden Sie besser alle Argumente heraus, die Sie an den jar-Befehl übergeben müssen, um den endgültigen Build zu erstellen. Denken Sie auch daran, wie Sie es hochladen und in das zentrale Repository verschieben können. Erstellen und übertragen Sie die Dokumentationsupdates und senden Sie eine Benachrichtigung an die Nutzer. Hm, das könnte ein anderes Skript sein...

  • Was für eine Katastrophe! Ihre Festplatte stürzt ab und Sie müssen nun das gesamte System neu erstellen. Sie waren intelligent genug, um alle Ihre Quelldateien in der Versionsverwaltung zu belassen, aber was ist mit den heruntergeladenen Bibliotheken? Können Sie sie alle wiederfinden und kontrollieren, ob sie dieselbe Version wie beim ersten Herunterladen waren? Ihre Skripts ließen sich wahrscheinlich davon ab, dass bestimmte Tools an bestimmten Orten installiert wurden. Können Sie dieselbe Umgebung wiederherstellen, damit die Skripts wieder funktionieren? Was ist mit all den Umgebungsvariablen, die Sie vor längerer Zeit festgelegt haben, damit der Compiler ordnungsgemäß funktioniert und dann vergessen wird?

  • Trotz der Probleme ist Ihr Projekt so erfolgreich, dass Sie weitere Entwickler einstellen können. Jetzt wissen Sie, dass es keine Katastrophe dauert, bis die vorherigen Probleme auftreten. Sie müssen jedes Mal denselben mühsamen Bootstrapping-Prozess durchlaufen, wenn ein neuer Entwickler Ihrem Team beitritt. Trotz aller Anstrengungen gibt es immer noch kleine Unterschiede im System der einzelnen Personen. Häufig funktioniert das, was auf dem Rechner einer Person funktioniert, nicht auf dem des anderen und nicht jedes Mal, wenn der Fehlerbehebungspfad oder die Bibliotheksversion mehrere Stunden benötigt wird, um den Unterschied zu ermitteln.

  • Sie möchten Ihr Build-System automatisieren. Theoretisch ist das so einfach wie das Erwerben eines neuen Computers und das Einrichten des Build-Skripts für jede Nacht mithilfe von Cron. Sie müssen weiterhin den mühsamen Einrichtungsprozess durchlaufen, aber jetzt haben Sie nicht den Vorteil, dass ein menschliches Gehirn kleinere Probleme erkennen und lösen kann. jeden Morgen, wenn Sie einsteigen, sehen Sie, dass der Build der letzten Nacht fehlgeschlagen ist, weil gestern ein Entwickler eine Änderung vorgenommen hat, die an seinem System funktionierte, aber nicht an dem automatisierten Build-System. Jedes Mal, wenn es sich um eine einfache Lösung handelt, die so häufig vorkommt, dass Sie jeden Tag viel Zeit dafür verbringen, diese einfachen Lösungen zu finden und anzuwenden.

  • Builds werden langsamer und langsamer, wenn das Projekt wächst. Eines Tages, während Sie auf den Abschluss eines Builds warten, blicken Sie traurig auf den inaktiven Desktop Ihres Kollegen, der im Urlaub ist, und wünschen sich eine Möglichkeit, das alles zu verschwenden, was Rechenaufgaben verschwendet haben. Leistung.

Es gibt ein klassisches Problem der Skalierung. Für einen einzelnen Entwickler, der höchstens ein paar Zeilen Code in maximal ein paar Zeilen Code arbeitet (was bei einem Juniorentwickler, der gerade sein Universitätsstudium gemacht hat, bereits die gesamte Erfahrung gemacht haben), Sie benötigen lediglich einen Compiler. Skripts sind manchmal etwas komplizierter. Sobald Sie jedoch mehrere Entwickler und deren Maschinen koordinieren müssen, reicht selbst ein perfektes Build-Skript nicht mehr aus, da es sehr schwierig ist, die geringfügigen Unterschiede auf diesen Maschinen zu berücksichtigen. An dieser Stelle wird dieser einfache Ansatz aufgeschlüsselt und es ist an der Zeit, in ein echtes Build-System zu investieren.