Use Workers API for NMPP tasks

This change enables parallel execution of compile tasks in NMPP projects
within a subproject.

    #KT-28155 In Progress
This commit is contained in:
Alexey Tsvetkov
2018-10-25 00:30:15 +03:00
parent ba5795519b
commit 37dfe2b608
22 changed files with 700 additions and 468 deletions
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.compilerRunner
object KotlinCompilerClass {
const val JVM = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler"
const val JS = "org.jetbrains.kotlin.cli.js.K2JSCompiler"
const val METADATA = "org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler"
}
@@ -43,115 +43,28 @@ interface KotlinLogger {
} }
abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> { abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> {
protected val K2JVM_COMPILER = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler"
protected val K2JS_COMPILER = "org.jetbrains.kotlin.cli.js.K2JSCompiler"
protected val K2METADATA_COMPILER = "org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler"
protected val INTERNAL_ERROR = ExitCode.INTERNAL_ERROR.toString() protected val INTERNAL_ERROR = ExitCode.INTERNAL_ERROR.toString()
protected abstract val log: KotlinLogger protected abstract val log: KotlinLogger
protected abstract fun getDaemonConnection(environment: Env): CompileServiceSession? protected open fun runCompiler(
compilerClassName: String,
@Synchronized compilerArgs: CommonCompilerArguments,
protected fun newDaemonConnection( environment: Env
compilerId: CompilerId,
clientAliveFlagFile: File,
sessionAliveFlagFile: File,
environment: Env,
daemonOptions: DaemonOptions = configureDaemonOptions(),
additionalJvmParams: Array<String> = arrayOf()
): CompileServiceSession? {
val daemonJVMOptions = configureDaemonJVMOptions(
additionalParams = *additionalJvmParams,
inheritMemoryLimits = true,
inheritOtherJvmOptions = false,
inheritAdditionalProperties = true
)
val daemonReportMessages = ArrayList<DaemonReportMessage>()
val daemonReportingTargets = DaemonReportingTargets(messages = daemonReportMessages)
val profiler = if (daemonOptions.reportPerf) WallAndThreadAndMemoryTotalProfiler(withGC = false) else DummyProfiler()
val connection = profiler.withMeasure(null) {
KotlinCompilerClient.connectAndLease(compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
daemonReportingTargets,
autostart = true,
leaseSession = true,
sessionAliveFlagFile = sessionAliveFlagFile)
}
if (connection == null || log.isDebugEnabled) {
for (message in daemonReportMessages) {
val severity = when(message.category) {
DaemonReportCategory.DEBUG -> CompilerMessageSeverity.INFO
DaemonReportCategory.INFO -> CompilerMessageSeverity.INFO
DaemonReportCategory.EXCEPTION -> CompilerMessageSeverity.EXCEPTION
}
environment.messageCollector.report(severity, message.message)
}
}
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()}")
}
}
reportTotalAndThreadPerf("Daemon connect", daemonOptions, environment.messageCollector, profiler)
return connection
}
protected fun processCompilerOutput(
environment: Env,
stream: ByteArrayOutputStream,
exitCode: ExitCode?
) { ) {
val reader = BufferedReader(StringReader(stream.toString())) try {
CompilerOutputParser.parseCompilerMessagesFromReader(environment.messageCollector, reader, environment.outputItemsCollector)
if (ExitCode.INTERNAL_ERROR == exitCode) {
reportInternalCompilerError(environment.messageCollector)
}
}
protected fun reportInternalCompilerError(messageCollector: MessageCollector): ExitCode {
messageCollector.report(CompilerMessageSeverity.ERROR, "Compiler terminated with internal error")
return ExitCode.INTERNAL_ERROR
}
protected fun runCompiler(
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env): ExitCode {
return try {
compileWithDaemonOrFallback(compilerClassName, compilerArgs, environment) compileWithDaemonOrFallback(compilerClassName, compilerArgs, environment)
} } catch (e: Throwable) {
catch (e: Throwable) {
MessageCollectorUtil.reportException(environment.messageCollector, e) MessageCollectorUtil.reportException(environment.messageCollector, e)
reportInternalCompilerError(environment.messageCollector) reportInternalCompilerError(environment.messageCollector)
} }
} }
protected abstract fun compileWithDaemonOrFallback( protected abstract fun compileWithDaemonOrFallback(
compilerClassName: String, compilerClassName: String,
compilerArgs: CommonCompilerArguments, compilerArgs: CommonCompilerArguments,
environment: Env environment: Env
): ExitCode )
/**
* Returns null if could not connect to daemon
*/
protected abstract fun compileWithDaemon(
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env
): ExitCode?
protected fun exitCodeFromProcessExitCode(code: Int): ExitCode = Companion.exitCodeFromProcessExitCode(log, code) protected fun exitCodeFromProcessExitCode(code: Int): ExitCode = Companion.exitCodeFromProcessExitCode(log, code)
@@ -163,6 +76,69 @@ abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> {
log.debug("Could not find exit code by value: $code") log.debug("Could not find exit code by value: $code")
return if (code == 0) ExitCode.OK else ExitCode.COMPILATION_ERROR return if (code == 0) ExitCode.OK else ExitCode.COMPILATION_ERROR
} }
@Synchronized
@JvmStatic
protected fun newDaemonConnection(
compilerId: CompilerId,
clientAliveFlagFile: File,
sessionAliveFlagFile: File,
messageCollector: MessageCollector,
isDebugEnabled: Boolean,
daemonOptions: DaemonOptions = configureDaemonOptions(),
additionalJvmParams: Array<String> = arrayOf()
): CompileServiceSession? {
val daemonJVMOptions = configureDaemonJVMOptions(
additionalParams = *additionalJvmParams,
inheritMemoryLimits = true,
inheritOtherJvmOptions = false,
inheritAdditionalProperties = true
)
val daemonReportMessages = ArrayList<DaemonReportMessage>()
val daemonReportingTargets = DaemonReportingTargets(messages = daemonReportMessages)
val profiler = if (daemonOptions.reportPerf) WallAndThreadAndMemoryTotalProfiler(withGC = false) else DummyProfiler()
val connection = profiler.withMeasure(null) {
KotlinCompilerClient.connectAndLease(
compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
daemonReportingTargets,
autostart = true,
leaseSession = true,
sessionAliveFlagFile = sessionAliveFlagFile
)
}
if (connection == null || isDebugEnabled) {
for (message in daemonReportMessages) {
val severity = when (message.category) {
DaemonReportCategory.DEBUG -> CompilerMessageSeverity.INFO
DaemonReportCategory.INFO -> CompilerMessageSeverity.INFO
DaemonReportCategory.EXCEPTION -> CompilerMessageSeverity.EXCEPTION
}
messageCollector.report(severity, message.message)
}
}
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()}"
)
}
}
reportTotalAndThreadPerf("Daemon connect", daemonOptions, MessageCollector.NONE, profiler)
return connection
}
} }
} }
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.compilerRunner
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.StringReader
fun reportInternalCompilerError(messageCollector: MessageCollector) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Compiler terminated with internal error")
}
fun processCompilerOutput(
environment: CompilerEnvironment,
stream: ByteArrayOutputStream,
exitCode: ExitCode?
) {
processCompilerOutput(environment.messageCollector, environment.outputItemsCollector, stream, exitCode)
}
fun processCompilerOutput(
messageCollector: MessageCollector,
outputItemsCollector: OutputItemsCollector,
stream: ByteArrayOutputStream,
exitCode: ExitCode?
) {
val reader = BufferedReader(StringReader(stream.toString()))
CompilerOutputParser.parseCompilerMessagesFromReader(messageCollector, reader, outputItemsCollector)
if (ExitCode.INTERNAL_ERROR == exitCode) {
reportInternalCompilerError(messageCollector)
}
}
@@ -17,8 +17,13 @@
package org.jetbrains.kotlin.incremental package org.jetbrains.kotlin.incremental
import java.io.File import java.io.File
import java.io.Serializable
sealed class ChangedFiles { sealed class ChangedFiles : Serializable {
class Known(val modified: List<File>, val removed: List<File>) : ChangedFiles() class Known(val modified: List<File>, val removed: List<File>) : ChangedFiles()
class Unknown : ChangedFiles() class Unknown : ChangedFiles()
companion object {
const val serialVersionUID: Long = 0
}
} }
@@ -105,7 +105,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
arguments.destination = arguments.destination ?: destination arguments.destination = arguments.destination ?: destination
withCompilerSettings(compilerSettings) { withCompilerSettings(compilerSettings) {
runCompiler(K2METADATA_COMPILER, arguments, environment) runCompiler(KotlinCompilerClass.METADATA, arguments, environment)
} }
} }
@@ -119,7 +119,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
val arguments = mergeBeans(commonArguments, XmlSerializerUtil.createCopy(k2jvmArguments)) val arguments = mergeBeans(commonArguments, XmlSerializerUtil.createCopy(k2jvmArguments))
setupK2JvmArguments(moduleFile, arguments) setupK2JvmArguments(moduleFile, arguments)
withCompilerSettings(compilerSettings) { withCompilerSettings(compilerSettings) {
runCompiler(K2JVM_COMPILER, arguments, environment) runCompiler(KotlinCompilerClass.JVM, arguments, environment)
} }
} }
@@ -149,7 +149,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
log.debug("K2JS: arguments after setup" + ArgumentUtils.convertArgumentsToStringList(arguments)) log.debug("K2JS: arguments after setup" + ArgumentUtils.convertArgumentsToStringList(arguments))
withCompilerSettings(compilerSettings) { withCompilerSettings(compilerSettings) {
runCompiler(K2JS_COMPILER, arguments, environment) runCompiler(KotlinCompilerClass.JS, arguments, environment)
} }
} }
@@ -157,24 +157,24 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
compilerClassName: String, compilerClassName: String,
compilerArgs: CommonCompilerArguments, compilerArgs: CommonCompilerArguments,
environment: JpsCompilerEnvironment environment: JpsCompilerEnvironment
): ExitCode { ) {
log.debug("Using kotlin-home = " + environment.kotlinPaths.homePath) log.debug("Using kotlin-home = " + environment.kotlinPaths.homePath)
return withDaemonOrFallback( withDaemonOrFallback(
withDaemon = { compileWithDaemon(compilerClassName, compilerArgs, environment) }, withDaemon = { compileWithDaemon(compilerClassName, compilerArgs, environment) },
fallback = { fallbackCompileStrategy(compilerArgs, compilerClassName, environment) } fallback = { fallbackCompileStrategy(compilerArgs, compilerClassName, environment) }
) )
} }
override fun compileWithDaemon( private fun compileWithDaemon(
compilerClassName: String, compilerClassName: String,
compilerArgs: CommonCompilerArguments, compilerArgs: CommonCompilerArguments,
environment: JpsCompilerEnvironment environment: JpsCompilerEnvironment
): ExitCode? { ) {
val targetPlatform = when (compilerClassName) { val targetPlatform = when (compilerClassName) {
K2JVM_COMPILER -> CompileService.TargetPlatform.JVM KotlinCompilerClass.JVM -> CompileService.TargetPlatform.JVM
K2JS_COMPILER -> CompileService.TargetPlatform.JS KotlinCompilerClass.JS -> CompileService.TargetPlatform.JS
K2METADATA_COMPILER -> CompileService.TargetPlatform.METADATA KotlinCompilerClass.METADATA -> CompileService.TargetPlatform.METADATA
else -> throw IllegalArgumentException("Unknown compiler type $compilerClassName") else -> throw IllegalArgumentException("Unknown compiler type $compilerClassName")
} }
val compilerMode = CompilerMode.JPS_COMPILER val compilerMode = CompilerMode.JPS_COMPILER
@@ -186,7 +186,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
reportSeverity(verbose), reportSeverity(verbose),
requestedCompilationResults = emptyArray() requestedCompilationResults = emptyArray()
) )
return doWithDaemon(environment) { sessionId, daemon -> doWithDaemon(environment) { sessionId, daemon ->
environment.withProgressReporter { progress -> environment.withProgressReporter { progress ->
progress.compilationStarted() progress.compilationStarted()
daemon.compile( daemon.compile(
@@ -197,7 +197,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
null null
) )
} }
}?.let { exitCodeFromProcessExitCode(it) } }
} }
private fun <T> withDaemonOrFallback(withDaemon: () -> T?, fallback: () -> T): T = private fun <T> withDaemonOrFallback(withDaemon: () -> T?, fallback: () -> T): T =
@@ -253,7 +253,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
compilerArgs: CommonCompilerArguments, compilerArgs: CommonCompilerArguments,
compilerClassName: String, compilerClassName: String,
environment: JpsCompilerEnvironment environment: JpsCompilerEnvironment
): ExitCode { ) {
if ("true" == System.getProperty("kotlin.jps.tests") && "true" == System.getProperty(FAIL_ON_FALLBACK_PROPERTY)) { if ("true" == System.getProperty("kotlin.jps.tests") && "true" == System.getProperty(FAIL_ON_FALLBACK_PROPERTY)) {
error("Fallback strategy is disabled in tests!") error("Fallback strategy is disabled in tests!")
} }
@@ -278,8 +278,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
// exec() returns an ExitCode object, class of which is loaded with a different class loader, // exec() returns an ExitCode object, class of which is loaded with a different class loader,
// so we take it's contents through reflection // so we take it's contents through reflection
val exitCode = ExitCode.valueOf(getReturnCodeFromObject(rc)) val exitCode = ExitCode.valueOf(getReturnCodeFromObject(rc))
processCompilerOutput(environment, stream, exitCode) processCompilerOutput(environment.messageCollector, environment.outputItemsCollector, stream, exitCode)
return exitCode
} }
private fun setupK2JvmArguments(moduleFile: File, settings: K2JVMCompilerArguments) { private fun setupK2JvmArguments(moduleFile: File, settings: K2JVMCompilerArguments) {
@@ -317,7 +316,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
else -> throw IllegalStateException("Unexpected return: " + rc) else -> throw IllegalStateException("Unexpected return: " + rc)
} }
override fun getDaemonConnection(environment: JpsCompilerEnvironment): CompileServiceSession? = private fun getDaemonConnection(environment: JpsCompilerEnvironment): CompileServiceSession? =
getOrCreateDaemonConnection { getOrCreateDaemonConnection {
environment.progressReporter.progress("connecting to daemon") environment.progressReporter.progress("connecting to daemon")
val libPath = CompilerRunnerUtil.getLibPath(environment.kotlinPaths, environment.messageCollector) val libPath = CompilerRunnerUtil.getLibPath(environment.kotlinPaths, environment.messageCollector)
@@ -334,7 +333,15 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
environment.withProgressReporter { progress -> environment.withProgressReporter { progress ->
progress.progress("connecting to daemon") progress.progress("connecting to daemon")
newDaemonConnection(compilerId, clientFlagFile, sessionFlagFile, environment, daemonOptions, additionalJvmParams.toTypedArray()) newDaemonConnection(
compilerId,
clientFlagFile,
sessionFlagFile,
environment.messageCollector,
log.isDebugEnabled,
daemonOptions,
additionalJvmParams.toTypedArray()
)
} }
} }
} }
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project
import org.jetbrains.kotlin.daemon.common.CompilationResultCategory import org.jetbrains.kotlin.daemon.common.CompilationResultCategory
import org.jetbrains.kotlin.daemon.common.CompilationResults import org.jetbrains.kotlin.daemon.common.CompilationResults
import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface
@@ -8,22 +7,20 @@ import org.jetbrains.kotlin.daemon.common.SOCKET_ANY_FREE_PORT
import org.jetbrains.kotlin.daemon.common.CompileIterationResult import org.jetbrains.kotlin.daemon.common.CompileIterationResult
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
import org.jetbrains.kotlin.gradle.utils.pathsAsStringRelativeTo import org.jetbrains.kotlin.gradle.utils.pathsAsStringRelativeTo
import java.io.File
import java.io.Serializable import java.io.Serializable
import java.rmi.RemoteException import java.rmi.RemoteException
import java.rmi.server.UnicastRemoteObject import java.rmi.server.UnicastRemoteObject
internal class GradleCompilationResults( internal class GradleCompilationResults(
project: Project private val log: KotlinLogger,
private val projectRootFile: File
) : CompilationResults, ) : CompilationResults,
UnicastRemoteObject( UnicastRemoteObject(
SOCKET_ANY_FREE_PORT, SOCKET_ANY_FREE_PORT,
LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.clientLoopbackSocketFactory,
LoopbackNetworkInterface.serverLoopbackSocketFactory LoopbackNetworkInterface.serverLoopbackSocketFactory
) { ) {
private val log = project.logger
private val projectRootFile = project.rootProject.projectDir
@Throws(RemoteException::class) @Throws(RemoteException::class)
override fun add(compilationResultCategory: Int, value: Serializable) { override fun add(compilationResultCategory: Int, value: Serializable) {
if (compilationResultCategory == CompilationResultCategory.IC_COMPILE_ITERATION.code) { if (compilationResultCategory == CompilationResultCategory.IC_COMPILE_ITERATION.code) {
@@ -5,38 +5,20 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings
import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector
import org.jetbrains.kotlin.gradle.tasks.findToolsJar import org.jetbrains.kotlin.gradle.tasks.findToolsJar
import org.jetbrains.kotlin.incremental.ChangedFiles
import java.io.File import java.io.File
import java.net.URL
internal open class GradleCompilerEnvironment( internal class GradleCompilerEnvironment(
val compilerClasspath: List<File>, val compilerClasspath: List<File>,
messageCollector: GradleMessageCollector, messageCollector: GradleMessageCollector,
outputItemsCollector: OutputItemsCollector, outputItemsCollector: OutputItemsCollector,
val compilerArgs: CommonCompilerArguments val incrementalCompilationEnvironment: IncrementalCompilationEnvironment? = null
) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) { ) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) {
val toolsJar: File? by lazy { findToolsJar() } val toolsJar: File? by lazy { findToolsJar() }
val compilerFullClasspath: List<File> val compilerFullClasspath: List<File>
get() = (compilerClasspath + toolsJar).filterNotNull() get() = (compilerClasspath + toolsJar).filterNotNull()
val compilerClasspathURLs: List<URL>
get() = compilerFullClasspath.map { it.toURI().toURL() }
} }
internal class GradleIncrementalCompilerEnvironment(
compilerClasspath: List<File>,
val changedFiles: ChangedFiles,
val workingDir: File,
messageCollector: GradleMessageCollector,
outputItemsCollector: OutputItemsCollector,
compilerArgs: CommonCompilerArguments,
val usePreciseJavaTracking: Boolean = false,
val localStateDirs: List<File> = emptyList(),
val multiModuleICSettings: MultiModuleICSettings
) : GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemsCollector, compilerArgs)
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project
import org.gradle.workers.IsolationMode
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
internal class GradleCompilerRunnerWithWorkers(
project: Project,
private val workersExecutor: WorkerExecutor
) : GradleCompilerRunner(project) {
override fun compileWithDaemonOrFallback(
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: GradleCompilerEnvironment
) {
workersExecutor.submit(KotlinCompilerRunnable::class.java) { config ->
config.isolationMode = IsolationMode.NONE
config.params(
ProjectFilesForCompilation(project),
environment.compilerFullClasspath,
compilerClassName,
ArgumentUtils.convertArgumentsToStringList(compilerArgs).toTypedArray(),
compilerArgs.verbose,
environment.incrementalCompilationEnvironment,
buildModulesInfo(project.gradle)
)
}
}
}
@@ -5,8 +5,6 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.daemon.client.reportFromDaemon import org.jetbrains.kotlin.daemon.client.reportFromDaemon
import org.jetbrains.kotlin.daemon.common.* import org.jetbrains.kotlin.daemon.common.*
@@ -16,15 +14,13 @@ import java.rmi.Remote
import java.rmi.server.UnicastRemoteObject import java.rmi.server.UnicastRemoteObject
internal open class GradleCompilerServicesFacadeImpl( internal open class GradleCompilerServicesFacadeImpl(
project: Project, private val log: KotlinLogger,
val compilerMessageCollector: MessageCollector, private val compilerMessageCollector: MessageCollector,
port: Int = SOCKET_ANY_FREE_PORT port: Int = SOCKET_ANY_FREE_PORT
) : UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory), ) : UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory),
CompilerServicesFacadeBase, CompilerServicesFacadeBase,
Remote { Remote {
protected val log: Logger = project.logger
override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) { override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
when (ReportCategory.fromCode(category)) { when (ReportCategory.fromCode(category)) {
ReportCategory.IC_MESSAGE -> { ReportCategory.IC_MESSAGE -> {
@@ -47,8 +43,8 @@ internal open class GradleCompilerServicesFacadeImpl(
} }
internal class GradleIncrementalCompilerServicesFacadeImpl( internal class GradleIncrementalCompilerServicesFacadeImpl(
project: Project, log: KotlinLogger,
environment: GradleIncrementalCompilerEnvironment, messageCollector: MessageCollector,
port: Int = SOCKET_ANY_FREE_PORT port: Int = SOCKET_ANY_FREE_PORT
) : GradleCompilerServicesFacadeImpl(project, environment.messageCollector, port), ) : GradleCompilerServicesFacadeImpl(log, messageCollector, port),
IncrementalCompilerServicesFacade IncrementalCompilerServicesFacade
@@ -21,31 +21,20 @@ import org.gradle.api.invocation.Gradle
import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.jvm.tasks.Jar import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.build.JvmSourceRoot import org.jetbrains.kotlin.build.JvmSourceRoot
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
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.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.daemon.client.CompileServiceSession import org.jetbrains.kotlin.daemon.client.CompileServiceSession
import org.jetbrains.kotlin.daemon.common.* import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.InspectClassesForMultiModuleIC
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.gradle.utils.newTmpFile import org.jetbrains.kotlin.gradle.utils.newTmpFile
import org.jetbrains.kotlin.gradle.utils.relativeToRoot import org.jetbrains.kotlin.gradle.utils.relativeToRoot
import org.jetbrains.kotlin.incremental.* import org.jetbrains.kotlin.incremental.*
import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.PrintStream
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.net.URLClassLoader
import java.rmi.RemoteException
internal const val KOTLIN_COMPILER_EXECUTION_STRATEGY_PROPERTY = "kotlin.compiler.execution.strategy" internal const val KOTLIN_COMPILER_EXECUTION_STRATEGY_PROPERTY = "kotlin.compiler.execution.strategy"
internal const val DAEMON_EXECUTION_STRATEGY = "daemon" internal const val DAEMON_EXECUTION_STRATEGY = "daemon"
@@ -58,37 +47,12 @@ const val EXISTING_SESSION_FILE_PREFIX = "Existing session-is-alive flag file: "
const val DELETED_SESSION_FILE_PREFIX = "Deleted session-is-alive flag file: " const val DELETED_SESSION_FILE_PREFIX = "Deleted session-is-alive flag file: "
const val COULD_NOT_CONNECT_TO_DAEMON_MESSAGE = "Could not connect to Kotlin compile daemon" const val COULD_NOT_CONNECT_TO_DAEMON_MESSAGE = "Could not connect to Kotlin compile daemon"
internal class GradleCompilerRunner(private val project: Project) : KotlinCompilerRunner<GradleCompilerEnvironment>() { internal fun kotlinCompilerExecutionStrategy(): String =
System.getProperty(KOTLIN_COMPILER_EXECUTION_STRATEGY_PROPERTY) ?: DAEMON_EXECUTION_STRATEGY
internal open class GradleCompilerRunner(protected val project: Project) : KotlinCompilerRunner<GradleCompilerEnvironment>() {
override val log = GradleKotlinLogger(project.logger) override val log = GradleKotlinLogger(project.logger)
// used only for process launching so far, but implements unused proper contract
private val loggingMessageCollector: MessageCollector by lazy {
object : MessageCollector {
private var hasErrors = false
private val messageRenderer = MessageRenderer.PLAIN_FULL_PATHS
override fun clear() {
hasErrors = false
}
override fun hasErrors(): Boolean = hasErrors
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
val locMessage = messageRenderer.render(severity, message, location)
when (severity) {
CompilerMessageSeverity.EXCEPTION -> log.error(locMessage)
CompilerMessageSeverity.ERROR,
CompilerMessageSeverity.STRONG_WARNING,
CompilerMessageSeverity.WARNING,
CompilerMessageSeverity.INFO -> log.info(locMessage)
CompilerMessageSeverity.LOGGING -> log.debug(locMessage)
CompilerMessageSeverity.OUTPUT -> {
}
}
}
}
}
fun runJvmCompiler( fun runJvmCompiler(
sourcesToCompile: List<File>, sourcesToCompile: List<File>,
commonSources: List<File>, commonSources: List<File>,
@@ -96,7 +60,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
javaPackagePrefix: String?, javaPackagePrefix: String?,
args: K2JVMCompilerArguments, args: K2JVMCompilerArguments,
environment: GradleCompilerEnvironment environment: GradleCompilerEnvironment
): ExitCode { ) {
val buildFile = makeModuleFile( val buildFile = makeModuleFile(
args.moduleName!!, args.moduleName!!,
isTest = false, isTest = false,
@@ -109,12 +73,12 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
) )
args.buildFile = buildFile.absolutePath args.buildFile = buildFile.absolutePath
if (environment !is GradleIncrementalCompilerEnvironment || kotlinCompilerExecutionStrategy != "daemon") { if (environment.incrementalCompilationEnvironment == null || kotlinCompilerExecutionStrategy() != DAEMON_EXECUTION_STRATEGY) {
args.destination = null args.destination = null
} }
try { try {
return runCompiler(K2JVM_COMPILER, args, environment) runCompiler(KotlinCompilerClass.JVM, args, environment)
} finally { } finally {
if (System.getProperty(DELETE_MODULE_FILE_PROPERTY) != "false") { if (System.getProperty(DELETE_MODULE_FILE_PROPERTY) != "false") {
buildFile.delete() buildFile.delete()
@@ -127,26 +91,26 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
kotlinCommonSources: List<File>, kotlinCommonSources: List<File>,
args: K2JSCompilerArguments, args: K2JSCompilerArguments,
environment: GradleCompilerEnvironment environment: GradleCompilerEnvironment
): ExitCode { ) {
args.freeArgs += kotlinSources.map { it.absolutePath } args.freeArgs += kotlinSources.map { it.absolutePath }
args.commonSources = kotlinCommonSources.map { it.absolutePath }.toTypedArray() args.commonSources = kotlinCommonSources.map { it.absolutePath }.toTypedArray()
return runCompiler(K2JS_COMPILER, args, environment) runCompiler(KotlinCompilerClass.JS, args, environment)
} }
fun runMetadataCompiler( fun runMetadataCompiler(
kotlinSources: List<File>, kotlinSources: List<File>,
args: K2MetadataCompilerArguments, args: K2MetadataCompilerArguments,
environment: GradleCompilerEnvironment environment: GradleCompilerEnvironment
): ExitCode { ) {
args.freeArgs += kotlinSources.map { it.absolutePath } args.freeArgs += kotlinSources.map { it.absolutePath }
return runCompiler(K2METADATA_COMPILER, args, environment) return runCompiler(KotlinCompilerClass.METADATA, args, environment)
} }
override fun compileWithDaemonOrFallback( override fun runCompiler(
compilerClassName: String, compilerClassName: String,
compilerArgs: CommonCompilerArguments, compilerArgs: CommonCompilerArguments,
environment: GradleCompilerEnvironment environment: GradleCompilerEnvironment
): ExitCode { ) {
if (compilerArgs.version) { if (compilerArgs.version) {
project.logger.lifecycle( project.logger.lifecycle(
"Kotlin version " + loadCompilerVersion(environment.compilerClasspath) + "Kotlin version " + loadCompilerVersion(environment.compilerClasspath) +
@@ -154,206 +118,50 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
) )
compilerArgs.version = false compilerArgs.version = false
} }
val argsArray = ArgumentUtils.convertArgumentsToStringList(compilerArgs).toTypedArray() super.runCompiler(compilerClassName, compilerArgs, environment)
with(project.logger) {
kotlinDebug { "Kotlin compiler class: $compilerClassName" }
kotlinDebug { "Kotlin compiler classpath: ${environment.compilerFullClasspath.map { it.canonicalPath }.joinToString()}" }
kotlinDebug { "Kotlin compiler args: ${argsArray.joinToString(" ")}" }
}
val executionStrategy = kotlinCompilerExecutionStrategy
if (executionStrategy == DAEMON_EXECUTION_STRATEGY) {
val daemonExitCode = compileWithDaemon(compilerClassName, compilerArgs, environment)
if (daemonExitCode != null) {
return daemonExitCode
} else {
log.warn("Could not connect to kotlin daemon. Using fallback strategy.")
}
}
val isGradleDaemonUsed = System.getProperty("org.gradle.daemon")?.let(String::toBoolean)
return if (executionStrategy == IN_PROCESS_EXECUTION_STRATEGY || isGradleDaemonUsed == false) {
compileInProcess(argsArray, compilerClassName, environment)
} else {
compileOutOfProcess(argsArray, compilerClassName, environment)
}
} }
private val kotlinCompilerExecutionStrategy: String override fun compileWithDaemonOrFallback(
get() = System.getProperty(KOTLIN_COMPILER_EXECUTION_STRATEGY_PROPERTY) ?: DAEMON_EXECUTION_STRATEGY
override fun compileWithDaemon(
compilerClassName: String, compilerClassName: String,
compilerArgs: CommonCompilerArguments, compilerArgs: CommonCompilerArguments,
environment: GradleCompilerEnvironment environment: GradleCompilerEnvironment
): ExitCode? { ) {
val connection = val kotlinCompilerRunnable = KotlinCompilerRunnable(
try { projectFiles = ProjectFilesForCompilation(project),
getDaemonConnection(environment) compilerFullClasspath = environment.compilerFullClasspath,
} catch (e: Throwable) { compilerClassName = compilerClassName,
log.warn("Caught an exception trying to connect to Kotlin Daemon") compilerArgs = ArgumentUtils.convertArgumentsToStringList(compilerArgs).toTypedArray(),
e.printStackTrace() isVerbose = compilerArgs.verbose,
null incrementalCompilationEnvironment = environment.incrementalCompilationEnvironment,
} incrementalModuleInfo = buildModulesInfo(project.gradle)
if (connection == null) {
if (environment is GradleIncrementalCompilerEnvironment) {
log.warn("Could not perform incremental compilation: $COULD_NOT_CONNECT_TO_DAEMON_MESSAGE")
} else {
log.warn(COULD_NOT_CONNECT_TO_DAEMON_MESSAGE)
}
return null
}
val (daemon, sessionId) = connection
val targetPlatform = when (compilerClassName) {
K2JVM_COMPILER -> CompileService.TargetPlatform.JVM
K2JS_COMPILER -> CompileService.TargetPlatform.JS
K2METADATA_COMPILER -> CompileService.TargetPlatform.METADATA
else -> throw IllegalArgumentException("Unknown compiler type $compilerClassName")
}
val exitCode = try {
val res = if (environment is GradleIncrementalCompilerEnvironment) {
incrementalCompilationWithDaemon(daemon, sessionId, targetPlatform, environment)
} else {
nonIncrementalCompilationWithDaemon(daemon, sessionId, targetPlatform, environment)
}
exitCodeFromProcessExitCode(res.get())
} catch (e: Throwable) {
log.warn("Compilation with Kotlin compile daemon was not successful")
e.printStackTrace()
null
}
// todo: can we clear cache on the end of session?
// often source of the NoSuchObjectException and UnmarshalException, probably caused by the failed/crashed/exited daemon
// TODO: implement a proper logic to avoid remote calls in such cases
try {
daemon.clearJarCache()
} catch (e: RemoteException) {
log.warn("Unable to clear jar cache after compilation, maybe daemon is already down: $e")
}
logFinish(DAEMON_EXECUTION_STRATEGY)
return exitCode
}
private fun nonIncrementalCompilationWithDaemon(
daemon: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
environment: GradleCompilerEnvironment
): CompileService.CallResult<Int> {
val verbose = environment.compilerArgs.verbose
val compilationOptions = CompilationOptions(
compilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
targetPlatform = targetPlatform,
reportCategories = reportCategories(verbose),
reportSeverity = reportSeverity(verbose),
requestedCompilationResults = emptyArray()
) )
val servicesFacade = GradleCompilerServicesFacadeImpl(project, environment.messageCollector) kotlinCompilerRunnable.run()
val argsArray = ArgumentUtils.convertArgumentsToStringList(environment.compilerArgs).toTypedArray()
return daemon.compile(sessionId, argsArray, compilationOptions, servicesFacade, compilationResults = null)
}
private fun incrementalCompilationWithDaemon(
daemon: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
environment: GradleIncrementalCompilerEnvironment
): CompileService.CallResult<Int> {
val knownChangedFiles = environment.changedFiles as? ChangedFiles.Known
val verbose = environment.compilerArgs.verbose
val compilationOptions = IncrementalCompilationOptions(
areFileChangesKnown = knownChangedFiles != null,
modifiedFiles = knownChangedFiles?.modified,
deletedFiles = knownChangedFiles?.removed,
workingDir = environment.workingDir,
reportCategories = reportCategories(verbose),
reportSeverity = reportSeverity(verbose),
requestedCompilationResults = arrayOf(CompilationResultCategory.IC_COMPILE_ITERATION.code),
compilerMode = CompilerMode.INCREMENTAL_COMPILER,
targetPlatform = targetPlatform,
usePreciseJavaTracking = environment.usePreciseJavaTracking,
localStateDirs = environment.localStateDirs,
multiModuleICSettings = environment.multiModuleICSettings,
modulesInfo = buildModulesInfo(project.gradle)
)
log.info("Options for KOTLIN DAEMON: $compilationOptions")
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(project, environment)
val argsArray = ArgumentUtils.convertArgumentsToStringList(environment.compilerArgs).toTypedArray()
return daemon.compile(sessionId, argsArray, compilationOptions, servicesFacade, GradleCompilationResults(project))
}
private fun reportCategories(verbose: Boolean): Array<Int> =
if (!verbose) {
arrayOf(ReportCategory.COMPILER_MESSAGE.code)
} else {
ReportCategory.values().map { it.code }.toTypedArray()
}
private fun reportSeverity(verbose: Boolean): Int =
if (!verbose) {
ReportSeverity.INFO.code
} else {
ReportSeverity.DEBUG.code
}
private fun compileOutOfProcess(
argsArray: Array<String>,
compilerClassName: String,
environment: GradleCompilerEnvironment
): ExitCode {
return runToolInSeparateProcess(argsArray, compilerClassName, environment.compilerFullClasspath, log, loggingMessageCollector)
}
private fun compileInProcess(
argsArray: Array<String>,
compilerClassName: String,
environment: GradleCompilerEnvironment
): ExitCode {
val stream = ByteArrayOutputStream()
val out = PrintStream(stream)
// todo: cache classloader?
val classLoader = URLClassLoader(environment.compilerClasspathURLs.toTypedArray())
val servicesClass = Class.forName(Services::class.java.canonicalName, true, classLoader)
val emptyServices = servicesClass.getField("EMPTY").get(servicesClass)
val compiler = Class.forName(compilerClassName, true, classLoader)
val exec = compiler.getMethod(
"execAndOutputXml",
PrintStream::class.java,
servicesClass,
Array<String>::class.java
)
val res = exec.invoke(compiler.newInstance(), out, emptyServices, argsArray)
val exitCode = ExitCode.valueOf(res.toString())
processCompilerOutput(environment, stream, exitCode)
logFinish(IN_PROCESS_EXECUTION_STRATEGY)
return exitCode
}
private fun logFinish(strategy: String) = log.logFinish(strategy)
override fun getDaemonConnection(environment: GradleCompilerEnvironment): CompileServiceSession? {
synchronized(this.javaClass) {
val compilerId = CompilerId.makeCompilerId(environment.compilerFullClasspath)
val clientIsAliveFlagFile = getOrCreateClientFlagFile(project)
val sessionIsAliveFlagFile = getOrCreateSessionFlagFile(project)
return newDaemonConnection(compilerId, clientIsAliveFlagFile, sessionIsAliveFlagFile, environment)
}
} }
companion object { companion object {
@Synchronized
internal fun getDaemonConnectionImpl(
clientIsAliveFlagFile: File,
sessionIsAliveFlagFile: File,
compilerFullClasspath: List<File>,
messageCollector: MessageCollector,
isDebugEnabled: Boolean
): CompileServiceSession? {
val compilerId = CompilerId.makeCompilerId(compilerFullClasspath)
return newDaemonConnection(
compilerId, clientIsAliveFlagFile, sessionIsAliveFlagFile,
messageCollector = messageCollector,
isDebugEnabled = isDebugEnabled
)
}
@Volatile @Volatile
private var cachedGradle = WeakReference<Gradle>(null) private var cachedGradle = WeakReference<Gradle>(null)
@Volatile @Volatile
private var cachedModulesInfo: IncrementalModuleInfo? = null private var cachedModulesInfo: IncrementalModuleInfo? = null
@Synchronized @Synchronized
private fun buildModulesInfo(gradle: Gradle): IncrementalModuleInfo { internal fun buildModulesInfo(gradle: Gradle): IncrementalModuleInfo {
if (cachedGradle.get() === gradle && cachedModulesInfo != null) return cachedModulesInfo!! if (cachedGradle.get() === gradle && cachedModulesInfo != null) return cachedModulesInfo!!
val dirToModule = HashMap<File, IncrementalModuleEntry>() val dirToModule = HashMap<File, IncrementalModuleEntry>()
@@ -412,7 +220,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
private var clientIsAliveFlagFile: File? = null private var clientIsAliveFlagFile: File? = null
@Synchronized @Synchronized
private fun getOrCreateClientFlagFile(project: Project): File { internal fun getOrCreateClientFlagFile(project: Project): File {
val log = project.logger val log = project.logger
if (clientIsAliveFlagFile == null || !clientIsAliveFlagFile!!.exists()) { if (clientIsAliveFlagFile == null || !clientIsAliveFlagFile!!.exists()) {
val projectName = project.rootProject.name.normalizeForFlagFile() val projectName = project.rootProject.name.normalizeForFlagFile()
@@ -436,7 +244,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
// session files are deleted at org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices.buildFinished // session files are deleted at org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices.buildFinished
@Synchronized @Synchronized
private fun getOrCreateSessionFlagFile(project: Project): File { internal fun getOrCreateSessionFlagFile(project: Project): File {
val log = project.logger val log = project.logger
if (sessionFlagFile == null || !sessionFlagFile!!.exists()) { if (sessionFlagFile == null || !sessionFlagFile!!.exists()) {
val sessionFilesDir = sessionsDir(project).apply { mkdirs() } val sessionFilesDir = sessionsDir(project).apply { mkdirs() }
@@ -453,3 +261,4 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
File(File(project.rootProject.buildDir, "kotlin"), "sessions") File(File(project.rootProject.buildDir, "kotlin"), "sessions")
} }
} }
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.compilerRunner
import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings
import org.jetbrains.kotlin.incremental.ChangedFiles
import java.io.File
import java.io.Serializable
internal class IncrementalCompilationEnvironment(
val changedFiles: ChangedFiles,
val workingDir: File,
val usePreciseJavaTracking: Boolean = false,
val localStateDirs: List<File> = emptyList(),
val multiModuleICSettings: MultiModuleICSettings
) : Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
@@ -0,0 +1,265 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project
import org.jetbrains.kotlin.cli.common.ExitCode
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.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
import org.jetbrains.kotlin.incremental.ChangedFiles
import org.slf4j.LoggerFactory
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import java.io.Serializable
import java.net.URLClassLoader
import java.rmi.RemoteException
import javax.inject.Inject
internal class ProjectFilesForCompilation(
val projectRootFile: File,
val clientIsAliveFlagFile: File,
val sessionFlagFile: File
) : Serializable {
constructor(project: Project) : this(
projectRootFile = project.rootProject.projectDir,
clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(project),
sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(project)
)
companion object {
const val serialVersionUID: Long = 0
}
}
internal class KotlinCompilerRunnable @Inject constructor(
private val projectFiles: ProjectFilesForCompilation,
private val compilerFullClasspath: List<File>,
private val compilerClassName: String,
private val compilerArgs: Array<String>,
private val isVerbose: Boolean,
private val incrementalCompilationEnvironment: IncrementalCompilationEnvironment?,
private val incrementalModuleInfo: IncrementalModuleInfo?
) : Runnable {
private val log: KotlinLogger =
SL4JKotlinLogger(LoggerFactory.getLogger("KotlinCompilerRunnable"))
private val messageCollector = GradleMessageCollector(log)
private val isIncremental: Boolean
get() = incrementalCompilationEnvironment != null
override fun run() {
val exitCode = compileWithDaemonOrFallbackImpl()
throwGradleExceptionIfError(exitCode)
}
private fun compileWithDaemonOrFallbackImpl(): ExitCode {
with(log) {
kotlinDebug { "Kotlin compiler class: $compilerClassName" }
kotlinDebug { "Kotlin compiler classpath: ${compilerFullClasspath.map { it.canonicalPath }.joinToString()}" }
kotlinDebug { "Kotlin compiler args: ${compilerArgs.joinToString(" ")}" }
}
val executionStrategy = kotlinCompilerExecutionStrategy()
if (executionStrategy == DAEMON_EXECUTION_STRATEGY) {
val daemonExitCode = compileWithDaemon()
if (daemonExitCode != null) {
return daemonExitCode
} else {
log.warn("Could not connect to kotlin daemon. Using fallback strategy.")
}
}
val isGradleDaemonUsed = System.getProperty("org.gradle.daemon")?.let(String::toBoolean)
return if (executionStrategy == IN_PROCESS_EXECUTION_STRATEGY || isGradleDaemonUsed == false) {
compileInProcess()
} else {
compileOutOfProcess()
}
}
private fun compileWithDaemon(): ExitCode? {
val connection =
try {
GradleCompilerRunner.getDaemonConnectionImpl(
projectFiles.clientIsAliveFlagFile,
projectFiles.sessionFlagFile,
compilerFullClasspath,
messageCollector,
log.isDebugEnabled
)
} catch (e: Throwable) {
log.warn("Caught an exception trying to connect to Kotlin Daemon")
e.printStackTrace()
null
}
if (connection == null) {
if (isIncremental) {
log.warn("Could not perform incremental compilation: $COULD_NOT_CONNECT_TO_DAEMON_MESSAGE")
} else {
log.warn(COULD_NOT_CONNECT_TO_DAEMON_MESSAGE)
}
return null
}
val (daemon, sessionId) = connection
val targetPlatform = when (compilerClassName) {
KotlinCompilerClass.JVM -> CompileService.TargetPlatform.JVM
KotlinCompilerClass.JS -> CompileService.TargetPlatform.JS
KotlinCompilerClass.METADATA -> CompileService.TargetPlatform.METADATA
else -> throw IllegalArgumentException("Unknown compiler type $compilerClassName")
}
val exitCode = try {
val res = if (isIncremental) {
incrementalCompilationWithDaemon(daemon, sessionId, targetPlatform)
} else {
nonIncrementalCompilationWithDaemon(daemon, sessionId, targetPlatform)
}
exitCodeFromProcessExitCode(log, res.get())
} catch (e: Throwable) {
log.warn("Compilation with Kotlin compile daemon was not successful")
e.printStackTrace()
null
}
// todo: can we clear cache on the end of session?
// often source of the NoSuchObjectException and UnmarshalException, probably caused by the failed/crashed/exited daemon
// TODO: implement a proper logic to avoid remote calls in such cases
try {
daemon.clearJarCache()
} catch (e: RemoteException) {
log.warn("Unable to clear jar cache after compilation, maybe daemon is already down: $e")
}
log.logFinish(DAEMON_EXECUTION_STRATEGY)
return exitCode
}
private fun nonIncrementalCompilationWithDaemon(
daemon: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform
): CompileService.CallResult<Int> {
val compilationOptions = CompilationOptions(
compilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
targetPlatform = targetPlatform,
reportCategories = reportCategories(isVerbose),
reportSeverity = reportSeverity(isVerbose),
requestedCompilationResults = emptyArray()
)
val servicesFacade = GradleCompilerServicesFacadeImpl(log, messageCollector)
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null)
}
private fun incrementalCompilationWithDaemon(
daemon: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform
): CompileService.CallResult<Int> {
val icEnv = incrementalCompilationEnvironment ?: error("incrementalCompilationEnvironment is null!")
val knownChangedFiles = icEnv.changedFiles as? ChangedFiles.Known
val compilationOptions = IncrementalCompilationOptions(
areFileChangesKnown = knownChangedFiles != null,
modifiedFiles = knownChangedFiles?.modified,
deletedFiles = knownChangedFiles?.removed,
workingDir = icEnv.workingDir,
reportCategories = reportCategories(isVerbose),
reportSeverity = reportSeverity(isVerbose),
requestedCompilationResults = arrayOf(CompilationResultCategory.IC_COMPILE_ITERATION.code),
compilerMode = CompilerMode.INCREMENTAL_COMPILER,
targetPlatform = targetPlatform,
usePreciseJavaTracking = icEnv.usePreciseJavaTracking,
localStateDirs = icEnv.localStateDirs,
multiModuleICSettings = icEnv.multiModuleICSettings,
modulesInfo = incrementalModuleInfo!!
)
log.info("Options for KOTLIN DAEMON: $compilationOptions")
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(log, messageCollector)
val compilationResults = GradleCompilationResults(log, projectFiles.projectRootFile)
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults)
}
private fun compileOutOfProcess(): ExitCode =
runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log, loggingMessageCollector)
private fun compileInProcess(): ExitCode {
val stream = ByteArrayOutputStream()
val out = PrintStream(stream)
// todo: cache classloader?
val classLoader = URLClassLoader(compilerFullClasspath.map { it.toURI().toURL() }.toTypedArray())
val servicesClass = Class.forName(Services::class.java.canonicalName, true, classLoader)
val emptyServices = servicesClass.getField("EMPTY").get(servicesClass)
val compiler = Class.forName(compilerClassName, true, classLoader)
val exec = compiler.getMethod(
"execAndOutputXml",
PrintStream::class.java,
servicesClass,
Array<String>::class.java
)
val res = exec.invoke(compiler.newInstance(), out, emptyServices, compilerArgs)
val exitCode = ExitCode.valueOf(res.toString())
processCompilerOutput(
messageCollector,
OutputItemsCollectorImpl(),
stream,
exitCode
)
log.logFinish(IN_PROCESS_EXECUTION_STRATEGY)
return exitCode
}
private fun reportCategories(verbose: Boolean): Array<Int> =
if (!verbose) {
arrayOf(ReportCategory.COMPILER_MESSAGE.code)
} else {
ReportCategory.values().map { it.code }.toTypedArray()
}
private fun reportSeverity(verbose: Boolean): Int =
if (!verbose) {
ReportSeverity.INFO.code
} else {
ReportSeverity.DEBUG.code
}
// used only for process launching so far, but implements unused proper contract
private val loggingMessageCollector: MessageCollector by lazy {
object : MessageCollector {
private var hasErrors = false
private val messageRenderer = MessageRenderer.PLAIN_FULL_PATHS
override fun clear() {
hasErrors = false
}
override fun hasErrors(): Boolean = hasErrors
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
val locMessage = messageRenderer.render(severity, message, location)
when (severity) {
CompilerMessageSeverity.EXCEPTION -> log.error(locMessage)
CompilerMessageSeverity.ERROR,
CompilerMessageSeverity.STRONG_WARNING,
CompilerMessageSeverity.WARNING,
CompilerMessageSeverity.INFO -> log.info(locMessage)
CompilerMessageSeverity.LOGGING -> log.debug(locMessage)
CompilerMessageSeverity.OUTPUT -> {
}
}
}
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.compilerRunner
import org.slf4j.Logger
internal class SL4JKotlinLogger(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)
}
override val isDebugEnabled: Boolean
get() = log.isDebugEnabled
}
@@ -68,8 +68,11 @@ internal fun loadCompilerVersion(compilerClasspath: List<File>): String {
} }
internal fun runToolInSeparateProcess( internal fun runToolInSeparateProcess(
argsArray: Array<String>, compilerClassName: String, classpath: List<File>, argsArray: Array<String>,
logger: KotlinLogger, messageCollector: MessageCollector compilerClassName: String,
classpath: List<File>,
logger: KotlinLogger,
messageCollector: MessageCollector
): ExitCode { ): ExitCode {
val javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java" val javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"
val classpathString = classpath.map { it.absolutePath }.joinToString(separator = File.pathSeparator) val classpathString = classpath.map { it.absolutePath }.joinToString(separator = File.pathSeparator)
@@ -15,6 +15,7 @@ import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.compilerRunner.GradleKotlinLogger
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
@@ -61,15 +62,15 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
val args = prepareCompilerArguments() val args = prepareCompilerArguments()
val messageCollector = GradleMessageCollector(logger) val messageCollector = GradleMessageCollector(GradleKotlinLogger(logger))
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val environment = GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemCollector, args) val environment = GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemCollector)
if (environment.toolsJar == null && !isAtLeastJava9) { if (environment.toolsJar == null && !isAtLeastJava9) {
throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work") throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work")
} }
val compilerRunner = GradleCompilerRunner(project) val compilerRunner = GradleCompilerRunner(project)
val exitCode = compilerRunner.runJvmCompiler( compilerRunner.runJvmCompiler(
sourcesToCompile = emptyList(), sourcesToCompile = emptyList(),
commonSources = emptyList(), commonSources = emptyList(),
javaSourceRoots = javaSourceRoots, javaSourceRoots = javaSourceRoots,
@@ -77,7 +78,6 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
args = args, args = args,
environment = environment environment = environment
) )
throwGradleExceptionIfError(exitCode)
} }
private val isAtLeastJava9: Boolean private val isAtLeastJava9: Boolean
@@ -24,6 +24,7 @@ import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.internal.KaptVariantData import org.jetbrains.kotlin.gradle.internal.KaptVariantData
@@ -22,6 +22,7 @@ import org.gradle.api.internal.HasConvention
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.plugins.ExtensionAware import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.compilerRunner.KotlinLogger
import java.io.File import java.io.File
internal fun AbstractCompile.appendClasspathDynamically(file: File) { internal fun AbstractCompile.appendClasspathDynamically(file: File) {
@@ -76,4 +77,11 @@ internal inline fun Logger.kotlinDebug(message: () -> String) {
if (isDebugEnabled) { if (isDebugEnabled) {
kotlinDebug(message()) kotlinDebug(message())
} }
}
internal inline fun KotlinLogger.kotlinDebug(fn: () -> String) {
if (isDebugEnabled) {
val msg = fn()
debug("[KOTLIN] $msg")
}
} }
@@ -82,13 +82,10 @@ class KotlinMetadataTargetPreset(
override val platformType: KotlinPlatformType override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common get() = KotlinPlatformType.common
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> = override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
KotlinCommonSourceSetProcessor( val tasksProvider = KotlinTasksProvider(compilation.target.targetName, useWorkersForCompilation = true)
project, return KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
compilation, }
KotlinTasksProvider(compilation.target.targetName),
kotlinPluginVersion
)
companion object { companion object {
const val PRESET_NAME = "metadata" const val PRESET_NAME = "metadata"
@@ -131,8 +128,10 @@ class KotlinJvmTargetPreset(
override val platformType: KotlinPlatformType override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm get() = KotlinPlatformType.jvm
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> = override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(compilation.target.targetName), compilation, kotlinPluginVersion) val tasksProvider = KotlinTasksProvider(compilation.target.targetName, useWorkersForCompilation = true)
return Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object { companion object {
const val PRESET_NAME = "jvm" const val PRESET_NAME = "jvm"
@@ -158,8 +157,10 @@ class KotlinJsTargetPreset(
override val platformType: KotlinPlatformType override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js get() = KotlinPlatformType.js
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> = override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
Kotlin2JsSourceSetProcessor(project, KotlinTasksProvider(compilation.target.targetName), compilation, kotlinPluginVersion) val tasksProvider = KotlinTasksProvider(compilation.target.targetName, useWorkersForCompilation = true)
return Kotlin2JsSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object { companion object {
const val PRESET_NAME = "js" const val PRESET_NAME = "js"
@@ -179,7 +180,7 @@ class KotlinAndroidTargetPreset(
} }
KotlinAndroidPlugin.applyToTarget( KotlinAndroidPlugin.applyToTarget(
project, result, AndroidTasksProvider(name), project, result, AndroidTasksProvider(name, useWorkersForCompilation = true),
kotlinPluginVersion kotlinPluginVersion
) )
@@ -206,7 +207,8 @@ class KotlinJvmWithJavaTargetPreset(
} }
AbstractKotlinPlugin.configureTarget(target) { compilation -> AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion) val tasksProvider = KotlinTasksProvider(name, useWorkersForCompilation = true)
Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
} }
target.compilations.all { compilation -> target.compilations.all { compilation ->
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin
import org.slf4j.Logger
internal inline fun Logger.kotlinDebug(message: () -> String) {
if (isDebugEnabled) {
kotlinDebug(message())
}
}
internal fun Logger.kotlinInfo(message: String) {
this.info("[KOTLIN] $message")
}
internal fun Logger.kotlinDebug(message: String) {
this.debug("[KOTLIN] $message")
}
internal fun Logger.kotlinWarn(message: String) {
this.warn("[KOTLIN] $message")
}
@@ -67,9 +67,8 @@ internal open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompil
override fun callCompiler(args: K2MetadataCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { override fun callCompiler(args: K2MetadataCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) {
val messageCollector = GradleMessageCollector(logger) val messageCollector = GradleMessageCollector(logger)
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = GradleCompilerRunner(project) val compilerRunner = compilerRunner()
val environment = GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args) val environment = GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector)
val exitCode = compilerRunner.runMetadataCompiler(sourceRoots.kotlinSourceFiles, args, environment) compilerRunner.runMetadataCompiler(sourceRoots.kotlinSourceFiles, args, environment)
throwGradleExceptionIfError(exitCode)
} }
} }
@@ -15,6 +15,7 @@ import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
@@ -41,6 +42,7 @@ import org.jetbrains.kotlin.incremental.destinationAsFile
import org.jetbrains.kotlin.utils.LibraryUtils import org.jetbrains.kotlin.utils.LibraryUtils
import java.io.File import java.io.File
import java.util.* import java.util.*
import javax.inject.Inject
import kotlin.properties.Delegates import kotlin.properties.Delegates
const val KOTLIN_BUILD_DIR_NAME = "kotlin" const val KOTLIN_BUILD_DIR_NAME = "kotlin"
@@ -248,6 +250,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
} }
} }
internal open fun compilerRunner(): GradleCompilerRunner =
GradleCompilerRunner(project)
override fun compile() { override fun compile() {
assert(false, { "unexpected call to compile()" }) assert(false, { "unexpected call to compile()" })
} }
@@ -381,31 +386,25 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
val messageCollector = GradleMessageCollector(logger) val messageCollector = GradleMessageCollector(logger)
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = GradleCompilerRunner(project) val compilerRunner = compilerRunner()
val environment = when { val icEnv = if (incremental) {
!incremental -> logger.info(USING_INCREMENTAL_COMPILATION_MESSAGE)
GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args) IncrementalCompilationEnvironment(
else -> { if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(),
logger.info(USING_INCREMENTAL_COMPILATION_MESSAGE) taskBuildDirectory,
GradleIncrementalCompilerEnvironment( usePreciseJavaTracking = usePreciseJavaTracking,
computedCompilerClasspath, localStateDirs = outputDirectories,
if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(), multiModuleICSettings = multiModuleICSettings
taskBuildDirectory, )
messageCollector, outputItemCollector, args, } else {
usePreciseJavaTracking = usePreciseJavaTracking,
localStateDirs = outputDirectories,
multiModuleICSettings = multiModuleICSettings
)
}
}
if (!incremental) {
clearOutputDirectories(reason = "IC is disabled for the task") clearOutputDirectories(reason = "IC is disabled for the task")
null
} }
try { try {
val exitCode = compilerRunner.runJvmCompiler( val environment = GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, icEnv)
compilerRunner.runJvmCompiler(
sourceRoots.kotlinSourceFiles, sourceRoots.kotlinSourceFiles,
commonSourceSet.toList(), commonSourceSet.toList(),
sourceRoots.javaSourceRoots, sourceRoots.javaSourceRoots,
@@ -415,7 +414,6 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
) )
disableMultiModuleICIfNeeded() disableMultiModuleICIfNeeded()
processCompilerExitCode(exitCode)
} catch (e: Throwable) { } catch (e: Throwable) {
cleanupOnError() cleanupOnError()
throw e throw e
@@ -447,14 +445,6 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
destinationDir.deleteRecursively() destinationDir.deleteRecursively()
} }
private fun processCompilerExitCode(exitCode: ExitCode) {
if (exitCode != ExitCode.OK) {
cleanupOnError()
}
throwGradleExceptionIfError(exitCode)
}
// override setSource to track source directory sets and files (for generated android folders) // override setSource to track source directory sets and files (for generated android folders)
override fun setSource(sources: Any?) { override fun setSource(sources: Any?) {
sourceRootsContainer.set(sources) sourceRootsContainer.set(sources)
@@ -468,6 +458,27 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
} }
} }
@CacheableTask
internal open class KotlinCompileWithWorkers @Inject constructor(
@Suppress("UnstableApiUsage") private val workerExecutor: WorkerExecutor
) : KotlinCompile() {
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(project, workerExecutor)
}
@CacheableTask
internal open class Kotlin2JsCompileWithWorkers @Inject constructor(
@Suppress("UnstableApiUsage") private val workerExecutor: WorkerExecutor
) : Kotlin2JsCompile() {
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(project, workerExecutor)
}
@CacheableTask
internal open class KotlinCompileCommonWithWorkers @Inject constructor(
@Suppress("UnstableApiUsage") private val workerExecutor: WorkerExecutor
) : KotlinCompileCommon() {
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(project, workerExecutor)
}
@CacheableTask @CacheableTask
open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(), KotlinJsCompile { open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(), KotlinJsCompile {
private val kotlinOptionsImpl = KotlinJsOptionsImpl() private val kotlinOptionsImpl = KotlinJsOptionsImpl()
@@ -538,28 +549,19 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(),
val messageCollector = GradleMessageCollector(logger) val messageCollector = GradleMessageCollector(logger)
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = GradleCompilerRunner(project) val compilerRunner = compilerRunner()
val environment = when { val icEnv = if (incremental) {
incremental -> { logger.warn(USING_EXPERIMENTAL_JS_INCREMENTAL_COMPILATION_MESSAGE)
logger.warn(USING_EXPERIMENTAL_JS_INCREMENTAL_COMPILATION_MESSAGE) IncrementalCompilationEnvironment(
GradleIncrementalCompilerEnvironment( if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(),
computedCompilerClasspath, taskBuildDirectory,
if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(), multiModuleICSettings = multiModuleICSettings
taskBuildDirectory, )
messageCollector, } else null
outputItemCollector,
args,
multiModuleICSettings = multiModuleICSettings
)
}
else -> {
GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
}
}
val exitCode = compilerRunner.runJsCompiler(sourceRoots.kotlinSourceFiles, commonSourceSet.toList(), args, environment) val environment = GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, icEnv)
throwGradleExceptionIfError(exitCode) compilerRunner.runJsCompiler(sourceRoots.kotlinSourceFiles, commonSourceSet.toList(), args, environment)
} }
} }
@@ -577,7 +579,9 @@ private fun Task.getGradleVersion(): ParsedGradleVersion? {
return result return result
} }
internal class GradleMessageCollector(val logger: Logger) : MessageCollector { internal class GradleMessageCollector(val logger: KotlinLogger) : MessageCollector {
constructor(logger: Logger) : this(GradleKotlinLogger(logger))
private var hasErrors = false private var hasErrors = false
override fun hasErrors() = hasErrors override fun hasErrors() = hasErrors
@@ -22,25 +22,35 @@ import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
internal open class KotlinTasksProvider(val targetName: String) { internal open class KotlinTasksProvider(
val targetName: String,
private val useWorkersForCompilation: Boolean = false
) {
open fun createKotlinJVMTask( open fun createKotlinJVMTask(
project: Project, project: Project,
name: String, name: String,
compilation: KotlinCompilation compilation: KotlinCompilation
): KotlinCompile = ): KotlinCompile {
project.tasks.create(name, KotlinCompile::class.java).apply { val taskClass = if (useWorkersForCompilation) KotlinCompileWithWorkers::class.java else KotlinCompile::class.java
return project.tasks.create(name, taskClass).apply {
configure(this, project, compilation) configure(this, project, compilation)
} }
}
fun createKotlinJSTask(project: Project, name: String, compilation: KotlinCompilation): Kotlin2JsCompile = fun createKotlinJSTask(project: Project, name: String, compilation: KotlinCompilation): Kotlin2JsCompile {
project.tasks.create(name, Kotlin2JsCompile::class.java).apply { val taskClass = if (useWorkersForCompilation) Kotlin2JsCompileWithWorkers::class.java else Kotlin2JsCompile::class.java
return project.tasks.create(name, taskClass).apply {
configure(this, project, compilation) configure(this, project, compilation)
} }
}
fun createKotlinCommonTask(project: Project, name: String, compilation: KotlinCompilation): KotlinCompileCommon =
project.tasks.create(name, KotlinCompileCommon::class.java).apply { fun createKotlinCommonTask(project: Project, name: String, compilation: KotlinCompilation): KotlinCompileCommon {
val taskClass = if (useWorkersForCompilation) KotlinCompileCommonWithWorkers::class.java else KotlinCompileCommon::class.java
return project.tasks.create(name, taskClass).apply {
configure(this, project, compilation) configure(this, project, compilation)
} }
}
open fun configure( open fun configure(
kotlinTask: AbstractKotlinCompile<*>, kotlinTask: AbstractKotlinCompile<*>,
@@ -64,7 +74,10 @@ internal open class KotlinTasksProvider(val targetName: String) {
RegexTaskToFriendTaskMapper.Default(targetName) RegexTaskToFriendTaskMapper.Default(targetName)
} }
internal class AndroidTasksProvider(targetName: String) : KotlinTasksProvider(targetName) { internal class AndroidTasksProvider(
targetName: String,
useWorkersForCompilation: Boolean = false
) : KotlinTasksProvider(targetName, useWorkersForCompilation = useWorkersForCompilation) {
override val taskToFriendTaskMapper: TaskToFriendTaskMapper = override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
RegexTaskToFriendTaskMapper.Android(targetName) RegexTaskToFriendTaskMapper.Android(targetName)