Systèmes de Construction (Les bases)
Dans cette unité, nous aborderons les bases des systèmes de construction et illustrerons leur fonctionnement à l'exemple de Maven. Nous commencerons par une brève récapitulation de la compilation de langages, notamment dans le contexte du langage de programmation Java, puis nous examinerons les défis associés à l'assemblage des binaires. Enfin, nous examinerons les caractéristiques principales de Maven, notamment la gestion des dépendances, et nous ferons nos premiers essais pour personnaliser le comportement du système de construction à l'aide d'un fichier de configuration.
Résumé de la lecture
Les systèmes de construction ont un seul but : s'assurer que votre code source peut être traduit de manière fiable en un produit utilisable. Bien que cela puisse sembler simple, ce n'est pas du tout une tâche facile. Les systèmes de construction sont un moyen puissant et hautement configurable d'apporter de l'ordre et de la fiabilité dans le chemin allant du code source au produit.
Récapitulatif du compilateur Java
Pour commencer, nous allons examiner de plus près comment un programme simple est exécuté sur un ordinateur.
Exécution du code
Il existe deux façons d'exécuter du code. Le chemin emprunté dépend du langage de programmation.
- En utilisant un interpréteur : L'ordinateur essaie de comprendre votre code source, au fur et à mesure qu'il le
traite, ligne par ligne. Exemples :
- Python
- Bash
- Javascript
- En utilisant un compilateur : L'ordinateur ne comprend pas votre code source, mais s'attend à ce que vous le
traduisiez d'abord en bytecode, en utilisant un compilateur. Exemples :
- Basic
- C/C++
- Java ???
À propos des binaires
En général, le code compilé est lié à une plateforme cible spécifique. Autrement dit, une fois qu'un compilateur a traduit le code source en bytecode, le résultat ne peut être utilisé que sur un matériel spécifique.
Langages interprétés VS compilés
Alors, lequel est le mieux ?
- Langages interprétés :
- Compatibilité multiplateforme
- Développement légèrement plus rapide, pas besoin d'attendre le compilateur
- Syntaxe souvent "plus facile", conviviale pour les débutants
- Langages compilés :
- Plus performant : optimisations du compilateur pour la plateforme cible, exécuté en code natif
- Plus sûr : Moins d'erreurs d'exécution, plus d'erreurs de compilation
Notez qu'un compilateur n'a besoin de faire son travail qu'une seule fois, contre un interpréteur qui doit s'exécuter chaque fois qu'un programme est exécuté.
Et qu'en est-il de Java ?
Java est un cas particulier...
- Java est un langage compilé
- Le bytecode Java fonctionne sur toutes les plateformes, car il est interprété par une machine virtuelle, la JVM.
- Java réunit certains avantages des deux :
- Compatibilité multiplateforme
- Optimisations de compilateur performantes pour la VM
- Sécurité grâce aux vérifications du compilateur
Illustration du compilateur Java
Étape 1 : Écrire du code Java
Un développeur humain écrit du code Java lisible par l'homme :
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Étape 2 : Compiler en bytecode Java
Le compilateur Java est appelé : javac HelloWorld.java et produit du bytecode :
CAFE BABE 0000 0042 001D 0A00 0200 0307 0004 0C00 0500 0601 0010 6A61 7661
2F6C 616E 672F 4F62 6A65 6374 0100 063C 696E 6974 3E01 0003 2829 5609 0008
0009 0700 0A0C 000B 000C 0100 106A 6176 612F 6C61 6E67 2F53 7973 7465 6D01
0003 6F75 7401 0015 4C6A 6176 612F 696F 2F50 7269 6E74 5374 7265 616D 3B08
000E 0100 0D48 656C 6C6F 2C20 576F 726C 6421 0A00 1000 1107 0012 0C00 1300
1401 0013 6A61 7661 2F69 6F2F 5072 696E 7453 7472 6561 6D01 0007 7072 696E
746C 6E01 0015 284C 6A61 7661 2F6C 616E 672F 5374 7269 6E67 3B29 5607 0016
0100 0A48 656C 6C6F 576F 726C 6401 0004 436F 6465 0100 0F4C 696E 654E 756D
6265 7254 6162 6C65 0100 046D 6169 6E01 0016 285B 4C6A 6176 612F 6C61 6E67
2F53 7472 696E 673B 2956 0100 0A53 6F75 7263 6546 696C 6501 000F 4865 6C6C
6F57 6F72 6C64 2E6A 6176 6100 2000 1500 0200 0000 0000 0200 0000 0500 0600
0100 1700 0000 1D00 0100 0100 0000 052A B700 01B1 0000 0001 0018 0000 0006
0001 0000 0001 0009 0019 001A 0001 0017 0000 0025 0002 0001 0000 0009 B200
0712 0DB6 000F B100 0000 0100 1800 0000 0A00 0200 0000 0300 0800 0400 0100
1B00 0000 0200 1C
(Hex dump produit avec : xxd -u -p HelloWorld.class | sed 's/..../& /g'
)
Voyez-vous quelque chose d'inhabituel ?
Le dump hex du bytecode Java montre que chaque classe compilée commence par CAFEBABE
. Il semble que ce soit un œuf de Pâques ajouté par les développeurs Java.
Étape 3 : Exécuter le bytecode sur la JVM
Enfin, le bytecode est distribué aux différents systèmes cibles.
- Notez que tout système a besoin d'une JVM pour exécuter le bytecode Java.
- Les autres langages compilés n'ont pas cette exigence, car ils produisent directement du code exécutable par le CPU.
Comment la JVM est-elle le mieux décrite ?
La JVM est un interpréteur. Elle lit le bytecode Java et envoie immédiatement des instructions d'exécution au système hôte dans le langage machine natif du CPU.
JARs
- Dans la plupart des cas, votre programme Java sera plus qu'une seule classe.
- Vous pourriez toujours traduire toutes les classes et les expédier, peut-être sous la forme d'un fichier zip
- Mais Java a déjà un format de fichier pour cela : les JARs.
- JAR signifie "Java ARchive"
- Les JARs sont des fichiers zip
- Les JARs contiennent toutes les classes et un
manifest
, avec des informations meta, notamment le point d'entrée de votre application
- Les JARs sont toujours exécutés par la JVM, et dans le meilleur des cas, fonctionnent sur tous les systèmes
Utilisation des JARs
- Créer un fichier JAR à partir des sources est relativement simple :
# Compiler tous les fichiers Java, placer les fichiers de classe dans un nouveau répertoire "build"
javac -d ./build *java
# Aller dans le répertoire build
cd build
# Créer une archive Java (JAR), en utilisant tout le contenu de ce répertoire (*),
# ajouter un Manifest qui indique "HelloWorld" comme classe principale.
jar cfe MonLivrable.jar HelloWorld *class
-
Cela produit un fichier JAR :
MonLivrable.jar
-
Contenu :
-
Avec le contenu de
MANIFEST.MF
:
-
-
Le fichier JAR peut être directement exécuté, en utilisant la JVM :
java -jar MonLivrable.jar
Fichiers JAR pour les bibliothèques
Les fichiers JAR sont également un excellent moyen de fournir des fonctionnalités à d'autres programmeurs. La plupart des bibliothèques Java sont fournies sous forme de fichiers JAR. Ceux qui utilisent votre code sont le plus souvent seulement intéressés par les fonctionnalités que vous offrez, pas par le code source lui-même.
Dépendances
La plupart du temps, vous ne voulez pas tout programmer depuis le début ( voir leçon précédente sur le développement orienté vers la réutilisation)
Exemple JSON
- Nous allons maintenant examiner comment la compilation et l'exécution changent lorsque des bibliothèques supplémentaires sont impliquées.
-
Imaginez que nous voulons sérialiser (créer une représentation en chaîne de caractères lisible par machine) d'un objet Java :
-
Un objet étudiant, créé avec
new Student(34, "Maximilian", "Schiedermeier")
, devrait être sérialisé en :
Création manuelle de chaîne
-
Bien sûr, je pourrais construire manuellement une chaîne JSON :
// Créer un étudiant Student myStudent = new Student(34, "Maximilian", "Schiedermeier"); // Exporter l'étudiant String jsonString = "{\n" + "\t\"age\": " + myStudent.getAge() + ",\n\t\"firstName\": \"" + myStudent.getFirstName() + "\", \n\t\"lastName\": \"" + myStudent.getLastName() + "\"\n}"; System.out.println(jsonString);
-
Mais que faire si je dois exporter un autre objet ? Que se passe-t-il si la structure de l'objet change ?
La bibliothèque GSON
-
Il serait beaucoup plus facile de réutiliser la bibliothèque Google GSON existante :
-
Cependant, nous utilisons maintenant du code qui n'est pas le nôtre, et le compilateur, ainsi que le JDK, doivent connaître cette dépendance.
- Télécharger le fichier JAR de la bibliothèque Gson :
- Cette fois, nous compilons avec l'argument
-cp
(classpath), indiquant au compilateur qu'il y a des classes supplémentaires à prendre en compte.javac -cp gson-2.11.0.jar *java
- De même, lors de l'exécution du bytecode compilé, la JVM doit connaître la bibliothèque GSON :
java -cp gson-2.11.0.jar:. MainWithGson
Que pourrait-il mal se passer ?
En réutilisant la bibliothèque Google GSON, nous avons créé une "dépendance". Sans cette bibliothèque à portée de main, notre code ne peut ni être compilé, ni être exécuté.
Le problème avec les JARs
Les JARs sont un moyen simple de partager des fonctionnalités, mais à mesure que les projets se développent, plusieurs problèmes tendent à persister :
- Plus vous avez de dépendances, plus vous transportez de JARs avec vous.
- Où stocker les JARs ? Dans le dépôt ? Que faire si vous avez besoin du même JAR dans plusieurs projets, devez-vous les stocker deux fois ?
- Chaque fois qu'un nouveau développeur rejoint le projet, vous devez lui transmettre tous les JARs et lui faire étendre manuellement son classpath.
- Compiler votre projet devient quelque peu fastidieux, car vous devez toujours vérifier qu'une longue liste de dépendances est correctement installée.
- Le client se plaint que votre logiciel ne fonctionne pas. Il est probable qu'il ait négligé d'installer un JAR, ou a installé la mauvaise version. Comment savoir lequel c'est ?
- Un JAR est un instantané, c'est une version fixe.
- Que faire si une vulnérabilité de sécurité a été trouvée dans un JAR que vous avez téléchargé. Comment le sauriez-vous ?
- Vous avez perdu un JAR dont vous avez besoin pour construire votre projet, où le retrouver ? Quelle version était-elle déjà compatible avec votre projet ?
Une véritable histoire d'horreur
Dans un précédent laboratoire de recherche, nous avions un logiciel particulièrement difficile à utiliser.
Avant qu'un développeur puisse même écrire une seule ligne de code, il devait passer au moins 30 minutes à 1 heure à configurer manuellement le projet.
Le projet avait même des JARs dont personne ne savait exactement d'où ils venaient, s'ils étaient encore nécessaires, ou ce qu'ils apportaient exactement.
Il y avait des rumeurs selon lesquelles un stagiaire, qui avait travaillé il y a environ 3 ans, avait créé les JARs.
Mais le stagiaire était parti depuis longtemps et personne n'avait ses coordonnées. En même temps, ce étaient des artefacts logiciels volumineux qui alourdaient notre exécutable.
D'innombrables heures de développement ont été gaspillées à cause d'une mauvaise gestion des dépendances.
Gestion des dépendances
La gestion des dépendances vise à éliminer tous les problèmes mentionnés précédemment en spécifiant plutôt quelles dépendances existent (et où les obtenir), plutôt que de gérer manuellement les fichiers JAR.
Essentiellement, les ingrédients pour tout outil de gestion des dépendances sont :
- Un dépôt en ligne, archivant systématiquement toutes les versions de toutes les bibliothèques
- Un fichier de configuration local, décrivant pour chaque dépendance :
- Un identifiant unique, par exemple "Bibliothèque Google GSON"
- La version spécifique, par exemple "2.11.0"
Avantages :
- Les fichiers de configuration sont textuels et légers. Ils peuvent être stockés dans le projet lui-même.
- Les fichiers de configuration sont écrits dans une syntaxe interprétable par machine. Un outil peut collecter toutes les dépendances pour vous et même modifier le classpath si nécessaire.
- Vous avez une trace claire de toutes les versions exactes des dépendances. Vous pouvez facilement analyser votre projet pour détecter les vulnérabilités de sécurité.
- Aucun dommage n'est causé si vous perdez un JAR de bibliothèque, vous pouvez facilement le récupérer à partir du dépôt.
Maven
Maven est un système de construction pour Java qui offre exactement ces deux composants :
- Un dépôt central, contenant presque toutes les bibliothèques Java jamais créées : mavencentral.org
- Un fichier de configuration de projet qui (entre autres) liste toutes les dépendances du projet :
pom.xml
- POM signifie "Project Object Model"
- XML est un format de fichier lisible par machine
- Une dépendance est déclarée comme suit :
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
Au lieu de télécharger nous-mêmes des fichiers JAR et de les placer dans le classpath, nous demandons à Maven de s'assurer que toutes les dépendances listées sont en place.
Jamais, au grand jamais
Ne jamais, au grand jamais, interférer manuellement avec la gestion des dépendances dans un projet prêt pour Maven. Si vous avez besoin d'une bibliothèque supplémentaire, modifiez le pom.xml
, mais ne faites jamais glisser et déposer un fichier JAR dans votre projet, ni modifier le classpath.
Dépôts
Le dépôt local :
- Maven maintient également un dépôt local sur votre ordinateur, dans le répertoire
~/.m2
. Chaque bibliothèque que vous avez utilisée est mise en cache dans ce répertoire. - Le dépôt local a deux objectifs :
- Performance : Il est plus rapide de réutiliser un fichier JAR mis en cache que de le télécharger sur Internet à chaque fois.
- Mode hors ligne : Vous n'êtes peut-être pas toujours en ligne. Avec les dépendances mises en cache, vous pouvez développer sans connexion Internet.
Dépôts tiers :
- Vous pourriez rencontrer des situations où vous avez besoin d'une bibliothèque qui n'est pas dans le dépôt central officiel de Maven.
- Exemples :
- Bibliothèques qui ne sont pas libres d'utilisation et donc pas accessibles publiquement.
- Vos propres bibliothèques que vous ne souhaitez pas télécharger.
- Quiconque peut créer son propre dépôt.
- Un dépôt en ligne se compose simplement de quelques fichiers accessibles via un serveur web HTTP.
- Cependant, par défaut, Maven ne connaît pas les dépôts tiers. Si vous souhaitez que Maven recherche dans votre propre dépôt, vous devez modifier le fichier
pom.xml
et indiquer l'emplacement de votre dépôt tiers.
L'algorithme de résolution des dépendances de Maven
Pour construire un projet, Maven essaie de satisfaire toutes les dépendances avec les artefacts correspondants (les fichiers JAR et certaines métadonnées). Pour satisfaire une dépendance, Maven :
- Vérifie d'abord le dépôt local
.m2
pour un fichier mis en cache. - Si le fichier n'est pas mis en cache, il vérifie si des dépôts tiers sont définis. (Généralement, aucun n'est défini.)
- Contacte les serveurs du dépôt officiel de Maven pour récupérer l'artefact nécessaire.
flowchart LR
resolve[\Resolve depdendency/]
resolve --> localcheck{Artifact in local repo ?}
localcheck -. yes .-> done([Success])
localcheck ==>|no| remotecheck{3rd party repo defined ?}
remotecheck -. yes .-> 3rdpartycheck{Artifact in 3rd party ?}
3rdpartycheck -. yes .-> done
3rdpartycheck -. no .-> centralcheck{Artifact in central ?}
remotecheck ==>|no| centralcheck
centralcheck ==>|yes| done
centralcheck -. no .-> fail([Fail])
Que se passe-t-il lorsqu'un projet est construit pour la deuxième fois ?
Maven aura déjà toutes les dépendances mises en cache. Il prendra le chemin le plus élevé.
Maven en action
Nous allons maintenant couvrir quelques scénarios d'utilisation de base pour Maven.
Structure du projet Maven
- Les projets Maven stipulent une structure interne spécifique.
- Nous n'allons pas créer la structure du projet manuellement, mais utiliser Maven pour initialiser nos projets :
mvn archetype:generate \
-DgroupId=ca.uqam.info \
-DartifactId=MavenHelloWorld \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
Note: quelques systèmes (windows) n'aiment pas des commandes répartis sur plusieurs lignes. Enlevez les
\
et mettez tout dans une seule ligne.
Décomposons la commande ci-dessus :
archetype
se traduit par "nous voulons utiliser un modèle de projet"- Il existe différents archétypes pour différents usages. Par exemple, pour une application web ou un backend de serveur, nous aurions utilisé un
archetypeArtifactId
différent. - Semblable à toutes les dépendances dont vous pourriez avoir besoin, votre propre logiciel doit avoir un identifiant unique. D'autres développeurs pourraient même finir par utiliser votre logiciel comme bibliothèque !
groupId
représente une chaîne spécifique à l'organisation, généralement il s'agit simplement du nom de domaine inversé de l'entreprise pour laquelle vous travaillez. Comme nous sommes tous au département d'informatique de l'UQAM, nous utilisonsca.uqam.info
artifactId
désigne le logiciel que vous construisez. Il doit s'agir d'un nom descriptif, indiquant ce que fait votre logiciel.
Une fois exécutée, la commande ci-dessus aura créé la structure de dossier et de fichiers suivante :
MavenHelloWorld/
├── pom.xml
└── src
├── main
│ └── java
│ └── ca
│ └── uqam
│ └── info
│ └── App.java
└── test
└── java
└── ca
└── uqam
└── info
└── AppTest.java
12 directories, 3 files
Pour l'instant, nous ne sommes intéressés que par le
pom.xml
et le fichier de classe initialApp.java
. Nous traiterons des tests dans une prochaine leçon.
Classe App initiale
Le fichier pom initial est simplement un squelette de classe HelloWorld :
package ca.uqam.info;
/**
* Hello world!
*
*/
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Structures de packages
Remarquez comment l'argument groupId initial a influencé le nommage des packages du projet et la structure interne des dossiers ?
Fichier pom initial
Le fichier pom initial ressemble à ce qui suit, tel qu'il a été créé :
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uqam.info</groupId>
<artifactId>MavenHelloWorld</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>MavenHelloWorld</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Nous voyons déjà une première entrée de dépendance, à savoir pour junit
.
- Dans l'esprit d'un bon développement logiciel, Maven suppose que nous allons tester notre logiciel.
- Cependant, JUnit ne fait pas partie du standard Java. Par conséquent, nous avons besoin d'un bloc de dépendance.
Y a-t-il quelque chose de particulier à propos du bloc de dépendance ?
Le bloc de dépendance JUnit a en fait une entrée <scope>test</scope>
supplémentaire. Cela est dû au fait que Maven fait une distinction entre les dépendances nécessaires pour construire un logiciel et celles nécessaires pour exécuter un logiciel. JUnit n'est pas nécessaire à l'exécution, c'est pourquoi Maven a ajouté un tag de portée test
.
Construction avec Maven
Utilisons Maven pour construire le projet, c'est-à-dire créer du bytecode Java. La commande correspondante est mvn package
.
- La première fois que vous exécutez
mvn package
, vous verrez en fait comment Maven télécharge JUnit. - Il y aura quelques messages de journalisation :
...
Downloading from central:
https://repo.maven.apache.org/maven2/org/apache
/maven/surefire/common-java5/3.2.5/common-java5-3.2.5.pom
Downloaded from central:
https://repo.maven.apache.org/maven2/org/apache
/maven/surefire/common-java5/3.2.5/common-java5-3.2.5.pom
(2.8 kB at 156 kB/s)
...
-
Une fois la commande terminée, nous trouverons un nouveau répertoire
target
, avec le contenu suivant : -
Parmi d'autres, c'est exactement le même résultat que nous aurions pu créer manuellement, en utilisant le compilateur Java :
- Un fichier JAR
- Des fichiers de classe pour notre code source
Exécution des artefacts Maven
L'exécution des artefacts générés est presque identique à l'exécution des binaires créés manuellement.
Fichiers de classe
Nous pouvons sans problème exécuter les fichiers de classe générés. Notez cependant que nous devons être à la racine de la structure des packages pour appeler notre programme :
- Appel du programme
App.class
depuis un mauvais emplacement :
$ cd target/classes/ca/uqam/info; java App
Error: Could not find or load main class App
Caused by: java.lang.NoClassDefFoundError: App
(wrong name: ca/uqam/info/App)
- Appel du programme
App.class
depuis l'emplacement racine du package :
Fichiers JAR
L'exécution du fichier JAR n'est pas possible sans spécifier la classe principale, car par défaut le manifest
ne contient pas de référence à la classe de lancement.
-
Tentative d'exécution du fichier JAR sans arguments :
-
Lorsque nous inspectons le fichier MANIFEST interne du JAR, nous constatons qu'aucun lanceur n'est spécifié :
-
Exécution du fichier JAR avec une classe principale personnalisée comme argument de classpath :
Remarque : Maven offre bien sûr un moyen d'intégrer un MANIFEST fonctionnel dans le fichier JAR produit. Plus d'informations à ce sujet dans un instant.
Une construction propre
Le répertoire target accumule tous les artefacts jamais construits. Si vous modifiez votre code ou le pom.xml
et que vous reconstruisez, de nouveaux fichiers pourraient être ajoutés et il peut être déroutant de distinguer les anciens des nouveaux fichiers.
Une bonne astuce est d'utiliser toujours l'argument clean
avant de construire, ce qui efface l'ensemble du répertoire target
: Construisez votre projet systématiquement avec **mvn clean package**
Plugins Maven
En plus de télécharger et de mettre en cache les dépendances pour une utilisation dans le classpath local, Maven a également un second objectif : Modifier le pipeline de construction.
- Par défaut, tout ce qui se passe lors de
mvn clean package
est la compilation standard des fichiers sources (en utilisant toutes les bibliothèques spécifiées pour le processus). - Mais la plupart du temps, vous souhaitez faire plus, par exemple produire une documentation lisible par l'homme, exécuter des tests ou créer un artefact de construction avec toutes les dépendances incluses.
- Le comportement de Maven concernant le pipeline de construction peut être modifié avec des
plugins
.
Un plugin est un court extrait (ou parfois pas si court) dans une section dédiée plugins
du pom.xml
. Vous pouvez avoir autant de plugins que vous le souhaitez dans le pom.xml
:
<project>
<build>
<plugins>
<!-- First plugin details -->
<plugin>
...
</plugin>
<!-- Second plugin details -->
<plugin>
...
</plugin>
...
</plugins>
</build>
</project>
- Chaque plugin a une emplacement par défaut dans le pipeline de construction, car la plupart des tâches n'ont de sens qu'à un moment donné du processus.
- Exemple : construire un JAR avec toutes les dépendances à l'intérieur devrait se faire à la fin, après que toutes les classes aient été compilées, que tous les tests aient réussi, etc.
Nous examinerons comment les plugins fonctionnent plus en détail, ainsi que la compréhension par Maven des points de variation des plugins dans le processus de construction dans une future leçon. Pour l'instant, nous allons voir quelques exemples de plugins utiles.
Exec
Le plugin exec
vous permet de spécifier une classe principale pour votre code, qui devrait être appelée par défaut lorsque le code est exécuté.
- C'est ce qui se rapproche le plus du fameux triangle vert ("
▶ ") - Tout ce que vous devez faire est d'indiquer la classe principale à appeler lors de l'exécution :
<!-- Specify main class for exec goal -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>full.package.name.YourMainClassLauncher</mainClass>
</configuration>
</plugin>
Une fois le plugin défini, vous pouvez facilement exécuter votre programme avec : mvn clean compile exec:java
Ajouter une configuration d'exécution Maven dans l'IDE
Une fois le plugin exec défini dans votre pom.xml
, modifiez la "Configuration d'exécution" de l'IDE (aussi appelée lorsque le triangle vert est cliqué) pour simplement appeler le plugin exec de Maven !
Plugin Maven Jar
Le plugin JAR de Maven vous permet d'ajouter des informations supplémentaires lorsque votre programme est empaqueté dans un JAR.
- Précédemment, nous avons vu qu'un JAR produit par Maven ne peut pas être lancé, sans indiquer explicitement la classe principale.
- Le
maven-jar-plugin
vous permet de fournir une information par défaut, sur laquelle classe principale doit être listée dans le manifest du JAR.
<!-- specify main class for JAR manifest-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<mainClass>full.package.name.YourMainClassLauncher</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
JavaDoc
Lors de la deuxième séance de laboratoire, vous avez appris une commande pour extraire manuellement toutes les informations JavaDoc de votre code afin de générer un site Web lisible par l'homme. Le plugin JavaDoc vous permet d'automatiser cette étape, en tant que composant standard du processus de construction.
- Activer le plugin JavaDoc est également une bonne pratique, car vous pouvez directement voir s'il y a des problèmes dans votre style de code, chaque fois que vous compilez votre code.
- Idéalement, le plugin est configuré pour échouer en cas d'avertissements, afin qu'aucun développeur ne soit tenté de travailler avec ou de produire du code non documenté.
- "Je documenterai cela plus tard", se transforme facilement en "Je ne documenterai jamais cela."
<!-- Plugin to ensure all functions are commented and generate javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
<reportOutputDirectory>${project.reporting.outputDirectory}/docs
</reportOutputDirectory>
<failOnWarnings>true</failOnWarnings>
<quiet>true</quiet>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
Utilisez une bibliothèque de snippets
La plupart des développeurs ne créent pas manuellement leur pom.xml
ligne par ligne, mais l'assemblent à partir de blocs
préparés. Utilisez une bibliothèque de snippets, par exemple https://m5c.github.io/MavenSnippetLibrary/ pour créer rapidement un pipeline de construction fonctionnel.
Gestion des dépendances au-delà de Java
- La gestion des dépendances n'est pas un concept spécifique à Java.
- Presque chaque langage dispose d'outils pour assurer une gestion adéquate des dépendances, mais quel que soit le langage, les deux composants principaux restent :
- Un fichier local pour spécifier les dépendances
- Un dépôt central pour obtenir des artefacts
- Un cache local des dépendances installées
- Python :
- Les dépendances du projet sont spécifiées dans un fichier
requirements.txt
- Tout comme les artefacts Java, toutes les dépendances listent un identifiant unique et une version, par exemple :
- L'installateur de paquets de Python,
pip
, peut consommer un fichierrequirements.txt
pour télécharger des artefacts :pip install -r requirements.txt
- Python n'a pas de concept de dépôt local partagé, mais offre des environnements virtuels.
- Les environnements virtuels sont des caches locaux de dépendances.
- Ils remplacent l'interpréteur Python global et permettent l'installation de dépendances spécifiques au projet.
Littérature
Inspiration et lectures supplémentaires pour les esprits curieux :