Apache Ant è uno degli strumenti di build più diffusi: permette di definire diverse operazioni (task) per la gestione del ciclo di sviluppo/deploy del codice, definiti tramite appositi file xml (di solito build.xml
). Apache Ivy è invece un tool specificamente orientato alla gestione delle dipendenze, progettato per essere utilizzabile autonomamente o anche in sinergia con Ant.
Possiamo creare delle dipendenze tra i task in questo modo:
- Task 1: creazione folder su file system
- Task 2: compilazione del codice sorgente
- Task 3: creazione dell’archivio war
- Task 4: spostamento del war nella directory di deploy
- Task 5: cancellazione delle directory temporanee create
Ant è certamente uno strumento potente: permette di specificare delle property che rappresentano path o variabili di naming, permette di specificare cartelle in base a filtri, etc. L'esigenza di utilizzare uno strumento che permetta un controllo sempre più accurato sulla fase di build si sta traducendo nella realizzazione di strumenti software ancora più complessi, come Gradle o Maven.
Un aspetto particolarmente critico è quello legato alla gestione delle dipendenze di progetto (cioè la necessità di tenere traccia delle versioni corrette delle librerie di terze parti incluse), in particolar modo quelle transitive.
Immaginiamo un tipico caso d'uso di esempio:
Utilizzo nel mio progetto LIBRERIA 1 vers. 1.45
, questa a sua volta per funzionare ha necessità di LIBRERIA 2 vers. 1.58
, e quest’ultima contiene una classe chiamata ConnectionHandler.java
che ha il metodo recuperaConnection()
Immaginiamo ora di cambiare versione della libreria suddetta (magari la nuova versione risolverà un bug). Un caso abbastanza tipico che può presentarsi è quello in cui la firma del metodo che ci interessa rimane immutata, ma magari la differente nuova implementazione interna produce effetti collaterali imprevisti (è ad esempio incompatibile al contemporaneo utilizzo di una terza libreria). Come regolarsi in questi casi? Quali versioni scegliere? Allo stesso modo immaginiamo che la seconda libreria utilizzi al suo interno un'altra libreria interna che va aggiornata, e così via.
Anche partendo da casi semplici del genere appare evidente come non basti una gestione delle librerie basata sulla verifica della loro presenza e delle corrette firme dei metodi, ma che serva anche attenzione alle dipendenze e ai loro effetti collaterali.
Questo genere di possibilità sono offerte da tool come maven, e nel caso di Ant sono implementabili tramite l'utilizzo di Ivy
Installazione e configurazione di Apache Ivy
Effettuiamo il download dell’archivio: http://mirror.nohup.it/apache//ant/ivy/2.3.0-rc2/apache-ivy-2.3.0-rc2-bin.zip
Una volta effettuato il download estraiamo il jar contenuto e lo copiamo sotto la cartella lib
di Ant.
Oltre ad un progetto java dobbiamo ovviamente avere installato Apache Ant sul nostro sistema, configurando le variabili ANT_HOME
(alla directory di installazione di Ant) e aggiungendo alla variabile di sistema Path
il percorso alla directory bin
dell’installazione di Ant.
Creiamo un progetto Java e chiamiamolo IviProject
quindi creiamo una cartella lib
e copiamo il jar anche in questa posizione:
Aggiungiamo quindi il jar al build classpath del progetto come mostrato in figura.
Fatto questo possiamo creare una classe di test seguente:
package com.ivytest;
import org.apache.commons.lang.WordUtils;
public class TestIvyWithAnt {
public static void main(String[] args) {
final String msg = "hello, world!";
System.out.println(WordUtils.capitalizeFully(msg));
}
}
Ovviamente il codice non può essere compilato, mancano infatti le librerie necessarie per la compilazione stessa.
Il file build.xml
Creiamo un semplice file build.xml per utilizzarlo con ant, questo il contenuto:
<?xml version="1.0" encoding="UTF-8"?>
<project name="IviProject" basedir="." default="compile" >
<property name="src.dir" value="${basedir}/src" />
<property name="bin.dir" value="${basedir}/bin" />
<target name="init">
<mkdir dir="${bin.dir}" />
<ivy:retrieve/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${bin.dir}" fork="true"></javac>
</target>
</project>
Provando già a lanciare ant, la compilazione fallisce per l’impossibilità di risolvere le referenze interne al codice, mancando le librerie in apache.commons.lang
, necessarie per la corretta compilazione. Ora modifichiamo il build.xml
come segue:
i task
L’elemento
<path id="ivy.lib">
<pathelement location="${basedir}/lib/ivy-2.3.0-rc2.jar" />
</path>
dichiara il path alla libreria di ivy, mentre il taskdef (acronimo di task-definition) si occupa sommariamente dell’integrazione tra i due ambienti ed utilizza il path-reference prima creato:
<taskdef uri="antlib:fr.jayasoft.ivy.ant" resource="fr/jayasoft/ivy/ant/antlib.xml" classpathref="ivy.lib" />
Creiamo ora un file xml che chiamiamo Ivy.xml nella root del nostro progetto (dove risiede build.xml):
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="purpletube.net" module="IvyAnt" status="integration" />
<dependencies>
<dependency org="commons-lang" name="commons-lang" rev="2.0" />
</dependencies>
</ivy-module>
Ivy ed i repository maven
Per capire quanto scritto dobbiamo fare un passo indietro riprendendo Maven. Questo strumento in pratica prevede l’esistenza di un repository locale o remoto (un database di librerie, dove poter trovare quelle che servono). Il Repository quindi è un contenitore di librerie e per ognuna ovviamente è previsto il versionamento, cioè per ogni libreria esistono diverse versioni.
A partire dal repository remoto le librerie effettivamente necessarie saranno scaricate in quello locale (una sorta di “cache”, per i nostri progetti). Ivy riutilizza la stessa logica, ma dovremo stabilire dove cercare le librerie: per fare questo possiamo utilizzare lo stesso repository di Maven. Ad esempio cercando apache commons sul sito Maven Repository:
da cui possiamo ottenere:
Maven identifica ogni librerie attraverso delle "coordinate" che sono le seguenti:
<dependency>
<groupId>commons-lang<:/groupId>
<artifactId>commons-lang<:/artifactId>
<version>2.0<:/version>
</dependency>
Le dipendenze in ivy,xml
Ivy utilizza la stessa metodologia, quindi dopo aver localizzato le coordinate le inseriamo nel file ivy.xml come segue:
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="purpletube.net" module="IvyAnt" status="integration" />
<dependencies>
<dependency org="commons-lang" name="commons-lang" rev="2.3" />
</dependencies>
</ivy-module>
test di Ant
Ora apriamo una shell dos, ci accertiamo che ant funzioni correttamente lanciando il comando ant -version
, come segue:/p>
Quindi posizioniamoci sotto la nostra cartella e digitiamo ant
, la fase di build verrà avviata e per prima cosa Ivy scaricherà le librerie :
Come risultato le librerie di riferimento sono state scaricate dal repository e risolte le dipendenze (anche quelle transitive).
Aggiornando il build path in eclipse sul nostro progetto risolveremo anche gli errori di compilazione.