看懂Gradle腳本(3)- Groovy AST轉換

延續上一篇文章

上篇文章末尾有一段腳本,定義了一個Task:

task 'myTask' {  
    doLast {  
        println 'hello world!'  
    }  
}  
我們已經知道,這段腳本其實是調用Projecttask方法,並且傳入兩個參數:一個是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', {...})


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章