Is there a way to get the runtimeClasspath without...
# scripting
i
Is there a way to get the runtimeClasspath without the gradle API’s? I am adding it to the ScriptEngine - templateClasspath. Any help is welcomed. Right now I am collecting it here, but it doesn’t work consistently:
Copy code
val classpath: List<File>
  get() = ((Thread.currentThread().contextClassLoader as URLClassLoader).urLs ?: emptyArray<URL>()).toList()
    .filterNot { it.path.contains("Java/JavaVirtualMachines") }
    .map { Paths.get(it.toURI()).toFile() }
Any help is welcomed, how to add
List<File>
to the classpath of an ScriptEngine. Usually if one creates a ScriptEngineManager with a classloader the classpath get’s added automatically to the ScriptEngines, but that is not the case here https://github.com/JetBrains/kotlin/blob/d73e6c855baa6066ba784bf2c77a1ca5854e70fc/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineFactoryExamples.kt#L33
i
I'm not sure I understand your problem correctly. But Kotlin JSR-223 engine tries to extract all paths to jars from the classloaders and add them to the script compilation classpath - in the linked example the parameter
wholeClasspath = true
instruct it to use whole extracted classpath unfiltered. So, if it doesn't work for you, could you please elaborate. Anyway, I'd suggest to use the new JSR-223 implementation - https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-scripting-jsr223/1.3.61 - in additional to other things, it has more reliable classpath extraction.
i
Yes I understand what it tries to do, but the problem is that it does not add the jars from my runtimeClasspath If I run a snippet. It is not aware of classes and members of a given project. Or at least I don’t know how to add it. Rn I am excluding them in this example: https://github.com/i-walker/dokka/blob/b7992e7b9baa5162e1d521d0a3b49fe2bb74a599/plugins/tcPlugin/src/main/kotlin/Utils.kt#L34 and I am looking for a way to run a Snippet, which is defined as a String with either JSR-233 or the scripting API’s. Any help is welcome. It appears that using the scripting API’s is recommended, but I don’t know how to add the runtimeClasspath with this function: https://github.com/JetBrains/kotlin/blob/65244b4bea81f737466618927d4f3afe339cad0d/libraries/examples/scripting/jvm-embeddable-host/src/org/jetbrains/kotlin/script/examples/jvm/embeddable/host/host.kt#L20. I hope this is a bit more expressive.
@ilya.chernikov Do you know what might cause
ERROR USE_FIR: java.lang.NoSuchFieldError: USE_FIR
I defined this function:
Copy code
fun evaluate(snip: String, classpath: List<File> = empty()): ResultWithDiagnostics<EvaluationResult> =
  BasicJvmScriptingHost().eval(
    script = snip.toScriptSource(),
    compilationConfiguration = createJvmCompilationConfigurationFromTemplate<ScriptUtils> {
      jvm {
        updateClasspath(
          scriptCompilationClasspathFromContext(wholeClasspath = true).plus(classpath).distinct()
        )
      }
    },
    evaluationConfiguration = ScriptEvaluationConfiguration {
      jvm
    }
  )
it works fine in a main function, but doesnt in my project. Am I missing any dependencies?
i
Your usage of the JSR-223 looks correct, provided that you want only jars with names containing
kotlin
in your classpath. If so, I don't know why it doesn't work for you just looking at the code, some debugging is needed. If curios, you can step into the
KotlinJsr223JvmLocalScriptEngine
constructor and check whether the passed classpath is the one you expect.
The JSR-223 is in fact a REPL, and therefore is preferable if you need a REPL or you want to use a standard API. Otherwise the Kotlin Scripting API will provide you more control.
If you decide to stay with JSR-223, I'd recommend to use the new implementation - as I wrote above -
kotlin-scripting-jsr223
, and you can have a look at this test - https://github.com/JetBrains/kotlin/blob/master/libraries/scripting/jsr223-test/test/kotlin/script/experimental/jsr223/test/KotlinJsr223ScriptEngineIT.kt#L290 - to see how importing symbols from the classloader works.
If you want to switch to the scripting API, then adding static dependencies (the one known before script compilation) is as easy as adding
Copy code
dependency(JvmDependency(<list-of-classpath-files))
right after
dependenciesFromCurrentContext
call in the host example you linked above. Or even instead of
dependenciesFromCurrentContext
, if you want to control your dependencies explicitly.