ANT : le make de Java

 

 

 

 

 

Description

Objet :

Description technique de l'outil ANT

Résumé :

ANT est en passe de devenir au monde Java ce que MAKE est devenu au monde du C : un outil incontournable. Plutôt que d'offrir un nouveau tutorial technique (qui pourrait d'ailleurs faire l'objet d'un autre article), ce document vise à présenter différents cas d'utilisation et un ensemble de bonnes pratiques relatives à l'usage de ANT.

L'axe de réflexion principal porte sur le fait que l'utilisation de ANT va bien au delà d'un simple apport technique. ANT exige une réelle démarche méthodologique autour de l'organisation des fichiers et de leur devenir dans les différents processus de livraison (avec des problémes tels que la séparation sources-livrables ou la gestion multi-environnements/multi-OS). Les apports de la dernière version, sortie récemment, seront également abordés du fait qu'ils présentent des avancées majeures (interactivité, chaînage des actions, ...) [NON TRAITE].

Diffusion :

Libre

Auteur :

Sylvain FRANCOIS

Date de création :

26 août 2002

 

 

 

 

 

 

 

Historique

Version

Date

Destinataires

Pour

Commentaires

0.1

26/08/2002

Jean-Michel ROQUE

Relecture technique

-

0.2

28/08/2002

Jean-Michel ROQUE

Validation remarques

Précisions diverses

+ approfondissement sur la création d'un document à partir d'une configuration ANT

+ schéma construction sites WEB à partir d'XML

+ rappel vocabulaire ANT

0.3

28/08/2002

Cecile MARELLE

Jeanne LETHIELLEUX

Helene BASSET-CHERCOT

Relecture formelle

 


Heureux informaticiens pour qui la paresse est la plus belles des vertus ! Vertu qui peut être exercée de deux manières pour réaliser des tâches répétitives : en entrant  dans un mode de veille où les mains priment sur les neurones, ou en profitant des bienfaits de l'automatisation apportés par l'informatique. A moins de considérer l’informatique comme un exercice d’entretien physique, la deuxième solution est généralement plus efficace, et ANT fait partie des outils facilitant la définition de cette automatisation.

 

Dans le monde Java, ANT est devenu l’outil incontournable pour automatiser des traitements répétitifs en mode batch. Il possède tous les atouts propres aux standards : simple, bâti sur des technologies ouvertes (Java et XML), extensible, et supporté par des acteurs stratégiques (dont IBM, SUN et BEA pour les sociétés commerciales, mais aussi la grande majorité de la communauté Java OpenSource).

 

Les introductions techniques à  ANT sont désormais nombreuses. Cet article propose deux chapitres apportant une vision complémentaire. Le premier se positionne en amont pour préciser l'intérêt et les champs d'application d'un outil d'automatisation ainsi que les facteurs différenciateurs de ANT. Le second s'adresse aux utilisateurs novices souhaitant perfectionner leur pratique de cet outil magique.

Pourquoi utiliser ANT

Plusieurs raisons peuvent vous amener à vous intéresser à ANT :

 

§         c'est un ordre du chef

§         vous récupérez de plus en plus des projets qui deviennent infectés par un certain fichier build.xml et vous aimeriez en savoir plus avant de développer un anti-virus

§         vous considérez, à juste raison, que tout projet issu de Jakarta vaut le coup d'œil (Jakarta est le méta-projet de la fondation Apache hébergeant des projets Java tels que Tomcat, Struts ou Log4J).

§         vous cherchez un équivalent de Make en Java

§         après une douzaine de livraisons infructueuses réalisées dans l'après-midi, vous comprenez qu'un outil d'automatisation vous aurait évité de manquer la dernière séance de cinéma.

L’automatisation

Pour mesurer l'intérêt d'un outil tel que ANT, arrêtons-nous un instant sur les avantages apportés par l'automatisation. Alors que l'automatisation est au cœur-même de l'informatique, légions sont les informaticiens n'appliquant pas ses principes dans leur propres développements. Au moins 2 raisons à cela :

 

§         quand un processus fonctionne, même s'il est fastidieux, on a tendance à le garder tel quel pour ne pas troubler ses habitudes ni risquer des régressions en utilisant un autre processus pourtant mieux conçu

§         pas mieux loti qu'un cordonnier, l'informaticien, qui travaille pour automatiser les traitements de ses clients, n'est pas toujours dans un état d'esprit lui permettant de s'appliquer à lui-même ses compétences et de devenir ainsi son propre client.

 

Si le premier avantage visible de l'automatisation concerne les gains en terme de rapidité d'exécution (sauf cas plutôt rares, la machine est plus rapide que l'homme), ce n'est pas forcément le plus intéressant. Automatiser des traitements permet d'améliorer la productivité pour d'autres raisons :

 

§         fiabilité : force est de reconnaître que l'humain est largement plus faillible que la machine (et espérons qu'il le reste !). Même en suivant une procédure écrite, il est toujours possible d'oublier un point ou de le réaliser incorrectement une opération. Par définition, un processus automatisé exécute un traitement en respectant précisément une définition de tâches.

