Perché java non è usato come linguaggio di costruzione?


24

Se Java è un linguaggio generico e costruire un programma è qualcosa che può essere descritto usando il linguaggio Java, perché non è questo il modo migliore per scrivere file di build e invece usiamo strumenti come Ant, Maven e Gradle? Non sarebbe più semplice e rimuoverebbe anche la necessità di imparare ancora un altro linguaggio di programmazione? (A proposito: questa domanda può essere applicata anche ad altre lingue, come C #)


7
Potresti avere una domanda un po 'più interessante sul "perché le lingue per scopi generici non sono usate per costruire le lingue" - Voglio dire, C non è nemmeno un linguaggio di costruzione. Suggerirei anche di guardare la cerimonia intorno alla compilazione di un singolo file .java in Java

8
Questo potrebbe probabilmente essere generalizzato come "Perché preoccuparsi dei DSL?"
FrustratedWithFormsDesigner

1
Una domanda migliore potrebbe essere; perché gli IDE / i compilatori / gli strumenti sono così dannosi che gli strumenti di costruzione sono necessari in primo luogo.
Brendan,

6
@Brendan non è una domanda poiché gli strumenti di creazione e gli IDE servono a scopi diversi.
jwenting

1
Perché le lingue "per scopi generici" non dovrebbero mai essere usate al posto di lingue ben definite e specifiche per domini. E, se hai bisogno di imparare "ancora un altro linguaggio di programmazione", non dovresti davvero programmare, prova qualche altra operazione.
SK-logic,

Risposte:


21

Strumento specifico per uno scopo specifico

  • Verbosità

    Le lingue di uso generale sono spesso troppo dettagliate. Se dovessi gestire una build in Java, sarei molto depresso molto rapidamente dalle dimensioni della bestia. Tuttavia, potrebbe essere facilmente gestibile utilizzando un DSL scritto in Java. E in un certo senso è così che puoi vedere Gradle (per Groovy) e Buildr (per Ruby).

  • Difficoltà

    Le lingue per scopi generici sono difficili. Beh, non penso che la programmazione sia difficile e che non possa essere presa da nessuno, ma il punto è: il tuo ingegnere non è necessariamente il tuo programmatore!

  • Scopo

    Questo è più o meno all'intersezione di difficoltà e verbosità . Una lingua è progettata per uno scopo, e qui stiamo parlando di qualcosa di molto specifico. Allora perché si necessario o si desidera utilizzare un generico linguaggio di scopo? Per le build, hai bisogno di:

    • rami e condizionali per gestire configurazioni, ambienti, ecc. separati
    • una serie di istruzioni per estrarre i dati dal tuo ambiente,
    • un insieme di istruzioni per produrre risultati.

    Non hai davvero bisogno di molto di più.

Sicuramente è fastidioso quando il tuo sistema di build sembra non essere abbastanza flessibile per quell'unico caso d'uso che stai cercando, ma probabilmente è molto meglio dell'alternativa per la maggior parte delle persone.

Questo è probabilmente il motivo per cui c'è una polarità tra le persone che preferiscono i sistemi di build dichiarativi rispetto alla maggior parte dei programmi programmabili: immagino che uno sviluppatore potrebbe avere una naturale tendenza a cercare modi per uscire fuori dagli schemi.

Aspetta, abbiamo davvero bisogno di uno strumento?

Un'altra domanda correlata sarebbe: abbiamo davvero bisogno di uno strumento di costruzione? Il fatto che esistano in tutte le lingue non è forse il segno che colmano una lacuna che non dovrebbe nemmeno esserci?

Alcune lingue non richiedono necessariamente uno strumento di compilazione. Ad esempio, la maggior parte dei linguaggi di scripting non ne ha bisogno e si risolve al momento del caricamento. Oppure prendi Go, il cui compilatore gestirà tutto per te, il che è un bel contrappunto: cosa succederebbe se un compilatore come gcc improvvisamente non avesse bisogno di un mucchio di flag, un linker e un makefile per far morire tutto insieme? O se javac non avesse bisogno di build.xml o pom.xml per dirgli cosa fare? La gestione delle dipendenze non dovrebbe essere direttamente parte degli strumenti del linguaggio, poiché le dipendenze fanno parte del programma finale?

Sicuramente sembra un approccio molto più semplice per l'utente (il costruttore). Sebbene si possa sostenere che si stiano semplicemente comportando da soli e togliendo la tua scelta e le opportunità per influenzare quel processo di compilazione (ma poi speri che un tale strumento consenta estensioni del compilatore e cose simili). Inoltre, vedevamo gli strumenti e il linguaggio come due cose separate, quindi potrebbe sembrare impuro improvvisamente averli così strettamente accoppiati.

Non credo che la lingua che usi per costruire i tuoi programmi sia un problema. È il linguaggio che usi per programmare e la sua piattaforma e gli strumenti di base che dovrebbero avere importanza, e stiamo ancora facendo progressi su questo.


Personalmente, ho usato make / gmake / autotools / pmk e sono stato contento di loro per C, e ho iniziato con Java quando tutto ciò che avevamo era make, quindi formica, e ora in genere preferisco Maven a tutte queste alternative. Anche se posso vedere il valore in gradle, buildr e altri, ma mi piace la prevalenza di maven finora, fino a quando si verifica un cambiamento più significativo. Inoltre mi piace che sia rigido, ma ti lascia comunque la possibilità di aggirare questo, se necessario. Che non sia facile è una buona cosa.

È uno strumento di costruzione. Impara solo ad abbracciarlo e non combatterlo. È una battaglia persa. O almeno molto molto lungo.


Mi piace molto il tuo pragmatismo. La mia domanda è per curiosità. Uso Maven (che ha usato make e formica in passato), e fa "magia nera" che a volte è buona e talvolta cattiva. Ma è ancora magico.
vainolo,

1
"O se javac non avesse bisogno di un build.xml o pom.xml per dirgli cosa fare?" - eh. non lo fa e non l'ha mai fatto.
user253751

@immibis: è discutibile. Non mi piacerebbe davvero costruire i miei progetti in ufficio con solo javac :) Avrebbe sicuramente bisogno di un bel po 'di colla con un Makefile o uno script di shell o almeno con gli alias di shell per mettere insieme le cose. O un gigantesco programma con tutto in una cartella e tutti i deps nel repository. Non proprio qualcosa che mi piacerebbe! :) Ma questo è un altro dibattito, non correlato alla domanda del PO.
Hayylem,

