diff --git a/compiler/daemon/daemon-client/build.gradle.kts b/compiler/daemon/daemon-client/build.gradle.kts index 3576f26d217..f85e0a3aa56 100644 --- a/compiler/daemon/daemon-client/build.gradle.kts +++ b/compiler/daemon/daemon-client/build.gradle.kts @@ -30,4 +30,6 @@ javadocJar() dist() +ideaPlugin() + publish() diff --git a/idea/build.gradle.kts b/idea/build.gradle.kts index 8a015254af2..41c43687ac6 100644 --- a/idea/build.gradle.kts +++ b/idea/build.gradle.kts @@ -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() diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223StandardScriptEngineFactory4Idea.kt b/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223StandardScriptEngineFactory4Idea.kt index 4ce2ab0ff8c..d8e3b33c8f1 100644 --- a/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223StandardScriptEngineFactory4Idea.kt +++ b/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223StandardScriptEngineFactory4Idea.kt @@ -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? = - 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 = - ( 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) } diff --git a/libraries/examples/kotlin-jsr223-daemon-local-eval-example/build.gradle.kts b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/build.gradle.kts index f21ecf49421..b493d068d79 100644 --- a/libraries/examples/kotlin-jsr223-daemon-local-eval-example/build.gradle.kts +++ b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/build.gradle.kts @@ -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) + } +} diff --git a/libraries/tools/kotlin-script-util/build.gradle.kts b/libraries/tools/kotlin-script-util/build.gradle.kts index e923e516d7c..28290d55849 100644 --- a/libraries/tools/kotlin-script-util/build.gradle.kts +++ b/libraries/tools/kotlin-script-util/build.gradle.kts @@ -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() diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt index ff99fed4470..7761d7dca53 100644 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt @@ -39,7 +39,7 @@ import kotlin.reflect.KClass class KotlinJsr223JvmDaemonCompileScriptEngine( disposable: Disposable, factory: ScriptEngineFactory, - compilerJar: File, + compilerClasspath: List, templateClasspath: List, templateClassName: String, val getScriptArgs: (ScriptContext, Array>?) -> 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): CompileService { + val compilerId = CompilerId.makeCompilerId(*compilerCP.toTypedArray()) val daemonOptions = configureDaemonOptions() val daemonJVMOptions = DaemonJVMOptions() diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineFactoryExamples.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineFactoryExamples.kt index cb0054ded24..f6d93cc5718 100644 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineFactoryExamples.kt +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineFactoryExamples.kt @@ -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 Iterable.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? = - (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 = - (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) } diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/context.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/context.kt index f89b8267633..3f64a99bf3d 100644 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/context.kt +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/context.kt @@ -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? = - 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? = System.getProperty("java.class.path") @@ -33,19 +46,74 @@ fun classpathFromClass(classLoader: ClassLoader, klass: KClass): List? = - 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 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.takeIfContainsAll(vararg keyNames: String): List? = + takeIf { classpath -> + keyNames.all { key -> classpath.any { it.matchMaybeVersionedFile(key) } } + } + +internal fun List.takeIfContainsAny(vararg keyNames: String): List? = + takeIf { classpath -> + keyNames.any { key -> classpath.any { it.matchMaybeVersionedFile(key) } } + } + +fun scriptCompilationClasspathFromContext(vararg keyNames: String, classLoader: ClassLoader = Thread.currentThread().contextClassLoader): List = + 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? 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 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) + } diff --git a/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilContextTests.kt b/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilContextTests.kt new file mode 100644 index 00000000000..68e09806bd8 --- /dev/null +++ b/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilContextTests.kt @@ -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) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt b/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt index 8c79fc945ec..3dc1437ef55 100644 --- a/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt +++ b/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt @@ -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 Iterable.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? = - ( classpathFromClassloader(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) } - ?: manifestClassPath(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) } - )?.toList()