Maven Tmpfs
Introduction
Je suis un utilisateur convaincu de maven, malgré ces défauts, le
moto “Convention over configuration” me va vraiment bien. Que ce soit
au boulot ou à la maison, j’ai plus d’ordinateurs équipés de ssd (ou de
mémoire flash) que de disque traditionnel (mécanique ?). Pour augmenter
un peu la durée de vie de ces disques SSD, j’ai cherché à savoir comment
déporter le build de maven (qui, pour rappel, se passe dans le
dossier target/
) hors du SSD ; ici ce sera dans le dossier /tmp/
qui
est monté en mémoire (merci tmpfs
), mais on peut imaginer déporter ça
sur un autre disque, etc.. Après quelques recherches j’ai trouvés
quelques inspirations.
Limitations
Dans la solution présentée ci-dessous les principales limitations sont les suivantes (que j’essaierais de diminuer au fil du temp ;P) :
- Il est nécessaire de modifier le pom.xml du projet ; cela ne s’appliquera donc pas à tous les projets maven sans modification du pom.xml.
- Cela ne fonctionne que sur une plateforme qui support les liens symboliques (Linux, Mac OS X, et autre UNIX).
- Cela ne fonctionne qu’avec Java 7 ou plus.
- Si vous utilisez m2e, il va gentillement gueuler et c’est moche ; pour résoudre le problème, il faut faire un tour vers M2E plugin execution not covered.
Pour maven, le dossier target/
vient de la propriété
project.build.directory
. Dans la théorie, il suffirait de modifier
(dans $HOME/.m2/settings.xml
) cette propriété et le tour serait jouer.
Malheuresement ce n’est pas possible, project.build.directory
est une
propriété interne et n’est, à priori, pas modifiable.
Notre souhait est le suivant :
- Le build doit se faire dans
/tmp/m2/
, ce qui pour un projet se traduit par/tmp/m2/${groupId}:${artifactId}
. - Le dossier
target/
dans les sources est un lien symbolique vers le dossier dans/tmp/m2/
- On passe par un profile qui n’est pas actif par défaut (pour ne
pas faire chier le monde) mais activable via une propriété (maven
nous permet de le faire et c’est cool
^_^
). La propriété utilisée seraexternal.build.root
.
Le code ci-dessous est repris directement de mon inspiration1. Il
s’occupe de créer le dossier ${groupId}:${artifactId}
dans
external.build.root
et de faire le lien dans le dossier courant.
<project> <!-- […] --> <profiles> <profile> <id>external-build-dir</id> <activation> <activeByDefault>false</activeByDefault> <property> <name>external.build.root</name> </property> </activation> <build> <plugins> <plugin> <groupId>com.alexecollins.maven.plugin</groupId> <artifactId>script-maven-plugin</artifactId> <version>1.0.0</version> <executions> <execution> <id>prep-work-tree</id> <goals> <goal>execute</goal> </goals> <phase>initialize</phase> <configuration> <script> import java.nio.file.* def dir = "${external.build.root}/${project.groupId}:${project.artifactId}" println "using Maven dir ${dir}" def dirPath = Paths.get(dir) if (!Files.exists(dirPath)) { Files.createDirectories(dirPath) } def target = Paths.get("${project.build.directory}") if (!Files.exists(target)) { Files.createSymbolicLink(target, dirPath) }</script> </configuration> </execution> <execution> <id>drop-symlink</id> <goals> <goal>execute</goal> </goals> <phase>clean</phase> <configuration> <script> import java.nio.file.* def target = Paths.get("${project.build.directory}") if (Files.isSymbolicLink(target)) { Files.delete(target) } </script> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy</artifactId> <version>1.8.6</version> </dependency> </dependencies> <configuration> <language>groovy</language> </configuration> </plugin> </plugins> </build> </profile> </profiles> <!-- […] --> </project>
Ainsi, il suffit ensuite d’avoir quelques choses du genre dans son
$HOME/.m2/settings.xml
pour que les builds qui ont ce profil se
build dans /tmp/m2/
. On peut aussi ne rien avoir dans
$HOME/.m2/settings.xml
et utilise -Dexternal.build.root=/tmp/m2/
avec la commande mvn
.
<settings> <!-- […] --> <profiles> <profile> <id>build-in-ramfs</id> <properties> <external.build.root>/tmp/m2/</external.build.root> </properties> </profile> </profiles> <activeProfiles> <activeProfile>build-in-ramfs</activeProfile> </activeProfiles> <!-- […] --> </settings>