Refactor context classpath discovery, share it to idea's jsr223 host...
...from script-util fix daemon usage in repls define compiler classpath for script-util tests explicitly minor refactorings in the build scripts for better import into idea
This commit is contained in:
@@ -30,4 +30,6 @@ javadocJar()
|
||||
|
||||
dist()
|
||||
|
||||
ideaPlugin()
|
||||
|
||||
publish()
|
||||
|
||||
@@ -4,11 +4,10 @@ import org.gradle.jvm.tasks.Jar
|
||||
apply { plugin("kotlin") }
|
||||
|
||||
dependencies {
|
||||
compile(projectDist(":kotlin-stdlib"))
|
||||
compile(project(":kotlin-stdlib"))
|
||||
compile(project(":core"))
|
||||
compile(project(":compiler:backend"))
|
||||
compile(project(":compiler:cli-common"))
|
||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:frontend.script"))
|
||||
@@ -27,6 +26,8 @@ dependencies {
|
||||
compile(project(":idea:kotlin-gradle-tooling"))
|
||||
compile(project(":plugins:uast-kotlin"))
|
||||
compile(project(":plugins:uast-kotlin-idea"))
|
||||
// compile(project(":kotlin-daemon-client")) { isTransitive = false }
|
||||
compile(project(":kotlin-script-util")) { isTransitive = false }
|
||||
|
||||
compile(ideaSdkCoreDeps("intellij-core", "util"))
|
||||
|
||||
@@ -39,8 +40,7 @@ dependencies {
|
||||
|
||||
compile(preloadedDeps("markdown", "kotlinx-coroutines-core"))
|
||||
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-junit"))
|
||||
testCompile(project(":compiler:cli"))
|
||||
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||
testCompile(project(":compiler.tests-common"))
|
||||
testCompile(project(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
|
||||
@@ -92,6 +92,7 @@ sourceSets {
|
||||
java.srcDirs("idea-completion/src",
|
||||
"idea-live-templates/src",
|
||||
"idea-repl/src")
|
||||
resources.srcDirs("idea-repl/src").apply { include("META-INF/**") }
|
||||
}
|
||||
"test" {
|
||||
projectDefault()
|
||||
|
||||
+2
-50
@@ -18,13 +18,8 @@ package org.jetbrains.kotlin.jsr223
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
|
||||
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
|
||||
import org.jetbrains.kotlin.script.util.scriptCompilationClasspathFromContext
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR
|
||||
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_STDLIB_JAR
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import javax.script.ScriptContext
|
||||
import javax.script.ScriptEngine
|
||||
|
||||
@@ -34,53 +29,10 @@ class KotlinJsr223StandardScriptEngineFactory4Idea : KotlinJsr223JvmScriptEngine
|
||||
override fun getScriptEngine(): ScriptEngine =
|
||||
KotlinJsr223JvmScriptEngine4Idea(
|
||||
this,
|
||||
scriptCompilationClasspathFromContext(Thread.currentThread().contextClassLoader),
|
||||
scriptCompilationClasspathFromContext(PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR),
|
||||
"kotlin.script.templates.standard.ScriptTemplateWithBindings",
|
||||
{ ctx, argTypes -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), argTypes ?: emptyArray()) },
|
||||
arrayOf(Map::class)
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: some common parts with the code from script-utils, consider placing in a shared lib
|
||||
|
||||
private fun URL.toFile() =
|
||||
try {
|
||||
File(toURI().schemeSpecificPart)
|
||||
}
|
||||
catch (e: java.net.URISyntaxException) {
|
||||
if (protocol != "file") null
|
||||
else File(file)
|
||||
}
|
||||
|
||||
fun classpathFromClassloader(classLoader: ClassLoader): List<File>? =
|
||||
generateSequence(classLoader) { it.parent }.toList().flatMap { (it as? URLClassLoader)?.urLs?.mapNotNull(URL::toFile) ?: emptyList() }
|
||||
|
||||
private val kotlinCompilerJar: File by lazy {
|
||||
// highest prio - explicit property
|
||||
System.getProperty("kotlin.compiler.jar")?.let(::File)?.takeIf(File::exists)
|
||||
?: PathUtil.kotlinPathsForIdeaPlugin.compilerPath
|
||||
?: throw FileNotFoundException("Cannot find kotlin compiler jar, set kotlin.compiler.jar property to proper location")
|
||||
}
|
||||
|
||||
private fun scriptCompilationClasspathFromContext(classLoader: ClassLoader): List<File> =
|
||||
( System.getProperty("kotlin.script.classpath")?.split(File.pathSeparator)?.map(::File)
|
||||
?: classpathFromClassloader(classLoader)
|
||||
).let {
|
||||
it?.plus(kotlinScriptStandardJars) ?: kotlinScriptStandardJars
|
||||
}
|
||||
.map { it?.canonicalFile }
|
||||
.distinct()
|
||||
.mapNotNull { it?.takeIf(File::exists) }
|
||||
|
||||
|
||||
private val kotlinStdlibJar: File? by lazy {
|
||||
System.getProperty("kotlin.java.runtime.jar")?.let(::File)?.takeIf(File::exists)
|
||||
?: File(kotlinCompilerJar.parentFile, KOTLIN_JAVA_STDLIB_JAR).takeIf(File::exists)
|
||||
}
|
||||
|
||||
private val kotlinScriptRuntimeJar: File? by lazy {
|
||||
System.getProperty("kotlin.script.runtime.jar")?.let(::File)?.takeIf(File::exists)
|
||||
?: File(kotlinCompilerJar.parentFile, KOTLIN_JAVA_SCRIPT_RUNTIME_JAR).takeIf(File::exists)
|
||||
}
|
||||
|
||||
private val kotlinScriptStandardJars by lazy { listOf(kotlinStdlibJar, kotlinScriptRuntimeJar) }
|
||||
|
||||
@@ -3,15 +3,24 @@ description = "Sample Kotlin JSR 223 scripting jar with daemon (out-of-process)
|
||||
|
||||
apply { plugin("kotlin") }
|
||||
|
||||
val compilerClasspath by configurations.creating
|
||||
|
||||
dependencies {
|
||||
compile(projectDist(":kotlin-stdlib"))
|
||||
compile(projectDist(":kotlin-script-runtime"))
|
||||
compile(projectRuntimeJar(":kotlin-compiler"))
|
||||
compile(project(":kotlin-script-util"))
|
||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
||||
testCompile(project(":kotlin-stdlib"))
|
||||
testCompile(project(":kotlin-script-runtime"))
|
||||
testCompile(project(":kotlin-script-util"))
|
||||
testCompile(project(":kotlin-daemon-client"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-junit"))
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||
testRuntime(project(":kotlin-reflect"))
|
||||
compilerClasspath(projectRuntimeJar(":kotlin-compiler"))
|
||||
compilerClasspath(projectDist(":kotlin-reflect"))
|
||||
compilerClasspath(projectDist(":kotlin-stdlib"))
|
||||
compilerClasspath(projectDist(":kotlin-script-runtime"))
|
||||
}
|
||||
|
||||
projectTest()
|
||||
projectTest {
|
||||
doFirst {
|
||||
systemProperty("kotlin.compiler.classpath", compilerClasspath.asFileTree.asPath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,18 @@ description = "Kotlin scripting support utilities"
|
||||
apply { plugin("kotlin") }
|
||||
|
||||
dependencies {
|
||||
compile(projectDist(":kotlin-stdlib"))
|
||||
compile(projectDist(":kotlin-script-runtime"))
|
||||
compile(projectRuntimeJar(":kotlin-compiler"))
|
||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
||||
compile(project(":kotlin-stdlib"))
|
||||
compile(project(":kotlin-script-runtime"))
|
||||
compileOnly(project(":compiler:cli"))
|
||||
compileOnly(project(":compiler:daemon-common"))
|
||||
compile(project(":kotlin-daemon-client"))
|
||||
compileOnly("com.jcabi:jcabi-aether:0.10.1")
|
||||
compileOnly("org.sonatype.aether:aether-api:1.13.1")
|
||||
compileOnly("org.apache.maven:maven-core:3.0.3")
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-junit"))
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
runtime(projectRuntimeJar(":kotlin-compiler"))
|
||||
testCompileOnly(project(":compiler:cli"))
|
||||
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||
testRuntime(project(":kotlin-reflect"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
testRuntime("com.jcabi:jcabi-aether:0.10.1")
|
||||
testRuntime("org.sonatype.aether:aether-api:1.13.1")
|
||||
@@ -26,3 +29,5 @@ sourcesJar()
|
||||
javadocJar()
|
||||
|
||||
publish()
|
||||
|
||||
ideaPlugin()
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ import kotlin.reflect.KClass
|
||||
class KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||
disposable: Disposable,
|
||||
factory: ScriptEngineFactory,
|
||||
compilerJar: File,
|
||||
compilerClasspath: List<File>,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
val getScriptArgs: (ScriptContext, Array<out KClass<out Any>>?) -> ScriptArgsWithTypes?,
|
||||
@@ -47,7 +47,7 @@ class KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||
compilerOut: OutputStream = System.err
|
||||
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine {
|
||||
|
||||
private val daemon by lazy { connectToCompileService(compilerJar) }
|
||||
private val daemon by lazy { connectToCompileService(compilerClasspath) }
|
||||
|
||||
override val replCompiler by lazy {
|
||||
daemon.let {
|
||||
@@ -73,8 +73,8 @@ class KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||
|
||||
override fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = getScriptArgs(context, scriptArgsTypes)
|
||||
|
||||
private fun connectToCompileService(compilerJar: File): CompileService {
|
||||
val compilerId = CompilerId.makeCompilerId(compilerJar)
|
||||
private fun connectToCompileService(compilerCP: List<File>): CompileService {
|
||||
val compilerId = CompilerId.makeCompilerId(*compilerCP.toTypedArray())
|
||||
val daemonOptions = configureDaemonOptions()
|
||||
val daemonJVMOptions = DaemonJVMOptions()
|
||||
|
||||
|
||||
+2
-65
@@ -21,18 +21,10 @@ package org.jetbrains.kotlin.script.jsr223
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
|
||||
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.script.util.classpathFromClass
|
||||
import org.jetbrains.kotlin.script.util.classpathFromClassloader
|
||||
import org.jetbrains.kotlin.script.util.classpathFromClasspathProperty
|
||||
import org.jetbrains.kotlin.script.util.manifestClassPath
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import org.jetbrains.kotlin.script.util.*
|
||||
import javax.script.Bindings
|
||||
import javax.script.ScriptContext
|
||||
import javax.script.ScriptEngine
|
||||
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
||||
|
||||
class KotlinJsr223JvmLocalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() {
|
||||
|
||||
@@ -53,7 +45,7 @@ class KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory : KotlinJsr223JvmScriptE
|
||||
KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||
Disposer.newDisposable(),
|
||||
this,
|
||||
kotlinCompilerJar,
|
||||
KotlinJars.compilerClasspath,
|
||||
scriptCompilationClasspathFromContext("kotlin-script-util.jar"),
|
||||
KotlinStandardJsr223ScriptTemplate::class.qualifiedName!!,
|
||||
{ ctx, types -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), types ?: emptyArray()) },
|
||||
@@ -61,58 +53,3 @@ class KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory : KotlinJsr223JvmScriptE
|
||||
)
|
||||
}
|
||||
|
||||
private const val KOTLIN_COMPILER_JAR = "kotlin-compiler.jar"
|
||||
private const val KOTLIN_JAVA_STDLIB_JAR = "kotlin-stdlib.jar"
|
||||
private const val KOTLIN_JAVA_SCRIPT_RUNTIME_JAR = "kotlin-script-runtime.jar"
|
||||
|
||||
private fun File.existsOrNull(): File? = existsAndCheckOrNull { true }
|
||||
private inline fun File.existsAndCheckOrNull(check: (File.() -> Boolean)): File? = if (exists() && check()) this else null
|
||||
|
||||
private fun <T> Iterable<T>.anyOrNull(predicate: (T) -> Boolean) = if (any(predicate)) this else null
|
||||
|
||||
private fun File.matchMaybeVersionedFile(baseName: String) =
|
||||
name == baseName ||
|
||||
name == baseName.removeSuffix(".jar") || // for classes dirs
|
||||
name.startsWith(baseName.removeSuffix(".jar") + "-")
|
||||
|
||||
private fun contextClasspath(keyName: String, classLoader: ClassLoader): List<File>? =
|
||||
(classpathFromClassloader(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
?: manifestClassPath(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
)?.toList()
|
||||
|
||||
private val validJarExtensions = setOf("jar", "zip")
|
||||
|
||||
private fun scriptCompilationClasspathFromContext(keyName: String, classLoader: ClassLoader = Thread.currentThread().contextClassLoader): List<File> =
|
||||
(System.getProperty("kotlin.script.classpath")?.split(File.pathSeparator)?.map(::File)
|
||||
?: contextClasspath(keyName, classLoader)
|
||||
).let {
|
||||
it?.plus(kotlinScriptStandardJars) ?: kotlinScriptStandardJars
|
||||
}
|
||||
.mapNotNull { it?.canonicalFile }
|
||||
.distinct()
|
||||
.filter { (it.isDirectory || (it.isFile && it.extension.toLowerCase() in validJarExtensions)) && it.exists() }
|
||||
|
||||
private val kotlinCompilerJar: File by lazy {
|
||||
// highest prio - explicit property
|
||||
System.getProperty("kotlin.compiler.jar")?.let(::File)?.existsOrNull()
|
||||
// search classpath from context classloader and `java.class.path` property
|
||||
?: (classpathFromClass(Thread.currentThread().contextClassLoader, K2JVMCompiler::class)
|
||||
?: contextClasspath(KOTLIN_COMPILER_JAR, Thread.currentThread().contextClassLoader)
|
||||
?: classpathFromClasspathProperty()
|
||||
)?.firstOrNull { it.matchMaybeVersionedFile(KOTLIN_COMPILER_JAR) }
|
||||
?: throw FileNotFoundException("Cannot find kotlin compiler jar, set kotlin.compiler.jar property to proper location")
|
||||
}
|
||||
|
||||
private val kotlinStdlibJar: File? by lazy {
|
||||
System.getProperty("kotlin.java.runtime.jar")?.let(::File)?.existsOrNull()
|
||||
?: kotlinCompilerJar.let { File(it.parentFile, KOTLIN_JAVA_STDLIB_JAR) }.existsOrNull()
|
||||
?: PathUtil.getResourcePathForClass(JvmStatic::class.java).existsOrNull()
|
||||
}
|
||||
|
||||
private val kotlinScriptRuntimeJar: File? by lazy {
|
||||
System.getProperty("kotlin.script.runtime.jar")?.let(::File)?.existsOrNull()
|
||||
?: kotlinCompilerJar.let { File(it.parentFile, KOTLIN_JAVA_SCRIPT_RUNTIME_JAR) }.existsOrNull()
|
||||
?: PathUtil.getResourcePathForClass(ScriptTemplateWithArgs::class.java).existsOrNull()
|
||||
}
|
||||
|
||||
private val kotlinScriptStandardJars by lazy { listOf(kotlinStdlibJar, kotlinScriptRuntimeJar) }
|
||||
|
||||
+92
-24
@@ -1,23 +1,36 @@
|
||||
package org.jetbrains.kotlin.script.util
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.Manifest
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
||||
|
||||
private fun URL.toFile() =
|
||||
try {
|
||||
File(toURI().schemeSpecificPart)
|
||||
}
|
||||
catch (e: java.net.URISyntaxException) {
|
||||
if (protocol != "file") null
|
||||
else File(file)
|
||||
}
|
||||
// TODO: consider moving all these utilites to the build-common or some other shared compiler API module
|
||||
|
||||
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"
|
||||
internal const val KOTLIN_STDLIB_JAR_PROPERTY = "kotlin.java.stdlib.jar"
|
||||
// obsolete name, but maybe still used in the wild
|
||||
// TODO: consider removing
|
||||
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")
|
||||
|
||||
fun classpathFromClassloader(classLoader: ClassLoader): List<File>? =
|
||||
generateSequence(classLoader) { it.parent }.toList().flatMap { (it as? URLClassLoader)?.urLs?.mapNotNull(URL::toFile) ?: emptyList() }
|
||||
generateSequence(classLoader) { it.parent }.toList().flatMap {
|
||||
(it as? URLClassLoader)?.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 } }
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
fun classpathFromClasspathProperty(): List<File>? =
|
||||
System.getProperty("java.class.path")
|
||||
@@ -33,19 +46,74 @@ fun classpathFromClass(classLoader: ClassLoader, klass: KClass<out Any>): List<F
|
||||
}
|
||||
}
|
||||
|
||||
// Maven runners sometimes place classpath into the manifest, so we can use it for a fallback search
|
||||
fun manifestClassPath(classLoader: ClassLoader): List<File>? =
|
||||
classLoader.getResources("META-INF/MANIFEST.MF")
|
||||
.asSequence()
|
||||
.mapNotNull { ifFailed(null) { it.openStream().use { Manifest().apply { read(it) } } } }
|
||||
.flatMap { it.mainAttributes?.getValue("Class-Path")?.splitToSequence(" ") ?: emptySequence() }
|
||||
.mapNotNull { ifFailed(null) { File(URI.create(it)) } }
|
||||
.toList()
|
||||
.let { if (it.isNotEmpty()) it else null }
|
||||
fun File.matchMaybeVersionedFile(baseName: String) =
|
||||
name == baseName ||
|
||||
name == baseName.removeSuffix(".jar") || // for classes dirs
|
||||
Regex(Regex.escape(baseName.removeSuffix(".jar")) + "(-\\d.*)?\\.jar").matches(name)
|
||||
|
||||
private inline fun <R> ifFailed(default: R, block: () -> R) = try {
|
||||
block()
|
||||
} catch (t: Throwable) {
|
||||
default
|
||||
private const val KOTLIN_COMPILER_EMBEDDABLE_JAR = "${PathUtil.KOTLIN_COMPILER}-embeddable.jar"
|
||||
|
||||
internal fun List<File>.takeIfContainsAll(vararg keyNames: String): List<File>? =
|
||||
takeIf { classpath ->
|
||||
keyNames.all { key -> classpath.any { it.matchMaybeVersionedFile(key) } }
|
||||
}
|
||||
|
||||
internal fun List<File>.takeIfContainsAny(vararg keyNames: String): List<File>? =
|
||||
takeIf { classpath ->
|
||||
keyNames.any { key -> classpath.any { it.matchMaybeVersionedFile(key) } }
|
||||
}
|
||||
|
||||
fun scriptCompilationClasspathFromContext(vararg keyNames: String, classLoader: ClassLoader = Thread.currentThread().contextClassLoader): List<File> =
|
||||
System.getProperty(KOTLIN_SCRIPT_CLASSPATH_PROPERTY)?.split(File.pathSeparator)?.map(::File)
|
||||
?: classpathFromClassloader(classLoader)?.takeIfContainsAll(*keyNames)
|
||||
?: classpathFromClasspathProperty()?.takeIfContainsAll(*keyNames)
|
||||
?: KotlinJars.kotlinScriptStandardJars
|
||||
|
||||
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) }
|
||||
}
|
||||
|
||||
val compilerClasspath: List<File> by lazy {
|
||||
val kotlinCompilerJars = listOf(PathUtil.KOTLIN_COMPILER_JAR, KOTLIN_COMPILER_EMBEDDABLE_JAR)
|
||||
val kotlinLibsJars = listOf(PathUtil.KOTLIN_JAVA_STDLIB_JAR, PathUtil.KOTLIN_JAVA_REFLECT_JAR, PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR)
|
||||
val kotlinBaseJars = kotlinCompilerJars + kotlinLibsJars
|
||||
|
||||
val classpath = explicitCompilerClasspath
|
||||
// search classpath from context classloader and `java.class.path` property
|
||||
?: (classpathFromClass(Thread.currentThread().contextClassLoader, K2JVMCompiler::class)
|
||||
?: classpathFromClassloader(Thread.currentThread().contextClassLoader)?.takeIf { it.isNotEmpty() }
|
||||
?: classpathFromClasspathProperty()
|
||||
)?.filter { f -> kotlinBaseJars.any { f.matchMaybeVersionedFile(it) } }?.takeIf { it.isNotEmpty() }
|
||||
// if autodetected, additionaly check for presense of the compiler jars
|
||||
if (classpath == null || (explicitCompilerClasspath == null && classpath.none { f -> kotlinCompilerJars.any { f.matchMaybeVersionedFile(it) } })) {
|
||||
throw FileNotFoundException("Cannot find kotlin compiler jar, set kotlin.compiler.classpath property to proper location")
|
||||
}
|
||||
classpath!!
|
||||
}
|
||||
|
||||
private fun getLib(propertyName: String, jarName: String, markerClass: KClass<*>): File? =
|
||||
System.getProperty(propertyName)?.let(::File)?.takeIf(File::exists)
|
||||
?: explicitCompilerClasspath?.firstOrNull { it.matchMaybeVersionedFile(jarName) }?.takeIf(File::exists)
|
||||
?: PathUtil.getResourcePathForClass(markerClass.java).takeIf(File::exists)
|
||||
|
||||
val stdlib: File? by lazy {
|
||||
System.getProperty(KOTLIN_STDLIB_JAR_PROPERTY)?.let(::File)?.takeIf(File::exists)
|
||||
?: getLib(KOTLIN_RUNTIME_JAR_PROPERTY, PathUtil.KOTLIN_JAVA_STDLIB_JAR, JvmStatic::class)
|
||||
}
|
||||
|
||||
val scriptRuntime: File? by lazy { getLib(KOTLIN_SCRIPT_RUNTIME_JAR_PROPERTY, PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR, ScriptTemplateWithArgs::class) }
|
||||
|
||||
val kotlinScriptStandardJars get() = listOf(stdlib, scriptRuntime).filterNotNull()
|
||||
}
|
||||
|
||||
private fun URL.toFile() =
|
||||
try {
|
||||
File(toURI().schemeSpecificPart)
|
||||
}
|
||||
catch (e: java.net.URISyntaxException) {
|
||||
if (protocol != "file") null
|
||||
else File(file)
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.script.util
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class ScriptUtilContextTests {
|
||||
|
||||
@Test
|
||||
fun testExplicitScriptClasspath() {
|
||||
withSysProp(KOTLIN_SCRIPT_CLASSPATH_PROPERTY, "a:b:c") {
|
||||
val classpath = scriptCompilationClasspathFromContext()
|
||||
Assert.assertEquals("a:b:c", classpath.joinToString(":"))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: more tests
|
||||
}
|
||||
|
||||
private fun withSysProp(name: String, value: String, body: () -> Unit) {
|
||||
val savedValue = System.setProperty(name, value)
|
||||
try {
|
||||
body()
|
||||
}
|
||||
finally {
|
||||
if (savedValue != null) System.setProperty(name, savedValue)
|
||||
else System.clearProperty(name)
|
||||
}
|
||||
}
|
||||
+6
-27
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.script.util
|
||||
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.*
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
@@ -24,7 +25,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
@@ -34,16 +34,13 @@ import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.script.util.templates.BindingsScriptTemplateWithLocalResolving
|
||||
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithLocalResolving
|
||||
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithMavenResolving
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
//import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_RUNTIME_JAR
|
||||
import org.jetbrains.kotlin.utils.PathUtil.getResourcePathForClass
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import java.io.*
|
||||
import java.net.URI
|
||||
import java.util.jar.Manifest
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.PrintStream
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.test.*
|
||||
|
||||
const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar"
|
||||
|
||||
@@ -101,7 +98,7 @@ done
|
||||
|
||||
val scriptClass = compileScript("args-junit-hello-world.kts", StandardArgsScriptTemplateWithMavenResolving::class)
|
||||
if (scriptClass == null) {
|
||||
System.err.println(contextClasspath(KOTLIN_JAVA_RUNTIME_JAR, Thread.currentThread().contextClassLoader)?.joinToString())
|
||||
System.err.println(classpathFromClassloader(Thread.currentThread().contextClassLoader)?.takeIfContainsAll(KOTLIN_JAVA_RUNTIME_JAR)?.joinToString())
|
||||
}
|
||||
Assert.assertNotNull(scriptClass)
|
||||
captureOut {
|
||||
@@ -132,7 +129,7 @@ done
|
||||
val rootDisposable = Disposer.newDisposable()
|
||||
try {
|
||||
val configuration = CompilerConfiguration().apply {
|
||||
contextClasspath(KOTLIN_JAVA_RUNTIME_JAR, Thread.currentThread().contextClassLoader)?.let {
|
||||
classpathFromClassloader(Thread.currentThread().contextClassLoader)?.takeIfContainsAll(KOTLIN_JAVA_RUNTIME_JAR)?.let {
|
||||
addJvmClasspathRoots(it)
|
||||
}
|
||||
|
||||
@@ -142,13 +139,6 @@ done
|
||||
if (it.exists()) {
|
||||
addJvmClasspathRoot(it)
|
||||
}
|
||||
else {
|
||||
// attempt to workaround some maven quirks
|
||||
manifestClassPath(Thread.currentThread().contextClassLoader)?.let {
|
||||
val files = it.filter { it.name.startsWith("kotlin-") }
|
||||
addJvmClasspathRoots(files)
|
||||
}
|
||||
}
|
||||
}
|
||||
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script-util-test")
|
||||
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
||||
@@ -206,14 +196,3 @@ private class NullOutputStream : OutputStream() {
|
||||
override fun write(b: ByteArray, off: Int, len: Int) { }
|
||||
}
|
||||
|
||||
private fun <T> Iterable<T>.anyOrNull(predicate: (T) -> Boolean) = if (any(predicate)) this else null
|
||||
|
||||
private fun File.matchMaybeVersionedFile(baseName: String) =
|
||||
name == baseName ||
|
||||
name == baseName.removeSuffix(".jar") || // for classes dirs
|
||||
name.startsWith(baseName.removeSuffix(".jar") + "-")
|
||||
|
||||
private fun contextClasspath(keyName: String, classLoader: ClassLoader): List<File>? =
|
||||
( classpathFromClassloader(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
?: manifestClassPath(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
)?.toList()
|
||||
|
||||
Reference in New Issue
Block a user