L'integrazione del sistema di compilazione con il linguaggio è problematica anche per i progetti poliglotta. Se uso entrambe le lingue A e B e voglio avere un singolo processo di compilazione per loro, quale sistema di compilazione della lingua devo usare?
Sebastian Redl,

@SebastianRedl In che modo il problema diventa più semplice introducendo una terza lingua? Scegli la lingua che funziona meglio per te. (E ovviamente, se hai bisogno di una terza lingua, anche Java può esserlo.)
Ville Oikarinen,

21

Javaè un imperativo lingua, Ant, Maven, ecc, sono dichiarative lingue:

Potremmo definire la differenza come segue:

  • Programmazione imperativa: dire alla "macchina" come fare qualcosa e, di conseguenza, accadrà quello che vuoi che accada.
  • Programmazione dichiarativa: dire alla "macchina" cosa vorresti succedere e lasciare che il computer capisca come farlo. 1

Le lingue di costruzione indicano al costruttore cosa dovrebbe essere fatto, da dove dovrebbe essere preso, ecc. Il motore che esegue la build (che è scritto in un linguaggio imperativo, come ha notato @ElliottFrisch), legge queste istruzioni e le soddisfa.

I linguaggi dichiarativi possono sembrare più appropriati negli scenari di compilazione, dal momento che le attività di compilazione sono generalmente le stesse dappertutto, ed è considerata più gestibile e leggibile in tale forma rispetto al modulo di codice completo.


3
C'è almeno un sistema di compilazione che utilizza un linguaggio imperativo però. Scons utilizza Python.
user16764

Renderlo imperativo non è più difficile che scrivere una classe che può contenere un elenco di dipendenze e un metodo per risolverle. Ho il sospetto che ci sia un altro motivo, come forse il tempo di avvio di JVM.

