Gradle Source File Processing

I’m working on an Annotation Processor that would require editing source files in place, which is simply not supported by JSR-308. Instead, it seems like the only two options are Lombok or source manipulation.

I don’t really want to use Lombok, so I want to run a Gradle Task over the sources file. Each task can run independently inside a source file, and the source file will be rewritten. What is the best way of doing this? I would prefer being able to still do incremental compilation, and I only need to support Java right now.

Here’s how I’d do it

  1. Separate your files into two directories
    1.1 src/main/java for sources which won’t change
    1.2 src/transform-me/java for sources which will be transformed
  2. Have a task which uses QDox to transform files in src/transform-me/java and put the results in $buildDir/transformed/java. You might choose to do this as an incremental task to transform only files which have changed since last run
  3. Ensure you’ve specified the input/output dirs for the transform task so up to date checking can skip the task when nothing has changed
  4. Add $buildDir/transformed/java to the main sourceSet
  5. Wire the transform task into the DAG so that the compileJava task depends on it
  6. Sit back and admire the monster you just built

Do you need to use two directories - what would be the trade-off if you didn’t?

You could put everything in src/transform-me/java. Obviously there would be a bit more “work” for your transformer but I’m sure the extra time would be negligible

Note: Compared with an annotation processor you might find your transformer approach annoying for developers.

Consider using lombok’s @Getter on a bean then trying to use the getter() in a service in the same project as the bean. With lombok, you’ll have code completion in the IDE and you won’t see any errors.

With your approach, there will be no code completion and using the getter will be underlined in red in your IDE. For this reason you might need to separate the classes being transformed (ie your domain objects in the @Getter example) from the classes depending on the transform (ie your services). You might choose to put them in separate projects so that services depend on the transformed domain objects