Vai al contenuto

Ingegneria del Software - Maven

Pubblicato:

Il build automation tool più famoso.

Cosa è Maven?

Maven

Maven è un software che permette di automatizzare il processo di build di un progetto basato su Java.

Nato sotto l'egida dell'Apache Software Foundation, ha come obiettivo quello di semplificare la gestione dello sviluppo software.

Perché è utilizzato?

Abbiamo visto che, non appena un progetto software inizia ad includere più file o persino delle dipendenze esterne, il processo di compilazione inizia a diventare sempre più complesso.
Questo senza nemmeno considerare fattori come il testing, la distribuzione degli artefatti, la gestione delle versioni, la documentazione, la manipolazione del bytecode e così via.

Affinché il processo di build sia sempre riproducibile, bisogna quindi definire una sequenza di step standardizzata.
Anche in questo caso, poter definire tutti i dettagli in un file con un formato noto è praticamente una necessità.

Obiettivi di Maven

  • Rendere quanto più indolore possibile il processo di build
  • Fornire un build system uniforme e riproducibile
  • Gestire le dipendenze
  • Fornire informazioni di qualità sullo stato del progetto
  • Favorire l'utilizzo di best practices nel processo di sviluppo

Primi passi con Maven

Per poter utilizzare Maven, è necessario scaricandolo dal sito ufficiale ed installarlo o utilizzare il proprio gestore di pacchetti preferito.

Per assicurarci che l'installazione sia avvenuta con successo, si può controllare l'output del comando

mvn --version

Un help tooltip è disponibile con il comando

mvn -h

Iniziare un progetto

Per iniziare un nuovo progetto, è sufficiente creare una cartella e lanciare il comando

mvn archetype:generate \
-DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4

Il programma chiederà di inserire ulteriori informazioni:

  • groupId: identificativo univoco (dominio) dell'organizzazione o sviluppatore
  • artifactId: nome del progetto e della cartella che lo conterrà
  • version: versione del progetto
  • package: tipo di packaging (jar, war, ear, pom, rar, ejb)

Struttura del progetto

Una volta creato il progetto, la struttura della cartella sarà la seguente:

.
├── pom.xml # file di configurazione per Maven
└── src
    ├── main
    │   ├── java
    │   │   └── edu
    │   │       └── tend
    │   │           └── App.java # codice sorgente
    │   └── resources
    └── test
        ├── java
        │   └── edu
        │       └── tend
        │           └── AppTest.java # test
        └── resources

Project Object Model (POM)

Il file pom.xml è un file xml che serve a configurare il comportamento di Maven.

Pom content

Elementi necessari

Vi sono un numero spropositato di possibili configurazione utilizzabili.
Quelle necessarie, però, sono solo:

  • modelVersion: versione del pom
  • groupId: identificativo univoco (dominio) dell'organizzazione o sviluppatore
  • artifactId: nome del progetto e della cartella che lo conterrà
  • version: package usato dal progetto. Spesso corrisponde al groupId
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>edu.tend</groupId>
    <artifactId>maven-1</artifactId>
    <version>1</version>
</project>

Estensibilità del POM

Per estendere esplicitamente un pom, magari da un sotto-modulo, si può utilizzare il tag <parent>:

<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>edu.tend</groupId>
        <artifactId>maven-1</artifactId>
        <version>1</version>
        <!-- <relativePath>../original/pom.xml</relativePath> -->
    </parent>
    <artifactId>maven-module</artifactId>
</project>

La combinazione di groupId, artifactId e version prende il nome di coordinate.

Tutti i progetti derivano implicitamente da un pom di default.

Variabili

Per definire delle variabili, si può utilizzare il tag <properties>.

<project>
    <!-- ... -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <la.mia.variabile>valore</la.mia.variabile>
    </properties>
</project>

Alcune variabili speciali sono già definite, come ad esempio

  • project.basedir: la cartella del progetto
  • maven.build.timestamp: il timestamp in cui avviene la build (UTC)
  • env.PATH: la variabile d'ambiente PATH

