1. Introduction

1.1. Motivation

TODO

La construction ça regroupe pleeiin de choses

On va en regarder que quelques unes

Besoin d’outils pour ça

1.2. Objectifs et principes

TODO

1.3. Différences entre logiciels de construction

TODO

1.4. Exemples de logiciels de construction

cmake meson

make maven ant gradle ninja

2. Présentation de Maven

Maven est un logiciel de construction un peu complexe qui nécessite qu’on s’y intéresse un peu en profondeur pour comprendre son fonctionnement.

Fondamentalement, Maven est un outil qui va simplement récupérer des objets distants lorsqu’il en a besoin, et exécuter des commandes configurées dans un ordre donné.

On peut utiliser Maven pour un grand nombre de choses, notamment :

  • Compiler et packager un logiciel,

  • Exécuter des tests ou des analyses,

  • Générer une documentation,

  • Produire un "artefact Maven" réutilisatble et publiable,

  • Produire un plugin Maven.

Dans cette partie, on s’intéressera avant tout aux trois premiers points.

2.1. Exécution de maven (1) : phases et lifecycles

L’exécution de Maven repose sur la notion centrale de phase. Une phase correspond simplement à une intention comme celle de "compiler le projet" ou de "exécuter les tests", mais ne définit pas comment réaliser cette intention (voir ). Une phase peut être exécutée avec la commande :

$ mvn <nom de la phase>

Une phase appartient toujours à un lifecycle ("cycle de vie"), qui est une liste ordonnée de phases. Lorsqu’on exécute une phase, Maven va toujours automatiquement exécuter toutes les phases qui précèdent la phase choisie au sein du lifecycle auquel elle appartient.

Par défaut, Maven met à disposition trois lifecycles avec les phases suivantes (dans leur description officielle en anglais, et avec en gras les phases les plus connues):

  • Le lifecycle default :

    • validate : validate the project is correct and all necessary information is available.

    • initialize : initialize build state, e.g. set properties or create directories.

    • generate-sources : generate any source code for inclusion in compilation.

    • process-sources : process the source code, for example to filter any values.

    • generate-resources : generate resources for inclusion in the package.

    • process-resources : copy and process the resources into the destination directory, ready for packaging.

    • compile : compile the source code of the project.

    • process-classes : post-process the generated files from compilation, for example to do bytecode enhancement on Java classes.

    • generate-test-sources : generate any test source code for inclusion in compilation.

    • process-test-sources : process the test source code, for example to filter any values.

    • generate-test-resources : create resources for testing.

    • process-test-resources : copy and process the resources into the test destination directory.

    • test-compile : compile the test source code into the test destination directory

    • process-test-classes : post-process the generated files from test compilation, for example to do bytecode enhancement on Java classes.

    • test : run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed.

    • prepare-package : perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package.

    • package : take the compiled code and package it in its distributable format, such as a JAR.

    • pre-integration-test : perform actions required before integration tests are executed. This may involve things such as setting up the required environment.

    • integration-test : process and deploy the package if necessary into an environment where integration tests can be run.

    • post-integration-test : perform actions required after integration tests have been executed. This may including cleaning up the environment.

    • verify : run any checks to verify the package is valid and meets quality criteria.

    • install : install the package into the local repository, for use as a dependency in other projects locally.

    • deploy : done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects.

  • Le lifecycle clean :

    • pre-clean : execute processes needed prior to the actual project cleaning

    • clean : remove all files generated by the previous build

    • post-clean : execute processes needed to finalize the project cleaning

  • Le lifecycle site :

    • pre-site : execute processes needed prior to the actual project site generation

    • site : generate the project’s site documentation

    • post-site : execute processes needed to finalize the site generation, and to prepare for site deployment

    • site-deploy : deploy the generated site documentation to the specified web server

Lorsqu’on exécute communément mvn clean, l’argument clean fait ici référence à la phase clean, et non au lifecycle clean, car on ne peut pas "exécuter" un lifecycle ! Notons que cela signifie que cela entraînera l’exécution de la phase pre-clean qui précède la phase clean, mais n’entraînera pas l’exécution de la phase post-clean.

La même remarque peut être faite pour mvn site.

Ainsi, par exemple, lorsqu’on exécute mvn compile, cela indique à Maven que nous avons l’intention de compiler les sources de notre projet, et cela entrainera l’exécution des phases validate, initialize, generate-sources, process-sources, generate-resources, process-resources, et enfin compile.

