[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 {
|
dependencies {
|
||||||
api(project(":compiler:build-tools:kotlin-build-tools-api"))
|
api(project(":compiler:build-tools:kotlin-build-tools-api"))
|
||||||
implementation(kotlinStdlib())
|
implementation(kotlinStdlib())
|
||||||
|
compileOnly(project(":compiler:cli"))
|
||||||
|
compileOnly(project(":compiler:cli-js"))
|
||||||
|
runtimeOnly(project(":kotlin-compiler-embeddable"))
|
||||||
}
|
}
|
||||||
|
|
||||||
publish()
|
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.CompilationResult
|
||||||
import org.jetbrains.kotlin.buildtools.api.CompilationService
|
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.CompilerExecutionStrategyConfiguration
|
||||||
import org.jetbrains.kotlin.buildtools.api.jvm.*
|
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
|
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 {
|
internal class CompilationServiceImpl : CompilationService {
|
||||||
override fun calculateClasspathSnapshot(classpathEntry: File): ClasspathEntrySnapshot {
|
override fun calculateClasspathSnapshot(classpathEntry: File): ClasspathEntrySnapshot {
|
||||||
TODO("Calculating classpath snapshots via the Build Tools API is not yet implemented: KT-57565")
|
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>,
|
sources: List<File>,
|
||||||
arguments: List<String>
|
arguments: List<String>
|
||||||
): CompilationResult {
|
): CompilationResult {
|
||||||
println("I'm simulating compilation, nothing more yet")
|
check(strategyConfig is CompilerExecutionStrategyConfigurationImpl) {
|
||||||
return CompilationResult.COMPILATION_SUCCESS
|
"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 org.jetbrains.kotlin.buildtools.api.CompilerExecutionStrategyConfiguration
|
||||||
import java.io.File
|
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 {
|
override fun useInProcessStrategy(): CompilerExecutionStrategyConfiguration {
|
||||||
|
selectedStrategy = CompilerExecutionStrategy.InProcess
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun useDaemonStrategy(sessionDir: File, jvmArguments: List<String>): CompilerExecutionStrategyConfiguration {
|
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
|
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 {
|
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
|
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?) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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)
|
override val defaultBuildOptions = super.defaultBuildOptions.copy(runViaBuildToolsApi = true)
|
||||||
|
|
||||||
@GradleTest
|
@GradleTest
|
||||||
@Disabled
|
|
||||||
@DisplayName("Simple project non-incremental in-process compilation")
|
@DisplayName("Simple project non-incremental in-process compilation")
|
||||||
fun compileJvmInProcessNonIncremental(gradleVersion: GradleVersion) = testSimpleProject(
|
fun compileJvmInProcessNonIncremental(gradleVersion: GradleVersion) = testSimpleProject(
|
||||||
gradleVersion, defaultBuildOptions.copy(
|
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.BuildMetricsReporter
|
||||||
import org.jetbrains.kotlin.build.report.metrics.GradleBuildPerformanceMetric
|
import org.jetbrains.kotlin.build.report.metrics.GradleBuildPerformanceMetric
|
||||||
import org.jetbrains.kotlin.build.report.metrics.GradleBuildTime
|
import org.jetbrains.kotlin.build.report.metrics.GradleBuildTime
|
||||||
import org.jetbrains.kotlin.buildtools.api.CompilationService
|
import org.jetbrains.kotlin.buildtools.api.*
|
||||||
import org.jetbrains.kotlin.buildtools.api.ExperimentalBuildToolsApi
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.buildtools.api.SharedApiClassesClassLoader
|
|
||||||
import org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWorkArguments
|
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.ClassLoadersCachingBuildService
|
||||||
import org.jetbrains.kotlin.gradle.internal.ParentClassLoaderProvider
|
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
|
import java.io.File
|
||||||
|
|
||||||
@OptIn(ExperimentalBuildToolsApi::class)
|
@OptIn(ExperimentalBuildToolsApi::class)
|
||||||
@@ -35,21 +41,57 @@ internal abstract class BuildToolsApiCompilationWork : WorkAction<BuildToolsApiC
|
|||||||
private val workArguments
|
private val workArguments
|
||||||
get() = parameters.compilerWorkArguments.get()
|
get() = parameters.compilerWorkArguments.get()
|
||||||
|
|
||||||
override fun execute() {
|
private val taskPath
|
||||||
val classLoader = parameters.classLoadersCachingService.get()
|
get() = workArguments.taskPath
|
||||||
.getClassLoader(workArguments.compilerFullClasspath, SharedApiClassesClassLoaderProvider)
|
|
||||||
val compilationService = CompilationService.loadImplementation(classLoader)
|
private val log: KotlinLogger by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
val executionConfig = compilationService.makeCompilerExecutionStrategyConfiguration().apply {
|
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
|
||||||
useInProcessStrategy()
|
?: run {
|
||||||
}
|
val logger = LoggerFactory.getLogger("GradleKotlinCompilerWork")
|
||||||
val jvmCompilationConfig = compilationService.makeJvmCompilationConfiguration()
|
val kotlinLogger = if (logger is org.gradle.api.logging.Logger) {
|
||||||
compilationService.compileJvm(
|
GradleKotlinLogger(logger)
|
||||||
executionConfig,
|
} else SL4JKotlinLogger(logger)
|
||||||
jvmCompilationConfig,
|
|
||||||
emptyList(),
|
kotlinLogger.apply {
|
||||||
emptyList(),
|
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)
|
@OptIn(ExperimentalBuildToolsApi::class)
|
||||||
|
|||||||
+6
@@ -38,5 +38,11 @@
|
|||||||
<version>ArtifactsTest.version</version>
|
<version>ArtifactsTest.version</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-compiler-embeddable</artifactId>
|
||||||
|
<version>ArtifactsTest.version</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
Reference in New Issue
Block a user