Refactoring: extract JPS specific code from compiler-runner

Original commit: 0be4299c8a
This commit is contained in:
Alexey Tsvetkov
2016-11-03 15:47:01 +03:00
parent 3c97d0650a
commit df2ad5c856
5 changed files with 249 additions and 15 deletions
@@ -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 {
@@ -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)
}
}
}
@@ -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<JpsCompilerEnvironment>() {
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<File>,
libraryFiles: List<String>,
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<String>, 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<String>,
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<File>, _libraryFiles: List<String>, 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!!
}
}
@@ -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)
}
}
@@ -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<File>, chunk: ModuleChunk, commonArguments: CommonCompilerArguments, context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>, environment: CompilerEnvironment,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>, environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>, incrementalCaches: Map<ModuleBuildTarget, IncrementalCacheImpl<*>>,
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<JavaSourceRootDescriptor, ModuleBuildTarget>,
environment: CompilerEnvironment,
environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>, 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