§         procéduralisation : établir une procédure précise décrivant une séquence de traitements et la maintenir à jour est souvent vécue comme un travail pénible. On trouve alors des documents finaux qui ne sont pas toujours synchronisés avec la procédure appliquée. Dans le cas d'un processus automatisé la procédure référence est constituée par la configuration de l'outil. Cette configuration est plus ou moins lisible selon son format technique, mais on peut à tout moment en dégager une procédure accessible et actualisée (on verra que le choix d'XML pour la configuration de ANT constitue un véritable atout dans ce domaine).

§         traçabilité : quand une procédure a été appliquée, il est important de pouvoir récupérer un historique des opérations effectuées pour vérifier sa validité. C'est ce que fait généralement un humain à l'aide d'une "check-list" et en historisant les fichiers logs. Mais là encore, l'humain est faillible, et on accordera plus facilement confiance aux logs d'un processus automatisé pour déceler un éventuel problème.

§         structuration : c'est peut-être l'avantage le moins visible mais le plus important de l'automatisation sur le long terme. Etablir une procédure, même non automatisée, vous confronte quasiment systématiquement à la problématique de structuration de vos données. Cet aspect est souvent renforcé par les contraintes techniques inhérentes à un processus d'automatisation (par exemple pour définir des sélections de fichiers il est plus facile de les regrouper dans des répertoires distincts). Il devient alors nécessaire de mieux structurer ses fichiers selon leur format, selon qu'ils sont des fichiers sources, temporaires ou compilés, selon leur destination (eg. certains fichiers sont adaptés spécialement pour un OS), 

Pourquoi ANT ?

Selon les domaines d’application, il existe différents moyens d’automatiser des traitements batchs : fichiers batchs, outil MAKE, programmes d’ordonnancements, installateurs voire des programmes développés spécifiquement.

 

Par rapport à ces solutions, on serait de mauvaise foi de mettre en avant ANT sur les plans de la robustesse ou de l’efficacité. Utilisé quotidiennement depuis plus de 2 ans au sein de communautés OpenSource comme de grandes entreprises commerciales, ANT est assurément robuste, mais pas plus que d’autres produits déjà éprouvés depuis de nombreuses années. ANT est également extrêmement efficace dans le sens où il peut prendre en charge de nombreuses problématiques (cf. paragraphe suivant), mais s’il est performant dans certains domaines (eg. : construction d’application), d’autres produits présentent une meilleurs qualité de service dans d’autres domaines (eg. : installation de logiciels) .

 

Pourtant, ANT dispose de nombreux différenciateurs qui le rendent unique :

 

§         portabilité : implémenté en Java et configurable via XML, un module d'automatisation réalisé avec ANT fonctionne de la même façon sous n'importe quel OS supportant Java. ANT permet également d'effectuer des traitements spécifiques selon l'OS (chmod sous Unix, adaptation des caractères spéciaux CR/LF, …)

§         extensibilité : ANT est conçu de telle manière que l'intégration de nouvelles tâches (interfaçage avec un produit, opération utilitaire, …) est un jeu d'enfants. Il est aussi possible de particulariser l'exécution à l'aide d'un mécanisme de listeners suivant tout le déroulement du traitement.

§         simplicité : quiconque sait lire une documentation et peut atteindre les touches de son clavier est en mesure d'utiliser ANT. La configuration XML est simple, tout comme les concepts de base. Il est parfois plus facile d'utiliser certains outils via ANT (eg. : javadoc ou javac) du fait de l'homogénéité des tâches ANT qui les interfacent, que ce soit au niveau de leur syntaxe ou de leur documentation.

§         configuration XML : le fait que ANT soit configurable via XML permet à la fois de s'affranchir de l'apprentissage d'un nouveau format propriétaire (mais pas de la syntaxe !) et de bénéficier des nombreux moyens de traitement disponibles autour d'XML, par exemple pour créer un document procédural au format HTML directement à partir de la configuration ANT via XSL.

§         OpenSource : sans insister sur les avantages offerts par l'OpenSource, trois points méritent d'être soulignés : 1 - les sources sont facilement lisibles du fait que ANT est de taille modeste et bien écrit, 2 – contrairement à d'autres projets OpenSource, ANT n'induit aucun coût caché (assistance technique, documentation payante, …), 3 – la licence Apache protégeant ANT est très permissive, elle permet notamment de commercialiser un produit bâti sur ANT.

§         popularité : ANT s’impose peu à peu dans la plupart des développements Java en devenant un standard pour la construction d’applications, ce qui lui garantit une pérennité certaine.

 

L'absence d'interface graphique constitue néanmoins un frein important pour bon nombre d'utilisateurs. ANT n’en propose pas plus pour l'administration que pour le suivi de l'exécution. Le projet Antidote devait combler cette lacune, mais son développement se fait attendre. D’autres projets aboutiront sans aucun doute dans un futur proche. Il faut également souligner que la plupart des IDE Java disposent de plugins dédiés à l'utilisation de ANT, avec leur propre interface graphique : JBuilder, Eclipse/WSAD, NetBeans/Forte, Intellij IDEA, ...

Champs d'application

L’utilisation la plus répandue de ANT concerne le processus de construction d'applications Java. ANT était à l’origine le module de construction du projet Tomcat avant d’être externalisé dans projet autonome. Mais ANT ne se réduit pas à un outil de construction. Son caractère extensible lui a permis d’accueillir des tâches diverses (commandes FTP, envoi de mail, manipulations XML, interfaçage avec des systèmes de gestion de versions, …) lui conférant ainsi un intérêt dans une multitude de domaines. Extensibilité qui permet également de lui greffer de nouvelles tâches répondant à des besoins spécifiques.

 

A ceux qui se demanderaient en quoi ANT pourrait leur être utile, voici quelques idées d’utilisation :

 

Construction d'applications

On l’a déjà mentionné, c’est l’utilisation principale de ANT, celle pour qui l'offre de tâches est la plus riche.

 

L'intérêt d'un module de construction au sein d'une application est de garantir à tout moment une livraison fiable de l'application sous sa forme finale (.jar, .war., .ear, …) à partir de ses sources, voire son déploiement sur un serveur. Quand l'application est sensible, il devient inconcevable de réaliser l'installation manuellement en raison des risques d'oubli de livraison de fichiers ou d'opérations diverses (eg. adaptation des propriétés de configuration, correction des caractères spéciaux CR/LF, …). Le gain de temps apporté par une installation automatisée est également un facteur primordial, surtout quand le mode de développement choisi requiert un rythme d'intégration élevé (cf. processus d'intégration continue de la méthode Extreme Programming).

 

Pour schématiser, un module de construction pourra réaliser les traitements suivants :

 

Figure 1 : Exemple de processus de construction

Ce module propose deux branches de construction : l’une destinée à déployer l’application, l’autre à distribuer l’application (avec ou sans les sources).

 

ANT est particulièrement efficace pour prendre en charge les problématiques inhérentes à ce type de processus :

 

§         interfaçage avec la plupart des systèmes de gestion de versions (CVS, PVCS, Perforce, SourceSafe, Starteam, ClearCase, …), pour extraire les sources, poser des labels, récupérer des historiques, …

§         compilations Java autorisées avec la plupart des compilateurs Java (JDK 1.1->1.4, Jikes, GJC, …) et configurables de manière très fine (sélections de fichiers, classpaths, gestion de la mémoire, …)

§         aide au packaging d'EJB pour les principaux serveurs (JBoss, Jonas, WebLogic, WebSphere, …)

§         interfaçage avec JUnit pour réaliser des tests unitaires

