[BT] Implement basic non-incremental in-process JVM compilation

This commit is contained in:
Alexander.Likhachev
2023-06-27 22:43:48 +02:00
committed by Space Team
parent 6356a40fe8
commit 28c059f25b
8 changed files with 186 additions and 28 deletions
@@ -6,6 +6,9 @@ plugins {
dependencies {
api(project(":compiler:build-tools:kotlin-build-tools-api"))
implementation(kotlinStdlib())
compileOnly(project(":compiler:cli"))
compileOnly(project(":compiler:cli-js"))
runtimeOnly(project(":kotlin-compiler-embeddable"))
}
publish()
@@ -7,10 +7,26 @@ package org.jetbrains.kotlin.buildtools.internal
import org.jetbrains.kotlin.buildtools.api.CompilationResult
import org.jetbrains.kotlin.buildtools.api.CompilationService
import org.jetbrains.kotlin.buildtools.api.CompilerArgumentsParseException
import org.jetbrains.kotlin.buildtools.api.CompilerExecutionStrategyConfiguration
import org.jetbrains.kotlin.buildtools.api.jvm.*
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.cli.common.arguments.validateArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.config.Services
import java.io.File
private val ExitCode.asCompilationResult
get() = when (this) {
ExitCode.OK -> CompilationResult.COMPILATION_SUCCESS
ExitCode.COMPILATION_ERROR -> CompilationResult.COMPILER_INTERNAL_ERROR
ExitCode.INTERNAL_ERROR -> CompilationResult.COMPILER_INTERNAL_ERROR
ExitCode.OOM_ERROR -> CompilationResult.COMPILATION_OOM_ERROR
else -> error("Unexpected exit code: $this")
}
internal class CompilationServiceImpl : CompilationService {
override fun calculateClasspathSnapshot(classpathEntry: File): ClasspathEntrySnapshot {
TODO("Calculating classpath snapshots via the Build Tools API is not yet implemented: KT-57565")
@@ -26,7 +42,32 @@ internal class CompilationServiceImpl : CompilationService {
sources: List<File>,
arguments: List<String>
): CompilationResult {
println("I'm simulating compilation, nothing more yet")
return CompilationResult.COMPILATION_SUCCESS
check(strategyConfig is CompilerExecutionStrategyConfigurationImpl) {
"Initial strategy configuration object must be acquired from the `makeCompilerExecutionStrategyConfiguration` method."
}
check(compilationConfig is JvmCompilationConfigurationImpl) {
"Initial JVM compilation configuration object must be acquired from the `makeJvmCompilationConfiguration` method."
}
val loggerAdapter = KotlinLoggerMessageCollectorAdapter(compilationConfig.logger)
return when (strategyConfig.selectedStrategy) {
is CompilerExecutionStrategy.InProcess -> compileInProcess(loggerAdapter, sources, arguments)
is CompilerExecutionStrategy.Daemon -> TODO("The daemon strategy is not yet supported in the Build Tools API")
}
}
private fun compileInProcess(
loggerAdapter: KotlinLoggerMessageCollectorAdapter,
sources: List<File>,
arguments: List<String>
): CompilationResult {
val compiler = K2JVMCompiler()
val parsedArguments = compiler.createArguments()
parseCommandLineArguments(arguments, parsedArguments)
validateArguments(parsedArguments.errors)?.let {
throw CompilerArgumentsParseException(it)
}
parsedArguments.freeArgs += sources.map { it.absolutePath } // TODO: they're not actually passed yet
loggerAdapter.report(CompilerMessageSeverity.INFO, arguments.toString())
return compiler.exec(loggerAdapter, Services.EMPTY, parsedArguments).asCompilationResult
}
}
@@ -8,12 +8,22 @@ package org.jetbrains.kotlin.buildtools.internal
import org.jetbrains.kotlin.buildtools.api.CompilerExecutionStrategyConfiguration
import java.io.File
class CompilerExecutionStrategyConfigurationImpl : CompilerExecutionStrategyConfiguration {
internal sealed interface CompilerExecutionStrategy {
data object InProcess : CompilerExecutionStrategy
data class Daemon(val sessionDir: File, val jvmArguments: List<String>) : CompilerExecutionStrategy
}
internal class CompilerExecutionStrategyConfigurationImpl : CompilerExecutionStrategyConfiguration {
internal var selectedStrategy: CompilerExecutionStrategy = CompilerExecutionStrategy.InProcess
override fun useInProcessStrategy(): CompilerExecutionStrategyConfiguration {
selectedStrategy = CompilerExecutionStrategy.InProcess
return this
}
override fun useDaemonStrategy(sessionDir: File, jvmArguments: List<String>): CompilerExecutionStrategyConfiguration {
TODO("Daemon strategy is not yet supported in the Build Tools API")
selectedStrategy = CompilerExecutionStrategy.Daemon(sessionDir, jvmArguments)
return this
}
}
@@ -7,27 +7,54 @@ package org.jetbrains.kotlin.buildtools.internal
import org.jetbrains.kotlin.buildtools.api.KotlinLogger
private enum class LogLevel {
ERROR,
WARN,
LIFECYCLE,
INFO,
DEBUG,
;
companion object {
fun fromString(rawValue: String) = entries.firstOrNull { it.name.equals(rawValue, ignoreCase = true) }
?: error("Unknown log level for the DefaultKotlinLogger: $rawValue")
}
}
internal object DefaultKotlinLogger : KotlinLogger {
private val logLevel: LogLevel = System.getProperty("kotlin.build-tools-api.log.level")?.let {
LogLevel.fromString(it)
} ?: LogLevel.WARN
private val LogLevel.isEnabled: Boolean
get() = logLevel >= this
override val isDebugEnabled: Boolean
get() = TODO("Default KotlinLogger is not yet implemented in the Build Tools API")
get() = LogLevel.DEBUG.isEnabled
override fun error(msg: String, throwable: Throwable?) {
TODO("Default KotlinLogger is not yet implemented in the Build Tools API")
if (!LogLevel.ERROR.isEnabled) return
System.err.println("e: $msg")
throwable?.printStackTrace()
}
override fun warn(msg: String) {
TODO("Default KotlinLogger is not yet implemented in the Build Tools API")
if (!LogLevel.WARN.isEnabled) return
System.err.println("w: $msg")
}
override fun info(msg: String) {
TODO("Default KotlinLogger is not yet implemented in the Build Tools API")
if (!LogLevel.INFO.isEnabled) return
println("i: $msg")
}
override fun debug(msg: String) {
TODO("Default KotlinLogger is not yet implemented in the Build Tools API")
if (!LogLevel.DEBUG.isEnabled) return
println("d: $msg")
}
override fun lifecycle(msg: String) {
TODO("Default KotlinLogger is not yet implemented in the Build Tools API")
if (!LogLevel.LIFECYCLE.isEnabled) return
println("l: $msg")
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.buildtools.internal
import org.jetbrains.kotlin.buildtools.api.KotlinLogger
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
internal class KotlinLoggerMessageCollectorAdapter(private val kotlinLogger: KotlinLogger) : MessageCollector {
private val messageRenderer = MessageRenderer.GRADLE_STYLE
override fun clear() {}
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) {
val renderedMessage = messageRenderer.render(severity, message, location) // TODO: move rendering to the KotlinLogger side
when (severity) {
CompilerMessageSeverity.EXCEPTION, CompilerMessageSeverity.ERROR -> kotlinLogger.error(renderedMessage)
CompilerMessageSeverity.STRONG_WARNING, CompilerMessageSeverity.WARNING -> kotlinLogger.warn(renderedMessage)
CompilerMessageSeverity.INFO -> kotlinLogger.info(renderedMessage)
CompilerMessageSeverity.OUTPUT, CompilerMessageSeverity.LOGGING -> kotlinLogger.debug(renderedMessage)
}
}
override fun hasErrors() = false
}
@@ -20,7 +20,6 @@ class BuildToolsApiJvmCompilationIT : KGPBaseTest() {
override val defaultBuildOptions = super.defaultBuildOptions.copy(runViaBuildToolsApi = true)
@GradleTest
@Disabled
@DisplayName("Simple project non-incremental in-process compilation")
fun compileJvmInProcessNonIncremental(gradleVersion: GradleVersion) = testSimpleProject(
gradleVersion, defaultBuildOptions.copy(
@@ -13,12 +13,18 @@ import org.gradle.workers.WorkParameters
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
import org.jetbrains.kotlin.build.report.metrics.GradleBuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.GradleBuildTime
import org.jetbrains.kotlin.buildtools.api.CompilationService
import org.jetbrains.kotlin.buildtools.api.ExperimentalBuildToolsApi
import org.jetbrains.kotlin.buildtools.api.SharedApiClassesClassLoader
import org.jetbrains.kotlin.buildtools.api.*
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWorkArguments
import org.jetbrains.kotlin.compilerRunner.asFinishLogMessage
import org.jetbrains.kotlin.gradle.internal.ClassLoadersCachingBuildService
import org.jetbrains.kotlin.gradle.internal.ParentClassLoaderProvider
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
import org.jetbrains.kotlin.gradle.logging.SL4JKotlinLogger
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
import org.jetbrains.kotlin.gradle.tasks.throwExceptionIfCompilationFailed
import org.slf4j.LoggerFactory
import java.io.File
@OptIn(ExperimentalBuildToolsApi::class)
@@ -35,21 +41,57 @@ internal abstract class BuildToolsApiCompilationWork : WorkAction<BuildToolsApiC
private val workArguments
get() = parameters.compilerWorkArguments.get()
override fun execute() {
val classLoader = parameters.classLoadersCachingService.get()
.getClassLoader(workArguments.compilerFullClasspath, SharedApiClassesClassLoaderProvider)
val compilationService = CompilationService.loadImplementation(classLoader)
val executionConfig = compilationService.makeCompilerExecutionStrategyConfiguration().apply {
useInProcessStrategy()
}
val jvmCompilationConfig = compilationService.makeJvmCompilationConfiguration()
compilationService.compileJvm(
executionConfig,
jvmCompilationConfig,
emptyList(),
emptyList(),
)
private val taskPath
get() = workArguments.taskPath
private val log: KotlinLogger by lazy(LazyThreadSafetyMode.NONE) {
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
?: run {
val logger = LoggerFactory.getLogger("GradleKotlinCompilerWork")
val kotlinLogger = if (logger is org.gradle.api.logging.Logger) {
GradleKotlinLogger(logger)
} else SL4JKotlinLogger(logger)
kotlinLogger.apply {
debug("Could not get logger for '$taskPath'. Falling back to sl4j logger")
}
}
}
override fun execute() {
val executionStrategy = workArguments.compilerExecutionSettings.strategy
try {
val classLoader = parameters.classLoadersCachingService.get()
.getClassLoader(workArguments.compilerFullClasspath, SharedApiClassesClassLoaderProvider)
val compilationService = CompilationService.loadImplementation(classLoader)
val executionConfig = compilationService.makeCompilerExecutionStrategyConfiguration().apply {
when (executionStrategy) {
KotlinCompilerExecutionStrategy.DAEMON -> TODO("The daemon strategy is not yet supported in the Build Tools API")
KotlinCompilerExecutionStrategy.IN_PROCESS -> useInProcessStrategy()
else -> error("The \"$executionStrategy\" execution strategy is not supported by the Build Tools API")
}
}
val jvmCompilationConfig = compilationService.makeJvmCompilationConfiguration()
val result = compilationService.compileJvm(
executionConfig,
jvmCompilationConfig,
emptyList(),
workArguments.compilerArgs.toList(),
)
throwExceptionIfCompilationFailed(result.asExitCode, executionStrategy)
} finally {
log.info(executionStrategy.asFinishLogMessage)
}
}
// temporary adapter property
private val CompilationResult.asExitCode
get() = when (this) {
CompilationResult.COMPILATION_ERROR -> ExitCode.COMPILATION_ERROR
CompilationResult.COMPILER_INTERNAL_ERROR -> ExitCode.INTERNAL_ERROR
CompilationResult.COMPILATION_OOM_ERROR -> ExitCode.OOM_ERROR
else -> ExitCode.OK
}
}
@OptIn(ExperimentalBuildToolsApi::class)
@@ -38,5 +38,11 @@
<version>ArtifactsTest.version</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-compiler-embeddable</artifactId>
<version>ArtifactsTest.version</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>