有没有一种方法在Gradle KTS中示例化KTS脚本引擎?

gr8qqesn  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(130)

我想在我的项目建设过程中使用3d党库。库方法需要ScriptEngine。当我尝试示例化它时,我得到了一个错误:

java.lang.IllegalArgumentException: Unable to construct script definition: Unable to load base class kotlin.script.experimental.api.KotlinType@42842bb8
    at kotlin.script.experimental.host.ConfigurationFromTemplateKt.getTemplateClass(configurationFromTemplate.kt:189)
    at kotlin.script.experimental.host.ConfigurationFromTemplateKt.createScriptDefinitionFromTemplate(configurationFromTemplate.kt:36)
    at kotlin.script.experimental.jsr223.KotlinJsr223DefaultScriptEngineFactory.<init>(KotlinJsr223DefaultScriptEngineFactory.kt:74)
    at ce.domain.usecase.load.LoadMetaFilesForTargetUseCase.invoke(LoadMetaFilesUseCase.kt:17)
    at ce.domain.usecase.entry.BuildProjectUseCase.invoke(BuildProjectUseCase.kt:24)
    at ce.domain.usecase.entry.BuildProjectUseCase.invoke$default(BuildProjectUseCase.kt:18)
    at Build_gradle$$$result$1.invoke(build.gradle.kts:68)
    at Build_gradle$$$result$1.invoke(build.gradle.kts:60)
    at org.gradle.kotlin.dsl.ProjectExtensionsKt$sam$org_gradle_api_Action$0.execute(ProjectExtensions.kt)
    at org.gradle.api.internal.tasks.DefaultTaskContainer.create(DefaultTaskContainer.java:368)
    at org.gradle.kotlin.dsl.ProjectExtensionsKt.task(ProjectExtensions.kt:147)
    at Build_gradle.<init>(build.gradle.kts:60)
    ...

我复制了简单的grdale项目的问题:示例项目等级:

import kotlin.script.experimental.jsr223.KotlinJsr223DefaultScriptEngineFactory

plugins {
    kotlin("jvm") version "1.8.21"
    application
}

repositories {
    mavenCentral()
}

buildscript {
    repositories {
        mavenCentral()
        google()
    }
    dependencies {
        classpath("org.jetbrains.kotlin:kotlin-scripting-jsr223:1.8.10")
        classpath("org.jetbrains.kotlin:kotlin-scripting-common:1.8.10")
        classpath("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.10")
        classpath("org.jetbrains.kotlin:kotlin-reflect")
    }
}

dependencies {
    testImplementation(kotlin("test"))
}

abstract class TestProjectTask : DefaultTask() {
    @get: InputFile
    abstract val projectFile: RegularFileProperty

    @TaskAction
    fun execute() {
        try {
            val eng2 = KotlinJsr223DefaultScriptEngineFactory().getScriptEngine()
            println("Project file = ${projectFile.get()} $eng2")
            val worker = Worker()
            worker.doWork(eng2, projectFile.asFile.get().absolutePath)
        } catch (err: Throwable) {
            err.printStackTrace()
        }
    }
}

task("hello2", TestProjectTask::class) {
    projectFile.set(File("./project.kts"))
}

KotlinJsr223DefaultScriptEngineFactory().getScriptEngine()总是抛出相同的异常。

carvr3hs

carvr3hs1#

感谢this issuelinked comment thread中贡献者所做的工作,答案相对简单,即使发现如何做到这一点并不容易!我已经清理了提供的变通方案。
总结:

  • 创建Gradle任务以运行Kotlin脚本
  • 获取所需的编译和运行时依赖项
  • 运行K2JVMCompiler生成源代码

我建议使用buildSrc convention plugin来设置必要的逻辑。它有助于保持构建脚本更清晰、更具有声明性,并且setup-logic包含在buildSrc中。

Kotlin依赖

首先,确保K2JVMCompiler类可用。
如果你在一个build.gradle.kts中工作,那么这可以通过应用Kotlin插件来实现:

// build.gradle.kts

plugins {
  kotlin("jvm") version "1.8.22"
}

或者,如果编写插件/预编译脚本插件,请在项目的build.gradle.kts中添加对KotlinGradle插件的依赖。
访问K2JVMCompiler类需要对kotlin-compiler-embeddable的编译时依赖。

// buildSrc/build.gradle.kts
plugins {
  `kotlin-dsl`
}

repositories {
  mavenCentral()
}

dependencies {
  implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22")

  // required for K2JVMCompiler::class - will be provided at runtime by Gradle
  compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.22")
}