§         gestion des principaux format d'archivage et de compression (ZIP, JAR, TAR, GZIP, BZIP2)

§         compilations JSP, JavaCC

§         gestion de fichiers complète (créations, suppressions, copies, déplacements, renommages, remplacements, …)

 

Si l'offre de tâches dédiées à Java est pléthorique, ANT n’est pas réservé à des applications Java. ANT propose suffisamment de tâches implémentant des opérations génériques (gestion de fichiers, compression/décompression, FTP, Telnet, ... ) pour être utile pour d’autres types d'applications, en bénéficiant toujours de la portabilité. Des tâches spécifiques à d’autres environnements de développement sont également proposées, par exemple pour C++ (projet AntContrib) ou .NET (qui fait également l'objet d'un nouvel outil dérivé de ANT nommé NAnt).

 

Construction de sites basés sur XML

La séparation contenu/présentation des sites Web a tendance à se généraliser. Le contenu informatif est souvent réparti dans des documents XML pour les informations textuelles, avec des liens vers des ressources multimédia. Les éléments de présentation sont généralement implémentés à l’aide de feuilles de styles XSL. Les mécanismes de publication visant à générer les documents visibles par le client (au format HTML, WML, PDF, …) doivent impérativement être mis en œuvre de manière générique pour être efficaces.

 

Figure 2 : Construction d'un site WEB basé sur XML

 

ANT est parfaitement adapté à ce type de problématique basée sur des traitements par lots de fichiers. Son utilisation est d’autant plus utile qu’il propose des tâches permettant d’effectuer des validations XML ou des transformations XSLT. Rajouter des tâches implémentant des opérations telles que des compilations XSL ne demande pas non plus un effort insurmontable. Les relations de dépendances entre les fichiers sources, les fichiers de transformations, les ressources multimédia et les documents finaux peuvent également être facilitées par les mécanismes de sélection de fichiers de ANT qui peuvent s'appuyer sur les différences de date.

 

ANT peut ainsi constituer le noyau d'un moteur de publication, en bénéficiant toujours de l’énorme avantage que constitue sa portabilité dans ce domaine (on rappellera que la licence Apache permet d'utiliser et de distribuer librement ANT, même sous forme commerciale). De nombreux systèmes de documentation basés sur XML l'utilisent également comme moteur de transformation.

 

Synchronisation d'espaces de données (eg. : sites WEB, espaces de développement)

La synchronisation d'un espace de données distant avec un environnement de travail local est encore à la portée de ANT. Transférer des fichiers via FTP/SCP ne pose aucun problème (s'il n'existe pas encore de tâche implémentant SCP, il est toujours possible d'invoquer directement l'exécutable à partir de ANT). Effectuer des sélections de fichiers optimales pour les transferts (suivant la date de modification pour gérer des transferts incrémentaux, suivant la taille pour ne pas surcharger la bande passante, …) constitue l’un des points forts de ANT.

 

Voici un schéma illustrant une architecture utilisable pour synchroniser un espace de données distant :

 

Figure 3 : Gestion d'une synchronisation d'espace distant

 

Opérations utilitaires sur des sources d'un projet

Il existe une multitude d'outils permettant d'optimiser les sources ou d'améliorer leur compréhension . La plupart de ces outils s'interfacent désormais avec ANT, à l'aide de tâches dédiées :

 

§         construction de la Javadoc (tâche appartenant au noyau de ANT) ;

§         mise en forme des sources Java au format HTML pour faciliter leur accès (Java2Html) ;

§         reformattage selon des conventions de codage (CheckStyle, Jalopy, …) ;

§         analyse des dépendances du code (JDepend) ;

§         couverture de code (JProbe, Metamata, …).

 

Quel intérêt à les lancer par l’intermédiaire de ANT ?

 

§         il peuvent être enchaînés automatiquement à partir d’une seule exécution ;

§         la récupération des sources à partir du référentiel de versions peut être définie en pré-requis;

§         ils partagent tous la même configuration (emplacement des sources, répertoires de destination, ...);

§         ils bénéficient des mécanismes de sélection de fichiers de ANT (par exemple pour exclure des classes de tests de la compilation ou traiter différemment des sous-modules du projet)

 

Créer un sous-projet ANT dédié à ce type d’opérations représente un investissement négligeable par rapport aux bénéfices dégagés. A l’aide d’un planificateur de tâches, il devient facile de maintenir un portail des sources du projet réactualisé régulièrement à partir des derniers sources.

 

Installation logicielle d'un poste

Répéter l’installation d’un ensemble de logiciels sur des postes, par exemple dans le cadre d’une démonstration ou d’une formation, est encore une opération fastidieuse. Copier, décompresser, renommer, déplacer, supprimer ne contribuent guère à l’épanouissement du responsable de l’installation. Ajoutés à cela la perte de temps occasionnée et le risque de fautes dans la procédure, on conviendra qu’un processus automatisé n'est pas un luxe.

 

Quand la procédure peut être effectuée sans interactivité, ANT est parfaitement adapté. Il gère notamment les méthodes d’archivage les plus courantes (ZIP, JAR, TAR, GZIP, BZIP2) et est capable d’exploiter les propriétés de l’environnement de la machine (par exemple pour récupérer le répertoire de l’utilisateur, le nom de son compte, la version Java installée, …). Il suffit alors d’écrire le fichier XML de configuration et de l’exécuter en lui fournissant éventuellement des paramètres d’exécution spécifiques au poste (eg. : chemin du CD-ROM contenant les sources).  Si l’installation doit être supprimée par la suite, une autre cible suffit à supprimer tous les éléments installés.

 

Maintenance du document procédural

Les avantages découlant du choix d'XML pour la configuration d'une procédure ANT sont nombreux, on retiendra notamment la richesse de l'offre en terme de solutions de traitement. Il est ainsi possible d'utiliser le fichier de configuration ANT pour produire un document procédural plus facilement accessible par son format (HTML, PDF, ...) et son contenu (moins technique). Le fichier de configuration ANT devient alors la référence unique de la procédure. Procédure qui peut être validée à tout moment par une personne ne disposant d'aucune compétence sur ANT (eg. : responsable technique).

 

