[BT] Implement basic non-incremental in-process JVM compilation
This commit is contained in:
committed by
Space Team
parent
6356a40fe8
commit
28c059f25b
@@ -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()
|
||||
|
||||
+43
-2
@@ -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
|
||||
}
|
||||
}
|
||||
+12
-2
@@ -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
|
||||
}
|
||||
}
|
||||
+33
-6
@@ -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")
|
||||
}
|
||||
}
|
||||
+30
@@ -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
|
||||
}
|
||||
-1
@@ -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(
|
||||
|
||||
+59
-17
@@ -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)
|
||||
|
||||
+6
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user