我正在尝试创建一个 gradle 项目,将项目 A 中定义的全局 AST 转换应用到项目 B 中定义的类。我一直按照本指南中的说明进行操作,但没有成功:
https://groovy-lang.org/metaprogramming.html#transforms-global
我的项目结构是:
|- project-root
|- settings.gradle
|- A
|-- build.gradle
|-- src
└── main
├── groovy
│ ├── gep
│ └── WithLoggingASTTransformation.groovy
└── resources
└── META-INF
└── services
└── org.codehaus.groovy.transform.ASTTransformation
|- B
├── build.gradle
└── src
└── main
└── groovy
└── package1
└── Application.groovy
根项目的settings.gradle:
rootProject.name = 'test-global-ast'
include 'A'
include 'B'
项目A的build.gradle
plugins {
id 'groovy'
}
dependencies {
implementation platform('org.apache.groovy:groovy-bom:4.0.5')
implementation 'org.apache.groovy:groovy'
}
项目 B 的 build.gradle
plugins {
id 'groovy'
id 'application'
}
mainClassName = 'project1.App'
configurations { astTransformation }
dependencies {
implementation platform('org.apache.groovy:groovy-bom:4.0.5')
implementation 'org.apache.groovy:groovy'
astTransformation project(':A')
}
tasks.withType(GroovyCompile).configureEach {
astTransformationClasspath.from(configurations.astTransformation)
}
B/gep/WithLoggingASTTransformation.groovy
package gep
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.expr.ArgumentListExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.expr.MethodCallExpression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.ast.stmt.Statement
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
@CompileStatic
@GroovyASTTransformation(phase= CompilePhase.SEMANTIC_ANALYSIS)
class WithLoggingASTTransformation implements ASTTransformation {
@Override
void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
def methods = sourceUnit.AST.methods
methods.each { method ->
def startMessage = createPrintlnAst("Starting $method.name")
def endMessage = createPrintlnAst("Ending $method.name")
def existingStatements = ((BlockStatement)method.code).statements
existingStatements.add(0, startMessage)
existingStatements.add(endMessage)
}
}
private static Statement createPrintlnAst(String message) {
new ExpressionStatement(
new MethodCallExpression(
new VariableExpression("this"),
new ConstantExpression("println"),
new ArgumentListExpression(
new ConstantExpression(message)
)
)
)
}
}
A/src/main/resources/META-INF/service/org.codehaus.groovy.transform.ASTTransformation
gep.WithLoggingASTTransformation
B/package1/Application.groovy
package package1
class Application {
def x() {
println "x"
}
static void main(args) {
println "hi"
def a = new Application()
a.x()
}
}
如果我运行
./gradlew :B:compileGroovy
,一切都会编译。然而,当我使用 ./gradlew :B:run
运行时,如果应用了 AST,我希望看到
开始主线
开始打印
嗨
打印结束
开始x
开始打印
x
打印结束
结局x
主要结局
然而,我却看到:
嗨
x
这让我相信全球 AST 并未得到应用。如何调整我的多项目构建,以便项目“A”中定义的全局 AST 成功应用于项目“B”中的类?
我能够通过
应用全局 AST./gradlew :A:jar
cd B
groovyc -cp A.jar:src/main/groovy/package1/ src/main/groovy/package1/Application.groovy
java -cp groovy-4.0.13.jar:A.jar:. package1.Application
我还修改了 package1.Application 以更好地匹配 groovy-lang.org 中的示例:
B/package1/Application.groovy
package package1
def x() {
println "x"
}
x()
通过这些更改,应用了全局 AST,但我仍然在努力让它在多项目构建中工作。
事情似乎比我谷歌搜索建议的要简单。需要改变的主要内容是:
astTransformation
配置的引用替换为 implementation
,并删除配置 GroovyCompile AST 类路径的块B/build.gradle
plugins {
id 'groovy'
id 'application'
}
mainClassName = 'project1.App'
dependencies {
implementation platform('org.apache.groovy:groovy-bom:4.0.5')
implementation 'org.apache.groovy:groovy'
implementation project(':A')
}
package package1
def x() {
println "x"
}
x()
需要 2. 的原因是因为 WithLoggingASTTransformation 定义的 AST 转换应用于引用顶级方法的
sourceUnit.AST.methods
,而不是类内的方法。
通过这些更改
./gradlew :transformed:run
产生转换后的输出:
开始x
x
结局x