Spec-Zone.ru › Kotlin 1.6

Начало работы с пользовательскими скриптами Kotlin – учебник

Скриптинг Kotlin в Экспериментальном режиме. Его могут удалить или изменить в любое время. Используйте только для оценочных целей. Мы ценим ваши отзывы о нём в YouTrack.

Скриптинг Kotlin — технология, которая позволяет выполнять код Kotlin в виде скриптов без предварительной компиляции или упаковки в исполняемые файлы.

Для ознакомления со скриптингом Kotlin и примерами, ознакомьтесь с презентацией Реализация Gradle Kotlin DSL от Rodrigo Oliveira с KotlinConf'19.

В этом учебнике вы создадите проект скриптов Kotlin, который выполняет произвольный код Kotlin с зависимостями Maven. Вы сможете выполнять скрипты следующим образом:

@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 содержит две части:

  • Определение скрипта — набор параметров и конфигураций, определяющих, как должен распознаваться, обрабатываться, компилироваться и выполняться этот тип скрипта.

  • Хост скриптов — приложение или компонент, который обрабатывает компиляцию и выполнение скриптов — фактически выполняет скрипты этого типа.

Учитывая всё это, лучше разделить проект на два модуля.

Перед началом

Загрузите и установите последнюю версию IntelliJ IDEA.

Настройка структуры проекта

Создайте проект Gradle Kotlin/JVM с двумя модулями:

  1. Перейдите в Файл | Новый | Проект.

  2. Создайте новый проект Gradle с Kotlin/JVM. Установите флажок Kotlin DSL build script, чтобы написать скрипт сборки на Kotlin.

    Create a root project for custom Kotlin scripting

    Теперь у вас есть пустой проект Gradle Kotlin/JVM, куда вы добавите необходимые модули: определение скрипта и хост скриптов.

  3. Перейдите в Файл | Новый | Модуль и добавьте новый модуль Gradle с Kotlin/JVM. Установите флажок Kotlin DSL build script, если хотите написать скрипт сборки на Kotlin. Этот модуль будет определением скрипта.

  4. Присвойте модулю имя и выберите корневой модуль в качестве родительского.

    Create script definition module
  5. В файле build.gradle(.kts) модуля удалите version плагина Kotlin Gradle. Он уже находится в скрипте сборки корневого проекта.

  6. Повторите шаги 3, 4 и 5 ещё раз, чтобы создать модуль для хоста скриптов.

Структура проекта должна быть такой:

Custom scripting project structure

Вы можете найти пример такого проекта и больше примеров скриптов Kotlin в репозитории kotlin-script-examples GitHub.

Создание определения скрипта

Сначала определите тип скрипта: что разработчики могут писать в скриптах этого типа и как это будет обрабатываться. В этом учебнике это включает поддержку аннотаций @Repository и @DependsOn в скриптах.

  1. В модуле определения скрипта добавьте зависимости от компонентов Kotlin scripting в блок dependencies файла build.gradle(.kts). Эти зависимости предоставят 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.6.0") 
    }
    
    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-jvm:1.6.0'
    
    }
    
  2. Создайте директорию src/main/kotlin/ в модуле и добавьте Kotlin-файл, например, scriptDef.kt.

  3. В scriptDef.kt, создайте класс. Он будет суперклассом для скриптов этого типа, поэтому объявите его как abstract или open.

    // abstract (or open) superclass for scripts of this type
    abstract class ScriptWithMavenDeps
    

    Этот класс также будет служить ссылкой на определение скрипта позднее.

  4. Чтобы сделать класс определением скрипта, отметьте его аннотацией @KotlinScript. Передайте два параметра в аннотацию:

    • fileExtension — строка, заканчивающаяся .kts, которая определяет расширение файла для скриптов этого типа.

    • compilationConfiguration — класс Kotlin, который расширяет ScriptCompilationConfiguration и определяет детали компиляции для этого определения скрипта. Вы создадите его на следующем шаге.

     // @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()
    

    В этом учебнике мы предоставляем только рабочий код без объяснения API скриптинга Kotlin. Вы можете найти тот же код с подробным объяснением на GitHub.

  5. Определите конфигурацию компиляции скрипта, как показано ниже.

     object 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 выглядит следующим образом:

     // 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())
    

    Полный код вы можете найти здесь.

Создание хоста скриптов

Следующий шаг — создание хоста скриптов — компонента, который обрабатывает выполнение скрипта.

  1. В модуле хоста скриптов добавьте зависимости в блок dependencies файла build.gradle(.kts):

    • Компоненты Kotlin scripting, предоставляющие необходимые API для хоста скриптов

    • Модуль определения скриптов, созданный ранее

    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
    }
    
  2. Создайте директорию src/main/kotlin/ в модуле и добавьте Kotlin-файл, например, host.kt.

  3. Определите функцию main для приложения. В её теле проверьте, что у неё один аргумент — путь к файлу скрипта — и выполните скрипт. Вы определите выполнение скрипта в отдельной функции evalFile в следующем шаге. Объявите её пустой на данный момент.

    main может выглядеть так:

     fun 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)
         }
     }
    
  4. Определите функцию оценки скрипта. Здесь вы будете использовать определение скрипта. Получите его, вызвав createJvmCompilationConfigurationFromTemplate с классом определения скрипта в качестве параметра типа. Затем вызовите BasicJvmScriptingHost().eval, передав ей код скрипта и его конфигурацию компиляции. eval возвращает экземпляр ResultWithDiagnostics, поэтому установите его в качестве возвращаемого типа вашей функции.

     fun evalFile(scriptFile: File): ResultWithDiagnostics<EvaluationResult> {
         val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<ScriptWithMavenDeps>()
         return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), compilationConfiguration, null)
     }
    
  5. Отредактируйте функцию main так, чтобы она выводила информацию о выполнении скрипта:

     fun 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}")
                 }
             }
         }
     }
    

Полный код вы можете найти здесь

Запуск скриптов

Чтобы проверить работу вашего хоста скриптов, подготовьте скрипт для выполнения и конфигурацию запуска.

  1. Создайте файл html.scriptwithdeps.kts с указанным ниже содержимым в корневой директории проекта:

    @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.

  2. Создайте конфигурацию запуска, которая запускает хост скриптов и выполняет этот файл:

    1. Откройте host.kt и перейдите к функции main. На левой стороне находится значок Запуск.

    2. Щелкните правой кнопкой мыши значок и выберите Изменить конфигурацию запуска.

    3. В диалоговом окне Создать конфигурацию запуска добавьте имя файла скрипта в поле Аргументы программы и нажмите OK.

      Scripting host run configuration
  3. Запустите созданную конфигурацию.

Вы увидите, как скрипт выполняется, разрешая зависимость от kotlinx-html-jvm в указанном репозитории и печатая результаты вызова его функций:

<html>
  <body>
    <h1>Hello, World!</h1>
  </body>
</html>

Разрешение зависимостей может занять некоторое время при первом запуске. Последующие запуски будут выполняться намного быстрее, потому что они используют загруженные зависимости из локального репозитория Maven.

Что дальше?

После создания простого проекта Kotlin Scripting, найдите более подробную информацию по этой теме:

  • Kotlin scripting KEEP

  • Kotlin scripting examples

  • Посмотрите доклад Реализация Gradle Kotlin DSL Родриго Оливейры

Последнее изменение: 07 апреля 2022
Kotlin/Native FAQ Kotlin releases

© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/custom-script-deps-tutorial.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API