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:
Ilya Chernikov
2017-09-27 19:16:19 +02:00
parent 12e35ccf96
commit cff6d8cf17
10 changed files with 185 additions and 188 deletions
@@ -30,4 +30,6 @@ javadocJar()
dist() dist()
ideaPlugin()
publish() publish()
+5 -4
View File
@@ -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()
@@ -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()
@@ -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()
@@ -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) }
@@ -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)
}
@@ -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)
}
}
@@ -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()