diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java b/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java index 6f93cb03680..ab4343f24ee 100644 --- a/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java +++ b/jps/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/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsCompilerEnvironment.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsCompilerEnvironment.kt new file mode 100644 index 00000000000..dd35f5e1ad0 --- /dev/null +++ b/jps/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/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt new file mode 100644 index 00000000000..ad0760629b2 --- /dev/null +++ b/jps/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/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinLogger.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinLogger.kt new file mode 100644 index 00000000000..eadd0f387cc --- /dev/null +++ b/jps/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/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt index 21eb4ad0cc8..1fbc4e667b1 100644 --- a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt +++ b/jps/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