gianluca.aguzzi@unibo.it
angelo.filaseta@unibo.it
Questo materiale è ampiamente basato su quello realizzato dai Prof. Mirko Viroli e Roberto Casadei, che ringrazio.
Ogni errore riscontratovi è esclusiva responsabilità degli autori di questo documento.
nos esse quasi nanos gigantium humeris insidentes Bernardo di Chartres
Tutto il software moderno dipende da altro software!
java.*
e javax.*
)Tutto il software che costruiamo e usiamo dipende da altro sofware
$\Rightarrow$ Le applicazioni hanno un albero di dipendenze!
Proviamo a costruire una semplice applicazione che:
Una possibile soluzione: https://github.com/APICe-at-DISI/sample-gradle-project/blob/master/src/main/java/it/unibo/sampleapp/SimplerRateAMovie.java
È stata sfruttata una libreria per OMDB
Ma a sua volta, questa libreria usa librerie che usano librerie…
+--- com.omertron:API-OMDB:1.5
| +--- commons-codec:commons-codec:1.10
| +--- org.apache.commons:commons-lang3:3.4
| +--- com.fasterxml.jackson.core:jackson-core:2.8.7
| +--- com.fasterxml.jackson.core:jackson-annotations:2.8.7
| +--- com.fasterxml.jackson.core:jackson-databind:2.8.7
| | +--- com.fasterxml.jackson.core:jackson-annotations:2.8.0
| | \--- com.fasterxml.jackson.core:jackson-core:2.8.7
| +--- org.slf4j:slf4j-api:1.7.24
| \--- org.yamj:api-common:2.1
| +--- org.apache.httpcomponents:httpclient:4.5.3
| | +--- org.apache.httpcomponents:httpcore:4.4.6
| | +--- commons-logging:commons-logging:1.2
| | \--- commons-codec:commons-codec:1.9
| \--- org.slf4j:slf4j-api:1.7.24
Le dipendenze indirette (dipendenze di dipendenze) sono dette transitive
In progetti non giocattolo, le dipendenze transitive sono la maggioranza
Gestire il classpath diventa molto difficile! Ogni libreria va:
L’applicazione di prima viene lanciata con:
java -cp "build/classes/java/main:lib/API-OMDB-1.5.jar:lib/jool-0.9.14.jar:lib/logback-classic-1.4.1.jar:lib/api-common-2.1.jar:lib/slf4j-api-2.0.2.jar:lib/httpclient-4.5.3.jar:lib/commons-codec-1.10.jar:lib/commons-lang3-3.4.jar:lib/jackson-databind-2.8.7.jar:lib/jackson-core-2.8.7.jar:lib/jackson-annotations-2.8.7.jar:lib/logback-core-1.4.1.jar:lib/httpcore-4.4.6.jar:lib/commons-logging-1.2.jar" it.unibo.sampleapp.SimplerRateAMovie
Ci servirebbe uno strumento capace di:
Per farlo, però, abbiamo bisogno di conoscere qualche archivio ("repository") di librerie, e di sapere come reperirle, ossia conoscere il loro nome e versione…
Al compilatore Java e alla JVM (a differenza di quello che accade con altri linguaggi) è ignoto il concetto di “libreria”. L’unica astrazione che abbiamo in mano è quella di classpath, ma è troppo grezza!
Quando Java ha preso piede, è stato necessario sopperire a questa mancanza. Un particolare build system, Apache Maven, ha elaborato una propria convenzione per i nomi, divenuta oggi sostanzialmente standard (qualunque build system per Java la adotta).
Una libreria Java in formato compatibile con Maven si compone di:
it.unibo
, com.google
, io.github
commons-math
, guava
, junit-jupiter-assertions-jvm
.
, -
, o +
(solitamente numeri e punti)
1.0
, 1.0.1
, 2.3.5-beta4
, 28ae10dd
, 4.0.2-alpha+28ae10dd
Per riferirsi ad una libreria specifica, si usa la sintassi: groupId:artifactId:version
com.google.guava:guava:32-jre
it.unibo.alchemist:alchemist-api:25.0.1
Ora sappiamo come si chiamano, ma non dove trovarle…
Assieme alla convenzione per i nomi, Maven definì un repository (archivio) dove i creatori di software Java open source potessero:
La disponibilità e la possibilità di riuso ha consentito la nascita dell’“ecosistema” Java, rendendolo uno dei linguaggi/piattaforme di più ampio successo di sempre.
Sappiamo dove trovare le librerie e come riferirle, ma ci serve ancora uno strumento per:
Gradle consente di gestire le dipendenze, specificando:
In Gradle è possibile “puntare” ad archivi di librerie specificandolo in un blocco repositories
Per dire a Gradle di:
è sufficiente configurare build.gradle.kts
come segue:
plugins { java } // Carica il necessario per Java
repositories { mavenCentral() } // Configura Gradle per cercare e scaricare da Maven Central
Siamo pronti per importare le librerie che vogliamo! Dobbiamo solo:
groupId
, artifactId
, e version
com.omertron:API-OMDB:1.5
Gradle consente di (costringe a) dire chiaramente “a cosa serve” una certa libreria. Noi vedremo solo alcuni degli scope disponibili:
implementation
: la libreria ci serve sia per compilare che per eseguire la nostra applicazione
testImplementation
: la libreria ci serve per compilare ed eseguire i test
testRuntimeOnly
: la libreria ci serve per eseguire i test (sarà nel -cp
di java
), ma non per compilarli (non sarà nel -cp
di javac
)Una volta identificata la libreria
com.omertron:API-OMDB:1.5
e scelto lo scope che vogliamo usare
implementation
Possiamo semplicemente configurare Gradle per importarla dentro il blocco dependencies
:
plugins { java } // Carica il necessario per Java
repositories { mavenCentral() } // Configura Gradle per cercare e scaricare da Maven Central
dependencies {
implementation("com.omertron:API-OMDB:1.5")
}
Quando lanceremo il task compileJava
, Gradle si occuperà di:
plugins {
java // JavaPlugin aggiunge un source set "test" e un task "test"
}
repositories {
mavenCentral() // occorre per risolvere le dipendenze di JUnit
}
dependencies {
// Dipendenza per scrivere i test
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.1")
// Dipendenza per eseguire i test
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1")
}
tasks.test {
useJUnitPlatform() // configura il task "test" per usare la JUnit Platform
testLogging { events("passed", "skipped", "failed") } // per stampare l'esito di ogni test
}
$ ./gradlew test
$ ./gradlew test --tests it.unibo.*.Buggy*Test # filtra i test da eseguire