開始使用 Kotlin 自訂腳本 – 教學
Kotlin 腳本 (scripting) 是一種技術,它允許 Kotlin 程式碼作為腳本執行,而無需事先編譯或打包成可執行檔。
如需了解 Kotlin 腳本的概述和範例,請查閱 Rodrigo Oliveira 在 KotlinConf'19 上的演講 實作 Gradle Kotlin DSL。
在本教學中,您將建立一個 Kotlin 腳本專案,該專案能執行帶有 Maven 依賴項的任意 Kotlin 程式碼。您將能夠像這樣執行腳本:
@file:Repository("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.3")
import kotlinx.html.*
import kotlinx.html.stream.*
import kotlinx.html.attributes.*
val addressee = "World"
print(
createHTML().html {
body {
h1 { +"Hello, $addressee!" }
}
}
)
指定的 Maven 依賴項(此範例為 kotlinx-html-jvm
)將在執行期間從指定的 Maven 儲存庫或本地快取中解析,並用於腳本的其餘部分。
專案結構
一個最簡 Kotlin 自訂腳本專案包含兩個部分:
- 腳本定義 (Script definition) – 一組參數和配置,用於定義此腳本類型應如何被識別、處理、編譯和執行。
- 腳本主機 (Scripting host) – 處理腳本編譯和執行(即實際執行此類型腳本)的應用程式或元件。
考量到以上所述,最好將專案拆分為兩個模組。
開始之前
下載並安裝最新版本的 IntelliJ IDEA。
建立專案
在 IntelliJ IDEA 中,選擇 File | New | Project。
在左側面板中,選擇 New Project。
命名新專案並在必要時更改其位置。
TIP
勾選 Create Git repository 核取方塊,將新專案置於版本控制之下。您也可以隨時在之後進行此操作。
從 Language 清單中,選擇 Kotlin。
選擇 Gradle 建置系統。
從 JDK 清單中,選擇您要在專案中使用的 JDK。
- 如果 JDK 已安裝在您的電腦上,但未在 IDE 中定義,請選擇 Add JDK 並指定 JDK 主目錄的路徑。
- 如果您的電腦上沒有必要的 JDK,請選擇 Download JDK。
為 Gradle DSL 選擇 Kotlin 或 Gradle 語言。
點擊 Create。
加入腳本模組
現在您有一個空的 Kotlin/JVM Gradle 專案。加入所需的模組,即腳本定義和腳本主機:
在 IntelliJ IDEA 中,選擇 File | New | Module。
在左側面板中,選擇 New Module。此模組將作為腳本定義。
命名新模組並在必要時更改其位置。
從 Language 清單中,選擇 Java。
選擇 Gradle 建置系統,如果您想用 Kotlin 撰寫建置腳本,請為 Gradle DSL 選擇 Kotlin。
將根模組選為此模組的父級。
點擊 Create。
在模組的
build.gradle(.kts)
檔案中,移除 Kotlin Gradle 外掛程式的version
。它已存在於根專案的建置腳本中。重複前述步驟一次,為腳本主機建立一個模組。
專案應具有以下結構:
您可以在 kotlin-script-examples GitHub 儲存庫 中找到此類專案的範例以及更多 Kotlin 腳本範例。
建立腳本定義
首先,定義腳本類型:開發者可以在此類型的腳本中撰寫什麼,以及如何處理它。在本教學中,這包括在腳本中支援 @Repository
和 @DependsOn
註解。
在腳本定義模組中,將 Kotlin 腳本元件的依賴項加入到
build.gradle(.kts)
的dependencies
區塊中。這些依賴項提供了腳本定義所需的 API:
dependencies {
implementation("org.jetbrains.kotlin:kotlin-scripting-common")
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm")
implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies")
implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies-maven")
// coroutines dependency is required for this particular definition
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
}
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-scripting-common'
implementation 'org.jetbrains.kotlin:kotlin-scripting-jvm'
implementation 'org.jetbrains.kotlin:kotlin-scripting-dependencies'
implementation 'org.jetbrains.kotlin:kotlin-scripting-dependencies-maven'
// coroutines dependency is required for this particular definition
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2'
}
:::
在模組中建立
src/main/kotlin/
目錄,並加入一個 Kotlin 原始碼檔案,例如scriptDef.kt
。在
scriptDef.kt
中,建立一個類別。它將作為此類型腳本的父類別,因此請將其宣告為abstract
或open
。kotlin// abstract (or open) superclass for scripts of this type abstract class ScriptWithMavenDeps
此類別稍後也將作為腳本定義的參考。
若要使此類別成為腳本定義,請使用
@KotlinScript
註解來標記它。將兩個參數傳遞給該註解:fileExtension
– 一個以.kts
結尾的字串,用於定義此腳本類型檔案的副檔名。compilationConfiguration
– 一個繼承自ScriptCompilationConfiguration
的 Kotlin 類別,用於定義此腳本定義的編譯細節。您將在下一步中建立它。
kotlin// @KotlinScript annotation marks a script definition class @KotlinScript( // File extension for the script type fileExtension = "scriptwithdeps.kts", // Compilation configuration for the script type compilationConfiguration = ScriptWithMavenDepsConfiguration::class ) abstract class ScriptWithMavenDeps object ScriptWithMavenDepsConfiguration: ScriptCompilationConfiguration()
如下所示定義腳本編譯配置。
kotlinobject ScriptWithMavenDepsConfiguration : ScriptCompilationConfiguration( { // Implicit imports for all scripts of this type defaultImports(DependsOn::class, Repository::class) jvm { // Extract the whole classpath from context classloader and use it as dependencies dependenciesFromCurrentContext(wholeClasspath = true) } // Callbacks refineConfiguration { // Process specified annotations with the provided handler onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations) } } )
configureMavenDepsOnAnnotations
函數如下:kotlin// Handler that reconfigures the compilation on the fly fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> { val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() } ?: return context.compilationConfiguration.asSuccess() return runBlocking { resolver.resolveFromScriptSourceAnnotations(annotations) }.onSuccess { context.compilationConfiguration.with { dependencies.append(JvmDependency(it)) }.asSuccess() } } private val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), MavenDependenciesResolver())
您可以在此處找到完整的程式碼。
建立腳本主機
下一步是建立腳本主機 – 處理腳本執行的元件。
在腳本主機模組中,將依賴項加入到
build.gradle(.kts)
的dependencies
區塊中:- 提供腳本主機所需 API 的 Kotlin 腳本元件
- 您之前建立的腳本定義模組
dependencies {
implementation("org.jetbrains.kotlin:kotlin-scripting-common")
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm")
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm-host")
implementation(project(":script-definition")) // the script definition module
}
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-scripting-common'
implementation 'org.jetbrains.kotlin:kotlin-scripting-jvm'
implementation 'org.jetbrains.kotlin:kotlin-scripting-jvm-host'
implementation project(':script-definition') // the script definition module
}
:::
在模組中建立
src/main/kotlin/
目錄,並加入一個 Kotlin 原始碼檔案,例如host.kt
。為應用程式定義
main
函數。在其主體中,檢查它是否只有一個參數 – 腳本檔案的路徑 – 並執行該腳本。您將在下一步中在單獨的evalFile
函數中定義腳本執行。現在請將其宣告為空。main
可以像這樣:kotlinfun main(vararg args: String) { if (args.size != 1) { println("usage: <app> <script file>") } else { val scriptFile = File(args[0]) println("Executing script $scriptFile") evalFile(scriptFile) } }
定義腳本評估函數。這就是您將使用腳本定義的地方。透過呼叫
createJvmCompilationConfigurationFromTemplate
並將腳本定義類別作為類型參數來取得它。然後呼叫BasicJvmScriptingHost().eval
,將腳本程式碼及其編譯配置傳遞給它。eval
回傳ResultWithDiagnostics
的實例,因此請將其設定為您函數的回傳類型。kotlinfun evalFile(scriptFile: File): ResultWithDiagnostics<EvaluationResult> { val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<ScriptWithMavenDeps>() return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), compilationConfiguration, null) }
調整
main
函數以印出腳本執行資訊:kotlinfun main(vararg args: String) { if (args.size != 1) { println("usage: <app> <script file>") } else { val scriptFile = File(args[0]) println("Executing script $scriptFile") val res = evalFile(scriptFile) res.reports.forEach { if (it.severity > ScriptDiagnostic.Severity.DEBUG) { println(" : ${it.message}" + if (it.exception == null) "" else ": ${it.exception}") } } } }
您可以在此處找到完整的程式碼。
執行腳本
若要檢查您的腳本主機如何運作,請準備一個要執行的腳本和一個執行配置。
在專案根目錄中建立檔案
html.scriptwithdeps.kts
,內容如下:kotlin@file:Repository("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") @file:DependsOn("org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.3") import kotlinx.html.*; import kotlinx.html.stream.*; import kotlinx.html.attributes.* val addressee = "World" print( createHTML().html { body { h1 { +"Hello, $addressee!" } } } )
它使用了
kotlinx-html-jvm
函式庫中的函數,該函式庫在@DependsOn
註解引數中被引用。建立一個啟動腳本主機並執行此檔案的執行配置:
開啟
host.kt
並導覽至main
函數。它在左側有一個 Run 邊欄圖示。右鍵點擊邊欄圖示並選擇 Modify Run Configuration。
在 Create Run Configuration 對話框中,將腳本檔案名加入到 Program arguments 中,然後點擊 OK。
執行所建立的配置。
您將看到腳本如何執行,從指定的儲存庫中解析 kotlinx-html-jvm
的依賴項,並印出呼叫其函數的結果:
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
第一次執行時解析依賴項可能需要一些時間。後續執行將會快得多,因為它們使用來自本地 Maven 儲存庫的已下載依賴項。
接下來是什麼?
一旦您建立了簡單的 Kotlin 腳本專案,請了解更多關於此主題的資訊:
- 閱讀 Kotlin 腳本 KEEP
- 瀏覽更多 Kotlin 腳本範例
- 觀看 Rodrigo Oliveira 的演講 實作 Gradle Kotlin DSL