How to implement the Groovy AST global transformation in the Grails plugin?

I would like to change some of my Grails domain classes at compile time. Initially, I thought it was work for the ASTM global strategy, but I do not want to comment on my domain classes (which require local transformers). What is the best way to do this?

I also tried to imitate DefaultGrailsDomainClassInjector.java by creating my own class in one package, implementing the same interfaces, but I probably just did not know how to pack it in the right place, I never saw my methods being called.

On the other hand, I was able to manually create a JAR that contained the compiled AST transform class, as well as the META-INF / services artifacts that Groovy global transforms needed. I threw this JAR into the "lib" project directory and successfully visited (). Obviously, this was a messy job, because I hope to get the source code for my AST transformation in the Grails plugin and I don’t need a separate JAR artifact if I don’t need it, plus I couldn’t get this approach to work if the JAR in my Grails plugin "lib", but he had to put it in the Grails "lib" application.

This post also helped: Grails 2.1.1 - How to develop a plugin with AstTransformer?

+3
source share
1 answer

The fact is that global transform code transformations must be available at compilation time. Having a transformer in the bank was what I did first! But, as you said, this is a sloppy job. What you want to do is compile the ast conversion class before others go to the compilation stage. Here is what you do!

Transformer Preparation

Create a directory that is precompiled to the src folder! and add the Transformation class and classes (such as annotations) that the transformer uses in this directory with the correct packaging structure.

Then create the org.codehaus.groovy.transform.ASTTransformation file in the called precompiled/META-INF/services and you will have the following structure.

 precompiled --amanu ----LoggingASTTransformation.groovy --META-INF ----services ------org.codehaus.groovy.transform.ASTTransformation 

Then write the full name of the transformer in the file org.codehaus.groovy.transform.ASTTransformation , for the example above, the full name will be amanu.LoggingASTTransformation

Implementation

 package amanu import org.codehaus.groovy.transform.GroovyASTTransformation import org.codehaus.groovy.transform.ASTTransformation import org.codehaus.groovy.control.CompilePhase import org.codehaus.groovy.ast.ASTNode import org.codehaus.groovy.control.SourceUnit @GroovyASTTransformation(phase=CompilePhase.CANONICALIZATION) class TeamDomainASTTransformation implements ASTTransformation{ public void visit(ASTNode[] nodes, SourceUnit sourceUnit) { println ("*********************** VISIT ************") source.getAST()?.getClasses()?.each { classNode -> //Class node is a class that is contained in the file being compiled classNode.addProperty("filed", ClassNode.ACC_PUBLIC, new ClassNode(Class.forName("java.lang.String")), null, null, null) } } } 

compilation

After that you can go in two ways! The first approach is to put it in a jar, just like you! and the other is to use a strict script to compile it before others. To do this in grails, we use the _Events.groovy script.

You can do this from a plugin or main project, it does not matter. If it does not exist, create a file named _Events.groovy and add the following content.

The code is copied from reinhard-seiler.blogspot.com with changes

 eventCompileStart = {target -> ... compileAST(pluginBasedir, classesDirPath) ... } def compileAST(def srcBaseDir, def destDir) { ant.sequential { echo "Precompiling AST Transformations ..." echo "src ${srcBaseDir} ${destDir}" path id: "grails.compile.classpath", compileClasspath def classpathId = "grails.compile.classpath" mkdir dir: destDir groovyc(destdir: destDir, srcDir: "$srcBaseDir/src/precompiled", classpathref: classpathId, stacktrace: "yes", encoding: "UTF-8") copy(toDir:"$destDir/META-INF"){ fileset(dir:"$srcBaseDir/src/precompiled/META-INF") } echo "done precompiling AST Transformations" } } 

The previous script compiles the transformer before compiling others! This allows you to convert the transformer to convert domain classes.

Do not forget

If you use a class other than those added to your class path, you will also have to copy them first. The above script will compile everything in the precompiled directory, and you can also add classes that ast do not need, but are needed for this in this directory!

If you want to use the domain classes in the conversion, you can do a preliminary evenCompileEnd block evenCompileEnd ! But it will make things slower!

+5
source

All Articles