Warum wird Java nicht als Build-Sprache verwendet?

24

Wenn Java eine Allzwecksprache ist und das Erstellen eines Programms mit Java beschrieben werden kann, warum ist dies nicht die beste Methode, Build-Dateien zu schreiben, und stattdessen verwenden wir Tools wie Ant, Maven und Gradle? Wäre das nicht unkomplizierter und müsste nicht noch eine weitere Programmiersprache erlernt werden? (Übrigens - diese Frage kann auch auf andere Sprachen wie C # angewendet werden.)

vainolo
quelle
7
Vielleicht haben Sie eine etwas interessantere Frage: "Warum werden keine Mehrzwecksprachen für Build-Sprachen verwendet?" - Ich meine, C ist auch keine Build-Sprache. Ich würde auch vorschlagen, sich die Zeremonie anzusehen, in der eine einzelne .java-Datei in Java
8
Dies könnte wahrscheinlich verallgemeinert werden als "Warum sich mit DSLs beschäftigen?"
FrustratedWithFormsDesigner
1
Eine bessere Frage könnte sein; Warum sind IDEs / Compiler / Tools so schlecht, dass überhaupt Build-Tools benötigt werden?
Brendan
6
@Brendan, das ist keine Frage, da Build-Tools und IDEs unterschiedlichen Zwecken dienen.
Jwenting
1
Weil die "Allzweck" -Sprachen niemals anstelle der gut definierten, einfachen domänenspezifischen Sprachen verwendet werden sollten. Und wenn Sie die Notwendigkeit haben, "noch eine andere Programmiersprache" zu lernen, sollten Sie nicht wirklich programmieren, probieren Sie einen anderen Beruf aus.
SK-logic

Antworten:

21

Spezifisches Werkzeug für einen bestimmten Zweck

  • Ausführlichkeit

    Allzwecksprachen sind oft zu ausführlich. Wenn ich einen Build in Java verwalten müsste, wäre ich von der Größe des Tieres sehr schnell sehr deprimiert. Mit einem in Java geschriebenen DSL kann es jedoch problemlos verwaltet werden. Und bis zu einem gewissen Grad sieht man Gradle (für Groovy) und Buildr (für Ruby).

  • Schwierigkeit

    Allzwecksprachen sind schwierig. Nun, ich denke nicht, dass Programmierung schwierig ist und von niemandem aufgegriffen werden kann, aber der Punkt ist: Ihr Build-Ingenieur ist nicht unbedingt Ihr Programmierer!

  • Zweck

    Das ist mehr oder weniger der Schnittpunkt von Schwierigkeit und Ausführlichkeit . Eine Sprache ist für einen Zweck konzipiert, und hier geht es um etwas ganz Bestimmtes. Also warum sollten Sie brauchen oder einen verwenden möchten allgemeine Zweck Sprache? Für Builds benötigen Sie:

    • Zweige und Bedingungen für separate Konfigurationen, Umgebungen usw.
    • eine Reihe von Anweisungen zum Extrahieren von Daten aus Ihrer Umgebung,
    • eine Reihe von Anweisungen zur Erstellung von Liefergegenständen.

    Du brauchst nicht wirklich viel mehr.

Sicher, es ist ärgerlich, wenn Ihr Build-System nicht flexibel genug für den speziellen Anwendungsfall ist, den Sie suchen, aber es ist wahrscheinlich weitaus besser als die Alternative für die meisten Menschen.

Das ist wahrscheinlich der Grund, warum Menschen, die deklarative Build-Systeme gegenüber den meisten programmierbaren bevorzugen, eine Polarität haben: Ich denke, ein Entwickler hat eine natürliche Tendenz, nach Wegen zu suchen, um aus der Box auszubrechen.

Warten Sie, brauchen wir wirklich ein Werkzeug?

Eine andere verwandte Frage wäre: Brauchen wir wirklich ein Build-Tool? Ist die Tatsache, dass sie in allen Sprachen existieren, nicht das Zeichen dafür, dass sie eine Lücke füllen, die gar nicht erst vorhanden sein sollte?

Einige Sprachen erfordern nicht unbedingt ein Build-Tool. Beispielsweise benötigen die meisten Skriptsprachen keine und werden beim Laden aufgelöst. Oder nimm Go, dessen Compiler alles für dich erledigt, was ein netter Kontrapunkt ist: Was wäre, wenn ein Compiler wie gcc plötzlich nicht mehr ein paar Flags, einen Linker und ein Makefile bräuchte, um alles zusammen zu sterben? Oder wenn Javac keine build.xml oder pom.xml brauchte, um ihm zu sagen, was er tun soll? Sollte das Abhängigkeitsmanagement nicht direkt Teil der sprachspezifischen Tools sein, da die Abhängigkeiten Teil des endgültigen Programms sind?

Dies scheint für den Benutzer (den Builder) ein viel einfacherer Ansatz zu sein. Man könnte jedoch argumentieren, dass sie sich nur im Verborgenen bewegen und Ihnen die Wahl und die Möglichkeiten nehmen, auf diesen Build-Prozess Einfluss zu nehmen (aber dann würden Sie hoffen, dass ein solches Tool Compiler-Erweiterungen und ähnliches ermöglicht). Außerdem sahen wir die Werkzeuge und die Sprache als zwei getrennte Dinge an, sodass es unsicher erscheinen könnte, wenn sie plötzlich so eng miteinander verbunden sind.

Ich denke nicht, dass die Sprache, die Sie verwenden, um Ihre Programme zu erstellen, das Problem ist. Es ist die Sprache, mit der Sie programmieren, und die Kernplattform und das Kernwerkzeug, die von Bedeutung sein sollten, und wir kommen immer noch voran.


Persönlich habe ich make / gmake / autotools / pmk verwendet und war mit C zufrieden, und ich habe mit Java angefangen, als wir nur make und dann ant hatten, und jetzt bevorzuge ich Maven im Allgemeinen all diesen Alternativen. Obwohl ich Wert in Gradle, Buildr und anderen sehen kann, mag ich die Prävalenz von Maven bis jetzt, bis eine signifikantere Verschiebung eintritt. Außerdem gefällt mir, dass es starr ist, aber es Ihnen trotzdem die Möglichkeit lässt, das bei Bedarf zu umgehen. Dass es nicht einfach ist, ist eine gute Sache.

Es ist ein Build-Tool. Lerne es einfach zu umarmen und bekämpfe es nicht. Es ist eine verlorene Schlacht. Oder zumindest eine sehr, sehr lange.

Haylem
quelle
Mag deinen Pragmatismus wirklich. Meine Frage ist aus Neugier. Ich benutze Maven (nachdem ich in der Vergangenheit Make und Ant verwendet habe) und es macht "schwarze Magie", die manchmal gut und manchmal schlecht ist. Aber es ist immer noch Magie.
Vainolo
1
"Oder wenn Javac keine build.xml oder pom.xml brauchte, um ihm zu sagen, was er tun soll?" - heh. es tut nicht und hat es nie getan.
user253751
@immibis: das ist umstritten. Ich würde es nicht wirklich genießen, meine Projekte im Büro mit nur Javac zu erstellen :) Es würde sicherlich ein bisschen Kleber mit entweder einem Makefile oder einem Shell-Skript oder zumindest Shell-Aliasen erfordern, um die Dinge zusammenzusetzen. Oder ein gigantisches Programm mit allem in einem Ordner und allem, was im Repo enthalten ist. Nicht wirklich etwas, was ich gerne hätte! :) Aber das ist eine ganz andere Debatte, die nichts mit der Frage des OP zu tun hat.
Haylem
Die Integration des Build-Systems in die Sprache ist auch bei mehrsprachigen Projekten problematisch. Wenn ich sowohl die Sprache A als auch die Sprache B verwende und einen einzigen Build-Prozess für sie haben möchte, welches Build-System der Sprache verwende ich?
Sebastian Redl
@SebastianRedl Wie kann das Problem durch die Einführung einer dritten Sprache gelöst werden ? Wählen Sie einfach die Sprache, die für Sie besser funktioniert. (Und natürlich, wenn Sie eine dritte Sprache benötigen, kann es auch Java sein.)
Ville Oikarinen
21

