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.
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.
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), …
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, ...
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 :
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).
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.
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
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.
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.
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.
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.
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
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.
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...
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>
...
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
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.
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.
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, ...
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 :
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.
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.
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, …
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