请️注意,在buildSrc/build.gradle.kts中添加对KGP的依赖意味着必须删除 * 所有 * 其他KGP版本。

// build.gradle.kts
plugins {
  kotlin("jvm") // no version needed - it's set in buildSrc/build.gradle.kts
}

运行任务

接下来,让我们创建用于运行.main.kts文件的任务。
为了运行一个Kotlin脚本,我们需要一些东西:

  • Kotlin脚本的位置(很明显!)
  • 用于 * 编译 * Kotlin脚本的类路径
  • 用于 * 运行 * Kotlin脚本的类路径

为了遵循Gradle的最佳实践,跟踪任务的输入和输出文件也很重要(但不是严格要求的)。
(As前面提到过,最好在buildSrc目录中执行此任务,但您也可以将此任务粘贴到常规的build.gradle.kts中。

// buildSrc/src/main/kotlin/RunKotlinScript.kt

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import org.gradle.process.ExecOperations
import javax.inject.Inject

/** Task for running Kotlin Scripts */
abstract class RunKotlinScript @Inject constructor(
    private val executor: ExecOperations
) : DefaultTask() {
    /** Location of the `.kts` file (required) */
    @get:InputFile
    abstract val script: RegularFileProperty

    /** (optional) Files that the script uses as an input */
    @get:InputFiles
    @get:Optional
    abstract val scriptInputs: ConfigurableFileCollection

    /** (optional) Files that the script produces as output */
    @get:OutputFiles
    abstract val scriptOutputs: ConfigurableFileCollection

    @get:Classpath
    abstract val compileClasspath: ConfigurableFileCollection

    @get:Classpath
    abstract val runtimeClasspath: ConfigurableFileCollection

    init {
        group = "run kts"
        description = "Runs a Kotlin script"
    }

    @TaskAction
    fun run() {
        val scriptPath = script.get().asFile.invariantSeparatorsPath
        val runtimeClasspath = runtimeClasspath.asPath

        executor.javaexec {
            classpath(compileClasspath)
            mainClass.set(org.jetbrains.kotlin.cli.jvm.K2JVMCompiler::class.qualifiedName)
            args(
                "-no-stdlib",
                "-no-reflect",
                "-classpath", runtimeClasspath,
                "-script", scriptPath,
            )
        }
    }
}

(As前面提到过,最好在buildSrc目录中执行此任务,但您也可以将此任务粘贴到常规的build.gradle.kts中。

编译和运行时依赖

让我们使用一个预编译的约定插件来定义如何获取编译和运行Kotlin脚本所需的依赖项。

// buildSrc/src/main/kotlin/kotlin-script-runner.gradle.kts

plugins {
  kotlin("jvm") // no version needed - it's set in buildSrc/build.gradle.kts
}

// Fetch dependencies necessary to compile and run kts scripts inside Gradle,
// so installing the kotlin CLI is not required (e.g. on CI/CD, or Heroku)
val ktsCompileClasspath by configurations.creating<Configuration> {
    description = "Dependencies used to compile Kotlin scripts"
    isCanBeConsumed = false
}

val ktsRuntimeClasspath by configurations.creating<Configuration> {
    description = "Dependencies used to run Kotlin scripts"
    isCanBeConsumed = false
    // only depend on direct dependencies - the scripting context will pull in other dependencies
    isTransitive = false
}

dependencies {
    // add compile-time dependencies on the regular and scripting Kotlin compilers
    ktsCompileClasspath(kotlin("compiler"))
    ktsCompileClasspath(kotlin("scripting-compiler"))
    // only depend on Kotlin main-kts for runtime
    ktsRuntimeClasspath(kotlin("main-kts"))
}

我们现在有两个包含必要依赖项的配置。在相同的约定插件中,让我们将这些依赖项添加到 all RunKotlinScript任务中。

// buildSrc/src/main/kotlin/kotlin-script-runner.gradle.kts

// ...

tasks.withType<RunKotlinScript>().configureEach {
    runtimeClasspath.from(ktsRuntimeClasspath)
    compileClasspath.from(ktsCompileClasspath)
}

创建运行任务

这个约定插件可以应用于项目中的任何脚本:

// my-subproject/build.gradle.kts

plugins {
  `kotlin-script-runner`
}

然后您可以创建一个任务,该任务将被正确配置

// my-subproject/build.gradle.kts

tasks.register<RunKotlinScript>("runDoSomethingKts") {
    script.set(file("scripts/do-something.main.kts"))
    scriptOutputs.from("scripts/out.txt")    
    scriptInputs.from("script/input.txt")
}

并且可以使用Gradle运行

./gradlew runDoSomethingKts

相关问题