Spec-Zone.ru › Kotlin 1.8

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

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

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

Для ознакомления со скриптами Kotlin с примерами, посмотрите выступление Implementing the 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.

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

  1. В IntelliJ IDEA выберите Файл | Новый | Проект.

  2. В панели слева выберите Создать проект.

  3. Назовите новый проект и измените его расположение при необходимости.

    Установите флажок Создать репозиторий Git, чтобы поместить новый проект под контроль версий. Вы сможете сделать это позже в любое время.

  4. В списке Язык выберите Kotlin.

  5. Выберите систему сборки Gradle.

  6. В списке JDK выберите JDK, который вы хотите использовать в проекте.

    • Если JDK установлен на вашем компьютере, но не определен в IDE, выберите Добавить JDK и укажите путь к домашнему каталогу JDK.

    • Если необходимый JDK не установлен на вашем компьютере, выберите Загрузить JDK.

  7. Выберите язык Kotlin или Gradle для Gradle DSL.

  8. Нажмите Создать.

Create a root project for custom Kotlin scripting

Добавление модулей скриптов

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

  1. В IntelliJ IDEA выберите Файл | Новый | Модуль.

  2. В панели слева выберите Новый модуль. Этот модуль будет определением скрипта.

  3. Назовите новый модуль и измените его расположение при необходимости.

  4. В списке Язык выберите Java.

  5. Выберите систему сборки Gradle и Kotlin для Gradle DSL, если хотите написать скрипт сборки на Kotlin.

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

  7. Нажмите Создать.

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

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

Проект должен иметь следующую структуру:

Custom scripting project structure

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

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

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

  1. В модуле определения скрипта добавьте зависимости от компонентов скриптов Kotlin в блок 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.4") 
    }
    
    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.4'
    
    }
    
  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 для сценариев, предоставляющие необходимые 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. В диалоговом окне Создать конфигурацию запуска добавьте имя файла сценария в Аргументы программы и нажмите ОК.

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

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

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

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

Что дальше?

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

  • Прочитайте Kotlin scripting KEEP

  • Просмотрите другие примеры Kotlin для сценариев

  • Посмотрите презентацию Реализация Kotlin DSL для Gradle Rodrigo Oliveira

Последнее изменение: 10 января 2023 г.
Kotlin/Native FAQ Выпуски Kotlin

© 2010–2023 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