首页 > 代码库 > 看懂Gradle脚本(3)- Groovy AST转换

看懂Gradle脚本(3)- Groovy AST转换

延续上一篇文章

上篇文章末尾有一段脚本,定义了一个Task:

task 'myTask' {  
    doLast {  
        println 'hello world!'  
    }  
}  
我们已经知道,这段脚本其实是调用Project的task方法,并且传入两个参数:一个是Task的名字,另外一个是闭包,用来配置Task(在这段脚本中,给Task添加了一个Action)。

Task名的引号去哪儿了?

但是Gradle官方推荐的写法,是像下面这样定义Task:

task myTask { // <--
    doLast {  
        println 'hello world!'  
    }  
}  
那么这个语法怎么解释呢?

用GroovyConsole观察AST

所谓AST,就是抽象语法树,简单的说,就是编译器分析完代码之后,生成的一个树形数据结构。我们用Groovy自带的GroovyConsole就可以观察AST。打开GroovyConsole,把脚本复制到编辑器中,然后按ctrl+t就可以看到AST,如下图所示:

可以看出来,上面的脚本会被编译器解释成下面这样:

task(myTask({ 
    doLast({ 
        println('hello world!')
    })
}))
也就是说,先调用myTask方法,然后再把返回结果当成参数,调用task方法。那么到底是不是这样呢?

AST转换

没有定义过myTask方法?这个好说,因为Groovy提供了强大的元编程机制,比如methodMissing方法,可以做到这一点。那么到底是不是这样呢?一番搜索之后,我认为我找到了正确答案:Gradle是利用AST转换来实现这种特殊语法的。为了给出确凿的证据,我找出了相关的Gradle源代码。最主要的两个类是org.gradle.groovy.scripts.internal.TaskDefinitionScriptTransformerorg.gradle.groovy.scripts.internal.AstUtils。下面列出TaskDefinitionScriptTransformertransformVariableExpression方法以供参考:

        private void transformVariableExpression(MethodCallExpression call, int index) {
            ArgumentListExpression args = (ArgumentListExpression) call.getArguments();
            VariableExpression arg = (VariableExpression) args.getExpression(index);
            if (!isDynamicVar(arg)) {
                return;
            }

            // Matches: task args?, <identifier>, args? or task(args?, <identifier>, args?)
            // Map to: task(args?, '<identifier>', args?)
            String taskName = arg.getText();
            call.setMethod(new ConstantExpression("task"));
            args.getExpressions().set(index, new ConstantExpression(taskName));
        }

结论

Gradle是通过AST转换来实现task定义特殊语法的,下面这三行代码完全等价:

task myTask {...}
task 'myTask' {...}
task('myTask', {...})