Mais comment faire en sorte que Maven fasse réellement quelque chose lorsqu’on exécute une phase ? Voyons la notion de plugin.

2.2. Exécution de maven (2) : plugins et goals

Pour que Maven fasse quelque chose durant une phase, il faut faire appels à des plugins. Un plugin Maven et un composant fournissant un ensemble de services appelés goals ("objectifs"). Un goal est simplement un programme qui va agir sur l’ensemble du projet, que ce soit en analysant, en compilant, ou en produisant des fichiers.

Mais pour qu’un goal soit appelé, il faut au préalable l’associer à une phase ! Ainsi, dès qu’une phase sera exécutée, tous les goals associés à la phase seront exécutés les uns à la suite des autres.

Même s’il est possible de définir explicitement des associations phase-goal manuellement dans un projet, chaque plugin définit souvent implicitement des associations "par défaut" pour ses goals les plus importants.

Notons enfin que même si c’est rarement nécessaire, il est possible de directement demander à Maven d’exécuter un goal donné à l’aide de la commande :

$ mvn <nom du goal>
Exemple 1. Le plugin maven-compiler-plugin

Le plugin org.apache.maven.plugins:maven-compiler-plugin, développé par l’équipe de développement de Maven, permet de compiler le code source d’un projet Java, autrement dit d’utiliser javac afin de de produire des fichiers .class à partir de fichiers .java.

Pour remplir cette mission, ce plugin possède trois goals (avec leurs descriptions officielles en anglais) :

  • compiler:compile : Compiles application sources.

  • compiler:help : Display help information on maven-compiler-plugin.

  • compiler:testCompile : Compiles application test sources.

Pour que ces goals soient bien exécutés, le plugin définit "par défaut" les associations phase-goal suivantes (provenant du lifecycle default) :

  • compiler:compile est associé à la phase compile,

  • compiler:testCompile est associé à la phase test-compile,

Ainsi, si ce plugin est chargé dans un projet maven, et que la phase compile est exécutée (par exemple à la fin de la commande mvn compile), cela exécutera automatiquement le goal compiler:compile.

En revanche, notons que le goal compiler:help n’est par défaut associé à aucune phase. On pourra appeler ce goal manuellement avec la commande mvn compiler:help.

2.3. Structure standard d’un projet Maven

Un projet Maven prend la forme d’un répertoire structuré d’une manière précise :

  • pom.xml : fichier le plus important, qui définit toutes les informations et la configuration du projet (voir prochaine section) ;

  • src : répertoire contenant tous les fichiers du projet qui pourront être lus par les goals exécutés ;

    • main : répertoire contenant tous les fichiers nécessaires pour fabriquer le produit final (code source, fichiers de configuration, images, resources, etc.), avec un sous-répertoire par type de fichiers :

      • java : répertoire contenant tout le code source Java du projet ;

        Même si Maven est surtout utilisé pour des projets Java, on peut avoir autant de sous-répertoires que de langages de programmation utilisés, par exemple des sous-répertoires kotlin, groovy, etc.
      • resources : répertoire contenant tout ce qui n’est pas du code source (images, fichiers de configuration, etc.) ;

    • test : répertoire contenant tous les fichiers nécessaires pour tester le projet, avec là aussi un sous-répertoire par type de fichiers :

      • java : répertoire contenant tout le code source des tests Java du projet ;

        Même remarque que plus haut.
      • resources : répertoire avec toutes les resources nécessaires à la compilation ou l’exécution des tests (snapshots, données de test, etc.) ;

  • target: répertoire qui contient l’intégralité de ce qui sera généré/produit par tous les goals exécutés (code compilé/généré, rapports d’analyse, documentation générée, etc.).

D’autres répertoires standards existent dans la configuration Maven par défaut, mais ils ne seront pas traités ici. Pour plus d’information : https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

Si on résume plus simplement :

  • on configure le projet dans le pom.xml,

  • on développe le projet dans src,

  • et on compile le projet dans target !

Exemple 2. Structure d’un petit projet Java avec Maven

TODO

2.4. Configuration d’un projet Maven : le POM

Le POM ("Project Object Model") est un fichier pom.xml qui définir tout le projet Maven. Il influencera l’intégralité de ce que Maven produira sur un projet. Dit autrement, sans pom.xml, alors Maven n’a aucune idée de quels goals exécuter durant chaque phase.

