Search code examples
javakotlinkotlin-script

Evaluate kotlin script file with dependencies


Question: how to execute set of kotlin files in runtime and return result of the one kts file from them?

I write system, which is able to execute kts file with instructions. For current moment it supports the following execution schema:

main.kts - this file will be executed. It should return List<Step>

However user is able to put any other file at the same folder. For example, folder can have the following files:

  • main.kts
  • Constants.kt // it has some constants
  • Helpers.kt // some additional methods

ScriptEngine has methods to evaluate code, however it has only one input file.

Question: how can I ask ScriptEngine to compile classes into classpath, however execute only one of them?

This solution isn't correct, because file order is important (e.g. compilation fails if the first file depends on the last one):

// there is security issue here
val classLoader = Thread.currentThread().contextClassLoader

val engineManager = ScriptEngineManager(classLoader)

setIdeaIoUseFallback()

val ktsEngine: ScriptEngine = engineManager.getEngineByExtension("kts")

/**
 * There is issue here: if file1 requires file2 compilation then execution below fails.
 *
 * Right way: find the solution to compile whole folder and evaluate the single file.
 */
filesFromFolderExceptMain.forEach {
    ktsEngine.eval(it)
}

return ktsEngine.eval(mainScriptFile) as List<Step>

Another solution (which can lead to unpredictable compilation fluctuations):

val context = filesFromFolderExceptMain.joinToString(System.lineSeparator()

ktsEngine.eval(context)

return ktsEngine.eval(mainScriptFile) as List<Step>

So, question: how to execute set of kotlin files in runtime and return result of the one kts file from them?


Solution

  • What you are supposed to do is add @file:Import("Constants.kt", "Helpers.kt") to the main script (see https://github.com/Kotlin/KEEP/blob/master/proposals/scripting-support.md#kotlin-main-kts). Though I am not sure if it's relative to the directory the script is in or to the working directory.

    If you don't want the users to have to do that, you could pass them in importedScripts.