| Ce site met a disposition le build journalier de la traduction francaise du Maven: The Definitive Guide Consultez : | ![]() |
Les builds multimodules sont généralement liés par les balises
parent et modules dans les POMs.
Typiquement, les POMs parents spécifient leurs fils dans une section modules,
qui, en temps normal, aura pour effet de les inclure dans la procédure d'exécution du build du projet parent.
La relation entre ces deux projets, et comment elle a été contruite,
peut avoir des implications importantes sur la manière dont le plugin Assembly participe à ce processus.
Nous discuterons de cela un peu plus tard.
Pour l'instant, contentons-nous de garder à l'esprit la relation parent-enfant
pendant que nous disctons de la section moduleSets.
Les projets sont construits sous la forme d'un projet multimodule
parce qu'ils font partie d'un système plus vaste.
Ces projets sont conçus pour être utilisés ensemble, un module unique
dans un build important n'a que peu de valeur en lui-même.
De cette façon, la structure du build du projet est liée à la façon
dont nous espérons que ce projet (et ses modules) soit utilisé.
Si vous considérez le projet du point de vue de l'utilisateur,
il semble logique que l'objectif final de ce build soit de construire et distribuer un seul fichier
qu'il pourra directement déployer sans trop de soucis.
Comme les builds multimodules Maven s'appuient habituellement sur une structure top-down,
où les informations de dépendances, les configurations de plugin et bien d'autres informations
sont héritées du projet parent par l'enfant, il semble naturel que la tâche de transformation
de ces modules en un fichier unique et distribuable doive incomber au projet de plus haut niveau
dans la hiérarchie. C'est là qu'intervient la balise moduleSet.
Les ensembles de modules permettent l'inclusion de ressources appartenant à chacun des
modules du projet dans l'assembly final. Tout comme vous pouvez choisir un groupe de fichiers à inclure dans un assembly
en utilisant les balises fileSet et dependencySet,
vous pouvez inclure un ensemble de fichiers et de ressources en utilisant la balise moduleSet
pour se référer aux modules d'un build multimodule.
Cela est rendu possible grâce aux deux types d'inclusion spécifiques aux modules : l'un basé sur les fichiers, l'autre
sur les artefacts.
Avant d'entrer dans les particularités ces deux types d'inclusion de modules ressources dans un assembly,
regardons un peu de comment sélectionner les modules à traiter.
Maintenant, vous devriez commencer à maîtriser les patterns includes/excludes.
Ils sont également utilisés dans les descripteurs d'assembly pour sélectionner les fichiers et les dépendances.
Dans un descripteur d'assembly, lorsque vous faites référence à des modules,
vous pouvez également utiliser ces patterns includes/excludes
pour définir les règles qui s'appliquent sur différents ensembles de modules.
La particularité des patterns includes et excludes
d'un moduleSet est qu'ils ne permettent pas d'utiliser de jokers.
(Du moins dans Maven 2.2-beta-2, cette fonctionnalité n'ayant pas été très demandée, elle n'a donc pas été implémentée.)
À la place de cela, chaque balise include et exclude
correspond simplement au groupId et à l'artifactId du module,
séparés par un caractère ':' ainsi :
groupId:artifactId
En plus des balises includes et excludes,
le moduleSet propose d'autres outils de sélection :
le flag includeSubModules (dont la valeur par défaut est true).
La relation parent-enfant d'une structure multimodule n'est pas limitée à deux niveaux d'un projet.
En fait, vous pouvez inclure un module, peu importe son niveau de profondeur dans votre build.
Chaque projet qui est un module d'un module du projet courant est considéré comme un sous-module.
Dans certains cas, vous souhaitez construire chaque module séparément (y compris les sous-modules).
Pour cela, affectez simplement la valeur du flag useSubModules à true.
Lorsque vous essayez d'inclure des fichiers de la structure de répertoires de chaque module,
vous souhaitez déclarer cette structure de répertoires une seule fois.
Si votre structure de répertoires du projet correspond aux déclarations des relations parent-enfant
définies dans les POMs, alors les patterns de fichiers
comme **/src/main/java peuvent s'appliquer non seulement aux répertoires
directs de ce module, mais également pour les répertoires de ses propres modules.
Dans ce cas, il n'est pas nécessaire de traiter les sous-modules directement
(ils seront traités comme des sous-répertoires des modules de votre projet), et donc il faut
affecter la valeur du flag useSubModules à false.
Une fois que nous avons déterminé comment la sélection module doit s'exécuter sur l'ensemble des modules, nous sommes prêts à choisir ce qu'ils doivent contenir. Comme mentionné ci-dessus, il est possible d'inclure des fichiers ou des artefacts provenant du module du projet.
Supposez que vous désirez inclure le code sources de tous les modules dans votre assembly,
mais que vous voulez exclure un module en particulier.
Peut-être avez-vous un projet appelé secret-sauce
qui contient du code secret et sensible que vous ne voulez pas distribuer dans votre projet.
Le moyen le plus simple d'effectuer cela est d'utiliser la balise moduleSet qui inclus chaque répertoire d'un projet
dans ${module.basedir.name} et qui exclu le module secret-sauce de l'assembly.
Exemple 8.12. Inclusion et exclusion de modules dans un moduleSet
<assembly>
...
<moduleSets>
<moduleSet>
<includeSubModules>false</includeSubModules>
<excludes>
<exclude>
com.mycompany.application:secret-sauce
</exclude>
</excludes>
<sources>
<outputDirectoryMapping>
${module.basedir.name}
</outputDirectoryMapping>
<excludeSubModuleDirectories>
false
</excludeSubModuleDirectories>
<fileSets>
<fileSet>
<directory>/</directory>
<excludes>
<exclude>**/target</exclude>
</excludes>
</fileSet>
</fileSets>
</sources>
</moduleSet>
</moduleSets>
...
</assembly>
Dans l'Exemple 8.12, « Inclusion et exclusion de modules dans un moduleSet »,
puisque nous devons gérer les sources de chaque module,
il est plus simple de traiter seulement les modules directs du projet en cours,
en manipulant les sous-modules avec le joker sur le chemin/fichier.
Renseignez l'élément includeSubModules à false.
Ainsi, nous n'avons donc pas à nous soucier de l'apparition de sous-modules dans le répertoire racine de l'archive
assebly.
La balise exclude s'occupera d'exclure votre module secret secret-sauce.
Normalement, les sources d'un module sont incluses dans l'assembly
dans un sous-répertoire qui porte le nom de l'artifactId du module.
Toutefois, comme Maven permet d'avoir des modules dans des répertoires dont le nom ne correspond
pas à leur artifactId, il est souvent préférable d'utiliser une
expression ${module.basedir.name} pour préserver le nom du répertoire du module courant.
(${module.basedir.name} revient au même que d'appeler
la méthode MavenProject.getBasedir().getName()).
Il est important de se rappeler que les modules ne sont pas nécessairement des sous-répertoires du projet qui les
déclare.
Si votre projet possède une structure un peu particulière,
vous pouvez avoir besoin de recourir à la déclaration de balises moduleSet spéciales
qui sauront comprendre et tenir compte des particularités de votre projet.
Pour essayer de minimiser les particularités votre projet, comme Maven est flexible, si vous vous surprenez à écrire trop de configuration, c'est qu'il y a probablement un moyen plus facile d'y arriver.
Continuons à parcourir l'Exemple 8.12, « Inclusion et exclusion de modules dans un moduleSet ».
Comme nous ne traitons pas les sous-modules de manière explicite,
nous devons faire en sorte que les répertoires des sous-modules ne soient pas exclus
des répertoires sources que nous avons pour chaque module direct.
Affecter le flag excludeSubModuleDirectories à false
permet d'appliquer les mêmes patterns de fichiers sur les structures de répertoires du module
en cours de traitement et sur ses sous-modules.
Enfin, dans l'Exemple 8.12, « Inclusion et exclusion de modules dans un moduleSet », le résultat produit par chacun des modules
ne nous intéresse pas. Nous avons donc exclu le répertoire target/ de tous les modules.
Il est intéressant de mentionner que la balise sources peut inclure des éléments de type fileSet directement ou dans ses sous-balises imbriquées.
Ces balises de configuration sont utilisées pour fournir une rétrocompatibilité avec les anciennes versions du plugin
Assembly (versions 2.1 et précédentes)
qui ne prenaient pas en charge plusieurs ensembles de fichiers pour un même module sans créer de modulesSet séparés.
Elles sont dépréciées, vous ne devez pas les utiliser.
Dans la Section 8.5.4.1, « Configurer l'emplacement des dépendances »,
nous avons utilisé la balise outputDirectoryMapping
pour changer le nom du répertoire sous lequel est inclue chaque source des modules.
Les expressions contenues dans cette balise sont résolues exactement de la même manière
que celles de la balise outputFileNameMapping,
qui utilisait des sets de dépendances (référez-vous à l'explication de
cet algorithme dans la section Section 8.5.4, « Section dependencySets »).
Dans l'Exemple 8.12, « Inclusion et exclusion de modules dans un moduleSet », vous avons
utilisé l'expression ${module.basedir.name}.
Vous avez peut-être remarqué que le début de cette expression, module,
n'est pas listé dans l'algorithme de résolution des expressions de la section Section 8.5.4, « Section dependencySets ».
Cet objet à la base de cette expression est spécifique à la configuration des moduleSets.
Il fonctionne de la même manière que les références à ${artifact.*}
disponibles pour la balise outputFileNameMapping,
à la différence qu'il s'applique aux instances MavenProject,
Artifact et ArtifactHandler du module
au lieu de celles d'un artefact de dépendance.
Tout comme la balise sources se charge de l'inclusion des sources d'un module,
la balise binaries se charge d'inclure le résultat du build d'un module, ou ses artefacts.
Bien que cette section fonctionne essentiellement comme une façon de
spécifier un dependencySets à appliquer à chaque module de la série,
quelques fonctionnalités propres aux artefacts des modules méritent d'être explorées :
attachmentClassifier et includeDependencies.
En plus de cela, la balise binaries contient des options de configuration
similaires à la balise dependencySet,
en rapport avec la manipulation de l'artefact du module lui-même.
Il s'agit des balises : unpack, outputFileNameMapping,
outputDirectory, directoryMode et fileMode.
Enfin, ces balises binaries d'un module peuvent contenir une balise dependencySets
pour spécifier comment les dépendances de chaque module doivent être incluses dans l'assembly.
D'abord, jetons un coup d'oeil à la façon dont ces options peuvent être
utilisées pour gérer les artefacts propres au module.
Supposons que nous voulons inclure les jars de Javadoc de nos modules dans notre assembly.
Dans ce cas, nous ne nous soucions pas de l'inclusion des dépendances, nous voulons simplement ajouter le jar de la Javadoc.
Toutefois, comme ce jar un peu particulier est toujours présent en tant que pièce jointe de l'artefact principal,
nous devons spécifier le classifieur à utiliser pour le récupérer.
Pour simplifier, nous ne couvrirons pas dépaquetage des jars de Javadoc des modules,
puisque la configuration est exactement la même que celle utilisée pour les dépendances que nous avons déjà traitée
dans ce chapitre.
Le moduleSet devrait ressembler à l'Exemple 8.13, « Inclure la Javadoc des modules dans un assembly ».
Exemple 8.13. Inclure la Javadoc des modules dans un assembly
<assembly>
...
<moduleSets>
<moduleSet>
<binaries>
<attachmentClassifier>javadoc</attachmentClassifier>
<includeDependencies>false</includeDependencies>
<outputDirectory>apidoc-jars</outputDirectory>
</binaries>
</moduleSet>
</moduleSets>
...
</assembly>
Dans l'Exemple 8.13, « Inclure la Javadoc des modules dans un assembly »,
vous ne spécifiez pas directement de valeur au flag includeSubModules flag, celui-ci est activé par défaut.
Cependant, nous tenons absolument à traiter tous les modules - même les sous-modules - en utilisant ce moduleSet :
nous n'utilisons aucune sorte de pattern de fichier qui pourrait correspondre
à des structures de sous-répertoire à l'intérieur du module.
Pour chaque module, la balise attachmentClassifier récupére l'artefact
qui lui est attaché avec le classifieur Javadoc.
La balise includeDependencies signale au plugin Assembly
que les dépendances des modules ne nous intéressent pas, nous récupérons juste les pièces jointes.
Enfin, la balise outputDirectory demande au plugin Assembly
de mettre tous les jars de Javadoc dans un répertoire nommé
apidoc-jars/ en dehors du répertoire de l'assembly.
Nous ne faisons rien de très compliqué dans cet exemple. Cependant,
il est important de noter que les mêmes changements pour la résolution des expressions,
dont nous avons parlé à propos de la balise outputDirectoryMapping
de la section sources, s'appliquent également ici.
Tout ce qui est accessible par ${artifact.*} dans la
configuration de la balise outputFileNameMapping du
dependencySet est également disponible dans ${module.*}.
La même chose s'applique pour toute balise outputFileNameMapping lorsqu'elle est
utilisée directement dans une balise binaries.
Enfin, examinons un exemple dans lequel nous voulons simplement
traiter l'artefact du module et ses dépendances dans le scope runtime.
Dans ce cas, nous voulons mettre les artefacts de chaque module une structure de répertoires séparée,
en fonction de l'artifactId et de la version des modules.
Cette configuration du moduleSet reste simple et ressemble au code de l'Exemple 8.14, « Inclusion des artefacts d'un module et de ses dépendances dans un assembly » :
Exemple 8.14. Inclusion des artefacts d'un module et de ses dépendances dans un assembly
<assembly>
...
<moduleSets>
<moduleSet>
<binaries>
<outputDirectory>
${module.artifactId}-${module.version}
</outputDirectory>
<dependencySets>
<dependencySet/>
</dependencySets>
</binaries>
</moduleSet>
</moduleSets>
...
</assembly>
Dans l'Exemple 8.14, « Inclusion des artefacts d'un module et de ses dépendances dans un assembly », nous utilisons la balise dependencySet en la laissant vide.
Comme vous devez inclure toutes les dépendances, par défaut, vous n'avez pas besoin d'effectuer de configuration.
Lorsque la balise outputDirectory est spécifiée dans la balise binaires,
toutes les dépendances vont être incluses dans le même répertoire, aux côtés de l'artefact du module.
Ainsi, nous n'avons pas besoin le configurer tout cela dans le dependencySet.
La plupart du temps, les binaires d'un module restent assez simples.
Dans les deux parties
- la partie principale, chargée de la manipulation de l'artefact du module lui-même,
et la partie chargée des ensembles de dépendances, et qui traite les dépendances du module -
les options de configuration restent très similaires à celles des ensembles de dépendances.
Bien entendu, la balise binaires fournit également des options pour contrôler
quelles dépendances sont incluses et quel artefact principal de projet vous souhaitez utiliser.
Comme pour la balise source, la balise binaries
dispose d'options de configuration qui sont fournies uniquement pour des causes de rétrocompatibilité.
Celles-ci devraient être dépréciées, comment l'utilisation des sous-sections
includes et excludes.
Enfin, clôturons cette discussion avec un avertissement.
En ce qui concerne les relations parents-module, il existe des interactions
subtiles entre le fonctionnement interne de Maven et l'exécution d'un
moduleSet dans une balise binaires.
Lorsqu'un POM déclare un parent,
ce parent doit être résolu d'une façon ou d'une autre avant que le
POM en question puisse être construit.
Si un parent est dans un dépôt Maven, pas de problème.
Cependant, vous vous exposez à de gros problèmes si le parent dispose
d'un POM de plus haut niveau dans le même build,
en particulier si le POM parent utilise les binaires de ces modules.
Maven 2.0.9 trie les projets d'un build multimodule en fonction de leurs dépendances,
de manière à construire les dépendances d'un projet avant celui-ci.
Le problème est que l'élément parent est considéré comme une dépendance,
ce qui signifie que le build du projet parent doit être effectué avant que le projet enfant soit construit.
Si une partie du build de ce parent inclut la création d'un assembly qui utilise les binaires des modules,
ces binaires ne seront pas encore créés et ne pourront donc pas être inclus.
Cela provoquera ainsi l'échec de la construction de l'assembly.
Il s'agit d'une question complexe et subtile.
Elle limite sérieusement l'utilité de la section binaires de la partie module du descripteur d'assembly.
En fait, à ce sujet, un bug a été créé sur le gestionnaire d'anomalie du plugin Assembly : http://jira.codehaus.org/browse/MASSEMBLY-97.
Il faut espérer que les futures versions de Maven vont trouver un moyen de réparer cette fonctionnalité,
puisque l'obligation de construire le parent en premier n'est pas forcément nécessaire.