Il est possible de configurer un très grand nombre de choses dans un pom.xml. Nous allons voir ici les parties les plus importantes pour mener un projet de développement Java classique.

2.4.1. Structure d’un fichier pom.xml

Un fichier pom.xml est un fichier XML prenant la forme suivante, avec un grand nombre de sous-éléments :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

  <!-- Version du POM utilisée -->
  <modelVersion>4.0.0</modelVersion>


  <!-- Carte d'identité du projet -->
  <groupId>...</groupId>
  <artifactId>...</artifactId>
  <version>...</version>
  <packaging>...</packaging>

  <!-- Définition de propriétés -->
  <properties>...</properties>

  <!-- Déclaration des dépendances -->
  <dependencies>...</dependencies>
  <dependencyManagement>...</dependencyManagement>

  <!-- Configuration de la construction (plugins et goals) -->
  <build>...</build>
  <reporting>...</reporting>


  <!-- Relations directes à d'autres projets Maven -->
  <parent>...</parent>
  <modules>...</modules>

  <!-- Déclaration des dépôts utilisés -->
  <repositories>...</repositories>
  <pluginRepositories>...</pluginRepositories>

  <!-- Gestion de profils  -->
  <profiles>...</profiles>

  <!-- Autres informations sur le projet -->
  <name>...</name>
  <description>...</description>
  <url>...</url>
  <inceptionYear>...</inceptionYear>
  <licenses>...</licenses>
  <organization>...</organization>
  <developers>...</developers>
  <contributors>...</contributors>
  <issueManagement>...</issueManagement>
  <ciManagement>...</ciManagement>
  <mailingLists>...</mailingLists>
  <scm>...</scm>
  <distributionManagement>...</distributionManagement>


</project>

On peut voir qu’un pom.xml commence toujours par un élément racine de type project, dont les attributs indiquent quel standard XML à utiliser, à savoir POM 4.0.0.

Puis, le premier sous-élément au sein du project est le modelVersion qui déclare à nouveau la version du format POM, à savoir 4.0.0—identique à ce qui a été déjà déclaré dans les attributs du project. La déclaration du modelVersion est obligatoire.

On notera que seuls certains des sous-éléments d’un pom.xml sont obligatoires, la plupart étant optionnels.

2.4.2. Carte d’identité du projet

Quatre propriétés doivent obligatoirement être déclarées dans un pom.xml, à savoir :

  • groupId : Un identifiant unique pour un groupe de projets maven, auquel se projet appartient. Souvent basé sur le nom du groupe, de l’association, ou de l’entreprise à l’origine du projet.

  • artifactId : Un identifiant pour le projet, unique au sein du groupe.

    Pour le groupId ou le artifactId, il est courant d’utiliser la syntaxe dite reverse-DNS, qui consiste à prendre un nom de domaine (comme example.com) et de l’écrire en inversé (comme com.example).
  • version : La version du projet, à faire évoluer au fur et à mesure des releases.

    Lorsque les attributs groupID, artifactID et versionID sont regroupés sous la forme groupId:artifactId:version, cela constitue un identifiant unique pour le projet dans une version donnée.
  • packaging : Le type d’artefact produit par ce projet Maven. Par défaut, Maven fournit notamment les types suivants :

    • jar : un projet dont le résultat final est un fichier .jar (type le plus couramment utilisé) ;

    • war : un projet dont le résultat final est un fichier .war, à savoir une application web en Java ;

    • pom : un projet qui ne produit rien, et qui n’est qu’un simple conteneur pour d’autres projets (voir plus loin l’attribut modules) ;

    • maven-plugin : un projet dont le résultat final est un plugin Maven ;

Exemple 3. Carte d’identité d’un projet exemple
  <groupId>fr.univ-nantes</groupId>
  <artifactId>sampleproject</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

Si groupID, artifactID et versionID ne servent qu’à identifier le projet, l’attribut packaging va lui avoir une grande influence sur le projet. En effet, le type de packaging définit quels sont les plugins chargés par défaut par Maven pour ce projet, et à quelles phases associer les goals offerts par ces différents plugins !