La solution la plus évidente pour réaliser cette opération consiste à utiliser des transformations XSLT. Une feuille XSL permet de transformer un fichier XML dans un autre format (eg. : HTML) en adaptant éventuellement son contenu. A l'aide d'un fichier XML définissant une correspondance entre les éléments techniques de ANT (nom des tâches, des attributs, ...) et des libellés explicites ("Déplacement de fichiers", "Transfert FTP", ...), il est possible de présenter la procédure sous une autre forme, tout en n'omettant aucun de ses éléments.

 

Figure 4 : Création d'un document procédural

 

Comme exemple d'application, on peut envisager un document PDF ou HTML présentant un graphe des opérations avec leurs relations de dépendances (en utilisant le format graphique SVG, basé sur XML), chaque élément du graphe renvoyant vers une description textuelle de l'opération,  via à une lien hypertexte. Corsé mais faisable !

 

Cette opération est généralisable : la feuille de transformation ainsi que le fichier de libellés peuvent être conçus de manière exhaustive et adaptés pour toute configuration ANT. On peut donc espérer que des outils de génération de documentations pour ANTapparaissent prochainement, définissant alors des standards pour la documentation technique d'un projet ANT (à l'instar d'un Javadoc, voir AntDoc) ou pour des documentations accessibles à un plus large public.

 

Divers

Ceux qui, comme moi, montrent des prédispositions certaines pour la fainéantise auront toujours matière à alimenter leur outil favori. ANT peut être utilisé quotidiennement pour des tâches anodines : récupérations répétées des sources d’un projet, compilation ou archivage d’une sélection de fichiers, installation d’une nouvelle version d’un logiciel en maintenant la configuration de la version précédente, détection de fichiers modifiés, …. Les gains en terme de rapidité seront plus ou moins signifiants selon la complexité des traitements, mais dans tous les cas, l’automatisation garantit des traitements identiques et simplifie la traçabilité de la procédure.

Améliorer sa pratique de ANT

Evidemment, le meilleur moyen d'améliorer sa pratique de ANT est de s’approprier le manuel d'utilisation et d'accumuler de l’expérience. Comme dans d'autres domaines, on conseillera également de se nourrir du travail des autres. C'est d'autant plus facile avec ANT que ses configurations sont faciles à lire (généralement un seul fichier comportant un ensemble de cibles définissant chacune un traitement unitaire) et que l'on retrouve souvent les mêmes traitements d'un projet à l'autre.

 

Avant d'aborder la suite, constituée de "bonnes pratiques" d'ordre technique ou structurel tirées de mon expérience personnelle, il est indispensable de maîtriser les rudiments du vocabulaire ANT. A ceux pour qui ce vocabulaire est encore abscons et qui montrent peu d'enthousiasme à l'idée de parcourir une des nombreuses introductions techniques disponibles sur internet, voici quelques définitions essentielles :

 

§         le fichier de configuration de ANT, exprimé au format XML (ceux qui l'aprennent à ce stade de l'exposé devraient VRAIMENT lire une introduction technique), décrit un projet ANT

§         un projet ANT est essentiellement constitué d'une liste de cibles, correspondant à des opérations unitaires plus ou moins complexes : récupération des sources, compilation Java, déploiement d'une application,... Il est possible de définir des relations de dépendance entre les différentes cibles, par exemple pour s'assurer que la cible de compilation ne soit invoquée que si la cible de récupération des sources s'est bien déroulée

§         une cible est elle-même composée de tâches. Une tâche correspond à un traitement unitaire, non décomposable : copie de fichier, compression JAR, transfert FTP, ...

§         un projet ANT est configurable à l'aide de propriétés. Ces propriétés peuvent être définies dans le fichier de configuration , dans un fichier de propriétés ou en argument de l'exécution

 

Structurer ses informations

J'entends deux choses par "structurer ses informations" :

 

§         établir une organisation rigoureuse des fichiers

§         pour des fichiers de même type, gérer autant que possible des contenu au format homogène et unique (externalisation de code Javascript dans les pages HTML, respect de mêmes DTD pour des fichiers XML, …)

 

Cette structuration facilite le processus de construction, mais aussi la gestion technique du projet en général.

Les critères susceptibles d'être utilisés pour organiser ses fichiers sont nombreux :

 

§         type du fichier (Java, HTML, SQL, …)

§         module fonctionnel concerné

§         cible du fichier (configuration d'un serveur de développement, d'intégration, de production, …)

§         rôle du fichier dans le contexte de la construction (fichier source, fichier de test, fichier temporaire, fichier compilé, …)

§         OS concerné

 

Parmi ces critères, deux me semblent fondamentaux : le type de fichiers et son rôle dans la construction. Séparer les fichiers selon leur type est une pratique courante parce qu'elle permet à la fois de délimiter les espaces de travail des différents contributeurs (intégrateur WEB, développeurs Java, …) et de faciliter les traitements en lots pour une catégorie de fichiers (compilations, reformattage, …). En revanche distinguer les fichiers selon leur rôle dans le processus de construction est moins appliqué. On retrouve alors des répertoires où se côtoient dans la joie et la bonne humeur des fichiers sources, des fichiers compilés, des classes de test, des fichiers temporaires, etc. Ce mode de gestion présente le risque de livrer des fichiers sources, ce qui pose des problèmes de confidentialité, de supprimer ces fichiers sources au cours d'une opération de nettoyage voire d'ajouter des fichiers inappropriés dans le système de contrôle de versions. Il ne facilite pas non plus la manipulation des fichiers dans le quotidien.

 

Il n'existe pas d'arborescence modèle pour chaque type de projet; même pour un projet donné, on pourra confronter plusieurs arborescences toutes valables (ce qui donne parfois lieu à quelques discussions houleuses en début de projet). Toute définition d'arborescence doit notamment respecter un compromis entre structuration et lisibilité : plus le découpage est fin plus il est fastidieux de retrouver ses informations. Les modèles présentés ci-dessous, avant et après construction d'une application WEB, sont une illustration des règles énoncées précédemment.

 

Figure 5 : Exemple d'arborescence avant construction

Quelques remarques sur cette arborescence :

§         certains (ils sont rares) gèrent les scripts de construction ANT dans un répertoire spécifique. Je trouve plus judicieux de placer ces scripts à la racine (qui ne comporte généralement pas d'autres fichiers si ce n'est un README) pour les rendre plus visibles et parce qu'il est dommage de créer un nouveau répertoire à la racine pour 2 ou 3 fichiers

