iT邦幫忙

2022 iThome 鐵人賽

DAY 25
0
Mobile Development

Kotlin 全面啟動 系列 第 25

[Kotlin 全面啟動] KSP

  • 分享至 

  • xImage
  •  

KSP 是 Kotlin Symbol Processing 的簡稱,本質上它可以做很多很多種事情,但我們今天主要會延續昨天 KotlinPoet 的部分聚焦於 code generation。

如果是寫一陣子 Java 的資深開發者可能有聽過 Annotation Processing,而 KSP 如果拿來做 code generation 的話,其實跟 Annotation Processing 的結構非常的像,就讓我們一步一步解析囉!

如果對 Java 的 Annotation Processing 有興趣可以參考 Android 十全大補的文章
https://ithelp.ithome.com.tw/articles/10222408

因為內容有點多,我們會拆成上下二集介紹。

Annotation

KSP 是在 compile 階段可以透過讀取程式碼的資訊來分析、或是產生程式碼的工具,而 annotation 就是其中一種可以方便解析出來的資訊。你或許很少有機會自己寫一個 annotation,以下是一個 custom annotation 的範例程式碼:

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class AutoElement

我們可以發現它跟 data classenum class 等都一樣遵守 Kotlin 的 pattern。而一般 annotation 的宣告通常都還會伴隨著二種 annotation - TargetRetention

Target

代表著這個 annotation 可以作用在哪些物件上,像 CLASS 就是只能標在 class 上,共有以下幾種值:

/** Class, interface or object, annotation class is also included */
CLASS,
/** Annotation class only */
ANNOTATION_CLASS,
/** Generic type parameter */
TYPE_PARAMETER,
/** Property */
PROPERTY,
/** Field, including property's backing field */
FIELD,
/** Local variable */
LOCAL_VARIABLE,
/** Value parameter of a function or a constructor */
VALUE_PARAMETER,
/** Constructor only (primary or secondary) */
CONSTRUCTOR,
/** Function (constructors are not included) */
FUNCTION,
/** Property getter only */
PROPERTY_GETTER,
/** Property setter only */
PROPERTY_SETTER,
/** Type usage */
TYPE,
/** Any expression */
EXPRESSION,
/** File */
FILE,
/** Type alias */
@SinceKotlin("1.1")
TYPEALIAS

Retention

RetentionTarget 稍微難理解,由於 annotation 的作用是給程式碼物件提供額外的標註,所以它的生命週期通常會有不同的需求,像是 KSP 或是 IDE 專用的可能就不需要 compile 之後還留著,只是增加檔案大小而已沒有額外的幫助,但如果要在 runtime 時透過 reflection 等讀取 annotation 的資訊就必須存在 binary 裡。

所以設定正確的 Retention 就很重要,依照不同需求有以下幾種值:

/** Annotation isn't stored in binary output */
SOURCE,
/** Annotation is stored in binary output, but invisible for reflection */
BINARY,
/** Annotation is stored in binary output and visible for reflection (default retention) */
RUNTIME

Project Structure

依照每人使用習慣不同,可能多少會有些出入,但通常會有以下三個 module 分別負責不同的角色:

  • Annotation module:定義共用的 annotation 讓其他的 module 使用。
  • Processor module:作為 compile 的其中一環,解析程式碼並依此建立新的程式碼共同 compile。
  • Usage module:使用定義好的 annotation 並把 Processor module 定義為 compile 的一部分,通常也會依賴自動產生出來的程式碼才能順利的 compile。

有些人也習慣把 Annotation module 跟 Processor module 合起來,也沒有太多明顯的差異,大家可以自己決定。

Install

由於 KSP 是個輕量的 compiler plugin,所以不少額外的設定要做,以下會分各個 module 作介紹。

首先在 root project 新增以下設定:

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.7.10' apply false 
    id 'com.google.devtools.ksp' version '1.7.10-1.0.6' apply false
}  
buildscript {
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10'     
    } 
}

KSP 的版本很有趣,由於它依賴於 Kotlin 的版本,所以前半段指的是 Kotlin 的版本,後面才是 KSP 自己的 release 版本,更多資訊可以參考:https://github.com/google/ksp/releases

Annotation module 是單純提供 annotation 的 module,所以就照一般 pure Kotlin library 的設定就行了。

Processor module 則要新增以下設定:

plugins {
    id 'org.jetbrains.kotlin.jvm'
}

dependencies {
    implementation project(":annotation")
    implementation 'com.google.devtools.ksp:symbol-processing-api:1.7.10-1.0.6'
    implementation 'com.squareup:kotlinpoet:1.11.0'
    implementation 'com.squareup:kotlinpoet-ksp:1.12.0'
}

這邊我們可以發現 Processor module 依賴了 Annotation module,同時也需要 KSP 的 api,最後的二個則是 KotlinPoet 以及 KotlinPoet 在 KSP 上的一些轉換 extension library。

最後就是我們的 Usage module 怎麼定義:

plugins {
    id 'com.google.devtools.ksp'
}

sourceSets {
    main {
        java {
            srcDir "${buildDir.absolutePath}/generated/ksp/"
        }
    }
}
dependencies {
    implementation project(":annotation")
    ksp project(":processor")
}

一開始也是要 apply 這個 plugin,讓他在 compile 的時候起作用,然後加上 Annotation module 跟 Processor module 的 dependency。

sourceSets 的額外設定是因為 KSP 目前還很新,自動建立的檔案 IDE 可能還不認得,必須手動加上去,但這個問題隨著時間我相信會自然改善。

記得 Processor module 的宣告不是使用 implementation,而是 ksp 喔!

介紹完怎麼設定就差不多了,明天我們繼續講 Processor module 的更多具體細節,敬請不要錯過!


上一篇
[Kotlin 全面啟動] KotlinPoet
下一篇
[Kotlin 全面啟動] KSP II
系列文
Kotlin 全面啟動 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言