Ainsi, choisir un packaging jar va automatiquement ajouter les associations phases-goals suivantes :

  • goal resources du plugin org.apache.maven.plugins:maven-resources-plugin:2.6 associé à la phase process-resources,

  • goal compile du plugin org.apache.maven.plugins:maven-compiler-plugin:3.1 associé à la phase compile,

  • goal descriptor du plugin org.apache.maven.plugins:maven-plugin-plugin:3.2 associé à la phase process-classes,

  • goal testResources du plugin org.apache.maven.plugins:maven-resources-plugin:2.6 associé à la phase process-test-resources,

  • goal testCompile du plugin org.apache.maven.plugins:maven-compiler-plugin:3.1 associé à la phase test-compile,

  • goal test du plugin org.apache.maven.plugins:maven-surefire-plugin:2.12.4 associé à la phase test,

  • goal jar du plugin org.apache.maven.plugins:maven-jar-plugin:2.4 associé à la phase package

  • goal addPluginArtifactMetadata du plugin org.apache.maven.plugins:maven-plugin-plugin:3.2 associé à la phase package

  • goal install du plugin org.apache.maven.plugins:maven-install-plugin:2.4 associé à la phase install,

  • goal deploy du plugin org.apache.maven.plugins:maven-deploy-plugin:2.7 associé à la phase deploy

C’est pour cela que, sans ajouter de plugins à votre pom.xml (voir section plus bas à ce sujet), que tout projet Maven avec un packaging jar pourra être compilé avec mvn package.

2.4.3. Définition de propriétés

Il est possible de définir ou de redéfinir des propriétés à l’aide de la partie properties d’un pom.xml. Chaque propriété doit être écrite dans sa propre balise , et peut contenir n’importe quelle chaîne de caractères.

Cela peut avoir deux utilités : - Soit pour stocker dans des nouvelles propriétés des valeurs qu’on souhaite utiliser plus tard dans le pom.xml, par exemple des numéros de version. Pour utiliser une valeur déclarée dans une propriété, on fera ${ma.propriété}. - Soit pour redéfinir des propriétés déjà existantes au sein de plugin Maven, afin d’aller paramétrer ces derniers.

Exemple 4. Définition et usage de nouvelles propriétés

On choisit de mettre dans une propriété la version d’une dépendance (ici JUnit 4) :

  <properties>
        <junit.version>4.12</junit.version>
  </properties>

Et on utilise cette propriété lorsqu’on déclare cette dépendance :

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <type>jar</type>
      <scope>test</scope>
      <optional>true</optional>
    </dependency>
  </dependencies>

Comme les versions ont tendance à beaucoup changer, il peut être utile de toutes les déclarer au même endroit tout en haut du pom.xml plutôt que de les éparpiller dans tout le fichier.

Exemple 5. Redéfinition de propriétés

On redéfinit les propriétés maven.compiler.source et maven.compiler.target utilisées par le plugin org.apache.maven.plugins:maven-compiler-plugin pour savoir quelle version de Java est utilisée dans le projet :

  <properties>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

2.4.4. Déclaration des dépendances

  <!-- Déclaration des dépendances -->
  <dependencies>...</dependencies>
  <dependencyManagement>...</dependencyManagement>

TODO

mvn dependency:resolve

2.4.5. Configuration de la construction (plugins et goals)

  <!-- Configuration de la construction (plugins et goals) -->
  <build>...</build>
  <reporting>...</reporting>

build + reporting

plugins+goals

mapping phase→goals

→ show effective pom !! mvn help:effective-pom

2.4.6. Déclaration des dépôts utilisés

  <!-- Déclaration des dépôts -->
  <repositories>...</repositories>
  <pluginRepositories>...</pluginRepositories>

2.4.7. Relations directes à d’autres projets Maven

  <!-- Relations directes à d'autres projets Maven -->
  <parent>...</parent>
  <modules>...</modules>

2.4.8. Gestion de profils

  <!-- Gestion de profils  -->
  <profiles>...</profiles>

2.4.9. Autres informations sur le projet

  <!-- Autres informations sur le projet -->
  <name>...</name>
  <description>...</description>
  <url>...</url>
  <inceptionYear>...</inceptionYear>
  <licenses>...</licenses>
  <organization>...</organization>
  <developers>...</developers>
  <contributors>...</contributors>
  <issueManagement>...</issueManagement>
  <ciManagement>...</ciManagement>
  <mailingLists>...</mailingLists>
  <scm>...</scm>
  <distributionManagement>...</distributionManagement>