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()
|
dist()
|
||||||
|
|
||||||
|
ideaPlugin()
|
||||||
|
|
||||||
publish()
|
publish()
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import org.gradle.jvm.tasks.Jar
|
|||||||
apply { plugin("kotlin") }
|
apply { plugin("kotlin") }
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile(projectDist(":kotlin-stdlib"))
|
compile(project(":kotlin-stdlib"))
|
||||||
compile(project(":core"))
|
compile(project(":core"))
|
||||||
compile(project(":compiler:backend"))
|
compile(project(":compiler:backend"))
|
||||||
compile(project(":compiler:cli-common"))
|
compile(project(":compiler:cli-common"))
|
||||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
|
||||||
compile(project(":compiler:frontend"))
|
compile(project(":compiler:frontend"))
|
||||||
compile(project(":compiler:frontend.java"))
|
compile(project(":compiler:frontend.java"))
|
||||||
compile(project(":compiler:frontend.script"))
|
compile(project(":compiler:frontend.script"))
|
||||||
@@ -27,6 +26,8 @@ dependencies {
|
|||||||
compile(project(":idea:kotlin-gradle-tooling"))
|
compile(project(":idea:kotlin-gradle-tooling"))
|
||||||
compile(project(":plugins:uast-kotlin"))
|
compile(project(":plugins:uast-kotlin"))
|
||||||
compile(project(":plugins:uast-kotlin-idea"))
|
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"))
|
compile(ideaSdkCoreDeps("intellij-core", "util"))
|
||||||
|
|
||||||
@@ -39,8 +40,7 @@ dependencies {
|
|||||||
|
|
||||||
compile(preloadedDeps("markdown", "kotlinx-coroutines-core"))
|
compile(preloadedDeps("markdown", "kotlinx-coroutines-core"))
|
||||||
|
|
||||||
testCompile(projectDist(":kotlin-test:kotlin-test-junit"))
|
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||||
testCompile(project(":compiler:cli"))
|
|
||||||
testCompile(project(":compiler.tests-common"))
|
testCompile(project(":compiler.tests-common"))
|
||||||
testCompile(project(":idea:idea-test-framework")) { isTransitive = false }
|
testCompile(project(":idea:idea-test-framework")) { isTransitive = false }
|
||||||
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
|
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
|
||||||
@@ -92,6 +92,7 @@ sourceSets {
|
|||||||
java.srcDirs("idea-completion/src",
|
java.srcDirs("idea-completion/src",
|
||||||
"idea-live-templates/src",
|
"idea-live-templates/src",
|
||||||
"idea-repl/src")
|
"idea-repl/src")
|
||||||
|
resources.srcDirs("idea-repl/src").apply { include("META-INF/**") }
|
||||||
}
|
}
|
||||||
"test" {
|
"test" {
|
||||||
projectDefault()
|
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.KotlinJsr223JvmScriptEngineFactoryBase
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
|
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
|
||||||
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.ScriptContext
|
||||||
import javax.script.ScriptEngine
|
import javax.script.ScriptEngine
|
||||||
|
|
||||||
@@ -34,53 +29,10 @@ class KotlinJsr223StandardScriptEngineFactory4Idea : KotlinJsr223JvmScriptEngine
|
|||||||
override fun getScriptEngine(): ScriptEngine =
|
override fun getScriptEngine(): ScriptEngine =
|
||||||
KotlinJsr223JvmScriptEngine4Idea(
|
KotlinJsr223JvmScriptEngine4Idea(
|
||||||
this,
|
this,
|
||||||
scriptCompilationClasspathFromContext(Thread.currentThread().contextClassLoader),
|
scriptCompilationClasspathFromContext(PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR),
|
||||||
"kotlin.script.templates.standard.ScriptTemplateWithBindings",
|
"kotlin.script.templates.standard.ScriptTemplateWithBindings",
|
||||||
{ ctx, argTypes -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), argTypes ?: emptyArray()) },
|
{ ctx, argTypes -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), argTypes ?: emptyArray()) },
|
||||||
arrayOf(Map::class)
|
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") }
|
apply { plugin("kotlin") }
|
||||||
|
|
||||||
|
val compilerClasspath by configurations.creating
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile(projectDist(":kotlin-stdlib"))
|
testCompile(project(":kotlin-stdlib"))
|
||||||
compile(projectDist(":kotlin-script-runtime"))
|
testCompile(project(":kotlin-script-runtime"))
|
||||||
compile(projectRuntimeJar(":kotlin-compiler"))
|
testCompile(project(":kotlin-script-util"))
|
||||||
compile(project(":kotlin-script-util"))
|
testCompile(project(":kotlin-daemon-client"))
|
||||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
|
||||||
testCompile(commonDep("junit:junit"))
|
testCompile(commonDep("junit:junit"))
|
||||||
testCompile(projectDist(":kotlin-test:kotlin-test-junit"))
|
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||||
testRuntime(projectDist(":kotlin-reflect"))
|
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") }
|
apply { plugin("kotlin") }
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile(projectDist(":kotlin-stdlib"))
|
compile(project(":kotlin-stdlib"))
|
||||||
compile(projectDist(":kotlin-script-runtime"))
|
compile(project(":kotlin-script-runtime"))
|
||||||
compile(projectRuntimeJar(":kotlin-compiler"))
|
compileOnly(project(":compiler:cli"))
|
||||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
compileOnly(project(":compiler:daemon-common"))
|
||||||
|
compile(project(":kotlin-daemon-client"))
|
||||||
compileOnly("com.jcabi:jcabi-aether:0.10.1")
|
compileOnly("com.jcabi:jcabi-aether:0.10.1")
|
||||||
compileOnly("org.sonatype.aether:aether-api:1.13.1")
|
compileOnly("org.sonatype.aether:aether-api:1.13.1")
|
||||||
compileOnly("org.apache.maven:maven-core:3.0.3")
|
compileOnly("org.apache.maven:maven-core:3.0.3")
|
||||||
testCompile(projectDist(":kotlin-test:kotlin-test-junit"))
|
runtime(projectRuntimeJar(":kotlin-compiler"))
|
||||||
testRuntime(projectDist(":kotlin-reflect"))
|
testCompileOnly(project(":compiler:cli"))
|
||||||
|
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||||
|
testRuntime(project(":kotlin-reflect"))
|
||||||
testCompile(commonDep("junit:junit"))
|
testCompile(commonDep("junit:junit"))
|
||||||
testRuntime("com.jcabi:jcabi-aether:0.10.1")
|
testRuntime("com.jcabi:jcabi-aether:0.10.1")
|
||||||
testRuntime("org.sonatype.aether:aether-api:1.13.1")
|
testRuntime("org.sonatype.aether:aether-api:1.13.1")
|
||||||
@@ -26,3 +29,5 @@ sourcesJar()
|
|||||||
javadocJar()
|
javadocJar()
|
||||||
|
|
||||||
publish()
|
publish()
|
||||||
|
|
||||||
|
ideaPlugin()
|
||||||
|
|||||||
+4
-4
@@ -39,7 +39,7 @@ import kotlin.reflect.KClass
|
|||||||
class KotlinJsr223JvmDaemonCompileScriptEngine(
|
class KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||||
disposable: Disposable,
|
disposable: Disposable,
|
||||||
factory: ScriptEngineFactory,
|
factory: ScriptEngineFactory,
|
||||||
compilerJar: File,
|
compilerClasspath: List<File>,
|
||||||
templateClasspath: List<File>,
|
templateClasspath: List<File>,
|
||||||
templateClassName: String,
|
templateClassName: String,
|
||||||
val getScriptArgs: (ScriptContext, Array<out KClass<out Any>>?) -> ScriptArgsWithTypes?,
|
val getScriptArgs: (ScriptContext, Array<out KClass<out Any>>?) -> ScriptArgsWithTypes?,
|
||||||
@@ -47,7 +47,7 @@ class KotlinJsr223JvmDaemonCompileScriptEngine(
|
|||||||
compilerOut: OutputStream = System.err
|
compilerOut: OutputStream = System.err
|
||||||
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine {
|
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine {
|
||||||
|
|
||||||
private val daemon by lazy { connectToCompileService(compilerJar) }
|
private val daemon by lazy { connectToCompileService(compilerClasspath) }
|
||||||
|
|
||||||
override val replCompiler by lazy {
|
override val replCompiler by lazy {
|
||||||
daemon.let {
|
daemon.let {
|
||||||
@@ -73,8 +73,8 @@ class KotlinJsr223JvmDaemonCompileScriptEngine(
|
|||||||
|
|
||||||
override fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = getScriptArgs(context, scriptArgsTypes)
|
override fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = getScriptArgs(context, scriptArgsTypes)
|
||||||
|
|
||||||
private fun connectToCompileService(compilerJar: File): CompileService {
|
private fun connectToCompileService(compilerCP: List<File>): CompileService {
|
||||||
val compilerId = CompilerId.makeCompilerId(compilerJar)
|
val compilerId = CompilerId.makeCompilerId(*compilerCP.toTypedArray())
|
||||||
val daemonOptions = configureDaemonOptions()
|
val daemonOptions = configureDaemonOptions()
|
||||||
val daemonJVMOptions = DaemonJVMOptions()
|
val daemonJVMOptions = DaemonJVMOptions()
|
||||||
|
|
||||||
|
|||||||
+2
-65
@@ -21,18 +21,10 @@ package org.jetbrains.kotlin.script.jsr223
|
|||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
|
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
|
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
|
||||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
import org.jetbrains.kotlin.script.util.*
|
||||||
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 javax.script.Bindings
|
import javax.script.Bindings
|
||||||
import javax.script.ScriptContext
|
import javax.script.ScriptContext
|
||||||
import javax.script.ScriptEngine
|
import javax.script.ScriptEngine
|
||||||
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
|
||||||
|
|
||||||
class KotlinJsr223JvmLocalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() {
|
class KotlinJsr223JvmLocalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() {
|
||||||
|
|
||||||
@@ -53,7 +45,7 @@ class KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory : KotlinJsr223JvmScriptE
|
|||||||
KotlinJsr223JvmDaemonCompileScriptEngine(
|
KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||||
Disposer.newDisposable(),
|
Disposer.newDisposable(),
|
||||||
this,
|
this,
|
||||||
kotlinCompilerJar,
|
KotlinJars.compilerClasspath,
|
||||||
scriptCompilationClasspathFromContext("kotlin-script-util.jar"),
|
scriptCompilationClasspathFromContext("kotlin-script-util.jar"),
|
||||||
KotlinStandardJsr223ScriptTemplate::class.qualifiedName!!,
|
KotlinStandardJsr223ScriptTemplate::class.qualifiedName!!,
|
||||||
{ ctx, types -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), types ?: emptyArray()) },
|
{ 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
|
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.io.File
|
||||||
import java.net.URI
|
import java.io.FileNotFoundException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.util.jar.Manifest
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
||||||
|
|
||||||
private fun URL.toFile() =
|
// TODO: consider moving all these utilites to the build-common or some other shared compiler API module
|
||||||
try {
|
|
||||||
File(toURI().schemeSpecificPart)
|
internal const val KOTLIN_SCRIPT_CLASSPATH_PROPERTY = "kotlin.script.classpath"
|
||||||
}
|
internal const val KOTLIN_COMPILER_CLASSPATH_PROPERTY = "kotlin.compiler.classpath"
|
||||||
catch (e: java.net.URISyntaxException) {
|
internal const val KOTLIN_COMPILER_JAR_PROPERTY = "kotlin.compiler.jar"
|
||||||
if (protocol != "file") null
|
internal const val KOTLIN_STDLIB_JAR_PROPERTY = "kotlin.java.stdlib.jar"
|
||||||
else File(file)
|
// 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>? =
|
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>? =
|
fun classpathFromClasspathProperty(): List<File>? =
|
||||||
System.getProperty("java.class.path")
|
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 File.matchMaybeVersionedFile(baseName: String) =
|
||||||
fun manifestClassPath(classLoader: ClassLoader): List<File>? =
|
name == baseName ||
|
||||||
classLoader.getResources("META-INF/MANIFEST.MF")
|
name == baseName.removeSuffix(".jar") || // for classes dirs
|
||||||
.asSequence()
|
Regex(Regex.escape(baseName.removeSuffix(".jar")) + "(-\\d.*)?\\.jar").matches(name)
|
||||||
.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 }
|
|
||||||
|
|
||||||
private inline fun <R> ifFailed(default: R, block: () -> R) = try {
|
private const val KOTLIN_COMPILER_EMBEDDABLE_JAR = "${PathUtil.KOTLIN_COMPILER}-embeddable.jar"
|
||||||
block()
|
|
||||||
} catch (t: Throwable) {
|
internal fun List<File>.takeIfContainsAll(vararg keyNames: String): List<File>? =
|
||||||
default
|
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
|
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.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
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.addJvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||||
import org.jetbrains.kotlin.codegen.CompilationException
|
import org.jetbrains.kotlin.codegen.CompilationException
|
||||||
import com.intellij.openapi.util.Disposer
|
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
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.BindingsScriptTemplateWithLocalResolving
|
||||||
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithLocalResolving
|
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithLocalResolving
|
||||||
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithMavenResolving
|
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.jetbrains.kotlin.utils.PathUtil.getResourcePathForClass
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.*
|
import java.io.ByteArrayOutputStream
|
||||||
import java.net.URI
|
import java.io.OutputStream
|
||||||
import java.util.jar.Manifest
|
import java.io.PrintStream
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.test.*
|
|
||||||
|
|
||||||
const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar"
|
const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar"
|
||||||
|
|
||||||
@@ -101,7 +98,7 @@ done
|
|||||||
|
|
||||||
val scriptClass = compileScript("args-junit-hello-world.kts", StandardArgsScriptTemplateWithMavenResolving::class)
|
val scriptClass = compileScript("args-junit-hello-world.kts", StandardArgsScriptTemplateWithMavenResolving::class)
|
||||||
if (scriptClass == null) {
|
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)
|
Assert.assertNotNull(scriptClass)
|
||||||
captureOut {
|
captureOut {
|
||||||
@@ -132,7 +129,7 @@ done
|
|||||||
val rootDisposable = Disposer.newDisposable()
|
val rootDisposable = Disposer.newDisposable()
|
||||||
try {
|
try {
|
||||||
val configuration = CompilerConfiguration().apply {
|
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)
|
addJvmClasspathRoots(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,13 +139,6 @@ done
|
|||||||
if (it.exists()) {
|
if (it.exists()) {
|
||||||
addJvmClasspathRoot(it)
|
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")
|
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script-util-test")
|
||||||
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
||||||
@@ -206,14 +196,3 @@ private class NullOutputStream : OutputStream() {
|
|||||||
override fun write(b: ByteArray, off: Int, len: Int) { }
|
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