Javaist ein Imperativ Sprache Ant, Mavensind usw. deklarativen Sprachen:

Wir könnten den Unterschied wie folgt definieren:

  • Imperative Programmierung: Sagen Sie der "Maschine", wie sie etwas tun soll, und als Ergebnis wird passieren, was Sie tun möchten.
  • Deklarative Programmierung: Sagen Sie der "Maschine", was passieren soll, und lassen Sie den Computer herausfinden, wie es geht. 1

Build Sprachen sagen die Erbauer , was getan werden sollte, von wo sie getroffen werden sollten, usw. Der Motor, der läuft die Build (die in einer imperativen Sprache geschrieben ist, wie @ElliottFrisch bemerkt hat), liest diese Anweisungen, und erfüllt sie.

Deklarative Sprachen erscheinen in Build-Szenarien möglicherweise angemessener, da Build-Tasks im Allgemeinen überall gleich sind und in dieser Form als wartbarer und lesbarer angesehen werden als in vollständigem Code.

Uri Agassi
quelle
3
Es gibt jedoch mindestens ein Build-System, das eine imperative Sprache verwendet. Scons benutzt Python.
user16764
Es ist jedoch nicht schwieriger, es zu machen, als eine Klasse zu schreiben, die eine Liste von Abhängigkeiten und eine Methode zum Auflösen dieser Abhängigkeiten enthalten kann. Ich vermute, dass es einen anderen Grund gibt - wie zum Beispiel die Startzeit der JVM.
2
@Lee: Fast alle in der Java-Welt verwendeten Build-Tools (Ant, Maven, Gradle, SBT, ...) werden normalerweise auch auf der JVM ausgeführt (mit Ausnahme von Rake, Buildr oder Scons, die dies können , aber nicht müssen) laufen auf der JVM).
Jörg W Mittag
6
@vainolo "Die meisten Leute mögen Ant / Maven / Make nicht wirklich?" Das ist eine kühne Aussage!
Ben Thurley
1
@BenThurley Ich Leute mochten machen, warum wurde Ameise erstellt? und wenn Ameise gemocht wurde, warum war Maven? Und warum benutzen die Leute Gradle? Aber ich stimme zu, dass es eine kühne Aussage ist, und sie wird nur durch "anekdotische" Beweise von einigen zehn Java-Entwicklern
gestützt
4

