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 new file mode 100644 index 00000000000..af25893afd9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt @@ -0,0 +1,140 @@ +/* + * Copyright 2010-2016 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.compilerRunner + +import org.gradle.api.Project +import org.jetbrains.kotlin.cli.common.ExitCode +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.config.Services +import org.jetbrains.kotlin.gradle.plugin.ParentLastURLClassLoader +import org.jetbrains.kotlin.gradle.plugin.kotlinInfo +import org.jetbrains.kotlin.incremental.classpathAsList +import org.jetbrains.kotlin.incremental.destinationAsFile +import org.jetbrains.kotlin.incremental.makeModuleFile +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream +import java.net.URL +import java.util.zip.ZipFile +import kotlin.concurrent.thread + + +const val KOTLIN_COMPILER_JAR_PATH_PROPERTY = "kotlin.compiler.jar.path" + +internal class GradleCompilerRunner(private val project: Project) : KotlinCompilerRunner() { + override val log = GradleKotlinLogger(project.logger) + private val flagFile = run { + val dir = File(project.rootProject.buildDir, "kotlin").apply { mkdirs() } + File(dir, "daemon-is-alive").apply { + if (!exists()) { + createNewFile() + deleteOnExit() + } + } + } + + fun runJvmCompiler( + sourcesToCompile: List, + javaSourceRoots: Iterable, + args: K2JVMCompilerArguments, + messageCollector: MessageCollector, + outputItemsCollector: OutputItemsCollector + ): ExitCode { + val outputDir = args.destinationAsFile + log.debug("Removing all kotlin classes in $outputDir") + // we're free to delete all classes since only we know about that directory + outputDir.deleteRecursively() + + val moduleFile = makeModuleFile( + args.moduleName, + isTest = false, + outputDir = outputDir, + sourcesToCompile = sourcesToCompile, + javaSourceRoots = javaSourceRoots, + classpath = args.classpathAsList, + friendDirs = args.friendPaths?.map(::File) ?: emptyList()) + args.module = moduleFile.absolutePath + + val additionalArguments = "" + + try { + return runCompiler(K2JVM_COMPILER, args, additionalArguments, messageCollector, outputItemsCollector, GradleCompilerEnvironment(project, K2JVM_COMPILER)) + } + finally { + moduleFile.delete() + } + } + + @Synchronized + override fun getDaemonConnection(environment: GradleCompilerEnvironment, messageCollector: MessageCollector): DaemonConnection { + return newDaemonConnection(environment.compilerJar, messageCollector, flagFile) + } +} + +internal class GradleCompilerEnvironment( + private val project: Project, + private val compilerClassName: String +) : CompilerEnvironment(Services.EMPTY) { + val compilerJar: File by lazy { + val file = findKotlinCompilerJar(project, compilerClassName) + ?: throw IllegalStateException("Could not found Kotlin compiler jar. " + + "As a workaround you may specify path to compiler jar using " + + "\"$KOTLIN_COMPILER_JAR_PATH_PROPERTY\" system property") + + project.logger.kotlinInfo("Using kotlin compiler jar: $file") + file + } + +} + +fun findKotlinCompilerJar(project: Project, compilerClassName: String): File? { + fun Project.classpathJars(): Sequence = + buildscript.configurations.findByName("classpath")?.files?.asSequence() ?: emptySequence() + + val pathFromSysProperties = System.getProperty(KOTLIN_COMPILER_JAR_PATH_PROPERTY) + if (pathFromSysProperties != null) { + val fileFromSysProperties = File(pathFromSysProperties) + + if (fileFromSysProperties.exists()) return fileFromSysProperties + } + + val projectsToRoot = generateSequence(project) { if (it != it.rootProject) it.parent else null } + val classpathDeps = projectsToRoot.flatMap { it.classpathJars() } + val entryToFind = compilerClassName.replace(".", "/") + ".class" + val jarCandidate = classpathDeps.firstOrNull { it.hasEntry(entryToFind) } + + System.setProperty(KOTLIN_COMPILER_JAR_PATH_PROPERTY, jarCandidate?.absolutePath) + return jarCandidate +} + +private fun File.hasEntry(entryToFind: String): Boolean { + try { + val zip = ZipFile(this) + val enumeration = zip.entries() + + while (enumeration.hasMoreElements()) { + val entry = enumeration.nextElement() + if (entry.name.equals(entryToFind, ignoreCase = true)) return true + } + } + catch (e: Exception) { + return false + } + + return false +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinLogger.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinLogger.kt new file mode 100644 index 00000000000..f25bd5f82cd --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinLogger.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 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.compilerRunner + +import org.gradle.api.logging.Logger + +internal class GradleKotlinLogger(private val log: Logger) : KotlinLogger { + override fun debug(msg: String) { + log.debug(msg) + } + + override fun error(msg: String) { + log.error(msg) + } + + override fun info(msg: String) { + log.info(msg) + } + + override fun warn(msg: String) { + log.warn(msg) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt index c702243230c..9f375764096 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt @@ -68,6 +68,10 @@ internal fun Logger.kotlinDebug(message: String) { this.debug("[KOTLIN] $message") } +internal fun Logger.kotlinWarn(message: String) { + this.warn("[KOTLIN] $message") +} + internal inline fun Logger.kotlinDebug(message: () -> String) { if (isDebugEnabled) { kotlinDebug(message()) 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 a39fc735a3a..f243c91e632 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 @@ -38,6 +38,8 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.js.K2JSCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.compilerRunner.ArgumentUtils +import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner +import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.plugin.CoroutineSupport @@ -192,20 +194,20 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl override fun callCompiler(args: K2JVMCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { sourceRoots as SourceRoots.ForJvm - val outputDir = destinationDir + val messageCollector = GradleMessageCollector(logger) + args.classpathAsList = compileClasspath.toList() + args.destinationAsFile = destinationDir + val outputItemCollector = OutputItemsCollectorImpl() if (!incremental) { anyClassesCompiled = true - processCompilerExitCode(compileJvmNotIncrementally( - compiler, logger, - sourceRoots.kotlinSourceFiles, sourceRoots.javaSourceRoots, - compileClasspath, outputDir, args)) + val exitCode = GradleCompilerRunner(project).runJvmCompiler(sourceRoots.kotlinSourceFiles, sourceRoots.javaSourceRoots, args, messageCollector, outputItemCollector) + processCompilerExitCode(exitCode) return } logger.warn(USING_EXPERIMENTAL_INCREMENTAL_MESSAGE) val reporter = GradleIncReporter(project.rootProject.projectDir) - val messageCollector = GradleMessageCollector(logger) val compiler = IncrementalJvmCompilerRunner( taskBuildDirectory, sourceRoots.javaSourceRoots, @@ -216,8 +218,6 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl artifactDifferenceRegistryProvider, artifactFile ) - args.classpathAsList = classpath.toList() - args.destinationAsFile = destinationDir try { val exitCode = compiler.compile(sourceRoots.kotlinSourceFiles, args, messageCollector, { changedFiles }) processCompilerExitCode(exitCode)