From 0be4299c8a4ba3015437ecbc9c8c0e8eb53e0e88 Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Thu, 3 Nov 2016 15:47:01 +0300 Subject: [PATCH] Refactoring: extract JPS specific code from compiler-runner --- .../compilerRunner/CompilerEnvironment.kt | 35 +-- .../compilerRunner/KotlinCompilerRunner.kt | 260 +++++++----------- .../compilerRunner/CompilerRunnerUtil.java | 4 +- .../compilerRunner/JpsCompilerEnvironment.kt | 42 +++ .../compilerRunner/JpsKotlinCompilerRunner.kt | 154 +++++++++++ .../kotlin/compilerRunner/JpsKotlinLogger.kt | 37 +++ .../kotlin/jps/build/KotlinBuilder.kt | 27 +- 7 files changed, 352 insertions(+), 207 deletions(-) create mode 100644 jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsCompilerEnvironment.kt create mode 100644 jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt create mode 100644 jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinLogger.kt diff --git a/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/CompilerEnvironment.kt b/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/CompilerEnvironment.kt index 119b0357974..ea8a8117539 100644 --- a/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/CompilerEnvironment.kt +++ b/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/CompilerEnvironment.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -16,39 +16,8 @@ package org.jetbrains.kotlin.compilerRunner -import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.config.Services -import org.jetbrains.kotlin.preloading.ClassCondition import org.jetbrains.kotlin.utils.KotlinPaths -import org.jetbrains.kotlin.utils.PathUtil -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation.Companion.NO_LOCATION -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR +open class CompilerEnvironment(val services: Services) -class CompilerEnvironment private constructor( - val kotlinPaths: KotlinPaths, - val classesToLoadByParent: ClassCondition, - val services: Services -) { - - fun success(): Boolean { - return kotlinPaths.homePath.exists() - } - - fun reportErrorsTo(messageCollector: MessageCollector) { - if (!kotlinPaths.homePath.exists()) { - messageCollector.report(ERROR, "Cannot find kotlinc home: " + kotlinPaths.homePath + ". Make sure plugin is properly installed, " + - "or specify " + PathUtil.JPS_KOTLIN_HOME_PROPERTY + " system property", NO_LOCATION) - } - } - - companion object { - fun getEnvironmentFor( - kotlinPaths: KotlinPaths, - classesToLoadByParent: ClassCondition, - compilerServices: Services - ): CompilerEnvironment { - return CompilerEnvironment(kotlinPaths, classesToLoadByParent, compilerServices) - } - } -} diff --git a/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt b/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt index 011550c9845..7dd8f376eb3 100644 --- a/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt +++ b/compiler/compiler-runner/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt @@ -16,173 +16,143 @@ package org.jetbrains.kotlin.compilerRunner -import org.jetbrains.jps.api.GlobalOptions import org.jetbrains.kotlin.cli.common.ExitCode -import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments -import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments -import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments -import org.jetbrains.kotlin.cli.common.arguments.mergeBeans import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil -import org.jetbrains.kotlin.config.CompilerSettings import org.jetbrains.kotlin.daemon.client.CompilationServices import org.jetbrains.kotlin.daemon.client.DaemonReportMessage import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient import org.jetbrains.kotlin.daemon.common.* -import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents import org.jetbrains.kotlin.progress.CompilationCanceledStatus -import java.io.* +import java.io.BufferedReader +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.StringReader import java.util.* import java.util.concurrent.TimeUnit -object KotlinCompilerRunner { - private val K2JVM_COMPILER = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler" - private val K2JS_COMPILER = "org.jetbrains.kotlin.cli.js.K2JSCompiler" - private val INTERNAL_ERROR = ExitCode.INTERNAL_ERROR.toString() +interface KotlinLogger { + fun error(msg: String) + fun warn(msg: String) + fun info(msg: String) + fun debug(msg: String) +} - fun runK2JvmCompiler( - commonArguments: CommonCompilerArguments, - k2jvmArguments: K2JVMCompilerArguments, - compilerSettings: CompilerSettings, - messageCollector: MessageCollector, - environment: CompilerEnvironment, - moduleFile: File, - collector: OutputItemsCollector) { - val arguments = mergeBeans(commonArguments, k2jvmArguments) - setupK2JvmArguments(moduleFile, arguments) +abstract class KotlinCompilerRunner { + protected val K2JVM_COMPILER = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler" + protected val K2JS_COMPILER = "org.jetbrains.kotlin.cli.js.K2JSCompiler" + protected val INTERNAL_ERROR = ExitCode.INTERNAL_ERROR.toString() - runCompiler(K2JVM_COMPILER, arguments, compilerSettings.additionalArguments, messageCollector, collector, environment) + protected abstract val log: KotlinLogger + + class DaemonConnection(val daemon: CompileService?, val sessionId: Int = CompileService.NO_SESSION) + + protected abstract fun getDaemonConnection(environment: Env, messageCollector: MessageCollector): DaemonConnection + + @Synchronized + protected fun newDaemonConnection(compilerPath: File, messageCollector: MessageCollector, flagFile: File): DaemonConnection { + val compilerId = CompilerId.makeCompilerId(compilerPath) + val daemonOptions = configureDaemonOptions() + val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = true, inheritAdditionalProperties = true) + + val daemonReportMessages = ArrayList() + + val profiler = if (daemonOptions.reportPerf) WallAndThreadAndMemoryTotalProfiler(withGC = false) else DummyProfiler() + + val connection = profiler.withMeasure(null) { + val daemon = KotlinCompilerClient.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, DaemonReportingTargets(null, daemonReportMessages), true, true) + DaemonConnection(daemon, daemon?.leaseCompileSession(flagFile.absolutePath)?.get() ?: CompileService.NO_SESSION) + } + + for (msg in daemonReportMessages) { + messageCollector.report(CompilerMessageSeverity.INFO, + (if (msg.category == DaemonReportCategory.EXCEPTION && connection.daemon == null) "Falling back to compilation without daemon due to error: " else "") + msg.message, + CompilerMessageLocation.NO_LOCATION) + } + + fun reportTotalAndThreadPerf(message: String, daemonOptions: DaemonOptions, messageCollector: MessageCollector, profiler: Profiler) { + if (daemonOptions.reportPerf) { + fun Long.ms() = TimeUnit.NANOSECONDS.toMillis(this) + val counters = profiler.getTotalCounters() + messageCollector.report(CompilerMessageSeverity.INFO, + "PERF: $message ${counters.time.ms()} ms, thread ${counters.threadTime.ms()}", + CompilerMessageLocation.NO_LOCATION) + } + } + + reportTotalAndThreadPerf("Daemon connect", daemonOptions, messageCollector, profiler) + return connection } - fun runK2JsCompiler( - commonArguments: CommonCompilerArguments, - k2jsArguments: K2JSCompilerArguments, - compilerSettings: CompilerSettings, - messageCollector: MessageCollector, - environment: CompilerEnvironment, - collector: OutputItemsCollector, - sourceFiles: Collection, - libraryFiles: List, - outputFile: File) { - val arguments = mergeBeans(commonArguments, k2jsArguments) - setupK2JsArguments(outputFile, sourceFiles, libraryFiles, arguments) - - runCompiler(K2JS_COMPILER, arguments, compilerSettings.additionalArguments, messageCollector, collector, environment) - } - - private fun processCompilerOutput( + protected fun processCompilerOutput( messageCollector: MessageCollector, collector: OutputItemsCollector, stream: ByteArrayOutputStream, - exitCode: String) { + exitCode: ExitCode + ) { val reader = BufferedReader(StringReader(stream.toString())) CompilerOutputParser.parseCompilerMessagesFromReader(messageCollector, reader, collector) - if (INTERNAL_ERROR == exitCode) { + if (ExitCode.INTERNAL_ERROR == exitCode) { reportInternalCompilerError(messageCollector) } } - private fun reportInternalCompilerError(messageCollector: MessageCollector) { + protected fun reportInternalCompilerError(messageCollector: MessageCollector): ExitCode { messageCollector.report(ERROR, "Compiler terminated with internal error", CompilerMessageLocation.NO_LOCATION) + return ExitCode.INTERNAL_ERROR } - private fun runCompiler( + protected fun runCompiler( compilerClassName: String, arguments: CommonCompilerArguments, additionalArguments: String, messageCollector: MessageCollector, collector: OutputItemsCollector, - environment: CompilerEnvironment) { - try { - messageCollector.report(INFO, "Using kotlin-home = " + environment.kotlinPaths.homePath, CompilerMessageLocation.NO_LOCATION) - + environment: Env): ExitCode { + return try { val argumentsList = ArgumentUtils.convertArgumentsToStringList(arguments) argumentsList.addAll(additionalArguments.split(" ")) val argsArray = argumentsList.toTypedArray() - - if (!tryCompileWithDaemon(compilerClassName, argsArray, environment, messageCollector, collector)) { - // otherwise fallback to in-process - KotlinBuilder.LOG.info("Compile in-process") - - val stream = ByteArrayOutputStream() - val out = PrintStream(stream) - - // the property should be set at least for parallel builds to avoid parallel building problems (racing between destroying and using environment) - // unfortunately it cannot be currently set by default globally, because it breaks many tests - // since there is no reliable way so far to detect running under tests, switching it on only for parallel builds - if (System.getProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "false").toBoolean()) - System.setProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true") - - val rc = CompilerRunnerUtil.invokeExecMethod(compilerClassName, argsArray, environment, messageCollector, out) - - // exec() returns an ExitCode object, class of which is loaded with a different class loader, - // so we take it's contents through reflection - processCompilerOutput(messageCollector, collector, stream, getReturnCodeFromObject(rc)) - } + doRunCompiler(compilerClassName, argsArray, environment, messageCollector, collector) } catch (e: Throwable) { MessageCollectorUtil.reportException(messageCollector, e) reportInternalCompilerError(messageCollector) } - } - internal class DaemonConnection(val daemon: CompileService?, val sessionId: Int = CompileService.NO_SESSION) + protected abstract fun doRunCompiler( + compilerClassName: String, + argsArray: Array, + environment: Env, + messageCollector: MessageCollector, + collector: OutputItemsCollector + ): ExitCode - internal object getDaemonConnection { - private @Volatile var connection: DaemonConnection? = null - - @Synchronized operator fun invoke(environment: CompilerEnvironment, messageCollector: MessageCollector): DaemonConnection { - if (connection == null) { - val libPath = CompilerRunnerUtil.getLibPath(environment.kotlinPaths, messageCollector) - val compilerId = CompilerId.makeCompilerId(File(libPath, "kotlin-compiler.jar")) - val daemonOptions = configureDaemonOptions() - val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = true, inheritAdditionalProperties = true) - - val daemonReportMessages = ArrayList() - - val profiler = if (daemonOptions.reportPerf) WallAndThreadAndMemoryTotalProfiler(withGC = false) else DummyProfiler() - - profiler.withMeasure(null) { - val daemon = KotlinCompilerClient.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, DaemonReportingTargets(null, daemonReportMessages), true, true) - connection = DaemonConnection(daemon, daemon?.leaseCompileSession(makeAutodeletingFlagFile("compiler-jps-session").absolutePath)?.get() ?: CompileService.NO_SESSION) - } - - for (msg in daemonReportMessages) { - messageCollector.report(CompilerMessageSeverity.INFO, - (if (msg.category == DaemonReportCategory.EXCEPTION && connection?.daemon == null) "Falling back to compilation without daemon due to error: " else "") + msg.message, - CompilerMessageLocation.NO_LOCATION) - } - - reportTotalAndThreadPerf("Daemon connect", daemonOptions, messageCollector, profiler) - } - return connection!! - } - } - - private fun tryCompileWithDaemon(compilerClassName: String, - argsArray: Array, - environment: CompilerEnvironment, - messageCollector: MessageCollector, - collector: OutputItemsCollector, - retryOnConnectionError: Boolean = true): Boolean { - - if (isDaemonEnabled()) { - - KotlinBuilder.LOG.debug("Try to connect to daemon") + /** + * Returns null if could not connect to daemon + */ + protected open fun compileWithDaemon( + compilerClassName: String, + argsArray: Array, + environment: Env, + messageCollector: MessageCollector, + collector: OutputItemsCollector, + retryOnConnectionError: Boolean = true + ): ExitCode? { + log.debug("Try to connect to daemon") val connection = getDaemonConnection(environment, messageCollector) if (connection.daemon != null) { - KotlinBuilder.LOG.info("Connected to daemon") + log.info("Connected to daemon") val compilerOut = ByteArrayOutputStream() val daemonOut = ByteArrayOutputStream() @@ -197,13 +167,13 @@ object KotlinCompilerRunner { else -> throw IllegalArgumentException("Unknown compiler type $compilerClassName") } - fun retryOrFalse(e: Exception): Boolean { + fun retryOrFalse(e: Exception): ExitCode? { if (retryOnConnectionError) { - KotlinBuilder.LOG.debug("retrying once on daemon connection error: ${e.message}") - return tryCompileWithDaemon(compilerClassName, argsArray, environment, messageCollector, collector, retryOnConnectionError = false) + log.debug("retrying once on daemon connection error: ${e.message}") + return compileWithDaemon(compilerClassName, argsArray, environment, messageCollector, collector, retryOnConnectionError = false) } - KotlinBuilder.LOG.info("daemon connection error: ${e.message}") - return false + log.info("daemon connection error: ${e.message}") + return null } val res: Int = try { @@ -216,52 +186,24 @@ object KotlinCompilerRunner { return retryOrFalse(e) } - processCompilerOutput(messageCollector, collector, compilerOut, res.toString()) + val exitCode = exitCodeFromProcessExitCode(res) + processCompilerOutput(messageCollector, collector, compilerOut, exitCode) BufferedReader(StringReader(daemonOut.toString())).forEachLine { messageCollector.report(CompilerMessageSeverity.INFO, it, CompilerMessageLocation.NO_LOCATION) } - return true + return exitCode } - KotlinBuilder.LOG.info("Daemon not found") - } - return false + log.info("Daemon not found") + return null } - private fun reportTotalAndThreadPerf(message: String, daemonOptions: DaemonOptions, messageCollector: MessageCollector, profiler: Profiler) { - if (daemonOptions.reportPerf) { - fun Long.ms() = TimeUnit.NANOSECONDS.toMillis(this) - val counters = profiler.getTotalCounters() - messageCollector.report(INFO, - "PERF: $message ${counters.time.ms()} ms, thread ${counters.threadTime.ms()}", - CompilerMessageLocation.NO_LOCATION) - } - } - - private fun getReturnCodeFromObject(rc: Any?): String { - when { - rc == null -> return INTERNAL_ERROR - ExitCode::class.java.name == rc.javaClass.name -> return rc.toString() - else -> throw IllegalStateException("Unexpected return: " + rc) - } - } - - private fun setupK2JvmArguments(moduleFile: File, settings: K2JVMCompilerArguments) { - with(settings) { - module = moduleFile.absolutePath - noStdlib = true - noReflect = true - noJdk = true - } - } - - private fun setupK2JsArguments( _outputFile: File, sourceFiles: Collection, _libraryFiles: List, settings: K2JSCompilerArguments) { - with(settings) { - noStdlib = true - freeArgs = sourceFiles.map { it.path } - outputFile = _outputFile.path - metaInfo = true - libraryFiles = _libraryFiles.toTypedArray() - } - } + protected fun exitCodeFromProcessExitCode(res: Int): ExitCode = + if (res == 0) { + ExitCode.OK + } + else { + ExitCode.COMPILATION_ERROR + } } + diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java index 6f93cb03680..ab4343f24ee 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java +++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java @@ -39,7 +39,7 @@ public class CompilerRunnerUtil { @NotNull private static synchronized ClassLoader getOrCreateClassLoader( - @NotNull CompilerEnvironment environment, + @NotNull JpsCompilerEnvironment environment, @NotNull File libPath ) throws IOException { ClassLoader classLoader = ourClassLoaderRef.get(); @@ -73,7 +73,7 @@ public class CompilerRunnerUtil { public static Object invokeExecMethod( @NotNull String compilerClassName, @NotNull String[] arguments, - @NotNull CompilerEnvironment environment, + @NotNull JpsCompilerEnvironment environment, @NotNull MessageCollector messageCollector, @NotNull PrintStream out ) throws Exception { diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsCompilerEnvironment.kt b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsCompilerEnvironment.kt new file mode 100644 index 00000000000..dd35f5e1ad0 --- /dev/null +++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsCompilerEnvironment.kt @@ -0,0 +1,42 @@ +/* + * 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.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.config.Services +import org.jetbrains.kotlin.preloading.ClassCondition +import org.jetbrains.kotlin.utils.KotlinPaths +import org.jetbrains.kotlin.utils.PathUtil + +class JpsCompilerEnvironment( + val kotlinPaths: KotlinPaths, + services: Services, + val classesToLoadByParent: ClassCondition +) : CompilerEnvironment(services) { + fun success(): Boolean { + return kotlinPaths.homePath.exists() + } + + fun reportErrorsTo(messageCollector: MessageCollector) { + if (!kotlinPaths.homePath.exists()) { + messageCollector.report(CompilerMessageSeverity.ERROR, "Cannot find kotlinc home: " + kotlinPaths.homePath + ". Make sure plugin is properly installed, " + + "or specify " + PathUtil.JPS_KOTLIN_HOME_PROPERTY + " system property", CompilerMessageLocation.NO_LOCATION) + } + } +} \ No newline at end of file diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt new file mode 100644 index 00000000000..ad0760629b2 --- /dev/null +++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt @@ -0,0 +1,154 @@ +/* + * 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.jetbrains.jps.api.GlobalOptions +import org.jetbrains.kotlin.cli.common.ExitCode +import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY +import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.mergeBeans +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.config.CompilerSettings +import org.jetbrains.kotlin.daemon.common.isDaemonEnabled +import org.jetbrains.kotlin.jps.build.KotlinBuilder +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream + +class JpsKotlinCompilerRunner : KotlinCompilerRunner() { + override val log: KotlinLogger = JpsKotlinLogger(KotlinBuilder.LOG) + + companion object { + private @Volatile var jpsDaemonConnection: DaemonConnection? = null + } + + fun runK2JvmCompiler( + commonArguments: CommonCompilerArguments, + k2jvmArguments: K2JVMCompilerArguments, + compilerSettings: CompilerSettings, + messageCollector: MessageCollector, + environment: JpsCompilerEnvironment, + moduleFile: File, + collector: OutputItemsCollector + ) { + val arguments = mergeBeans(commonArguments, k2jvmArguments) + setupK2JvmArguments(moduleFile, arguments) + + runCompiler(K2JVM_COMPILER, arguments, compilerSettings.additionalArguments, messageCollector, collector, environment) + } + + fun runK2JsCompiler( + commonArguments: CommonCompilerArguments, + k2jsArguments: K2JSCompilerArguments, + compilerSettings: CompilerSettings, + messageCollector: MessageCollector, + environment: JpsCompilerEnvironment, + collector: OutputItemsCollector, + sourceFiles: Collection, + libraryFiles: List, + outputFile: File + ) { + val arguments = mergeBeans(commonArguments, k2jsArguments) + setupK2JsArguments(outputFile, sourceFiles, libraryFiles, arguments) + + runCompiler(K2JS_COMPILER, arguments, compilerSettings.additionalArguments, messageCollector, collector, environment) + } + + override fun doRunCompiler(compilerClassName: String, argsArray: Array, environment: JpsCompilerEnvironment, messageCollector: MessageCollector, collector: OutputItemsCollector): ExitCode { + messageCollector.report(CompilerMessageSeverity.INFO, "Using kotlin-home = " + environment.kotlinPaths.homePath, CompilerMessageLocation.NO_LOCATION) + + return if (isDaemonEnabled()) { + val daemonExitCode = compileWithDaemon(compilerClassName, argsArray, environment, messageCollector, collector) + daemonExitCode ?: fallbackCompileStrategy(argsArray, collector, compilerClassName, environment, messageCollector) + } + else { + fallbackCompileStrategy(argsArray, collector, compilerClassName, environment, messageCollector) + } + } + + private fun fallbackCompileStrategy( + argsArray: Array, + collector: OutputItemsCollector, + compilerClassName: String, + environment: JpsCompilerEnvironment, + messageCollector: MessageCollector + ): ExitCode { + // otherwise fallback to in-process + log.info("Compile in-process") + + val stream = ByteArrayOutputStream() + val out = PrintStream(stream) + + // the property should be set at least for parallel builds to avoid parallel building problems (racing between destroying and using environment) + // unfortunately it cannot be currently set by default globally, because it breaks many tests + // since there is no reliable way so far to detect running under tests, switching it on only for parallel builds + if (System.getProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "false").toBoolean()) + System.setProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true") + + val rc = CompilerRunnerUtil.invokeExecMethod(compilerClassName, argsArray, environment, messageCollector, out) + + // exec() returns an ExitCode object, class of which is loaded with a different class loader, + // so we take it's contents through reflection + val exitCode = ExitCode.valueOf(getReturnCodeFromObject(rc)) + processCompilerOutput(messageCollector, collector, stream, exitCode) + return exitCode + } + + private fun setupK2JvmArguments(moduleFile: File, settings: K2JVMCompilerArguments) { + with(settings) { + module = moduleFile.absolutePath + noStdlib = true + noReflect = true + noJdk = true + } + } + + private fun setupK2JsArguments( _outputFile: File, sourceFiles: Collection, _libraryFiles: List, settings: K2JSCompilerArguments) { + with(settings) { + noStdlib = true + freeArgs = sourceFiles.map { it.path } + outputFile = _outputFile.path + metaInfo = true + libraryFiles = _libraryFiles.toTypedArray() + } + } + + private fun getReturnCodeFromObject(rc: Any?): String { + when { + rc == null -> return INTERNAL_ERROR + ExitCode::class.java.name == rc.javaClass.name -> return rc.toString() + else -> throw IllegalStateException("Unexpected return: " + rc) + } + } + + @Synchronized + override fun getDaemonConnection(environment: JpsCompilerEnvironment, messageCollector: MessageCollector): DaemonConnection { + if (jpsDaemonConnection == null) { + val libPath = CompilerRunnerUtil.getLibPath(environment.kotlinPaths, messageCollector) + val compilerPath = File(libPath, "kotlin-compiler.jar") + val flagFile = File.createTempFile("kotlin-compiler-jps-session-", "-is-running").apply { + deleteOnExit() + } + newDaemonConnection(compilerPath, messageCollector, flagFile) + } + return jpsDaemonConnection!! + } +} \ No newline at end of file diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinLogger.kt b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinLogger.kt new file mode 100644 index 00000000000..eadd0f387cc --- /dev/null +++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinLogger.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 com.intellij.openapi.diagnostic.Logger + +internal class JpsKotlinLogger(private val log: Logger) : KotlinLogger { + override fun error(msg: String) { + log.error(msg) + } + + override fun warn(msg: String) { + log.warn(msg) + } + + override fun info(msg: String) { + log.info(msg) + } + + override fun debug(msg: String) { + log.debug(msg) + } +} \ No newline at end of file diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt index 21eb4ad0cc8..1fbc4e667b1 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt @@ -46,9 +46,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.* import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil -import org.jetbrains.kotlin.compilerRunner.CompilerEnvironment -import org.jetbrains.kotlin.compilerRunner.KotlinCompilerRunner -import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl +import org.jetbrains.kotlin.compilerRunner.* import org.jetbrains.kotlin.config.CompilerRunnerConstants import org.jetbrains.kotlin.config.CompilerRunnerConstants.INTERNAL_ERROR_PREFIX import org.jetbrains.kotlin.config.IncrementalCompilation @@ -63,6 +61,7 @@ import org.jetbrains.kotlin.jps.incremental.* import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents import org.jetbrains.kotlin.modules.TargetId +import org.jetbrains.kotlin.preloading.ClassCondition import org.jetbrains.kotlin.progress.CompilationCanceledException import org.jetbrains.kotlin.progress.CompilationCanceledStatus import org.jetbrains.kotlin.utils.JsLibraryUtils @@ -368,7 +367,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { private fun doCompileModuleChunk( allCompiledFiles: MutableSet, chunk: ModuleChunk, commonArguments: CommonCompilerArguments, context: CompileContext, - dirtyFilesHolder: DirtyFilesHolder, environment: CompilerEnvironment, + dirtyFilesHolder: DirtyFilesHolder, environment: JpsCompilerEnvironment, filesToCompile: MultiMap, incrementalCaches: Map>, messageCollector: MessageCollectorAdapter, project: JpsProject ): OutputItemsCollectorImpl? { @@ -413,7 +412,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { lookupTracker: LookupTracker, sourceRetentionAnnotationHandler: SourceRetentionAnnotationHandler?, context: CompileContext - ): CompilerEnvironment { + ): JpsCompilerEnvironment { val compilerServices = with(Services.Builder()) { register(IncrementalCompilationComponents::class.java, IncrementalCompilationComponentsImpl(incrementalCaches.mapKeys { TargetId(it.key) }, @@ -429,9 +428,10 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { build() } - return CompilerEnvironment.getEnvironmentFor( + return JpsCompilerEnvironment( PathUtil.getKotlinPathsForJpsPluginOrJpsTests(), - { className -> + compilerServices, + ClassCondition { className -> className.startsWith("org.jetbrains.kotlin.load.kotlin.incremental.components.") || className.startsWith("org.jetbrains.kotlin.incremental.components.") || className == "org.jetbrains.kotlin.config.Services" @@ -439,8 +439,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { || className == "org.jetbrains.kotlin.progress.CompilationCanceledStatus" || className == "org.jetbrains.kotlin.progress.CompilationCanceledException" || className == "org.jetbrains.kotlin.modules.TargetId" - }, - compilerServices + } ) } @@ -591,7 +590,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { // if null is returned, nothing was done private fun compileToJs(chunk: ModuleChunk, commonArguments: CommonCompilerArguments, - environment: CompilerEnvironment, + environment: JpsCompilerEnvironment, messageCollector: MessageCollectorAdapter, project: JpsProject ): OutputItemsCollectorImpl? { @@ -625,7 +624,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(representativeModule) val k2JsArguments = JpsKotlinCompilerSettings.getK2JsCompilerArguments(representativeModule) - KotlinCompilerRunner.runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, messageCollector, environment, outputItemCollector, sourceFiles, libraryFiles, outputFile) + val compilerRunner = JpsKotlinCompilerRunner() + compilerRunner.runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, messageCollector, environment, outputItemCollector, sourceFiles, libraryFiles, outputFile) return outputItemCollector } @@ -647,7 +647,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { commonArguments: CommonCompilerArguments, context: CompileContext, dirtyFilesHolder: DirtyFilesHolder, - environment: CompilerEnvironment, + environment: JpsCompilerEnvironment, filesToCompile: MultiMap, messageCollector: MessageCollectorAdapter ): OutputItemsCollectorImpl? { val outputItemCollector = OutputItemsCollectorImpl() @@ -691,7 +691,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { + (if (totalRemovedFiles == 0) "" else " ($totalRemovedFiles removed files)") + " in " + filesToCompile.keySet().joinToString { it.presentableName }) - KotlinCompilerRunner.runK2JvmCompiler(commonArguments, k2JvmArguments, compilerSettings, messageCollector, environment, moduleFile, outputItemCollector) + val compilerRunner = JpsKotlinCompilerRunner() + compilerRunner.runK2JvmCompiler(commonArguments, k2JvmArguments, compilerSettings, messageCollector, environment, moduleFile, outputItemCollector) moduleFile.delete() return outputItemCollector