Il y a 11 ans -
Temps de lecture 3 minutes
Debugger un annotation processor dans un IDE
Cet article est une traduction du billet "Debugging an annotation processor in every IDE" publié sur mon blog.
Durant le dernier HackerGarten, j’ai pu travailler avec Pierre-Yves Ricau sur le projet d’annotation processor "AndroidAnnotations". Si le debugging des annotation processors est plutôt bien documenté pour Eclipse, il ne l’est pas ou très peu pour IntelliJ. Dans cet article, nous comblerons cette lacune en décrivant un mode de debug fonctionnant sous tous environnements confondus.
Tout d’abord, une définition. Un annotation processor est une librairie à ajouter au compilateur javac et qui enrichit notre code source suivant les annotations qui s’y trouvent. Il permet donc d’alléger notre code tout en le rendant plus expressif.
Notons que l’expression "enrichir notre code source" doit être clarifiée. En effet, la JSR-269 n’autorise pas la modification de fichiers source .java. Cependant, il est tout à fait possible d’en créer de nouveaux, tout comme AndroidAnnotations le fait. Il est également possible, mais non standard, de modifier le bytecode généré, comme le fait le projet lombok en utilisant l’API interne de javac.
Les outils pour leur développement ne manquent pas : Eclipse, NetBeans et IntelliJ en ont un bon support. Mais la documentation sur leur debugging fait défaut. Par exemple, pour debugger un annotation processor sous éclipse, les rares pages mentionnent la création d’un plugin eclipse.
Durant le HackerGarten, nous avons opté pour une approche différente : puisque l’annotation processor est une librairie ajoutée au compilateur, si nous parvenons à lancer le compilateur javac en mode debug et à nous y connecter, le tour est joué.
Ajout de l’Annotation Processor (AP) au compilateur
Une fois le projet importé dans IntelliJ, nous pouvons d’ores et déjà ajouter l’annotation processor au process de compilation. Dans le cas d’AndroidAnnotations, nous l’activons en spécifiant le nom complet de la classe (FQN – fully-qualified name) et le module functionnal-test-1-5
. Pour cela, il ne faut pas oublier de spécifier un dossier de sortie relatif à la racine du module, ici target/processed
.
Vérification de l’AP
Lorsque nous passons la classe BeanInjectedActivity.java
à l’annotation processor, nous constatons qu’une classe BeanInjectedActivity_.java
est bien générée dans le dossier target/processed
et qu’elle est enrichie
Passage du compilateur en mode debug
Nous pouvons passer une instance de JVM en mode debug à l’aide de quelques options dont -Xdebug
. Dans notre cas, la commande javac n’est pas en elle-même une instance de la JVM et n’accepte donc pas ces options.
En revanche, javac crée une instance de la JVM dans laquelle le compilateur exécute le travail de compilation, et nous permet d’y envoyer des options en utilisant le paramètre -J
:
-J option Pass option to the java launcher called by javac. For example, -J-Xms48m sets the startup memory to 48 megabytes. It is a common convention for -J to pass options to the underlying VM executing applications written in Java.
Nous pouvons donc utiliser les options -J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
pour lancer le compilateur javac en mode debug. Nous ajoutons donc ces options dans IntelliJ et définissons un breakpoint dans la méthode init de la classe AndroidAnnotationProcessor.
Ajout d’une configuration et debugging
Nous pouvons maintenant ajouter une configuration de debugging pour le port 5005 dans IntelliJ et relancer l’annotation processor sur la classe BeanInjectedActivity.java
. Le processus de compilation bloque sur la phase "make", ce qui indique que le compilateur attend la connexion d’un debugger. Nous pouvons alors lancer le debugger d’intelliJ et constater que :
- le compilateur s’arrête au breakpoint de la classe AndroidAnnotationProcessor
- la stacktrace mentionne bien le compilateur javac
Conclusion
Nous avons vu comment le compilateur javac lui-même pouvait être utilisé en mode debug. La technique exposée ici a un avantage majeur : elle fonctionne quel que soit l’IDE utilisé, à compter que les paramètres du compilateur puissent être modifiés.
Commentaire