Ce site met a disposition le build journalier de la traduction francaise du Maven: The Definitive Guide
Consultez :
  • Les documents de reference sur le projet original
  • Les sources de la traduction fr sur GitHub
  • maven


    14.5.5. La balise moduleSets

    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.

    14.5.5.1. Sélection des modules

    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.

    14.5.5.2. Balise sources

    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 14.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 14.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.

    Avertissement

    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 14.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 14.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.

    14.5.5.3. Interpolation de l'outputDirectoryMapping dans les moduleSets

    Dans la Section 14.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 14.5.4, « Section dependencySets »).

    Dans l'Exemple 14.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 14.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.

    14.5.5.4. Balise binaries

    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 14.13, « Inclure la Javadoc des modules dans un assembly ».

    Exemple 14.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 14.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 14.14, « Inclusion des artefacts d'un module et de ses dépendances dans un assembly » :

    Exemple 14.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 14.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.

    14.5.5.5. moduleSets, POMs parents et balise binaries

    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.