Utilizzare le variabili

Le variabili possono essere referenziate con la sintassi ${nome.variabile}.

<project>
    <!-- ... -->
    <properties>
        <mavenVersion>3.6.3</mavenVersion>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-artifact</artifactId>
            <version>${mavenVersion}</version>
        </dependency>
    </dependencies>
</project>

Alcune variabili con nomi specifici potrebbero essere utilizzate implicitamente da plugin o da altri elementi del pom.

Dipendenze

Uno degli aspetti più comodi di Maven è la gestione automatica delle dipendenze del progetto. Per indicare una o più dipendenze, si può utilizzare il tag <dependencies>.

<project>
    <!-- ... -->
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-artifact</artifactId>
            <version>${mavenVersion}</version>
        </dependency>
    </dependencies>
</project>

Dipendenze: scope

Lo scope indica il contesto in cui la dipendenza è utilizzata, se e quando deve essere inclusa e in quali fasi di build del progetto.

I possibili valori sono:

  • compile: dipendenza di default. Viene inclusa in tutte le fasi di build.
  • provided: dipendenza che ci si aspetta sarà fornita a runtime, ma necessaria per la compilazione
  • runtime: dipendenza utilizzata solo in runtime, ma non in compilazione.
  • test: dipendenza utilizzata solo in fase di test.
  • system: dipendenza fornita dal sistema per cui è necessario indicare il path del jar.

Dipendenze: versione

Per indicare la versione di una dipendenza, si utilizza il tag <version>.
Si può anche specificare un range accettabile di versioni, dove [] indica inclusione e () esclusione.

<project>
    <!-- ... -->
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>[3.0,4.11)</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Dipendenze: transitività

Maven gestisce le dipendenze in maniera transitiva. Se B dipende da C e D, nel momento in cui A utilizza B, Maven si occuperà di aggiungere implicitamente le dipendenze di B.

Loading diagram...

Se si vuole dare maggiore libertà di scelta sulla specifica dipendenza all'utente finale, è possibile aggiungere nella dipendenza il tag <optional>true</optional>.

Lifecycle

Maven organizza gli step di build in una serie di fasi, la cui sequenza prende il nome di lifecycle.

I tre lifecycle principali sono:

  • default: lifecycle di default, che include le fasi validate, compile, test, package, verify, install e deploy.
  • clean: lifecycle che include la fase clean, che rimuove tutti i file generati dalla build.
  • site: lifecycle che include la fase site, che genera il sito di documentazione del progetto.

Fasi

Come un lifecycle è una sequenza di fasi, una fase è una sequenza di goal.

Quando viene invocata una fase, Maven esegue tutte le fasi precedenti nel lifecycle, e per ciascuna fase esegue i goal corrispondenti.

Goal

Un goal è un task specifico che viene eseguito da Maven quando si arriva alla fase corrispondente, ma possono anche essere lanciati singolarmente.

I goal sono definiti dai plugin e ne prendono il nome:

  • maven-compiler-plugin:compile
  • maven-surefire-plugin:test

Default lifecycle

Loading diagram...

Plugin

Un plugin forniscono un insieme di goal che per Maven.
Per aggiungere un plugin al progetto, si utilizza il tag <plugin>.

<project>
    <!-- ... -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Distribuzione

Maven permette di specificare una repository remota dove caricare il progetto per essere distribuito.

<project>
    <!-- ... -->
    <distributionManagement>
        <repository>
            <id>github</id>
            <name>GitHub Packages</name>
            <url>https://maven.pkg.github.com/User/Project</url>
        </repository>
    </distributionManagement>
</project>

Il repository remoto richiederà delle credenziali per potervi accedere. Queste vanno specificate nel file ~/.m2/settings.xml.

Ulteriori dettagli

Challenge

  • Creare un progetto Maven
  • Creare una test suite per il progetto
  • Utilizzare un sistema come le Github Actions per distribuire il progetto