dimanche 28 octobre 2012

Debugging an annotation processor in every IDE

During the latest Hackergarten, I worked with Pierre-Yves Ricau on the project AndroidAnnotations. This project consists in an annotation processor for our Android applications. If one can easily find documentation on how to debug an annotation processor using Eclipse, it is not the case for other IDE such as IntelliJ. In this article, we will see how we can achieve such a thing regardless of the IDE.

Introduction

First things first, a definition. An annotation processor is a library that can be added to the javac compiler and that can enhance our source code if the right annotations are used. It allors us to write more concise and mode expressive code.

Note that "enhancing source code" needs to be clarified, as JSR-269 does not allow modifications of existing .java files. However, it is possible to create new .java files, just like AndroidAnnotations does, or to modify generated bytecode, like project lombok does (with a little bit of hacking, though).

A great variety of IDE (Eclipse, NetBeans, IntelliJ) supports annotation processors, whether using or developing it. But there is a lack of documentation on debugging such tools. For instance, very few pages references debugging an annotation processor in Eclipse, and all mention the creation of a plugin.

During the HackerGarten, we chose a different method : since annotation processors are a java library that is added to javac, we "only" have to launch javac in debug mode, and connect to it. In this article, we will focus on IntelliJ but the solution will work for any IDE.

Adding the annotation processor to the compiler

Once the project is imported in Intellij, we can add the annotation processor to the compilation process. Do to so, we have to enter the fully-qualified name of AndroidAnnotations processor, the module on which we want to process the annotations and an output folder. There are lots of test classes in the module functionnal-test-1-5, let's use this one and specify the output directory target/processed.

Checking that annotations are processed

When we process the class BeanInjectedActivity.java, we can see that a new java source file named BeanInjectedActivity_.java is created in the folder target/processed. This new file contains the code of the original class and many more statements that have been added by the annotation processor.

Setting javac in debug mode

We know that any JVM instance can be started in debug mode using some JVM Options, such as -Xdebug. Unfortunately, in our case, we have to use the javac command which is not itself a JVM instance, so it is not possible to pass it such options.

However, javac *creates* a JVM instance in which the compilation process takes place. We can send JVM options to this instance using the command line parameter -J, as stated in the manpage :.

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


We can therefore use the options -J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 to run the compilation JVM in debug mode. Let's add those options in IntelliJ and set a breakpoint in the init method of AndroidAnnotationProcessor.java.

Adding a debugging configuration and debugging

We can now add a remote debugging configuration on port 5005 in IntelliJ and process the class BeanInjectedActivity.java. We see that the compilation process hangs on "make", which is as expected since the compiler is now waiting a connection from a debugger. When we start the remote debugging configuration in IntelliJ, we see that :
  • the compiler stops at the breakpoint we added in AndroidAnnotationProcessor.init
  • the stacktrace mentions the javac compiler itself.

Conclusion

We saw that the javac compiler itself could be run in debug mode and therefore, any annotation processor can be debugged using any IDE that supports setting additional javac arguments.

Do you have other techniques do debug annotation processors ? A particular opinion about them ? Let's talk about it in the comments !

2 commentaires:

  1. Another technique I have been using for debugging Annotation Processors, and thought I would share, is to use the following options through the Maven build:

    export MAVEN_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"

    .. which is nearly equivalent to the IDE techniques you mentioned. Im sure there is a simialr environment variable for Ant as well.

    Thanks for the great article, it was certainly a help with debugging Annotation Processors.

    RépondreSupprimer
    Réponses
    1. I'm glad it helped =)

      Thanks too for sharing this tip !

      Supprimer