如何在Grails插件中实现Groovy全局AST转换?

Vah*_*deh 3 grails groovy grails-plugin

我想在编译时修改一些Grails域类.我最初认为这是Groovy的全局ASTTransformation的工作,因为我不想注释我的域类(本地变换器需要).最好的方法是什么?

我也尝试通过在同一个包中创建我自己的类来模仿DefaultGrailsDomainClassInjector.java,实现相同的接口,但我可能只是不知道如何将它打包在正确的位置,因为我从未看到我的方法被调用.

另一方面,我能够手动创建一个包含已编译的AST转换类的JAR,以及纯Groovy全局转换所需的META-INF/services工件.我把JAR扔进了我的项目的"lib"目录,并成功调用了visit().显然这是一个草率的工作,因为我希望在Grails插件中获得我的AST转换的源代码,如果我不需要,则不需要单独的JAR工件,加上我无法通过拥有这种方法来实现我的Grails插件的"lib"中的JAR,但不得不将它放入Grails应用程序的"lib"中.

这篇文章也有所帮助:Grails 2.1.1 - 如何使用AstTransformer开发插件?

Ama*_*ega 5

关于全局转换的事情,编译开始时应该可以使用转换代码.将变压器放在罐子里是我先做的!但正如你所说,这是一项草率的工作.你想要做的是让你的ast转换类在其他人进入编译阶段之前编译.这是你做的!

准备变压器

创建一个名为precompiledin srcfolder的目录!并使用正确的打包结构添加Transformation类和转换器在此目录中使用的类(如注释).

然后创建一个名为call org.codehaus.groovy.transform.ASTTransformationin 的文件precompiled/META-INF/services,您将拥有以下结构.

precompiled
--amanu
----LoggingASTTransformation.groovy
--META-INF
----services
------org.codehaus.groovy.transform.ASTTransformation
Run Code Online (Sandbox Code Playgroud)

然后在org.codehaus.groovy.transform.ASTTransformation文件中写入变换器的完全限定名称,对于上面的示例,将使用完全限定名称amanu.LoggingASTTransformation

履行

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)
       }
   } 
} 
Run Code Online (Sandbox Code Playgroud)

汇编

执行此操作后,您可以通过两种方式进行操作!第一种方法是将它放在罐子里,就像你一样!另一种是使用groovy脚本在其他人之前编译它.要在grails中执行此操作,我们使用_Events.groovy脚本.

你可以从插件或主项目中做到这一点,没关系.如果它不存在,请创建一个名为的文件_Events.groovy并添加以下内容.

代码从reinhard-seiler.blogspot.com复制并进行修改

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"  
    }  
}  
Run Code Online (Sandbox Code Playgroud)

之前的脚本将在编译其他脚本之前编译变换器!这使变换器可用于转换域类.

别忘了

如果您使用除类路径中添加的类以外的任何类,则还必须预编译它们.上面的脚本将编译precompiled目录中的所有内容,您还可以添加不需要ast的类,但在该目录中需要它!

如果要在转换中使用域类,可能需要在evenCompileEnd块中进行预编译!但这会让事情变得更慢!

更新

@Douglas Mendes提到有一种简单的方法可以导致预编译.哪个更简洁.

eventCompileStart = { 
   target -> projectCompiler.srcDirectories.add(0, "./src/precompiled") 
}
Run Code Online (Sandbox Code Playgroud)