赞
踩
和groovy脚本一样,kts脚本也分为2个阶段
stage 1
执行buildscript和plugins部分,执行结果会对stage2 program的classpath有影响
stage 2
eval脚本剩余的部分
2个阶段都会生成Program子类,目录在$HOME/.gradle/caches/gradle-version/kotlin-dsl/scripts/hashcode/classes
下
和groovy语言本身提供了丰富的动态能力不同,gradle对kts的解析有大部分工作是自己完成的,涉及到编程原理部分,比如从lex阶段开始对kts内的token做解析等
整体流程如下图所示
如需全套 Gradle深度解析 学习资料 请点击免费领取
Lexer
接收脚本文件内容及脚本对应的TopLevelBlockIds(buildscript/initscript/pluginManagement/plugins)这些,返回经过lex处理过的Packaged<LexedScript>
,里面包含注释及提取的第一轮需要执行的顶层block
fun lex(script: String, vararg topLevelBlockIds: TopLevelBlockId): Packaged<LexedScript>
class Packaged<T>(
val packageName: String?,
val document: T
)
class LexedScript(
val comments: List<IntRange>,
val topLevelBlocks: List<TopLevelBlock>
)
其是通过org.jetbrains.kotlin.lexer.KotlinLexer
的能力进行解析的,
start()
advance()
tokenType
tokenText
tokenStart
tokenEnd
start
接收文本内容,开始进行词法分析,编译的第一步就是进行词法分析,程序的内容会被全部解析为token advance
跳至下一个token部分 tokenType
token的类型, 例如空格为KtTokens.WHITE_SPACE
注释为KtTokens.COMMENTS
(这是个集合,包含了EOL_COMMENT-行尾注释,BLOCK_COMMENT-块注释等) LBRACE
-> {
左花括号 RBRACE
-> }
右花括号 IDENTIFIER
,这个涵盖的范围特别广,以下面的statement为例 val a = buildscript()
val等关键字,变量如a、b,=等号,方法名buildscript等都属于此
tokenText
token的内容,分析IDENTIFIER
特别需要,还是上面的statement为例,val的tokenType是IDENTIFIER,它的tokenText为val tokenStart
,tokenEnd
token的起始,结束位置
gradle kts的Lexer
目的是为了将顶层的特定block解析出来,这些block比较特殊,是需要优先执行的。解析的结果就是包含哪些特殊的block以及它们的起始结束位置
Lexer
靠不断的迭代tokenType
来解析脚步内容,遇到WHITE_SPACE或者注释类型的token就跳过,它以state记录当前所处状态,state有3种类型
默认处于搜索SearchingTopLevelBlock
状态,在这个状态下如果碰到了PACKAGE,会把包名解析出来并保存,如果碰到IDENTIFIER,则判断是否有符合的toplevelblock,且当前depth为0,当前depth是对花括号的记录,碰到花括号depth会+1,出花括号-1,因为这里是要收集顶层的block,所以进入花括号属于内层的需要忽略
当SearchingTopLevelBlock
状态下检测到符合条件的block时会进入SearchingBlockStart
,很容易看出它和SearchingBlockEnd
是一对,目的就是为了将block的闭包起始结束位置记录下来,start就是在找 {
的位置,找到后就进入, start状态下只要不是IDENTIFIER或者LBRACE的情况,都会重置回SearchingTopLevelBlock
默认状态重新开始查找
SearchingBlockEnd
状态,因为内部还可能有闭包,所以end里仍需要进入{对depth+1,退出}时-1,当depth为0时将start和end点记录下来
还有个细节,是在start状态遇到IDENTIFIER时,还会检测block是否是符合条件的,如果不符合是会重置状态的,因为可能出现下面这种情况,buildscript
可以被引用,当解析到第一个buildscript
时会进入start状态,如果start不对IDENTIFIER检查,到第二个buildscript
时就会重置状态,而错过buildscript的解析了
val a = buildscript
buildscript {
}
ProgramParser
是把lex分析后的Packaged<LexedScript>
解析成Packaged<Program>
Program
子类,里面会记录其block的起始结束位置,将它们统一聚集到stage1
中Program.Script
内作为stage2
Program.Staged
返回,若只存在一个则只返回单个,若都没有则返回Program.Empty
// 只有stage1的block,且block内没有内容,解析后为Empty
buildscript {
}
plugins {
// comments
}
// stage1的block是空的,解析后为Program.Script
buildscript {
}
println
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。