打卡学习Gradle深度解析 - kts脚本加载流程

kts脚本加载流程

和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

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 tokenStarttokenEnd token的起始,结束位置

gradle kts的Lexer目的是为了将顶层的特定block解析出来,这些block比较特殊,是需要优先执行的。解析的结果就是包含哪些特殊的block以及它们的起始结束位置

Lexer靠不断的迭代tokenType来解析脚步内容,遇到WHITE_SPACE或者注释类型的token就跳过,它以state记录当前所处状态,state有3种类型

  • SearchingTopLevelBlock
  • SearchingBlockStart
  • SearchingBlockEnd

默认处于搜索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

ProgramParser是把lex分析后的Packaged<LexedScript>解析成Packaged<Program>

  1. 检查特定的顶层block每个只出一次(buildscript, plugins等)
  2. 检查特定的顶层block的顺序,pluginManagement一定要在第一个,plugins和buildscript优先级一样不做要求
  3. 将源码中的注释擦除,注意擦出不是删除,而是替换换行之外的字符为WHITESPACE,避免起始结束位置错乱
  4. 将特定的顶层block解析出来,若block内容不为空的话转为对应的Program子类,里面会记录其block的起始结束位置,将它们统一聚集到stage1
  5. 再将特定block的代码擦除,如果有剩余代码的话,则将其包在Program.Script内作为stage2
  6. 若stage1、2都存在,将其包在Program.Staged返回,若只存在一个则只返回单个,若都没有则返回Program.Empty
// 只有stage1的block,且block内没有内容,解析后为Empty
buildscript {
    }
plugins {
   
    // comments
}

// stage1的block是空的,解析后为Program.Script
buildscript {
    }
println 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值