diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt index bc15b8b56b4..b37381c5b28 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt @@ -2,7 +2,6 @@ package org.jetbrains.kotlin.compilerRunner import org.jetbrains.kotlin.annotation.AnnotationFileUpdater import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments -import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector import org.jetbrains.kotlin.gradle.tasks.findToolsJar @@ -13,22 +12,22 @@ import java.io.File import java.net.URL internal open class GradleCompilerEnvironment( - val compilerJar: File, + val compilerClasspath: List, messageCollector: GradleMessageCollector, outputItemsCollector: OutputItemsCollector, val compilerArgs: CommonCompilerArguments ) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) { val toolsJar: File? by lazy { findToolsJar() } - val compilerClasspath: List - get() = listOf(compilerJar, toolsJar).filterNotNull() + val compilerFullClasspath: List + get() = (compilerClasspath + toolsJar).filterNotNull() val compilerClasspathURLs: List - get() = compilerClasspath.map { it.toURI().toURL() } + get() = compilerFullClasspath.map { it.toURI().toURL() } } internal class GradleIncrementalCompilerEnvironment( - compilerJar: File, + compilerClasspath: List, val changedFiles: ChangedFiles, val reporter: ICReporter, val workingDir: File, @@ -38,4 +37,4 @@ internal class GradleIncrementalCompilerEnvironment( val kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null, val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null, val artifactFile: File? = null -) : GradleCompilerEnvironment(compilerJar, messageCollector, outputItemsCollector, compilerArgs) \ No newline at end of file +) : GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemsCollector, compilerArgs) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt index 26d5304928a..52f0f88b03d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt @@ -139,15 +139,14 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil environment: GradleCompilerEnvironment ): ExitCode { if (compilerArgs.version) { - project.logger.lifecycle("Kotlin version " + loadCompilerVersion(environment.compilerJar) + + project.logger.lifecycle("Kotlin version " + loadCompilerVersion(environment.compilerClasspath) + " (JRE " + System.getProperty("java.runtime.version") + ")") compilerArgs.version = false } - val argsArray = ArgumentUtils.convertArgumentsToStringList(compilerArgs).toTypedArray() with (project.logger) { kotlinDebug { "Kotlin compiler class: $compilerClassName" } - kotlinDebug { "Kotlin compiler classpath: ${environment.compilerClasspath.map { it.canonicalPath }.joinToString()}" } + kotlinDebug { "Kotlin compiler classpath: ${environment.compilerFullClasspath.map { it.canonicalPath }.joinToString()}" } kotlinDebug { "Kotlin compiler args: ${argsArray.joinToString(" ")}" } } @@ -284,7 +283,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil compilerClassName: String, environment: GradleCompilerEnvironment ): ExitCode { - return runToolInSeparateProcess(argsArray, compilerClassName, environment.compilerClasspath, log, loggingMessageCollector) + return runToolInSeparateProcess(argsArray, compilerClassName, environment.compilerFullClasspath, log, loggingMessageCollector) } private fun compileInProcess( @@ -317,7 +316,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil private fun logFinish(strategy: String) = log.logFinish(strategy) override fun getDaemonConnection(environment: GradleCompilerEnvironment): CompileServiceSession? { - val compilerId = CompilerId.makeCompilerId(environment.compilerClasspath) + val compilerId = CompilerId.makeCompilerId(environment.compilerFullClasspath) val clientIsAliveFlagFile = getOrCreateClientFlagFile(project) val sessionIsAliveFlagFile = getOrCreateSessionFlagFile(project) return newDaemonConnection(compilerId, clientIsAliveFlagFile, sessionIsAliveFlagFile, environment) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/utils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/utils.kt index e15510413a2..59299f57089 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/utils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/utils.kt @@ -32,26 +32,40 @@ import java.io.File import java.util.zip.ZipFile import kotlin.concurrent.thread -internal fun loadCompilerVersion(compilerJar: File): String { - var result = "" +internal fun loadCompilerVersion(compilerClasspath: List): String { + var result: String? = null + + fun checkVersion(bytes: ByteArray) { + ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { + override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor { + if (name == KotlinCompilerVersion::VERSION.name && value is String) { + result = value + } + return super.visitField(access, name, desc, signature, value) + } + }, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES or ClassReader.SKIP_DEBUG) + } try { - ZipFile(compilerJar).use { file -> - val fileName = KotlinCompilerVersion::class.java.name.replace('.', '/') + ".class" - val bytes = file.getInputStream(file.getEntry(fileName)).use { inputStream -> inputStream.readBytes() } - ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { - override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor { - if (name == KotlinCompilerVersion::VERSION.name && value is String) { - result = value - } - return super.visitField(access, name, desc, signature, value) + val versionClassFileName = KotlinCompilerVersion::class.java.name.replace('.', '/') + ".class" + for (cpFile in compilerClasspath) { + if (cpFile.isFile && cpFile.extension.toLowerCase() == "jar") { + ZipFile(cpFile).use { jar -> + val bytes = jar.getInputStream(jar.getEntry(versionClassFileName)).use { it.readBytes() } + checkVersion(bytes) } - }, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES or ClassReader.SKIP_DEBUG) + } + else if (cpFile.isDirectory) { + File(cpFile, versionClassFileName).takeIf { it.isFile }?.let { + checkVersion(it.readBytes()) + } + } + if (result != null) break } } catch (e: Throwable) {} - return result + return result ?: "" } internal fun runToolInSeparateProcess( diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt index 9e357a2ddd2..a10598de433 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt @@ -63,7 +63,7 @@ open class KaptTask : ConventionTask() { val messageCollector = GradleMessageCollector(logger) val outputItemCollector = OutputItemsCollectorImpl() - val environment = GradleCompilerEnvironment(kotlinCompileTask.compilerJar, messageCollector, outputItemCollector, args) + val environment = GradleCompilerEnvironment(kotlinCompileTask.computedCompilerClasspath, messageCollector, outputItemCollector, args) if (environment.toolsJar == null) { throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work") } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt index e1e4a4a8cab..4da21ccac20 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt @@ -32,8 +32,8 @@ internal open class KotlinCompileCommon : AbstractKotlinCompile = + findKotlinMetadataCompilerClasspath(project) override fun callCompiler(args: K2MetadataCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { val classpathList = classpath.files.toMutableList() @@ -49,7 +49,7 @@ internal open class KotlinCompileCommon : AbstractKotlinCompile(), KotlinJs override val keep: MutableList = mutableListOf() - override fun findKotlinCompilerJar(project: Project): File? = findKotlinJsDceJar(project) + override fun findKotlinCompilerClasspath(project: Project): List = findKotlinJsDceClasspath(project) override fun compile() {} @@ -58,7 +58,7 @@ open class KotlinJsDce : AbstractKotlinCompileTool(), KotlinJs val log = GradleKotlinLogger(project.logger) val allArgs = argsArray + outputDirArgs + inputFiles - val exitCode = runToolInSeparateProcess(allArgs, K2JSDce::class.java.name, listOf(compilerJar), + val exitCode = runToolInSeparateProcess(allArgs, K2JSDce::class.java.name, computedCompilerClasspath, log, createLoggingMessageCollector(log)) throwGradleExceptionIfError(exitCode) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 019d3f463c0..376edf79f86 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -55,12 +55,19 @@ const val USING_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin incremental comp const val USING_EXPERIMENTAL_JS_INCREMENTAL_COMPILATION_MESSAGE = "Using experimental Kotlin/JS incremental compilation" abstract class AbstractKotlinCompileTool() : AbstractCompile() { + // TODO: deprecate and remove var compilerJarFile: File? = null - internal val compilerJar: File - get() = compilerJarFile - ?: findKotlinCompilerJar(project) - ?: throw IllegalStateException("Could not find Kotlin Compiler jar. Please specify $name.compilerJarFile") - protected abstract fun findKotlinCompilerJar(project: Project): File? + var compilerClasspath: List? = null + + internal val computedCompilerClasspath: List + get() = compilerClasspath?.takeIf { it.isNotEmpty() } + ?: compilerJarFile?.let { + // a hack to remove compiler jar from the cp, will be dropped when compilerJarFile will be removed + listOf(it) + findKotlinCompilerClasspath(project).filter { it.name.startsWith("kotlin-compiler") } + } + ?: findKotlinCompilerClasspath(project).takeIf { it.isNotEmpty() } + ?: throw IllegalStateException("Could not find Kotlin Compiler classpath. Please specify $name.compilerClasspath") + protected abstract fun findKotlinCompilerClasspath(project: Project): List } abstract class AbstractKotlinCompile() : AbstractKotlinCompileTool(), CompilerArgumentAware { @@ -255,8 +262,8 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl incremental = true } - override fun findKotlinCompilerJar(project: Project): File? = - findKotlinJvmCompilerJar(project) + override fun findKotlinCompilerClasspath(project: Project): List = + findKotlinJvmCompilerClasspath(project) override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() @@ -306,10 +313,10 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl val reporter = GradleICReporter(project.rootProject.projectDir) val environment = when { - !incremental -> GradleCompilerEnvironment(compilerJar, messageCollector, outputItemCollector, args) + !incremental -> GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args) else -> { logger.warn(USING_INCREMENTAL_COMPILATION_MESSAGE) - GradleIncrementalCompilerEnvironment(compilerJar, changedFiles, reporter, taskBuildDirectory, + GradleIncrementalCompilerEnvironment(computedCompilerClasspath, changedFiles, reporter, taskBuildDirectory, messageCollector, outputItemCollector, args, kaptAnnotationsFileUpdater, artifactDifferenceRegistryProvider, artifactFile) @@ -403,8 +410,8 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile(), val outputFile: String get() = kotlinOptions.outputFile ?: defaultOutputFile.canonicalPath - override fun findKotlinCompilerJar(project: Project): File? = - findKotlinJsCompilerJar(project) + override fun findKotlinCompilerClasspath(project: Project): List = + findKotlinJsCompilerClasspath(project) override fun createCompilerArgs(): K2JSCompilerArguments = K2JSCompilerArguments() @@ -463,11 +470,11 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile(), incremental -> { logger.warn(USING_EXPERIMENTAL_JS_INCREMENTAL_COMPILATION_MESSAGE) GradleIncrementalCompilerEnvironment( - compilerJar, changedFiles, reporter, taskBuildDirectory, + computedCompilerClasspath, changedFiles, reporter, taskBuildDirectory, messageCollector, outputItemCollector, args) } else -> { - GradleCompilerEnvironment(compilerJar, messageCollector, outputItemCollector, args) + GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/jarSearchingUtil.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/jarSearchingUtil.kt index 017f253a538..12bdd052f60 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/jarSearchingUtil.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/jarSearchingUtil.kt @@ -32,21 +32,48 @@ private val K2JVM_COMPILER_CLASS = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler" private val K2JS_COMPILER_CLASS = "org.jetbrains.kotlin.cli.js.K2JSCompiler" private val K2JS_DCE_CLASS = "org.jetbrains.kotlin.cli.js.dce.K2JSDce" private val K2METADATA_COMPILER_CLASS = "org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler" +private val KOTLIN_STDLIB_EXPECTED_CLASS = "kotlin.collections.ArraysKt" +private val KOTLIN_SCRIPT_RUNTIME_EXPECTED_CLASS = "kotlin.script.templates.AnnotationsKt" +private val KOTLIN_REFLECT_EXPECTED_CLASS = "kotlin.reflect.full.KClasses" private val KOTLIN_MODULE_GROUP = "org.jetbrains.kotlin" private val KOTLIN_GRADLE_PLUGIN = "kotlin-gradle-plugin" private val KOTLIN_COMPILER_EMBEDDABLE = "kotlin-compiler-embeddable" +private val KOTLIN_STDLIB = "kotlin-stdlib" +private val KOTLIN_SCRIPT_RUNTIME = "kotlin-script-runtime" +private val KOTLIN_REFLECT = "kotlin-reflect" -internal fun findKotlinJvmCompilerJar(project: Project): File? = - findKotlinCompilerJar(project, K2JVM_COMPILER_CLASS) +internal fun findKotlinJvmCompilerClasspath(project: Project): List = + findKotlinModuleJar(project, K2JVM_COMPILER_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let { + if (it.isEmpty()) it + else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project) + } -internal fun findKotlinJsCompilerJar(project: Project): File? = - findKotlinCompilerJar(project, K2JS_COMPILER_CLASS) +internal fun findKotlinJsCompilerClasspath(project: Project): List = + findKotlinModuleJar(project, K2JS_COMPILER_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let { + if (it.isEmpty()) it + else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project) + } -internal fun findKotlinMetadataCompilerJar(project: Project): File? = - findKotlinCompilerJar(project, K2METADATA_COMPILER_CLASS) +internal fun findKotlinMetadataCompilerClasspath(project: Project): List = + findKotlinModuleJar(project, K2METADATA_COMPILER_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let { + if (it.isEmpty()) it + else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project) + } -internal fun findKotlinJsDceJar(project: Project): File? = - findKotlinCompilerJar(project, K2JS_DCE_CLASS) +internal fun findKotlinJsDceClasspath(project: Project): List = + findKotlinModuleJar(project, K2JS_DCE_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let { + if (it.isEmpty()) it + else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project) + } + +internal fun findKotlinStdlibClasspath(project: Project): List = + findKotlinModuleJar(project, KOTLIN_STDLIB_EXPECTED_CLASS, KOTLIN_STDLIB) + +internal fun findKotlinScriptRuntimeClasspath(project: Project): List = + findKotlinModuleJar(project, KOTLIN_SCRIPT_RUNTIME_EXPECTED_CLASS, KOTLIN_SCRIPT_RUNTIME) + +internal fun findKotlinReflectClasspath(project: Project): List = + findKotlinModuleJar(project, KOTLIN_REFLECT_EXPECTED_CLASS, KOTLIN_REFLECT) internal fun findToolsJar(): File? = Class.forName("com.sun.tools.javac.util.Context")?.let(::findJarByClass) @@ -61,13 +88,13 @@ private fun findJarByClass(klass: Class<*>): File? { return File(fileName) } -private fun findKotlinCompilerJar(project: Project, compilerClassName: String): File? { +private fun findKotlinModuleJar(project: Project, expectedClassName: String, moduleId: String): List { val pluginVersion = pluginVersionFromAppliedPlugin(project) val filesToCheck = sequenceOf(pluginVersion?.let(::getCompilerFromClassLoader)) + - Sequence { findPotentialCompilerJars(project).iterator() } //call the body only when queried - val entryToFind = compilerClassName.replace(".", "/") + ".class" - return filesToCheck.filterNotNull().firstOrNull { it.hasEntry(entryToFind) } + Sequence { findPotentialModuleJars(project, moduleId).iterator() } //call the body only when queried + val entryToFind = expectedClassName.replace(".", "/") + ".class" + return filesToCheck.filterNotNull().firstOrNull { it.hasEntry(entryToFind) }?.let { listOf(it) } ?: emptyList() } private fun pluginVersionFromAppliedPlugin(project: Project): String? = @@ -81,7 +108,7 @@ private fun getCompilerFromClassLoader(pluginVersion: String): File? { ?.takeIf(File::exists) } -private fun findPotentialCompilerJars(project: Project): Iterable { +private fun findPotentialModuleJars(project: Project, moduleId: String): Iterable { val projects = generateSequence(project) { it.parent } val classpathConfigurations = projects .map { it.buildscript.configurations.findByName(ScriptHandler.CLASSPATH_CONFIGURATION) } @@ -90,7 +117,7 @@ private fun findPotentialCompilerJars(project: Project): Iterable { val allFiles = HashSet() for (configuration in classpathConfigurations) { - val compilerEmbeddable = findCompilerEmbeddable(configuration) + val compilerEmbeddable = findKotlinModuleDependency(configuration, moduleId) if (compilerEmbeddable != null) { return compilerEmbeddable.moduleArtifacts.map { it.file } @@ -103,13 +130,13 @@ private fun findPotentialCompilerJars(project: Project): Iterable { return allFiles } -private fun findCompilerEmbeddable(configuration: Configuration): ResolvedDependency? { +private fun findKotlinModuleDependency(configuration: Configuration, moduleId: String): ResolvedDependency? { fun Iterable.findDependency(group: String, name: String): ResolvedDependency? = find { it.moduleGroup == group && it.moduleName == name } val firstLevelModuleDependencies = configuration.resolvedConfiguration.firstLevelModuleDependencies val gradlePlugin = firstLevelModuleDependencies.findDependency(KOTLIN_MODULE_GROUP, KOTLIN_GRADLE_PLUGIN) - return gradlePlugin?.children?.findDependency(KOTLIN_MODULE_GROUP, KOTLIN_COMPILER_EMBEDDABLE) + return gradlePlugin?.children?.findDependency(KOTLIN_MODULE_GROUP, moduleId) } private fun File.hasEntry(entryToFind: String): Boolean {