Wenn Sie sich die Merkmale eines typischen Build-Systems ansehen, finden Sie:

  1. Viele Daten: Namen, Schalter, Einstellungen, Konfigurationselemente, Zeichenfolgen usw
  2. Viel Interaktion mit der Umgebung: Befehle, Umgebungsvariablen
  3. Eine relativ einfache "Build-Engine", die Abhängigkeiten, Threading, Protokollierung usw. verarbeitet.

Wenn Sie beabsichtigen, eine Reihe von Build-Dateien in einer bestimmten Sprache (Java / C # / Python / etc) zu schreiben, würden Sie sich ab der dritten oder vierten Iteration darauf einigen, (a) die meisten Daten und externen Befehle als Daten in etwas zu speichern wie XML (b) Schreiben der "Build Engine" in Ihrer Lieblingssprache.

Sie finden es auch nützlich, einige der Daten in Ihrem XML als interpretierte Sprache zu behandeln, um verschiedene Funktionen in der Build-Engine auszulösen. Sie können auch einige Makros interpretieren oder Zeichenfolgensubstitutionen in den Daten durchführen.

Mit anderen Worten, Sie würden mit Make, Ant, Rake oder MsBuild abschließen. Eine zwingende Sprache für die Dinge, die es gut macht, und Datenstrukturen, um zu beschreiben, was Sie tun möchten, jetzt in der Regel in XML.

david.pfx
quelle
2

In diesen Fällen sprechen eine Reihe von Faktoren gegen die Verwendung von Java / C ++ / C #.

Zunächst müssen Sie Ihr Erstellungsskript kompilieren, bevor Sie es ausführen können, um Ihre App zu erstellen. Wie würden Sie Pakete, Flags, Compilerversionen und Toolpfade angeben, die zum Erstellen Ihres Build-Skripts benötigt werden? Natürlich könnten Sie eine Lösung finden, aber es ist viel einfacher, entweder eine Sprache zu haben, die diesen Build-Schritt nicht benötigt (z. B. Python), oder eine Sprache, die Ihr Build-Tool von Haus aus versteht.

Zweitens sind Build-Dateien datenintensiv, wohingegen Java / C ++ / C # wesentlich stärker auf das Schreiben von Code und Algorithmen ausgerichtet ist. Java und Freunde haben keine sehr prägnante Darstellung aller Daten, die Sie speichern möchten.

Drittens benötigen Java und Freunde eine Menge Boilerplate, um gültig zu sein. Die Build-Datei müsste sich innerhalb einer Methode innerhalb einer Klasse mit all ihren Importen befinden. Wenn Sie eine Skriptsprache oder eine benutzerdefinierte Sprache verwenden, können Sie alles Mögliche vermeiden und haben nur die Build-Details selbst.

Winston Ewert
quelle
0

Tatsächlich! Warum nicht eine mächtige und ausdrucksstarke Sprache für ein Problem verwenden, das komplexer ist, als die Leute (anfangs) oft denken? Vor allem, wenn die Betroffenen bereits mit einer solchen Sprache vertraut sind. (Bauen ist das Problem der Programmierer und wird am besten von den Programmierern gelöst.)

Diese Frage habe ich mir auch vor Jahren gestellt und festgestellt, dass Java eine gute Sprache für die Definition von Builds ist, insbesondere für Java-Projekte. Infolgedessen fing ich an, etwas dagegen zu unternehmen.

HAFTUNGSAUSSCHLUSS : In dieser Antwort bewerbe ich iwant , ein Build-System, das ich entwickle. Aber da dies sowieso eine Diskussion mit einer Meinung ist, bin ich mir sicher, dass es in Ordnung ist.

Auf die Vorteile von Java (Leistung und Ausdruckskraft) werde ich nicht näher eingehen. Wenn Sie interessiert sind, können Sie mehr auf der iwant Seite lesen .

Stattdessen werde ich überlegen, warum Java (und andere GPLs) so schnell als ungeeignet für das Bauen abgetan werden. Viele Antworten und Kommentare hier sind gute Beispiele für ein solches Denken. Betrachten wir einige der typischen Argumente:

"Java ist ein Muss, aber Builds lassen sich am besten deklarativ definieren" , könnten sie sagen.

Wahr. Bei der Verwendung einer Sprache als Metasprache für ein internes DSL zählt jedoch die Syntax . Selbst eine imperative Sprache wie Java kann ausgetrickst werden , um deklarativ zu sein. Wenn es deklarativ aussieht und sich deklarativ anfühlt, ist es (aus praktischen Gründen) deklarativ. Beispielsweise:

JacocoTargetsOfJavaModules.with()
    .jacocoWithDeps(jacoco(), modules.asmAll.mainArtifact())
    .antJars(TestedIwantDependencies.antJar(),
            TestedIwantDependencies.antLauncherJar())
    .modules(interestingModules).end().jacocoReport(name)

Dies ist ein echtes Beispiel aus dem Demo-Projekt von iwant .

Vergleichen Sie dies tatsächlich mit einigen vermeintlich deklarativen Buildsystemen, die ihre Benutzer solchen Imperativverben wie "test" oder "compile" aussetzen. Die obige Deklaration enthält nur Substantive, keine Verben. Kompilieren und Testen sind Aufgaben, die von iwant implizit ausgeführt werden, um dem Benutzer die gewünschten Substantive zuzuweisen. Es ist nicht die Sprache. So benutzt du es.

"Java ist wortreich"

Ja, viel Java-Code ist ausführlich. Aber es ist nicht die Sprache, sondern die Art und Weise, wie Sie sie verwenden. Wenn eine Implementierung ausführlich ist, kapseln Sie sie einfach hinter einer netten Abstraktion. Viele GPLs bieten hierfür angemessene Mechanismen.

Stellen Sie sich das obige Java-Snippet vor, das in XML geschrieben ist. Ersetzen Sie Klammern durch spitze Klammern und verschieben Sie sie. Und dann duplizieren Sie jedes Keyword als Abschluss-Tag! Java als Syntax ist nicht ausführlich.

(Ich weiß, ein Vergleich mit XML ist, als würde man einem Baby Süßigkeiten abnehmen, aber so viele Builds werden zufällig in XML definiert.)

"Sie müssten Ihr Build-Skript kompilieren"

Dies ist ein gültiger Punkt. Trotzdem ist es nur ein kleines technisches Problem, das gelöst werden muss. Ich hätte es mit einer Bohnenschale oder einem anderen Interpreter lösen können . Stattdessen habe ich es gelöst, indem ich es als ein weiteres Build-Problem behandelt und mit einer einfachen Shell oder einem Ant-Skript gebootet habe, das einen einfachen Java-Bootstrapper kompiliert und ausführt.

"Java hat Boilerplate"

Wahr. Sie müssen Klassen importieren, Sie müssen "public", "class" und so weiter erwähnen. Und hier erzielen einfache externe DSLs einen anfänglichen leichten Gewinn.

Und wenn Ihr Projekt so trivial ist, dass dieses Boilerplate von Bedeutung ist, herzlichen Glückwunsch. Ihr Problem ist nicht schwierig und es spielt keine Rolle, wie Sie es lösen.

Viele Projekte erfordern jedoch viel mehr als die Erstellung, Berichterstattung und Verpackung. Wenn das Boilerplate von Java für Kundenprobleme akzeptabel ist, warum keine Probleme erstellen? Warum Schuhe nur für Kinder anderer machen?

Ville Oikarinen
quelle
0

Eine Sache, auf die sich die anderen Antworten meines Erachtens nicht bezogen haben, ist, dass die Einschränkungen und Transparenz dieser Build-Sprachen einen großen Teil dessen ausmachen, was sie nützlich macht. Nehmen wir zum Beispiel Maven. Ja, es führt Builds aus, definiert aber auch Abhängigkeiten. Dies ermöglicht es einem Maven-Build, diese Abhängigkeiten abzurufen und ihre Abhängigkeiten usw. zu betrachten.

Überlegen Sie, ob dies direkt mit Java geschehen ist. Das Build-Tool würde feststellen, dass eine Abhängigkeit besteht. Es müsste dann also eine andere Java-Anwendung herunterfahren und ausführen, um festzustellen, welche Abhängigkeiten es gibt. Aber für Maven werden einfach die Abhängigkeitserklärungen betrachtet. Die Maven-Build-Datei ist transparent. Eine vollständige Sprache von Turing ist von Natur aus nicht transparent und daher für einige Zwecke wie diesen unterlegen.

JimmyJames
quelle
Wie passt Gradle dazu? Es definiert Abhängigkeiten in einem deklarativen Stil unter Verwendung einer Allzwecksprache (Groovy). In einem Groovy-, JavaScript- oder Lisp-basierten Tool können Sie natürlich den Compiler oder Interpreter der Sprache verwenden, um Deklarationen zu analysieren, unabhängig davon, ob Sie sie nur lesen oder ausführen möchten (wenden Sie eine Funktion an). Diese Dualität von Code und Daten ist nicht Teil der allgemein akzeptierten Java-Sprache, obwohl dies nicht unmöglich ist. Vollständigkeit verhindert nicht eine gute Datenrepräsentation. Es eröffnet die Möglichkeit, dass Ihre Daten etwas tun, was Sie nicht erwartet haben.
Joshp
@joshp Ich kenne Gradle nicht. Es wird als DSL beschrieben und die Dinge sehen ziemlich aussagekräftig aus. Auf Groovy zu basieren bedeutet nicht zwangsläufig, dass es in seiner Stärke Groovy entspricht. Sie wären wahrscheinlich besser in der Lage zu sagen, ob Gradle tatsächlich eine Turing-vollständige Sprache ist. Die Beispiele für Abhängigkeiten, die ich mir angesehen habe, scheinen ziemlich einfach zu sein. Können Sie beliebige Groovy-Ausdrücke in Dingen wie Abhängigkeitsdeklarationen verwenden?
JimmyJames
Persönlich mag ich nicht einmal transitive Abhängigkeiten. Oft sorgen sie nur für Überraschungen im Klassenpfad (mehrere inkompatible Versionen von Bibliotheken). Deshalb hat maven das Element exclude, aber der Aufwand lohnt sich nicht nur. Explizite Abhängigkeiten machen das Leben einfacher. Transitive Abhängigkeiten können jedoch mit Java implementiert werden. Ich habe so etwas mit iwant gemacht, indem ich Build-Definitionen anderer Projekte wiederverwendete.
Ville Oikarinen
@VilleOikarinen Ich stimme Ihnen meistens zu. Ich denke, es verursacht auch eine Menge Blähungen, da es keine wahrgenommenen Kosten verursacht, mehr Abhängigkeiten einzuziehen, selbst wenn Sie sie nicht verwenden. Im Zusammenhang mit dieser Antwort möchte ich jedoch nicht auf den Wert oder die Weisheit dieser Funktion eingehen, sondern darauf, wie die Verwendung eines DSL zur Erreichung dieses Ziels nützlich ist.
JimmyJames
1
@ VilleOikarinen Ich denke, wir haben das Ende erreicht, aber ich möchte nur klarstellen, dass ich nicht über Pom / Maven spreche. Dies ist ein Beispiel für eine gebaute DSL, aber ich denke nicht viel darüber nach. Ich benutze es widerwillig und finde es klobig. Was aber so dominant ist, sind die Abhängigkeitserklärungen. Ich habe Buildr eine Weile benutzt und es liest pom für Abhängigkeiten, aber es verwendet sie nicht für die Build-Spezifikationen. Es gibt eine Reihe von Tools, die nicht Java-basiert sind und diese Pom verstehen, aber AFAIK. Sie kümmern sich nur um die Abhängigkeiten.
JimmyJames