§         les librairies utilisées pour compiler les sources ne sont pas nécessairement à livrer sur un serveur (typiquement l’api des servlets). Pour opérer la distinction, on peut gérer les libraires internes au développement dans un sous-répertoire spécifique, ici /lib/dev.

 

Exemple suivant décrit le même projet après la phase de construction. Les fichiers/répertoires déjà mentionnés sont présentés en gris :

Figure 6 : Exemple d'arborescence après construction

Après la phase de construction, on voit apparaître 3 espaces de fichiers distincts:

 

§         l’espace des sources (décrit précédemment)

§         l’espace temporaire, ici /build : classes Java compilées avant archivage, fichiers XML transformés dans un état intermédiaire, répertoires réorganisés, … Ces fichiers n’ont aucun intérêt hors du processus d’installation, ils doivent être considérés comme supprimables sans dommage pour l’application puisqu'ils sont tous obtenus à partir des sources.

§         l’espace de livraison, ici /dist : l’arborescence ou l’archive de distribution contenues dans cet espaces sont directement exploitables sur le serveur cible. Elles peuvent également être supprimées à tout moment.

 

Proposer des cibles standard

On retrouve fréquemment les mêmes traitements entre différents processus de construction : préparation de la construction, compilations, réorganisation, déploiement, … Pour faciliter la compréhension de ces processus, il est conseillé d’implémenter des cibles réalisant un traitement unitaire et de les nommer selon le standard qui s’impose peu à peu dans les développements ANT.

 

Réaliser des traitements de nature différente au sein d’une même cible est dommageable dans le sens où chacun des traitements de la cible ne pourra être invoqués séparément. Par exemple, on pourrait être tenté de réaliser une cible de compilation dans laquelle on réaliserait une extraction des sources avant la compilation. Or le traitement d’extraction des sources présente un intérêt dans d’autres sous-processus qui n’ont que faire de la compilation: analyse des sources, reformattage suivant des conventions de code, … Dans ce cas, il est recommandé de réaliser deux cibles distinctes et d’exploiter la gestion de dépendances de cibles offerte par ANT.

 

Le nommage des cibles supporte plus facilement un manque d’inspiration total à une imagination débordante afin de simplifier le choix de la cible au moment de l’exécution. Il est par exemple facile de prévoir le comportement de cibles telles que "javadoc" ou "usage". On essaiera notamment de séparer différents termes constituant le nom d’une cible par un tiret et non un point, habituellement réservé pour les noms de propriétés.

 

Voici les noms des cibles les plus courantes, avec le traitement qui leur est généralement associé :

Figure 7 : description des cibles les plus courantes

 

Evidemment, le nom ne suffit pas toujours à décrire précisément ce que a cible va effectuer (la Javadoc est-elle créée également avec la cible doc ?), il est donc recommandé de proposer une cible usage décrivant les cibles disponibles. Depuis la version 1.2, l’option –projecthelp de ANT automatise cette tâche à partir des descriptions des cibles présentes dans le fichier de configuration. Cependant, on peut préférer écrire une cible dédiée afin de gérer une présentation différente et surtout de faire de cette cible la cible par défaut.

 

Une cible affichant les valeurs de toutes les propriétés de la construction est également appréciable. En effet, retrouver l’état de la configuration n’est pas toujours évident du fait que ANT peut être paramètré de 3 manières : en ligne de commande, par des fichiers de propriétés externes ou en interne dans le fichier de configuration. La version 1.5 introduit une tâche echoproperties qui n'est pas forcément adaptée au besoin étant donné qu'elle liste toutes les propriétés, aussi bien applicatives que systèmes...

Gérer les dépendances

La gestion des dépendances de cibles est un point assez délicat. Prenons le cas du processus suivant, qui transfert via FTP un fichier JAR construit à partir des sources récupérés sur le référentiel de sources:

 

Figure 8 : gestion de dépendances

 

Lorsque la cible FTP est exécutée, l’ordonnanceur de ANT invoque la cible get-src, la cible compile puis la cible jar avant de terminer par la cible ftp. Supposons que l’on souhaite transférer l’archive sur 2 serveurs distincts. On exécutera donc la cible 2 fois en modifiant le paramétrage du serveur FTP entre temps. ANT ne gardant pas trace des exécutions précédentes, toute la chaîne est de nouveau exécutée la deuxième fois, ce qui représente une perte de temps.

 

Pour éviter cette lourdeur, on pourrait envisager de modifier la définition du processus de la manière suivante :

Figure 9 : gestion de dépendances centralisée

Les traitements unitaires sont tous indépendants. Une tâche supplémentaire permet de définir leur enchaînement. Dans notre exemple, le deuxième appel de la cible ftp aboutit directement au transfert FTP, en utilisant l’archive construite à la première exécution.

 

Le problème de cette définition est qu’elle n’est pas auto-suffisante : quand l’utilisateur invoque les cibles indépendantes, c’est en effet à lui d’assurer la gestion des dépendances ! La cible compile tombera par exemple en échec si la cible get-src n’a pas été invoquée auparavant.

 

En fait, le problème posé par le premier exemple est plus ou moins résolu par ANT selon les tâches effectuées. Dans le cas d’une compilation Java par exemple, ne seront compilées que les classes ayant été modifiées depuis la dernière compilation. De même pour l’archivage, seuls les fichiers modifiés seront mis à jour. Cette gestion incrémentale est étendue à toutes les tâches qui le permettent (copies, transformations XSLT, transferts FTP, …). Dans notre exemple, les cibles compile et jar seront donc exécutées, mais leur temps d’exécution sera minimal (réduit au temps de comparaison des dates de fichiers).

 

Le problème est plus délicat pour un traitement comme celui de la cible get-src, où la gestion incrémentale est plus difficile à mettre en place (elle dépend du système de gestion de versions). Dans ce cas, on peut utilier le mécanisme d’inhibition suivant :

 

Figure 10 : inhibition d'une tâche

L’introduction de la propriété get.src permet d’activer ou de désactiver la cible get-src. Ce mécanisme constitue un moyen intéressant pour bénéficier de la gestion de dépendances de ANT tout en gardant un certain contrôle des opérations. Voici un extrait de la configuration ANT aboutissant à ce mécanisme :

 

...

<init>

  <condition property="must.get.src">

    <istrue value="${get.src}" />

  </condition>

</init>

 

