Intéressé par des cours d'informatique en ligne ?
Visitez mon nouveau site https://www.yesik.it !

Cet article est une reprise de l'article Construire une application Java avec Ant que j'avais précédemment publié sur http://wiki.esicom-st-malo.fr

Dans ce tutoriel, nous allons voir comment construire une application Java avec Ant.

Objectifs A la fin de ce tutoriel, vous saurez:
  • Utiliser la tâche javac dans un fichier build.xml,
  • Automatiser la compilation d'une application Java.
Prérequis
Moyens


Le projet

Fichiers sources

L'application qui va servir de support à ce tutoriel est un programme de simulation de compte bancaire. Elle est composées de deux classes Banque et Compte.

La classe Compte est chargée de la gestion d'un compte. Et permet de créditer ou débiter celui-ci. elle est définie dans le fichier Compte.java dont voici le source:

package fr.esicom.exemples;
 
/**
 * Un compte en banque
 */
public class Compte {
        private double solde;
 
        /**
         * Ouvre un compte.
         */
        Compte() {
                solde = 0;
        }
 
        /**
         * Depose de l'argent sur le compte
         */
        public void depose(double somme) {
                solde += somme;
        }
 
        /**
         * Retire de l'argent du compte.
         */
        public double retire(double somme) {
                if (somme > solde)
                        somme = solde;
                if (somme < 0)
                        return 0;
 
                return solde -= somme;
        }
 
        /**
         * Affichage
         */
        public String toString() {
                return "Le solde du compte est de " + solde;
        }
};

La classe Banque ne contient que le programme principal. C'est un simple programme de démonstration qui instancie un Compte, le crédite, puis le débite et enfin affiche le solde. Cette classe est définie dans le fichier Banque.java

package fr.esicom.exemples;
 
public class Banque
{
        public static void main(String argv[])
        {
                Compte compte = new Compte();
                compte.depose(3000);
                compte.retire(1200);
 
                System.out.println(compte.toString());
        }
};

Organisation du projet

Notre projet va être créé dans le répertoire ProjetBanque. Celui-ci contient deux sous répertoires:

Pour l'instant, le répertoire build est vide. Le répertoire src contient lui une hiérarchie de sous-répertoires qui reprennent les parties du nom de package fr.esicom.exemples. Les fichiers sources sont placés dans le dernier sous-répertoire.

Il est impératif de respecter cette hiérarchie pour que Ant puisse fonctionner correctement.

Compiler le projet

Le fichier build.xml

Pour fonctionner, Ant a besoin d'un fichier build.xml qui définit les différentes cibles et les tâches à effectuer. Notre fichier build.xml sera situé à la racine du projet.

Pour l'instant, il contiendra le code suivant:

<?xml version="1.0" ?>
<project name="ProjetBanque" default="compile">
        <target name="compile">
                <javac srcdir="src" destdir="build" />
        </target>
</project>

Ce fichier définit une cible appelée compile. Dans cette cible, on trouve une tâche javac.

Cette tâche a pour but de compiler des fichiers .java. Elle va chercher dans le répertoire indiqué par l'attribut srcdir (et tous ses sous-répertoires) les fichiers sources pour les compiler. Les fichiers compilés sont stockés dans le répertoire indiqué par l'attribut destdir.

La tâche javac ne compile que les fichiers dont le .class est absent du répertoire destdir ou ceux qui sont plus récents que ceux présents dans le répertoire destdir.

Compiler

Pour voir javac en action, ouvrez un terminal et tapez:

 sh$ cd ProjetBanque
 sh$ ant

Si tout s'est bien passé, vous devriez obtenir l'affichage suivant:

Buildfile: build.xml

compile:
    [javac] Compiling 2 source files to /Users/sylvain/ProjetBanque/build

BUILD SUCCESSFUL
Total time: 4 seconds

Tester

Pour tester le programme, il vous faut aller dans le sous-répertoire build (puisque c'est là que sont enregistrés les fichiers compilés), puis invoquer java (en précisant bien le nom qualifié de votre classe -- c'est à dire sans oublier le nom de paquetage):

 sh$ cd build
 sh$ java fr.esicom.exemples.Banque

Vous devriez avoir la joie de constater qu'il vous reste en banque:

Le solde du compte est de 1800.0

Que s'est-il passé?

Si l'on examine le contenu du répertoire build, on se rend compte que la tâche javac y a créé une hiérarchie similaire à celle du dossier src. Les fichiers Banque.class et Compte.class se trouvant dans le dernier sous-répertoire.

En fait, pour être plus précis, la tâche javac a créé tout une arborescence à partir de build qui reflète le nom du package auquel appartiennent les classes:

Ne recompiler que ce qui est nécessaire

Tout l'intérêt d'utiliser ant plutôt qu'un simple script shell, c'est qu'il ne va recompiler que ce qui est nécessaire.

Nous allons illustrer ici le fait que la tâche javac ne recompile que les sources dont la version compilée est absente ou ceux qui sont plus récent que leur version compilée.

.class absent

