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



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

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

另一方面,我能够手动创建一个 JAR,其中包含一个编译的 AST 转换类,以及普通 Groovy 全局转换所需的 META-INF/服务工件。我将那个JAR扔到我项目的"lib"目录中,然后访问()被成功调用。显然,这是一项草率的工作,因为我希望将我的 AST 转换的源代码放在 Grails 插件中,并且如果不需要的话,不需要单独的 JAR 工件,而且我无法通过将 JAR 放在我的 Grails 插件的"库"中来让这种方法工作,但不得不将其放入 Grails 应用程序的"库"中。

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

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

准备变压器

src文件夹中创建一个名为 precompiled 的目录! 并添加 Transformation 类和转换器在此目录中使用的具有正确打包结构的类(如注释)。

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

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

然后在 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)
       }
   } 
} 

汇编

实现这一点后,您可以通过两种方式离开!第一种方法是把它放在罐子里,就像你一样!另一种是先于别人使用时髦的脚本编译它。为了在圣杯中做到这一点,我们使用_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"  
    }  
}  

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

别忘了

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

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

更新

@Douglas Mendes提到过,有一种简单的方法可以进行预编译。哪个更简洁。

eventCompileStart = { 
   target -> projectCompiler.srcDirectories.add(0, "./src/precompiled") 
}

最新更新