diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt index ead9b0fc2bc..7efcc1bd1fc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt @@ -77,15 +77,11 @@ internal open class GradleCompilerRunner( * @see [GradleKotlinCompilerWork] */ fun runJvmCompilerAsync( - sourcesToCompile: List, - 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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt index e20e085e621..00c51943893 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt @@ -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 { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt index c868e069ab7..e2d0bad8c4b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt @@ -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, + 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 { + 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, @@ -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, diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/JvmCompilerArgumentsTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/JvmCompilerArgumentsTest.kt new file mode 100644 index 00000000000..e047f3a1c2f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/JvmCompilerArgumentsTest.kt @@ -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()!!.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()) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/compilerArgumentUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/compilerArgumentUtils.kt new file mode 100644 index 00000000000..b99a2638f54 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/compilerArgumentUtils.kt @@ -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.assertNotNull(property: KProperty1): R { + return property.get(this) ?: fail("Missing '${property.name}'") +} \ No newline at end of file