<target name="get-src" if="must.get.src">

  ...

</target>

 

<target name="compile" depends="get-src">

  ...

</target>

...

 

Gérer le paramétrage

Il existe 3 moyens de paramètrer une exécution ANT :

 

§         à l'aide de propriétés définies au sein même du fichier de configuration XML

§         en ligne de commande, avec la syntaxe Java (-Dkey=value)

§         à l'aide de fichiers de propriétés au format texte (.properties) ou XML

 

Propriétés externes

Comme pour tous les outils paramétrables, il est préférable d'externaliser toutes les propriétés susceptibles d'être modifiées par l'utilisateur hors du noyau (ici le fichier de configuration de ANT). Le fichier de propriétés porte généralement le nom du fichier de configuration avec l'extension .properties. L'utilisation de propriétés définies au format XML, possible depuis la version 1.5 de ANT, permet de mieux catégoriser les propriétés et de leur associer un modèle de validation (DTD ou schéma) tout en facilitant leur exploitation par d'autres outils.

 

Pour particulariser une exécution, l'utilisateur peut modifier le fichier de propriétés ou, si cette particularisation est ponctuelle, définir les nouvelles valeurs de propriétés en ligne de commandes. Une définition en ligne de commande surcharge une définition dans un fichier de propriétés, qui surcharge elle-même une définition interne au fichier de configuration.

 

Propriétés internes

Les propriétés définies dans le fichier de configuration sont généralement destinées à un usage interne (chemin relatif de ressources, flag indiquant si une librairie est présente, ...). Il existe 3 niveaux de définition pour ces propriétés, chacun présentant ses avantages et ses inconvénients:

 

Niveau

Portée

Utilisation

Projet

Globale au projet

La plupart les propriétés étant communes au projet, il semble logique de les définir à ce niveau. Cependant, tous les types de propriétés ne peuvent pas y être définis (e.g : définitions de conditions).

Cible init

Globale au projet du fait que la cible init est généralement liée à toutes les autres cibles

Permet de centraliser toutes les définitions de propriétés, quel que soit leur type. Exige que les cibles dépendent de la cible init pour bénéficier de ces propriétés.

Autre cible

Locale à la cible

Définition de propriétés exclusivement réservée à la cible. Evite de surcharger les définitions globales

 

Je recommanderais de réserver les définitions locales pour des propriétés qui ne sont utilisées que dans leur cible (et non dans les cibles qui en dépendent) et qui ne présentent aucune dépendance vis à vis de l'application. Prenons le cas d'une cible javadoc nécessitant une propriété javadoc.dir spécifiant le répertoire de construction de la Javadoc et une propriété javadoc.uptodate indiquant si la Javadoc est déjà à jour. La propriété javadoc.dir me semble stratégique dans le sens où elle fait partie de la définition de l'environnement de l'application. La propriété javadoc.uptodate n'est qu'un indicateur technique propre au processus de construction. Le fait de remonter les propriétés stratégiques au niveau du projet ou de la cible init facilite leur identification.

 

Récupération des propriétés d'environnement

ANT fournit un moyen élégant de récupérer les propriétés d'environnement à partir de la définition suivante :

 

 

<property environment="myenv"/>

 

 