2
@Lee: quasi tutti gli strumenti di compilazione utilizzati nel mondo Java (Ant, Maven, Gradle, SBT, ...) in genere funzionano anche su JVM (con l'eccezione di Rake, Buildr o Scons, che possono , ma non hanno per eseguire su JVM).
Jörg W Mittag,

6
@vainolo "Alla maggior parte delle persone non piace davvero Ant / Maven / Make"? È un'affermazione audace!
Ben Thurley,

1
@BenThurley Mi è piaciuto fare alla gente, perché è stata creata la formica? e se alla formica piaceva, perché era Maven? E ora perché le persone usano Gradle? Ma sono d'accordo che si tratta di un'affermazione audace, ed è supportata solo da prove "aneddotiche" di alcune decine di sviluppatori java
vainolo

4

Se guardi le caratteristiche di un tipico sistema di build trovi:

  1. Molti dati: nomi, switch, impostazioni, elementi di configurazione, stringhe, ecc
  2. Molta interazione con l'ambiente: comandi, variabili d'ambiente
  3. Un "motore di costruzione" relativamente semplice che gestisce dipendenze, threading, registrazione ecc.

Se decidi di scrivere una serie di file di compilazione usando un linguaggio (Java / C # / Python / ecc.), Di circa la terza o quarta iterazione dovresti accontentarti di (a) mantenere la maggior parte dei dati e comandi esterni come dati in qualcosa come XML (b) scrivere il "motore di costruzione" nella tua lingua preferita.

Troveresti anche utile trattare alcuni dei dati nel tuo XML come un linguaggio interpretato, per attivare varie funzionalità nel motore di compilazione. È inoltre possibile interpretare alcune macro o eseguire sostituzioni di stringhe nei dati.

In altre parole, finiresti con Make, o Ant, o Rake o MsBuild. Un linguaggio imperativo per le cose che fa bene e strutture di dati per descrivere ciò che vuoi fare, ora di solito in XML.


2

In questi casi, numerosi fattori contano per l'utilizzo di Java / C ++ / C #.

Innanzitutto, dovresti compilare lo script di compilazione prima di poterlo eseguire per creare l'app. Come specificare eventuali pacchetti, flag, versioni del compilatore, percorsi degli strumenti necessari per creare lo script di compilazione? Certamente, potresti trovare un modo per aggirarlo, ma è molto più semplice avere un linguaggio che non ha bisogno di quel passo di costruzione (ad esempio python) o un linguaggio che il tuo strumento di costruzione comprenda nativamente.

In secondo luogo, i file di build sono ricchi di dati mentre Java / C ++ / C # sono molto più orientati alla scrittura di codice e algoritmi. Java e gli amici non hanno una rappresentazione molto sintetica di tutti i dati che vorresti archiviare.

In terzo luogo, Java e gli amici hanno bisogno di molta piastra di cottura per essere validi. Il file build dovrebbe essere all'interno di un metodo all'interno di una classe con tutte le sue importazioni. Quando si utilizza un linguaggio di scripting o un linguaggio personalizzato, è possibile evitare tutto quello scaldabagno e avere solo i dettagli di costruzione.


0

Infatti! Perché non usare un linguaggio potente ed espressivo per un problema più complesso di quanto la gente (inizialmente) pensi? Soprattutto quando le persone che affrontano il problema sono già competenti con un linguaggio del genere. (La costruzione è il problema dei programmatori e risolto al meglio dai programmatori.)

Mi sono anche posto questa domanda anni fa e ho deciso che Java è un buon linguaggio per definire build, specialmente per progetti Java. E, di conseguenza, ho iniziato a fare qualcosa al riguardo.

DISCLAIMER : In questa risposta sto promuovendo iwant , un sistema di build che sto sviluppando. Ma dal momento che questa è una discussione supponente, sono sicuro che va bene.

Non approfondirò i vantaggi di Java (potenza ed espressività) o iwant in particolare. Se sei interessato, puoi leggere di più sulla pagina iwant .

Invece prenderò in considerazione il motivo per cui Java (e altri GPL) sono così prontamente liquidati come inadatti alla costruzione. Molte risposte e commenti qui sono buoni esempi di tale pensiero. Consideriamo alcuni degli argomenti tipici:

"Java è un imperativo, ma le build sono meglio definite in modo dichiarativo" , potrebbero dire.

Vero. Ma quando si utilizza una lingua come metalinguaggio per un DSL interno , ciò che conta davvero è la sua sintassi . Anche un linguaggio imperativo come Java può essere ingannato per essere dichiarativo. Se sembra dichiarativo, è (ai fini pratici) dichiarativo. Per esempio:

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

Questo è un vero esempio del progetto demo di iwant .

In effetti, confrontalo con alcuni sistemi di compilazione apparentemente dichiarativi che espongono i loro utenti a verbi imperativi come "test" o "compilazione". La dichiarazione sopra contiene solo sostantivi, nessun verbo. Compilazione e test sono compiti implicitamente gestiti da iwant al fine di garantire all'utente i nomi che desidera. Non è la lingua. È come lo usi.

"Java è prolisso"

Sì, un sacco di codice Java è dettagliato. Ma ancora una volta, non è la lingua, è come la usi. Se un'implementazione è dettagliata, basta incapsularla dietro una bella astrazione. Molti GPL forniscono meccanismi adeguati per questo.

Immagina solo il frammento Java sopra scritto in XML. Sostituire le parentesi con parentesi angolari e spostarle. E quindi duplica ogni parola chiave come tag di chiusura! Java come sintassi non è dettagliata.

(Lo so, il confronto con XML è come prendere caramelle da un bambino, ma così tante build sono definite in XML.)

"Dovresti compilare il tuo script di compilazione"

Questo è un punto valido Tuttavia, è solo un piccolo problema tecnico da risolvere. Avrei potuto risolverlo usando beanshell o qualche altro interprete. Invece, l'ho risolto trattandolo come un altro problema di build e eseguendo il bootstrap di Iwant con una semplice shell o script di formica che compila ed esegue un semplice bootstrapper Java.

"Java ha boilerplate"

Vero. Devi importare le classi, devi menzionare "pubblico", "classe" e così via. E qui i semplici DSL esterni segnano una vittoria facile iniziale.

E se il tuo progetto è così banale che questa piastra di cottura è significativa, congratulazioni. Il tuo problema non è difficile e in realtà non importa come lo risolvi.

Ma molti progetti hanno bisogno di molto più di compilazione, report di copertura e packaging. Se il boilerplate di Java è accettabile per i problemi dei clienti, perché non creare problemi? Perché realizzare scarpe solo per i bambini degli altri?


0

Una cosa che non credo siano le altre risposte è che i limiti e la trasparenza di questi linguaggi di costruzione sono una parte enorme di ciò che li rende utili. Prendiamo Maven, per esempio. Sì, esegue build ma definisce anche dipendenze. Ciò consente a una build Maven di abbattere quelle dipendenze e osservarne le dipendenze e così via e così via.

Considera se questo è stato fatto direttamente con Java. Lo strumento di compilazione vedrebbe che esiste una dipendenza. Dovrebbe quindi abbattere altre applicazioni Java ed eseguirle per determinare quali sono le dipendenze. Ma per Maven, guarda semplicemente alle dichiarazioni di dipendenza. Il file di build di Maven è trasparente. Un linguaggio completo di Turing è intrinsecamente non trasparente e quindi inferiore per alcuni scopi come questo.


Come si inserisce Gradle in questo? Definisce le dipendenze in uno stile dichiarativo, usando un linguaggio generico (Groovy). In uno strumento basato su Groovy, JavaScript o Lisp è naturale utilizzare il compilatore o l'interprete del linguaggio per analizzare le dichiarazioni, sia che si desideri solo leggerle o "eseguirle" (applicare alcune funzioni). Quella dualità di codice e dati non fa parte del linguaggio Java generalmente accettato, sebbene non impossibile. La completezza di Turing non impedisce una buona rappresentazione dei dati. Apre la possibilità che i tuoi dati facciano qualcosa che non ti aspettavi che facesse.
joshp,

@joshp Non ho familiarità con Gradle. È descritto come un DSL e le cose sembrano piuttosto dichiarative. Essere basati su Groovy non significa necessariamente che equivale a Groovy nel suo potere. Probabilmente saresti in una posizione migliore per dire se Gradle è in realtà un linguaggio completo di Turing. Gli esempi di dipendenza che ho visto sembrano abbastanza semplici. Puoi usare espressioni Groovy arbitrarie in cose come le dichiarazioni di dipendenza?
JimmyJames,

Personalmente non mi piacciono nemmeno le dipendenze transitive. Spesso causano solo sorprese nel percorso di classe (più versioni incompatibili delle librerie). Ecco perché Maven ha l'elemento exclude, ma la seccatura non vale solo la pena. Dipendenze esplicite semplificano la vita. Tuttavia, le dipendenze transitive possono essere implementate con Java. Ho fatto qualcosa del genere con iwant riutilizzando le definizioni di costruzione di altri progetti.
Ville Oikarinen,

@VilleOikarinen Mi capita di essere per lo più d'accordo con te. Penso che causi anche un sacco di gonfiore poiché non ci sono costi percepiti per attirare più dipendenze anche se non le usi. Tuttavia, nel contesto di questa risposta, non sto commentando il valore o la saggezza di quella funzione, ma su come l'uso di una DSL sia utile per raggiungerla.
JimmyJames,

1
@VilleOikarinen Penso che in un certo senso abbiamo raggiunto la fine, ma voglio solo chiarire che non sto parlando di pom / maven. Questo è un esempio di build DSL ma non ci penso molto. Lo uso a malincuore e penso che sia goffo. Ma ciò che ha pom così dominante sono le dichiarazioni di dipendenza. Ho usato Buildr per un po 'e legge pom per le dipendenze ma non le usa per le specifiche di build. Ci sono una serie di strumenti che non sono basati su Java che capiscono questo pom ma AFAIK, a loro importa solo le dipendenze.
JimmyJames,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.