Nous allons supprimer un des deux fichiers compilés, puis relancer ant. Dans un terminal, à partir du répertoire ProjetBanque:

 sh$ rm build/fr/esicom/exemples/Banque.class
 sh$ ant
Buildfile: build.xml

compile:
    [javac] Compiling 1 source file to /Users/sylvain/ProjetBanque/build

BUILD SUCCESSFUL
Total time: 4 seconds

Notez que la tâche javac indique qu'elle ne compile qu'un seul fichier cette fois ci.

.java plus récent que le .class

Maintenant voyons ce qui se passe quand on modifie un fichier .java.

Plutôt que de modifier vraiment le fichier, nous allons nous servir de la commande touch qui permet de changer la date de dernière modification d'un fichier. Ce sera suffisant pour abuser ant:

 sh$ touch src/fr/esicom/exemples/Compte.java
 sh$ ant

Vous devriez obtenir le résultat suivant:

Buildfile: build.xml

compile:
    [javac] Compiling 1 source file to /Users/sylvain/ProjetBanque/build

BUILD SUCCESSFUL
Total time: 3 seconds

Ici encore, un seul fichier recompilé: celui qui a été modifié.

Et si rien n'a changé

Si les fichiers .class sont présents, et si les fichiers .java n'ont pas été modifiés depuis la dernière compilation, la tâche javac ne fait rien:

 sh$ ant
Buildfile: build.xml

compile:

BUILD SUCCESSFUL
Total time: 1 seconds

Quelques cibles de plus

la cible clean

Que ce soit pour archiver ou pour diffuser son code, il n'est pas nécessaire de conserver les versions compilées des fichiers. Seuls les sources sont intéressants et de toute façon, il est toujours possible de recompiler l'application.

Généralement les buildfiles ant contiennent toujours une cible spécifique pour faire le ménage. traditionnellement cette cible s'appelle clean.

Dans notre cas, faire le ménage revient simplement à supprimer le répertoire build.

Nous allons donc ajouter la cible suivante au fichier build.xml:

<target name="clean" description="Fait le ménage">
        <delete dir="build" />
</target>

Empressons nous de tester:

 sh$ ant clean
Buildfile: build.xml

clean:
   [delete] Deleting directory /Users/sylvain/ProjetBanque/build

BUILD SUCCESSFUL
Total time: 1 second

Vous pourrez facilement vérifier que le répertoire build a été supprimé.

la cible init

Après avoir fait le ménage à l'étape précédente, on peut essayer de reconstruire l'application:

 sh$ ant
Buildfile: build.xml

compile:

BUILD FAILED
/Users/sylvain/ProjetBanque/build.xml:4: destination directory
"/Users/sylvain/ProjetBanque/build" does not exist or is not a directory

Total time: 1 second

Malheureusement, il y a une erreur: Ant nous signale qu'il ne peut pas construire la cible compile car le répertoire ProjetBanque/build n'existe pas. Effectivement, nous l'avons supprimé avec la cible clean.

Nous allons donc ajouter à notre build.xml la cible init spécialement pour créer ce répertoire. Et nous allons dire que la cible compile dépend de init. Voici le code complet de build.xml:

<?xml version="1.0" ?>
<project name="ProjetBanque" default="compile">
        <target name="init">
                <mkdir dir="build" />
        </target>
        <target name="compile" depends="init">
                <javac srcdir="src" destdir="build" />
        </target>
        <target name="clean" description="Fait le menage">
                <delete dir="build" />
        </target>
</project>

Exécuter la commande ant produit maintenant le résultat:

Buildfile: build.xml

init:
    [mkdir] Created dir: /Users/sylvain/ProjetBanque/build

compile:
    [javac] Compiling 2 source files to /Users/sylvain/ProjetBanque/build

BUILD SUCCESSFUL
Total time: 4 seconds

Le répertoire build est d'abord créé, puis l'application est compilée avec succès.

Keep it DRY

Si l'on examine la dernière version de notre fichier build.xml, on se rend compte que l'information qui précise le nom de notre répertoire build est répété dans chacune des trois cibles. En vertue du DRY principle, nous allons essayer d'éviter cette redondance.

Pour ce faire, Ant nous propose un outil sous la forme des propriétés. Nous allons donc créer une propriété qui mémorise le nom du dossier qui sert à enregistrer les fichiers compilés. Et dans un soucis d'uniformité, nous allons également créer une propriété pour le nom du dossier source.

Le code complet devient:

<?xml version="1.0" ?>
<project name="ProjetBanque" default="compile">
        <property name="build.dir" value="build" />
        <property name="src.dir" value="src" />
 
        <target name="init">
                <mkdir dir="${build.dir}" />
        </target>
        <target name="compile" depends="init">
                <javac srcdir="${src.dir}" destdir="${build.dir}" />
        </target>
        <target name="clean" description="Fait le menage">
                <delete dir="${build.dir}" />
        </target>
</project>

Maintenant, il n'y a que dans les deux éléments property que sont codés "en dur" les noms de nos répertoires src et build. Ce seront donc les seules lignes à changer si ces répertoires changent.

Voir aussi