Toutes les propriétés systèmes (celles accessibles en Java via la méthode java.lang.System#getProperties()) sont alors accessibles au travers de propriétés ANT portant le même nom, préfixé dans notre exemple par "myenv.". Ce qui permet notamment de récupérer le PATH, la version Java, le répertoire d'installation de J2EE, ...

Gérer des sous-projets

Il est tellement facile de réaliser un projet ANT que l'on a souvent tendance à l'enrichir de nouvelles fonctionnalités. Vient un moment où le fichier de configuration devient difficilement maintenable (dépasser le millier de lignes est fréquent pour des applications conséquentes). Il paraît alors judicieux d'éclater le projet en sous-projets disposant chacun de leur propre fichier de configuration : projet principal de construction, projet rassemblant des opérations utilitaires sur les sources, projet dédié au déploiement, …. Malheureusement, ANT ne propose pas encore de mécanisme d'inclusion. Voici trois manières de gérer cette problématique :

 

Gérer des sous-projets indépendants

Ce mode de gestion consiste à gérer des fichiers de configuration qui n'ont aucun lien entre eux. La plupart du temps, ces sous-projets partagent des propriétés communes : emplacement des sources, paramètres de connexion à un serveur, … Pour éviter de dupliquer les paramétrages, ces sous-projets peuvent utiliser le même fichier de propriétés, même si certaines seront inutiles pour un sous-projet donné.

 

Le problème de cette gestion est qu'elle ne permet pas de mutualiser tout ce qui est commun au différents sous-projets. Seules sont concernées les propriétés externalisables, i.e. des propriétés qui sont définies de manière statique avec une syntaxe clé=valeur. Toute une partie de l'initialisation du projet, définie de manière dynamique, est donc écartée : récupération de l'heure système, des poplitées d'environnement, vérification de la présence de ressources, définition de groupes de chemins, de patterns d'inclusion, …

 

Cette gestion n'est donc intéressante que dans le cas où les redondances de paramétrage sont infimes.

 

Inclusion XML

Une autre possibilité est d'exploiter le mécanisme d'inclusion XML. Un fichier de configuration principal peut ainsi inclure d'autres fichiers de configuration :

 

<?xml version="1.0"?>

 

<!DOCTYPE project [

  <!ENTITY sous-projet-1 SYSTEM "file:./sous-projet-1.xml">

  <!ENTITY sous-projet-2 SYSTEM "file:./sous-projet-2.xml">

]>

 

<project name="include-test" basedir="." default="test1">

  ...

  &sous-projet-1;

  &sous-projet-2;

  ...

</project>

 

 

Dans ce cas, ANT traite le fichier principal comme si le contenu des 2 fichiers inclus avaient été insérés à la place des appels d'inclusion. Les 2 sous-projets peuvent alors tirer partie des définitions de cibles et de propriétés effectuées au niveau du projet principal.

 

Cette solution n'est qu'à moitié satisfaisante dans la mesure ou les fichiers inclus ne sont pas valides au sens ANT ni même au sens XML. En effet, ils ne peuvent pas contenir l'entête XML ou l'élément racine <project> à moins de rendre le fichier principal invalide. Ce qui pose notamment problème pour l'édition de ces fichiers, qui ne peut bénéficier de services comme la complétion ou la validation XML.

 

Mécanisme de façade

Cette pratique combine les avantages des deux solutions précédentes, la mutualisation d'un même environnement associée à la maintenance de fichiers ANT valides, mais au profit d'une certaine lourdeur d'écriture. Elle reprend le concept de façade largement répandu dans le développement objet : un composant (ici le projet principal) offre un service qui est implémenté par un autre composant (un sous-projet). Le composant de façade ne fait que rediriger les appels :

 

...

<target name="un-service">

  <ant antfile="sous-projet-1.xml" target="un-service" inheritRefs="true"/>

</target>

...

 

 

L'attribut inheritRefs permet de propager le paramétrage disponible dans le projet principal aux sous-projets.

Le revers de cette méthode est qu'elle oblige à dupliquer toutes les déclarations de cibles proposées à l'utilisateur (avec un appel au lieu de l'implémentation)

 

Aucune de ces solutions n'est donc réellement satisfaisante. Heureusement, un mécanisme d'inclusions est prévu pour les versions prochaines de ANT. Il peut sembler étrange que ce mécanisme n'ait pas été disponible plus tôt, mais les problématiques en jeu sont loin d'être triviales : conflits de noms des propriétés et des cibles, gestion des priorités dans la définition des propriétés, imports récursifs, résolution des chemins relatifs suivant les racines des sous-projets, filtrage des inclusions, …

Gérer des structures conditionnelles

L'une des critiques récurrentes à propos de ANT consiste à lui reprocher son absence de structures IF-THEN-ELSE. ANT n'offre pas de syntaxe s'approchant de l'exemple suivant :

 

<target name="cible">

  if (condition.A) then

    traitementA

  else

    traitement B

 

  traitementC

</target>

 

Les concepteurs de ANT justifient ce manque par le fait qu'une gestion IF-THEN-ELSE alourdirait considérablement le code, alors que la simplicité d'utilisation de ANT constitue l'un de ses principaux atouts. Néanmoins, ANT propose une vision différente du problème en permettant d'activer ou de désactiver des cibles selon des valeurs de propriétés (attributs if et unless des cibles). Allié au mécanisme de dépendances des cibles, ce système permet d'implémenter une structure IF-THEN-ELSE en éclatant les traitements de la cible dans d'autres cibles :

 

<target name="cible" depends="cible-A, cible-B">

  traitementC

</target>

 

<target name="cible-A" if="condition.A">

  traitementA

</target>

 

<target name="cible-B" unless="condition.A">

  traitementB

</target>

 

Cette gestion multiplie le nombre de cibles, mais permet de garder une écriture très simple. Elle recèle malgré tout un petit piège : si la cible cible-B est aussi déclarée dépendante d'une cible cible-Z et que la condition condition.A est vraie, la cible cible-B sera ignorée mais la cible cible-Z sera exécutée ! Le graphe de dépendances est en effet systématiquement parcouru. Pour éviter ce comportement, il faut lier la cible cible-Z à l'aide d'une tâche <antcall> plutôt qu'un attribut depends...

Gérer plusieurs rapports d'exécution

Lorsque l'on implémente un processus automatisé complexe et/ou sensible (eg. : livraison d'une application en production), il est indispensable de produire un rapport d'exécution décrivant toutes les opérations effectuées.

 

Le système de loggers offert par ANT est particulièrement efficace dans ce domaine. Comme son nom le laisse supposer, un logger est un composant traçant les événements d'une exécution ANT. ANT met à disposition plusieurs types de loggers : sortie au format texte/XML, envoi de mail, interfaçage avec Log4J, ... .Il est possible de développer de nouveaux loggers avec un fort degré de liberté, que soit au niveau de la présentation des événements, de leur format (HTML, SQL, ...), de la destination des logs (sortie standard, fichiers, SGBD, ...) ou de déclenchement d'actions (envois FTP, exécution de sons, ...).

 

Le niveau de précision des logs est configurable à l'exécution. Il est sujet à dilemme pour l'utilisateur : en mode verbeux il permet de retrouver plus facilement la cause d'un échec mais noie l'utilisateur de son flot d'informations; en mode silencieux, il offre une vue plus synthétique mais n'est guère utile pour débugguer le traitement. Généralement, dans un contexte interactif, on lancera le processus en mode silencieux en se gardant la possibilité de lancer une deuxième exécution en mode verbeux si besoin. Pour éviter cette deuxième passe, il faudrait pouvoir spécifier plusieurs logs parallèles, ce que ANT ne permet pas.

 

La tâche <record> apporte une solution à cette problématique. Elle permet d'activer au sein même du fichier de configuration un logger avec un niveau de log déterminé. Cette tâche rend donc possible la création de deux rapports d'exécution avec des niveaux de précision différents. Elle est généralement déclarée dans la cible d'initialisation :

 

<target name="init">

  <record name="build-info.log" loglevel="info" />

  <record name="build-verbose.log" loglevel="verbose" />

</target>

 

Le seul défaut de cette tâche est qu'elle ne permet pas d'utiliser un logger autre que celui par défaut (alimentation d'un fichier texte).

Environnement d'exécution

ANT peut être exécuté de plusieurs manières :

 

A partir d'un script spécifique à l'OS

ANT étant une application Java, il suffit d'exécuter sa classe principale org.apache.tools.ant.Main en lui passant les arguments reconnus par ANT : fichier de configuration, niveau de logs, définitions de propriétés, ... La distribution de ANT fournit un script positionnant correctement le CLASSPATH. Il est recommandé d'inclure ce script dans le PATH pour n'avoir plus qu'à saisir "ant" en ligne de commande. Quand l'OS le permet, rajouter un item "Execute with ANT" dans le menu contextuel de l'explorateur de fichiers simplifie également bien les choses...

 

Un script peut également être exploité par un planificateur de tâches. Le planificateur de tâche est à ANT ce que la moustache est au baiser (d'après les dires des amatrices (-teurs) de moustachus (-ues ?)). Après avoir automatisé un processus devant être exécuté régulièrement (eg. construction de Javadocs, synchronisation de données), il serait dommage de ne pas automatiser cette exécution. Il suffit pour cela de fournir un script personnalisé au planificateur de tâches et de programmer son exécution aux périodes souhaitées.

 

Au sein d'un IDE.

La plupart des IDE Java disposent de plugins pour utiliser ANT, Intellij IDEA l'intégrant même dans son noyau. Dans le cadre du développement, ANT apporte des solutions à des problématiques mal voire non traitées par beaucoup d'IDE : séparation sources/compilés, exécutions/compilations multi-cibles, support multi-JDK... Il présente également l'énorme avantage d'externaliser la configuration d'un projet dans un format non propriétaire et donc réutilisable au sein d'autres IDE supportant ANT.

 

Exécuter ANT à partir d'un IDE permet de bénéficier de fonctionnalités supplémentaires par rapport un script :

 

§         vision synthétique de l'ensemble des cibles disponibles, avec leur description

§         exécution d'une cible d'un simple clic de souris ou d'un raccourci clavier

§         possibilité de suspendre temporairement l'exécution,

§         présentation de rapports d'exécution plus facilement exploitable (lien vers le code source, gestion en arborescence, possibilité de changer de niveau de log à posteriori, ...)

§         aide à la gestion du CLASSPATH

 

En plus de cette facilité d'exécution, ces produits offrent généralement une interface d'édition du fichier de configuration.

 

A partir d'une classe Java

Exécuter ANT à partir de classes Java est parfois nécessaire, par exemple dans le cas d'un IDE intégrant ANT ou d'un interfaçage avec une servlet permettant de lancer des constructions à distance. Pour profiter de la machine virtuelle Java courante, il est préférable d'invoquer directement les classes ANT plutôt que de laisser l'OS exécuter un script. ANT s'utilise alors comme toute autre librairie Java.

Gérer plusieurs environnements cibles

Un même processus pour différents résultats

Un processus d'automatisation ne doit pas nécessairement produire le même résultat à chaque exécution. Cela peut sembler contradictoire avec le principe de reproductibilité énoncé précédemment, mais la raison en est simple : un même processus peut être utilisé en vue d'une exploitation sur différents environnements, qui ont chacun leurs particularités. Ces particularités peuvent être d'ordre technique (format de fichier suivant l'OS, ports TCP, fichiers de configuration d'EJB, scripts de population de SGBD, ...), ou d'ordre fonctionnel (eg. : service exploitable exclusivement sur un serveur de production). En conséquence, la procédure n'exploite pas forcément les mêmes données voire suit des cheminements différents selon l'environnement cible.

 

Paramétrage unifié vs paramétrage exhaustif

Pour résoudre cette problématique multi-cibles, il est nécessaire de gérer autant de paramétrages de construction que d'environnements cibles. Le dilemme posé concerne la gestion des fichiers de paramétrages applicatifs : configuration de application WEB, des EJB, du système de logs, de scripts d'exploitation. Ceux-ci sont souvent différents d'un environnement à l'autre (ne serait-ce que pour les chemins d'accès au répertoires). Deux solutions sont envisageables :

 

§         gérer une version de ce fichiers pour chacun des environnements cibles. Ceci simplifie grandement le processus de construction : il suffit de prendre le fichier concerné et de l'inclure tel quel dans la distribution. En revanche, la maintenance est problématique, les différentes versions peuvent être désynchronisées si les modifications effectuées sur l'une des versions ne sont pas correctement reportées aux autres versions. Prenons le cas du fichier web.xml (définition de l'application WEB) : si un développeur ajoute une nouvelle définition de servlet, il doit veiller à faire évoluer toutes les versions de ce fichier (pour le serveur de développement, d'intégration de production, ...). Lorsque le nombre d'environnements géré est important, cette gestion n'est plus réaliste (je travaille par exemple sur une application exploitée sur 10 serveurs distincts).

§         la deuxième solution consiste à gérer une seule version des fichiers de paramétrage, commune à tous les environnements cibles. Toutes les valeurs spécifiques de ces fichiers sont remplacées par des patterns de substitution ANT, eg. @APP_ROOT@ pour représenter le répertoire racine de l'application. Toutes les valeurs de remplacement sont centralisées dans un fichier propre à un environnement donné. Les substitutions sont opérées lors du processus de construction ANT. L'avantage de cette gestion est double : la maintenance des paramétrages est facilitée et les patterns de substitution peuvent être réutilisés au sein des différents paramétrages.

 

Modalités d'exécution

En utilisant la deuxième solution, il est possible de particulariser la construction simplement en fournissant à ANT le fichier de propriétés spécifiques à l'environnement concerné. Les versions de ANT antérieures à la version 1.5 ne permettaient pas de passer un fichier de propriétés en ligne de commandes. Il était nécessaire de déclarer le chargement du fichier dont le nom était paramètré puis de fournir le chemin du fichier en paramètre, eg. :

 

<project ...>

  <property file="${properties.file}"/>

</project>

avec l'appel suivant : ant –Dproperties.file=/tmp/sample.properties.

 

La version de ANT permet de se dispenser de la déclaration XML et de passer directement le fichier en paramètre : ant –propertyfile=/tmp/sample.properties.

 

Pour faciliter l'exécution, il est conseillé de fournir des scripts dédiés se chargeant de fournir le bon fichier de paramètres : build-dev, build-prod, ...

Conclusion

Sans rentrer dans d'hasardeuses considérations mystiques, il faut bien reconnaître que ANT était l'outil attendu depuis des années par la communauté Java, dépassant même beaucoup d'espérances. Maintenant que la phase de maturité est atteinte, la direction que suivra son développement est attendue avec intérêt. ANT a beaucoup d'atouts qui pourraient lui permettre de jouer une place importante dans bien d'autres domaines que le développement Java. Même si l'on sait la communauté OpenSource guère intéressée par cette problématique, l'accessibilité au grand public pourrait constituer le prochain défi de ANT.

 

Liens

§         ANT (avec pléthore de liens externes) : http://jakarta.apache.org/ant

§         Antidote (interface graphique de ANT) : http://jakarta.apache.org/ant/manual/Integration/Antidote.html

§         AntDoc (Javadoc pour ANT) : http://mapage.noos.fr/antdoc/example_top.html

§         NAnt (ANT version .NET): http://nant.sourceforge.net/

§         AntContrib (tâches C++ pour ANT) : http://sourceforge.net/projects/ant-contrib/

§         CruiseControl (outil d'intégration continue basé sur ANT) : http://cruisecontrol.sourceforge.net/

§         Intellij IDEA : http://www.intellij.com