[Gradle] KotlinCompile: Implement KotlinCompilerArgumentsProducer

KTIJ-24976
This commit is contained in:
Sebastian Sellmair
2023-03-21 09:49:45 +01:00
committed by Space Team
parent 0a3eb68934
commit d4cc842200
5 changed files with 186 additions and 12 deletions
@@ -77,15 +77,11 @@ internal open class GradleCompilerRunner(
* @see [GradleKotlinCompilerWork]
*/
fun runJvmCompilerAsync(
sourcesToCompile: List<File>,
javaPackagePrefix: String?,
args: K2JVMCompilerArguments,
environment: GradleCompilerEnvironment,
jdkHome: File,
taskOutputsBackup: TaskOutputsBackup?
): WorkQueue? {
args.freeArgs += sourcesToCompile.map { it.absolutePath }
args.javaPackagePrefix = javaPackagePrefix
if (args.jdkHome == null && !args.noJdk) args.jdkHome = jdkHome.absolutePath
loggerProvider.kotlinInfo("Kotlin compilation 'jdkHome' argument: ${args.jdkHome}")
return runCompilerAsync(KotlinCompilerClass.JVM, args, environment, taskOutputsBackup)
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.measureTimeMillisWithResult
import kotlin.system.measureTimeMillis
@Suppress("LoggingStringTemplateAsArgument")
internal class IdeMultiplatformImportImpl(
private val extension: KotlinProjectExtension
) : IdeMultiplatformImport {
@@ -30,7 +30,9 @@ import org.jetbrains.kotlin.compilerRunner.IncrementalCompilationEnvironment
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsHelper
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.usesK2
import org.jetbrains.kotlin.gradle.internal.CompilerArgumentsContributor
import org.jetbrains.kotlin.gradle.internal.KotlinJvmCompilerArgumentsContributor
import org.jetbrains.kotlin.gradle.internal.compilerArgumentsConfigurationFlags
@@ -38,25 +40,25 @@ import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.ArgumentType
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext.Companion.create
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.report.BuildReportMode
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.gradle.utils.chainedDisallowChanges
import org.jetbrains.kotlin.gradle.utils.newInstance
import org.jetbrains.kotlin.gradle.utils.property
import org.jetbrains.kotlin.gradle.utils.propertyWithNewInstance
import org.jetbrains.kotlin.incremental.ClasspathChanges
import org.jetbrains.kotlin.incremental.ClasspathChanges.*
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.*
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.NoChanges
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.ToBeComputedByIncrementalCompiler
import org.jetbrains.kotlin.incremental.ClasspathSnapshotFiles
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
import org.jetbrains.kotlin.utils.addToStdlib.cast
import java.io.File
import javax.inject.Inject
import kotlin.text.appendLine
@CacheableTask
abstract class KotlinCompile @Inject constructor(
@@ -67,6 +69,7 @@ abstract class KotlinCompile @Inject constructor(
K2MultiplatformCompilationTask,
@Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl,
KotlinCompilationTask<KotlinJvmCompilerOptions>,
KotlinCompilerArgumentsProducer,
UsesKotlinJavaToolchain {
@get:Internal // covered by compiler options
@@ -250,6 +253,67 @@ abstract class KotlinCompile @Inject constructor(
KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this))
}
override fun createCompilerArguments(
context: KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext
): K2JVMCompilerArguments = context.create<K2JVMCompilerArguments> {
contribute(ArgumentType.Primitive) { args ->
args.allowNoSourceFiles = true
args.multiPlatform = multiPlatformEnabled.get()
args.pluginOptions = (pluginOptions.toSingleCompilerPluginOptions() + kotlinPluginData?.orNull?.options)
.arguments.toTypedArray()
args.moduleName = compilerOptions.moduleName.orNull
args.destinationAsFile = destinationDirectory.get().asFile
args.javaPackagePrefix = javaPackagePrefix
if (compilerOptions.usesK2.get()) {
args.fragments = multiplatformStructure.fragmentsCompilerArgs
args.fragmentRefines = multiplatformStructure.fragmentRefinesCompilerArgs
}
if (reportingSettings().buildReportMode == BuildReportMode.VERBOSE) {
args.reportPerf = true
}
KotlinJvmCompilerOptionsHelper.fillCompilerArguments(compilerOptions, args)
val localExecutionTimeFreeCompilerArgs = executionTimeFreeCompilerArgs
if (localExecutionTimeFreeCompilerArgs != null) {
args.freeArgs = localExecutionTimeFreeCompilerArgs
}
}
contribute(ArgumentType.PluginClasspath) { args ->
args.pluginClasspaths = tryLenient {
listOfNotNull(
pluginClasspath, kotlinPluginData?.orNull?.classpath
).reduce(FileCollection::plus).toPathsArray()
}
}
contribute(ArgumentType.DependencyClasspath) { args ->
args.friendPaths = friendPaths.toPathsArray()
args.classpathAsList = tryLenient {
libraries.toList().filter { it.exists() }
}.orEmpty()
}
contribute(ArgumentType.Sources) { args ->
if (compilerOptions.usesK2.get()) {
args.fragmentSources = multiplatformStructure.fragmentSourcesCompilerArgs
} else {
args.commonSources = commonSourceSet.asFileTree.toPathsArray()
}
args.freeArgs += (scriptSources.asFileTree.files + javaSources.files + sources.asFileTree.files).map { it.absolutePath }
}
}
override fun callCompilerAsync(
args: K2JVMCompilerArguments,
kotlinSources: Set<File>,
@@ -297,8 +361,6 @@ abstract class KotlinCompile @Inject constructor(
logger.info("Script source files: ${scriptSources.joinToString()}")
logger.info("Script file extensions: ${scriptExtensions.get().joinToString()}")
compilerRunner.runJvmCompilerAsync(
(kotlinSources + scriptSources + javaSources).toList(),
javaPackagePrefix,
args,
environment,
defaultKotlinJavaToolchain.get().buildJvm.get().javaHome,
@@ -0,0 +1,103 @@
/*
* 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.
*/
@file:Suppress("FunctionName")
package org.jetbrains.kotlin.gradle.unitTests
import org.gradle.kotlin.dsl.repositories
import org.jetbrains.kotlin.cli.common.arguments.Argument
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.gradle.dependencyResolutionTests.mavenCentralCacheRedirector
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.dsl.kotlinJvmExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext.Companion.lenient
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.util.assertNotNull
import org.jetbrains.kotlin.gradle.util.buildProjectWithJvm
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
import kotlin.reflect.full.findAnnotation
import kotlin.test.*
class JvmCompilerArgumentsTest {
@Suppress("DEPRECATION")
@Test
fun `test - simple project - compare CompilerArgumentsAware with KotlinCompilerArgumentsAware implementations`() {
val project = buildProjectWithJvm()
project.repositories {
mavenLocal()
mavenCentralCacheRedirector()
}
val kotlin = project.kotlinJvmExtension
project.evaluate()
val mainCompilation = kotlin.target.compilations.getByName("main")
val mainCompilationTask = mainCompilation.compileTaskProvider.get() as KotlinCompile
val argumentsFromCompilerArgumentsAware = mainCompilationTask.prepareCompilerArguments(true)
val argumentsFromKotlinCompilerArgumentsAware = mainCompilationTask.createCompilerArguments(lenient)
assertEquals(
ArgumentUtils.convertArgumentsToStringList(argumentsFromCompilerArgumentsAware),
ArgumentUtils.convertArgumentsToStringList(argumentsFromKotlinCompilerArgumentsAware)
)
}
/**
* The jvmTargets default argument value is up for change over time.
* The argument shall always be explicitly set!
*/
@Test
fun `test - simple project - jvmTarget is explicit - and uses correct default`() {
val project = buildProjectWithJvm()
val kotlin = project.kotlinJvmExtension
project.evaluate()
val mainCompilation = kotlin.target.compilations.getByName("main")
val mainCompilationTask = mainCompilation.compileTaskProvider.get() as KotlinCompile
val arguments = mainCompilationTask.createCompilerArguments(lenient)
val argumentsString = ArgumentUtils.convertArgumentsToStringList(arguments)
val jvmTargetArgument = K2JVMCompilerArguments::jvmTarget.findAnnotation<Argument>()!!.value
if (jvmTargetArgument !in argumentsString) fail("Missing '$jvmTargetArgument' in argument list")
val indexOfJvmTargetArgument = argumentsString.indexOf(jvmTargetArgument)
val jvmTargetTargetArgumentValue = argumentsString.getOrNull(indexOfJvmTargetArgument + 1)
assertEquals(JvmTarget.DEFAULT.description, jvmTargetTargetArgumentValue)
val parsedArguments = K2JVMCompilerArguments().apply { parseCommandLineArguments(argumentsString, this) }
assertNotNull(parsedArguments.jvmTarget)
assertEquals(JvmTarget.DEFAULT.description, parsedArguments.jvmTarget)
}
@Test
fun `test - multiplatform - with K2`() {
val project = buildProjectWithMPP()
val kotlin = project.multiplatformExtension
kotlin.jvm()
val jvmMainCompilation = kotlin.jvm().compilations.getByName("main")
jvmMainCompilation.compilerOptions.options.languageVersion.set(KotlinVersion.KOTLIN_2_0)
project.evaluate()
val jvmMainCompileTask = jvmMainCompilation.compileTaskProvider.get() as KotlinCompile
val arguments = jvmMainCompileTask.createCompilerArguments(lenient)
arguments.assertNotNull(CommonCompilerArguments::fragments).let { fragments ->
assertEquals(setOf("commonMain", "jvmMain"), fragments.toSet())
}
}
}
@@ -0,0 +1,14 @@
/*
* 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.gradle.util
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import kotlin.reflect.KProperty1
import kotlin.test.fail
fun <T : CommonToolArguments, R> T.assertNotNull(property: KProperty1<T, R?>): R {
return property.get(this) ?: fail("Missing '${property.name}'")
}