Improve classpath extraction from classloader:
- implement opt-in unpacking/caching of the jar collection archives, such as spring boot fat jars and WARs, to the temp dir, and creating a valid classpath from extracted archive - turning it on for the new default jsr223 engine - fallback to extracting classpath from resource URLs if the classloader is not known url-based one - refactor and optimize extraction - cache the last retrieved classpath in the default JSR-223 script engine factory
This commit is contained in:
+23
-1
@@ -6,10 +6,14 @@
|
||||
package kotlin.script.experimental.jsr223
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
|
||||
import java.io.File
|
||||
import javax.script.ScriptEngine
|
||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||
import kotlin.script.experimental.jvm.JvmScriptCompilationConfigurationBuilder
|
||||
import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
import kotlin.script.experimental.jvm.updateClasspath
|
||||
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContext
|
||||
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.jvmhost.createJvmEvaluationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.jvmhost.jsr223.KotlinJsr223ScriptEngineImpl
|
||||
@@ -18,13 +22,31 @@ class KotlinJsr223DefaultScriptEngineFactory : KotlinJsr223JvmScriptEngineFactor
|
||||
|
||||
private val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<KotlinJsr223DefaultScript>()
|
||||
private val evaluationConfiguration = createJvmEvaluationConfigurationFromTemplate<KotlinJsr223DefaultScript>()
|
||||
private var lastClassLoader: ClassLoader? = null
|
||||
private var lastClassPath: List<File>? = null
|
||||
|
||||
@Synchronized
|
||||
protected fun JvmScriptCompilationConfigurationBuilder.dependenciesFromCurrentContext() {
|
||||
val currentClassLoader = Thread.currentThread().contextClassLoader
|
||||
val classPath = if (lastClassLoader == null || lastClassLoader != currentClassLoader) {
|
||||
scriptCompilationClasspathFromContext(
|
||||
classLoader = currentClassLoader,
|
||||
wholeClasspath = true,
|
||||
unpackJarCollections = true
|
||||
).also {
|
||||
lastClassLoader = currentClassLoader
|
||||
lastClassPath = it
|
||||
}
|
||||
} else lastClassPath!!
|
||||
updateClasspath(classPath)
|
||||
}
|
||||
|
||||
override fun getScriptEngine(): ScriptEngine =
|
||||
KotlinJsr223ScriptEngineImpl(
|
||||
this,
|
||||
ScriptCompilationConfiguration(compilationConfiguration) {
|
||||
jvm {
|
||||
dependenciesFromCurrentContext(wholeClasspath = true)
|
||||
dependenciesFromCurrentContext()
|
||||
}
|
||||
},
|
||||
evaluationConfiguration
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.jvmhost.test
|
||||
|
||||
import junit.framework.TestCase
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.JarEntry
|
||||
import java.util.jar.JarOutputStream
|
||||
import kotlin.script.experimental.jvm.util.classpathFromClassloader
|
||||
|
||||
class ClassPathTest : TestCase() {
|
||||
|
||||
@Test
|
||||
fun testExtractFromFat() {
|
||||
val collection = createTempFile("col", ".jar").apply { createCollectionJar(emulatedCollectionFiles, "BOOT-INF") }
|
||||
val cl = URLClassLoader(arrayOf(collection.toURI().toURL()), null)
|
||||
val cp = classpathFromClassloader(cl, true)
|
||||
Assert.assertTrue(cp != null && cp.isNotEmpty())
|
||||
|
||||
testUnpackedCollection(cp!!, emulatedCollectionFiles)
|
||||
}
|
||||
}
|
||||
|
||||
private val emulatedCollectionFiles = arrayOf(
|
||||
"classes/a/b.class",
|
||||
"lib/c-d.jar"
|
||||
)
|
||||
|
||||
fun File.createCollectionJar(fileNames: Array<String>, infDirName: String) {
|
||||
FileOutputStream(this).use { fileStream ->
|
||||
val jarStream = JarOutputStream(fileStream)
|
||||
jarStream.putNextEntry(JarEntry("$infDirName/classes/"))
|
||||
jarStream.putNextEntry(JarEntry("$infDirName/lib/"))
|
||||
for (name in fileNames) {
|
||||
jarStream.putNextEntry(JarEntry("$infDirName/$name"))
|
||||
jarStream.write(name.toByteArray())
|
||||
}
|
||||
jarStream.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun testUnpackedCollection(classpath: List<File>, fileNames: Array<String>) {
|
||||
|
||||
fun List<String>.checkFiles(root: File) = forEach {
|
||||
val file = File(root, it)
|
||||
Assert.assertTrue(file.exists())
|
||||
Assert.assertEquals(it, file.readText())
|
||||
}
|
||||
|
||||
val (classes, jars) = fileNames.partition { it.startsWith("classes") }
|
||||
val (cpClasses, cpJars) = classpath.partition { it.isDirectory && it.name == "classes" }
|
||||
Assert.assertTrue(cpClasses.size == 1)
|
||||
classes.checkFiles(cpClasses.first().parentFile)
|
||||
jars.checkFiles(cpJars.first().parentFile.parentFile)
|
||||
}
|
||||
+1
-1
@@ -105,7 +105,7 @@ internal fun List<ScriptDependency>?.toClassPathOrEmpty() = this?.flatMap { (it
|
||||
|
||||
internal fun List<SourceCode>?.toFilesOrEmpty() = this?.map {
|
||||
val externalSource = it as? ExternalSourceCode
|
||||
externalSource?.externalLocation?.toFile()
|
||||
externalSource?.externalLocation?.toFileOrNull()
|
||||
?: throw RuntimeException("Unsupported source in requireSources parameter - only local files are supported now (${externalSource?.externalLocation})")
|
||||
} ?: emptyList()
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package kotlin.script.experimental.jvm.impl
|
||||
|
||||
import java.io.File
|
||||
import java.net.JarURLConnection
|
||||
import java.net.URL
|
||||
|
||||
// Based on an implementation in com.intellij.openapi.application.PathManager.getResourceRoot
|
||||
@@ -29,7 +30,7 @@ private fun extractRoot(resourceURL: URL, resourcePath: String): String? {
|
||||
var resultPath: String? = null
|
||||
val protocol = resourceURL.protocol
|
||||
if (protocol == FILE_PROTOCOL) {
|
||||
val path = resourceURL.toFile()!!.path
|
||||
val path = resourceURL.toFileOrNull()!!.path
|
||||
val testPath = path.replace('\\', '/')
|
||||
val testResourcePath = resourcePath.replace('\\', '/')
|
||||
if (testPath.endsWith(testResourcePath, ignoreCase = true)) {
|
||||
@@ -57,7 +58,7 @@ private fun splitJarUrl(url: String): Pair<String, String>? {
|
||||
|
||||
if (jarPath.startsWith(FILE_PROTOCOL)) {
|
||||
try {
|
||||
jarPath = URL(jarPath).toFile()!!.path.replace('\\', '/')
|
||||
jarPath = URL(jarPath).toFileOrNull()!!.path.replace('\\', '/')
|
||||
} catch (e: Exception) {
|
||||
jarPath = jarPath.substring(FILE_PROTOCOL.length)
|
||||
if (jarPath.startsWith(SCHEME_SEPARATOR)) {
|
||||
@@ -91,11 +92,15 @@ fun tryGetResourcePathForClassByName(name: String, classLoader: ClassLoader): Fi
|
||||
null
|
||||
}
|
||||
|
||||
internal fun URL.toFile() =
|
||||
internal fun URL.toFileOrNull() =
|
||||
try {
|
||||
File(toURI().schemeSpecificPart)
|
||||
File(toURI().schemeSpecificPart).canonicalFile
|
||||
} catch (e: java.net.URISyntaxException) {
|
||||
if (protocol != "file") null
|
||||
else File(file)
|
||||
else File(file).canonicalFile
|
||||
}
|
||||
|
||||
internal fun URL.toContainingFileOrNull(): File? =
|
||||
if (protocol == "jar") {
|
||||
(openConnection() as? JarURLConnection)?.jarFileURL?.toFileOrNull()
|
||||
} else toFileOrNull()
|
||||
|
||||
@@ -31,17 +31,24 @@ fun JvmScriptCompilationConfigurationBuilder.dependenciesFromClassContext(
|
||||
dependenciesFromClassloader(*libraries, classLoader = contextClass.java.classLoader, wholeClasspath = wholeClasspath)
|
||||
}
|
||||
|
||||
fun JvmScriptCompilationConfigurationBuilder.dependenciesFromCurrentContext(vararg libraries: String, wholeClasspath: Boolean = false) {
|
||||
dependenciesFromClassloader(*libraries, wholeClasspath = wholeClasspath)
|
||||
fun JvmScriptCompilationConfigurationBuilder.dependenciesFromCurrentContext(
|
||||
vararg libraries: String,
|
||||
wholeClasspath: Boolean = false,
|
||||
unpackJarCollections: Boolean = false
|
||||
) {
|
||||
dependenciesFromClassloader(*libraries, wholeClasspath = wholeClasspath, unpackJarCollections = unpackJarCollections)
|
||||
}
|
||||
|
||||
fun JvmScriptCompilationConfigurationBuilder.dependenciesFromClassloader(
|
||||
vararg libraries: String,
|
||||
classLoader: ClassLoader = Thread.currentThread().contextClassLoader,
|
||||
wholeClasspath: Boolean = false
|
||||
wholeClasspath: Boolean = false,
|
||||
unpackJarCollections: Boolean = false
|
||||
) {
|
||||
updateClasspath(
|
||||
scriptCompilationClasspathFromContext(*libraries, classLoader = classLoader, wholeClasspath = wholeClasspath)
|
||||
scriptCompilationClasspathFromContext(
|
||||
*libraries, classLoader = classLoader, wholeClasspath = wholeClasspath, unpackJarCollections = unpackJarCollections
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+145
-54
@@ -6,11 +6,13 @@
|
||||
package kotlin.script.experimental.jvm.util
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.JarInputStream
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.jvm.impl.toFile
|
||||
import kotlin.script.experimental.jvm.impl.toContainingFileOrNull
|
||||
import kotlin.script.experimental.jvm.impl.tryGetResourcePathForClass
|
||||
import kotlin.script.experimental.jvm.impl.tryGetResourcePathForClassByName
|
||||
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
||||
@@ -32,6 +34,11 @@ internal const val KOTLIN_SCRIPTING_JVM_JAR = "kotlin-scripting-jvm.jar"
|
||||
internal const val KOTLIN_COMPILER_NAME = "kotlin-compiler"
|
||||
internal const val KOTLIN_COMPILER_JAR = "$KOTLIN_COMPILER_NAME.jar"
|
||||
|
||||
private val JAR_COLLECTIONS_CLASSES_PATHS = arrayOf("BOOT-INF/classes", "WEB-INF/classes")
|
||||
private val JAR_COLLECTIONS_LIB_PATHS = arrayOf("BOOT-INF/lib", "WEB-INF/lib")
|
||||
private val JAR_COLLECTIONS_KEY_PATHS = JAR_COLLECTIONS_CLASSES_PATHS + JAR_COLLECTIONS_LIB_PATHS
|
||||
private const val JAR_MANIFEST_RESOURCE_NAME = "META-INF/MANIFEST.MF"
|
||||
|
||||
internal const val KOTLIN_SCRIPT_CLASSPATH_PROPERTY = "kotlin.script.classpath"
|
||||
internal const val KOTLIN_COMPILER_CLASSPATH_PROPERTY = "kotlin.compiler.classpath"
|
||||
internal const val KOTLIN_COMPILER_JAR_PROPERTY = "kotlin.compiler.jar"
|
||||
@@ -43,32 +50,105 @@ internal const val KOTLIN_RUNTIME_JAR_PROPERTY = "kotlin.java.runtime.jar"
|
||||
internal const val KOTLIN_SCRIPT_RUNTIME_JAR_PROPERTY = "kotlin.script.runtime.jar"
|
||||
|
||||
private val validClasspathFilesExtensions = setOf("jar", "zip", "java")
|
||||
private val validJarCollectionFilesExtensions = setOf("jar", "war", "zip")
|
||||
|
||||
fun classpathFromClassloader(classLoader: ClassLoader): List<File>? =
|
||||
allRelatedClassLoaders(classLoader).toList().flatMap {
|
||||
val urls = (it as? URLClassLoader)?.urLs?.asList()
|
||||
?: try {
|
||||
// e.g. for IDEA platform UrlClassLoader
|
||||
val getUrls = it::class.java.getMethod("getUrls")
|
||||
getUrls.isAccessible = true
|
||||
val result = getUrls.invoke(it) as? List<Any?>
|
||||
result?.filterIsInstance<URL>()
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
urls?.mapNotNull {
|
||||
// taking only classpath elements pointing to dirs (presumably with classes) or jars, because this classpath is intended for
|
||||
// usage with the kotlin compiler, which cannot process other types of entries, e.g. jni libs
|
||||
it.toFile()?.takeIf { el -> el.isDirectory || validClasspathFilesExtensions.any { el.extension == it } }
|
||||
fun classpathFromClassloader(currentClassLoader: ClassLoader, unpackJarCollections: Boolean = false): List<File>? {
|
||||
val processedJars = hashSetOf<File>()
|
||||
val unpackJarCollectionsDir by lazy {
|
||||
createTempDir("unpackedJarCollections").canonicalFile.also {
|
||||
Runtime.getRuntime().addShutdownHook(Thread {
|
||||
it.deleteRecursively()
|
||||
})
|
||||
}
|
||||
?: emptyList()
|
||||
}.distinct().takeIf { it.isNotEmpty() }
|
||||
}
|
||||
return allRelatedClassLoaders(currentClassLoader).flatMap { classLoader ->
|
||||
var classPath = emptySequence<File>()
|
||||
if (unpackJarCollections && JAR_COLLECTIONS_KEY_PATHS.any { classLoader.getResource(it)?.file?.isNotEmpty() == true }) {
|
||||
// if cache dir is specified, find all jar collections (spring boot fat jars and WARs so far, and unpack it accordingly
|
||||
val jarCollections = JAR_COLLECTIONS_KEY_PATHS.asSequence().flatMap { currentClassLoader.getResources(it).asSequence() }
|
||||
.mapNotNull {
|
||||
it.toContainingFileOrNull()?.takeIf { file ->
|
||||
// additionally mark/check processed collection jars since unpacking is expensive
|
||||
file.extension in validJarCollectionFilesExtensions && processedJars.add(file)
|
||||
}
|
||||
}
|
||||
classPath += jarCollections.flatMap { it.unpackJarCollection(unpackJarCollectionsDir) }.filter { it.isValidClasspathFile() }
|
||||
}
|
||||
classPath += when (classLoader) {
|
||||
is URLClassLoader -> {
|
||||
classLoader.urLs.asSequence().mapNotNull { url -> url.toValidClasspathFileOrNull() }
|
||||
}
|
||||
else -> {
|
||||
classLoader.classPathFromGetUrlsMethodOrNull()
|
||||
?: classLoader.classPathFromManifestResourceUrls()
|
||||
}
|
||||
}
|
||||
classPath
|
||||
}.filter { processedJars.add(it) }
|
||||
.toList().takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
internal fun URL.toValidClasspathFileOrNull(): File? = toContainingFileOrNull()?.takeIf { it.isValidClasspathFile() }
|
||||
|
||||
internal fun File.isValidClasspathFile(): Boolean =
|
||||
isDirectory || (isFile && extension in validClasspathFilesExtensions)
|
||||
|
||||
private fun ClassLoader.classPathFromGetUrlsMethodOrNull(): Sequence<File>? {
|
||||
return try {
|
||||
// e.g. for IDEA platform UrlClassLoader
|
||||
val getUrls = this::class.java.getMethod("getUrls")
|
||||
getUrls.isAccessible = true
|
||||
val result = getUrls.invoke(this) as? List<Any?>
|
||||
result?.asSequence()?.filterIsInstance<URL>()?.mapNotNull { it.toValidClasspathFileOrNull() }
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ClassLoader.classPathFromManifestResourceUrls(): Sequence<File> =
|
||||
getResources(JAR_MANIFEST_RESOURCE_NAME).asSequence().distinct().mapNotNull { it.toValidClasspathFileOrNull() }
|
||||
|
||||
private fun File.unpackJarCollection(rootTempDir: File?): Sequence<File> {
|
||||
val targetDir = createTempDir(nameWithoutExtension, directory = rootTempDir)
|
||||
return try {
|
||||
ArrayList<File>().apply {
|
||||
JarInputStream(FileInputStream(this@unpackJarCollection)).use { jarInputStream ->
|
||||
for (classesDir in JAR_COLLECTIONS_CLASSES_PATHS) {
|
||||
add(File(targetDir, classesDir))
|
||||
}
|
||||
do {
|
||||
val entry = jarInputStream.nextJarEntry
|
||||
if (entry != null) {
|
||||
try {
|
||||
if (!entry.isDirectory) {
|
||||
val file = File(targetDir, entry.name)
|
||||
if (JAR_COLLECTIONS_LIB_PATHS.any { entry.name.startsWith("$it/") }) {
|
||||
add(file)
|
||||
}
|
||||
file.parentFile.mkdirs()
|
||||
file.outputStream().use { outputStream ->
|
||||
jarInputStream.copyTo(outputStream)
|
||||
outputStream.flush()
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
jarInputStream.closeEntry()
|
||||
}
|
||||
}
|
||||
} while (entry != null)
|
||||
}
|
||||
}.asSequence()
|
||||
} catch (e: Throwable) {
|
||||
targetDir.deleteRecursively()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
fun classpathFromClasspathProperty(): List<File>? =
|
||||
System.getProperty("java.class.path")
|
||||
?.split(String.format("\\%s", File.pathSeparatorChar).toRegex())
|
||||
?.dropLastWhile(String::isEmpty)
|
||||
?.map(::File)
|
||||
System.getProperty("java.class.path")
|
||||
?.split(String.format("\\%s", File.pathSeparatorChar).toRegex())
|
||||
?.dropLastWhile(String::isEmpty)
|
||||
?.map(::File)
|
||||
|
||||
fun classpathFromClass(classLoader: ClassLoader, klass: KClass<out Any>): List<File>? =
|
||||
classpathFromFQN(classLoader, klass.qualifiedName!!)
|
||||
@@ -82,21 +162,22 @@ fun classpathFromFQN(classLoader: ClassLoader, fqn: String): List<File>? {
|
||||
}
|
||||
|
||||
fun File.matchMaybeVersionedFile(baseName: String) =
|
||||
name == baseName ||
|
||||
name == baseName.removeSuffix(".jar") || // for classes dirs
|
||||
Regex(Regex.escape(baseName.removeSuffix(".jar")) + "(-\\d.*)?\\.jar").matches(name)
|
||||
name == baseName ||
|
||||
name == baseName.removeSuffix(".jar") || // for classes dirs
|
||||
Regex(Regex.escape(baseName.removeSuffix(".jar")) + "(-\\d.*)?\\.jar").matches(name)
|
||||
|
||||
fun File.hasParentNamed(baseName: String): Boolean =
|
||||
nameWithoutExtension == baseName || parentFile?.hasParentNamed(baseName) ?: false
|
||||
|
||||
private const val KOTLIN_COMPILER_EMBEDDABLE_JAR = "$KOTLIN_COMPILER_NAME-embeddable.jar"
|
||||
|
||||
// Iterating over classloaders tree in a regular, parent-first order
|
||||
private fun allRelatedClassLoaders(clsLoader: ClassLoader, visited: MutableSet<ClassLoader> = HashSet()): Sequence<ClassLoader> {
|
||||
if (!visited.add(clsLoader)) return emptySequence()
|
||||
|
||||
val singleParent = clsLoader.parent
|
||||
if (singleParent != null)
|
||||
return sequenceOf(clsLoader) + sequenceOf(singleParent).flatMap { allRelatedClassLoaders(it, visited) }
|
||||
return sequenceOf(singleParent).flatMap { allRelatedClassLoaders(it, visited) } + clsLoader
|
||||
|
||||
return try {
|
||||
val field = clsLoader.javaClass.getDeclaredField("myParents") // com.intellij.ide.plugins.cl.PluginClassLoader
|
||||
@@ -104,7 +185,9 @@ private fun allRelatedClassLoaders(clsLoader: ClassLoader, visited: MutableSet<C
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val arrayOfClassLoaders = field.get(clsLoader) as Array<ClassLoader>
|
||||
sequenceOf(clsLoader) + arrayOfClassLoaders.asSequence().flatMap { allRelatedClassLoaders(it, visited) }
|
||||
// TODO: PluginClassLoader uses filtering (mustBeLoadedByPlatform), consider using the same logic, if possible
|
||||
// (untill proper compiling from classloader instead of classpath is implemented)
|
||||
arrayOfClassLoaders.asSequence().flatMap { allRelatedClassLoaders(it, visited) } + clsLoader
|
||||
} catch (e: Throwable) {
|
||||
sequenceOf(clsLoader)
|
||||
}
|
||||
@@ -112,9 +195,9 @@ private fun allRelatedClassLoaders(clsLoader: ClassLoader, visited: MutableSet<C
|
||||
|
||||
|
||||
internal fun List<File>.takeIfContainsAll(vararg keyNames: String): List<File>? =
|
||||
takeIf { classpath ->
|
||||
keyNames.all { key -> classpath.any { it.matchMaybeVersionedFile(key) } }
|
||||
}
|
||||
takeIf { classpath ->
|
||||
keyNames.all { key -> classpath.any { it.matchMaybeVersionedFile(key) } }
|
||||
}
|
||||
|
||||
internal fun List<File>.filterIfContainsAll(vararg keyNames: String): List<File>? {
|
||||
val res = hashMapOf<String, File>()
|
||||
@@ -133,25 +216,30 @@ internal fun List<File>.filterIfContainsAll(vararg keyNames: String): List<File>
|
||||
}
|
||||
|
||||
internal fun List<File>.takeIfContainsAny(vararg keyNames: String): List<File>? =
|
||||
takeIf { classpath ->
|
||||
keyNames.any { key -> classpath.any { it.matchMaybeVersionedFile(key) } }
|
||||
}
|
||||
takeIf { classpath ->
|
||||
keyNames.any { key -> classpath.any { it.matchMaybeVersionedFile(key) } }
|
||||
}
|
||||
|
||||
fun scriptCompilationClasspathFromContextOrNull(
|
||||
vararg keyNames: String,
|
||||
classLoader: ClassLoader = Thread.currentThread().contextClassLoader,
|
||||
wholeClasspath: Boolean = false
|
||||
wholeClasspath: Boolean = false,
|
||||
unpackJarCollections: Boolean = false
|
||||
): List<File>? {
|
||||
fun List<File>.takeAndFilter() = when {
|
||||
isEmpty() -> null
|
||||
wholeClasspath -> takeIfContainsAll(*keyNames)
|
||||
else -> filterIfContainsAll(*keyNames)
|
||||
}
|
||||
return System.getProperty(KOTLIN_SCRIPT_CLASSPATH_PROPERTY)?.split(File.pathSeparator)?.map(::File)
|
||||
?: classpathFromClassloader(classLoader)?.takeAndFilter()
|
||||
|
||||
val fromProperty = System.getProperty(KOTLIN_SCRIPT_CLASSPATH_PROPERTY)?.split(File.pathSeparator)?.map(::File)
|
||||
if (fromProperty != null) return fromProperty
|
||||
|
||||
return classpathFromClassloader(classLoader, unpackJarCollections)?.takeAndFilter()
|
||||
?: classpathFromClasspathProperty()?.takeAndFilter()
|
||||
}
|
||||
|
||||
|
||||
fun scriptCompilationClasspathFromContextOrStdlib(
|
||||
vararg keyNames: String,
|
||||
classLoader: ClassLoader = Thread.currentThread().contextClassLoader,
|
||||
@@ -162,25 +250,27 @@ fun scriptCompilationClasspathFromContextOrStdlib(
|
||||
classLoader = classLoader,
|
||||
wholeClasspath = wholeClasspath
|
||||
)
|
||||
?: KotlinJars.kotlinScriptStandardJars
|
||||
?: KotlinJars.kotlinScriptStandardJars
|
||||
|
||||
fun scriptCompilationClasspathFromContext(
|
||||
vararg keyNames: String,
|
||||
classLoader: ClassLoader = Thread.currentThread().contextClassLoader,
|
||||
wholeClasspath: Boolean = false
|
||||
wholeClasspath: Boolean = false,
|
||||
unpackJarCollections: Boolean = false
|
||||
): List<File> =
|
||||
scriptCompilationClasspathFromContextOrNull(
|
||||
*keyNames,
|
||||
classLoader = classLoader,
|
||||
wholeClasspath = wholeClasspath
|
||||
wholeClasspath = wholeClasspath,
|
||||
unpackJarCollections = unpackJarCollections
|
||||
)
|
||||
?: throw Exception("Unable to get script compilation classpath from context, please specify explicit classpath via \"$KOTLIN_SCRIPT_CLASSPATH_PROPERTY\" property")
|
||||
?: throw Exception("Unable to get script compilation classpath from context, please specify explicit classpath via \"$KOTLIN_SCRIPT_CLASSPATH_PROPERTY\" property")
|
||||
|
||||
object KotlinJars {
|
||||
|
||||
private val explicitCompilerClasspath: List<File>? by lazy {
|
||||
System.getProperty(KOTLIN_COMPILER_CLASSPATH_PROPERTY)?.split(File.pathSeparator)?.map(::File)
|
||||
?: System.getProperty(KOTLIN_COMPILER_JAR_PROPERTY)?.let(::File)?.takeIf(File::exists)?.let { listOf(it) }
|
||||
?: System.getProperty(KOTLIN_COMPILER_JAR_PROPERTY)?.let(::File)?.takeIf(File::exists)?.let { listOf(it) }
|
||||
}
|
||||
|
||||
val compilerClasspath: List<File> by lazy {
|
||||
@@ -215,7 +305,7 @@ object KotlinJars {
|
||||
|
||||
val classpath = explicitCompilerClasspath
|
||||
// search classpath from context classloader and `java.class.path` property
|
||||
?: (classpathFromFQN( Thread.currentThread().contextClassLoader, "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler" )
|
||||
?: (classpathFromFQN(Thread.currentThread().contextClassLoader, "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler")
|
||||
?: classpathFromClassloader(Thread.currentThread().contextClassLoader)?.takeIf { it.isNotEmpty() }
|
||||
?: classpathFromClasspathProperty()
|
||||
)?.filter { f -> kotlinBaseJars.any { f.matchMaybeVersionedFile(it) } }?.takeIf { it.isNotEmpty() }
|
||||
@@ -254,16 +344,16 @@ object KotlinJars {
|
||||
|
||||
val stdlibOrNull: File? by lazy {
|
||||
System.getProperty(KOTLIN_STDLIB_JAR_PROPERTY)?.let(::File)?.takeIf(File::exists)
|
||||
?: getLib(
|
||||
KOTLIN_RUNTIME_JAR_PROPERTY,
|
||||
KOTLIN_JAVA_STDLIB_JAR,
|
||||
JvmStatic::class
|
||||
)
|
||||
?: getLib(
|
||||
KOTLIN_RUNTIME_JAR_PROPERTY,
|
||||
KOTLIN_JAVA_STDLIB_JAR,
|
||||
JvmStatic::class
|
||||
)
|
||||
}
|
||||
|
||||
val stdlib: File by lazy {
|
||||
stdlibOrNull
|
||||
?: throw Exception("Unable to find kotlin stdlib, please specify it explicitly via \"$KOTLIN_STDLIB_JAR_PROPERTY\" property")
|
||||
?: throw Exception("Unable to find kotlin stdlib, please specify it explicitly via \"$KOTLIN_STDLIB_JAR_PROPERTY\" property")
|
||||
}
|
||||
|
||||
val reflectOrNull: File? by lazy {
|
||||
@@ -284,11 +374,12 @@ object KotlinJars {
|
||||
|
||||
val scriptRuntime: File by lazy {
|
||||
scriptRuntimeOrNull
|
||||
?: throw Exception("Unable to find kotlin script runtime, please specify it explicitly via \"$KOTLIN_SCRIPT_RUNTIME_JAR_PROPERTY\" property")
|
||||
?: throw Exception("Unable to find kotlin script runtime, please specify it explicitly via \"$KOTLIN_SCRIPT_RUNTIME_JAR_PROPERTY\" property")
|
||||
}
|
||||
|
||||
val kotlinScriptStandardJars get() = listOf(
|
||||
stdlibOrNull,
|
||||
scriptRuntimeOrNull
|
||||
).filterNotNull()
|
||||
val kotlinScriptStandardJars
|
||||
get() = listOf(
|
||||
stdlibOrNull,
|
||||
scriptRuntimeOrNull
|
||||
).filterNotNull()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user