diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/CompilerOptionsFactory.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/CompilerOptionsFactory.kt new file mode 100644 index 0000000000..7e4dd330d8 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/CompilerOptionsFactory.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Google LLC + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") + +package com.google.devtools.ksp.gradle + +import org.gradle.api.model.ObjectFactory +import org.jetbrains.kotlin.gradle.dsl.CompilerCommonOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerCommonOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptionsDefault +import org.jetbrains.kotlin.gradle.utils.newInstance + +// TODO: to be replaced by KotlinJvmFactory, etc. +class CompilerOptionsFactory { + companion object { + fun createCompilerJvmOptions(objectFactory: ObjectFactory): CompilerJvmOptions = + objectFactory.newInstance() + + fun createCompilerJsOptions(objectFactory: ObjectFactory): CompilerJsOptions = + objectFactory.newInstance() + + fun createCompilerCommonOptions(objectFactory: ObjectFactory): CompilerCommonOptions = + objectFactory.newInstance() + } +} diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunner.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunner.kt index 4c91df7e5f..938beca6c0 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunner.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunner.kt @@ -26,6 +26,9 @@ import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.Internal +import org.jetbrains.kotlin.gradle.dsl.CompilerCommonOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptions import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy import org.jetbrains.kotlin.gradle.utils.newInstance import java.io.File @@ -49,37 +52,54 @@ interface KotlinCompilerRunner { interface KotlinJvmCompilerRunner : KotlinCompilerRunner { fun runJvmCompilerAsync( - args: KotlinJvmCompilerArguments, + options: CompilerJvmOptions, + freeArgs: List, sources: List, commonSources: List, - outputs: List + friendPaths: List, + libraries: List, + outputs: List, + destination: File ) } interface KotlinJsCompilerRunner : KotlinCompilerRunner { fun runJsCompilerAsync( - args: KotlinJsCompilerArguments, + options: CompilerJsOptions, + freeArgs: List, sources: List, commonSources: List, - outputs: List + friendPaths: List, + libraries: List, + outputs: List, + destination: File ) } interface KotlinMetadataCompilerRunner : KotlinCompilerRunner { fun runMetadataCompilerAsync( - args: KotlinMetadataCompilerArguments, + options: CompilerCommonOptions, + freeArgs: List, sources: List, commonSources: List, - outputs: List + friendPaths: List, + libraries: List, + outputs: List, + destination: File ) } interface KotlinNativeCompilerRunner : KotlinCompilerRunner { fun runNativeCompilerAsync( - args: KotlinNativeCompilerArguments, + options: CompilerCommonOptions, + freeArgs: List, sources: List, commonSources: List, + friendPaths: List, + libraries: List, outputs: List, + destination: File, + target: String ) } diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunnerImpls.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunnerImpls.kt index 587b662275..5836a1380a 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunnerImpls.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinCompilerRunnerImpls.kt @@ -28,7 +28,6 @@ import org.gradle.process.ExecOperations import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporterImpl -import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments @@ -52,6 +51,13 @@ import org.jetbrains.kotlin.utils.JsLibraryUtils import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import java.io.File import javax.inject.Inject +import org.jetbrains.kotlin.gradle.dsl.CompilerCommonOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerCommonOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptionsDefault +import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector internal inline fun ObjectFactory.property() = property(T::class.java) internal inline fun ObjectFactory.property(initialValue: T) = property().value(initialValue) @@ -86,9 +92,10 @@ abstract class KotlinCompilerRunnerImpl @Inject constructor( @Internal internal fun prepareEnvironment(allWarningsAsErrors: Boolean, outputs: List): GradleCompilerEnvironment { val messageCollector = GradlePrintingMessageCollector(GradleKotlinLogger(logger), allWarningsAsErrors) + val errorMessageCollector = GradleErrorMessageCollector(messageCollector) val outputItemCollector = OutputItemsCollectorImpl() return GradleCompilerEnvironment( - compilerClasspath.files.toList(), messageCollector, outputItemCollector, + compilerClasspath.files.toList(), errorMessageCollector, outputItemCollector, reportingSettings = ReportingSettings(), outputFiles = outputs ) @@ -110,20 +117,6 @@ abstract class KotlinCompilerRunnerImpl @Inject constructor( } } -internal fun CommonCompilerArguments.copyFrom(args: KotlinCompilerArguments) { - freeArgs = args.freeArgs - verbose = args.verbose - allWarningsAsErrors = args.allWarningsAsErrors - languageVersion = args.languageVersion - apiVersion = args.apiVersion - useK2 = args.useK2 - incrementalCompilation = args.incrementalCompilation - pluginOptions = args.pluginOptions.toTypedArray() - pluginClasspaths = args.pluginClasspaths.map { it.absolutePath }.toTypedArray() - expectActualLinker = args.expectActualLinker - multiPlatform = args.multiPlatform -} - abstract class KotlinJvmCompilerRunnerImpl @Inject constructor( task: Task, objectFactory: ObjectFactory, @@ -131,28 +124,25 @@ abstract class KotlinJvmCompilerRunnerImpl @Inject constructor( ) : KotlinCompilerRunnerImpl(task, objectFactory, workerExecutor), KotlinJvmCompilerRunner { override fun runJvmCompilerAsync( - args: KotlinJvmCompilerArguments, + options: CompilerJvmOptions, + freeArgs: List, sources: List, commonSources: List, - outputs: List + friendPaths: List, + libraries: List, + outputs: List, + destination: File ) { - val environment = prepareEnvironment(args.allWarningsAsErrors, outputs) + val environment = prepareEnvironment(options.allWarningsAsErrors.get(), outputs) val compilerRunner = prepareCompilerRunner() val compilerArgs = K2JVMCompilerArguments().apply { - copyFrom(args) - - friendPaths = args.friendPaths.map { it.absolutePath }.toTypedArray() - classpath = args.libraries.map { it.absolutePath }.joinToString(File.pathSeparator) - destination = args.destination?.absolutePath - - noJdk = args.noJdk - noStdlib = args.noStdlib - noReflect = args.noReflect - moduleName = args.moduleName - jvmTarget = args.jvmTarget - jdkRelease = args.jdkRelease - allowNoSourceFiles = args.allowNoSourceFiles - javaSourceRoots = args.javaSourceRoots.map { it.absolutePath }.toTypedArray() + options as CompilerJvmOptionsDefault + options.fillCompilerArguments(this) + + this@apply.friendPaths = friendPaths.map { it.absolutePath }.toTypedArray() + this@apply.classpath = libraries.map { it.absolutePath }.joinToString(File.pathSeparator) + this@apply.freeArgs = options.freeCompilerArgs.get() + freeArgs + this@apply.destination = destination.absolutePath } compilerRunner.runJvmCompilerAsync( @@ -191,30 +181,34 @@ abstract class KotlinJsCompilerRunnerImpl @Inject constructor( } override fun runJsCompilerAsync( - args: KotlinJsCompilerArguments, + options: CompilerJsOptions, + freeArgs: List, sources: List, commonSources: List, - outputs: List + friendPaths: List, + libraries: List, + outputs: List, + destination: File ) { - val environment = prepareEnvironment(args.allWarningsAsErrors, outputs) + val environment = prepareEnvironment(options.allWarningsAsErrors.get(), outputs) val compilerRunner = prepareCompilerRunner() val compilerArgs = K2JSCompilerArguments().apply { - copyFrom(args) + options as CompilerJsOptionsDefault + options.fillCompilerArguments(this) - outputFile = args.destination?.absolutePath + this@apply.freeArgs = options.freeCompilerArgs.get() + freeArgs + this@apply.outputFile = destination.absolutePath - noStdlib = args.noStdlib - irOnly = args.irOnly - irProduceJs = args.irProduceJs - irProduceKlibDir = args.irProduceKlibDir - irProduceKlibFile = args.irProduceKlibFile - irBuildCache = args.irBuildCache - wasm = args.wasm - target = args.target + irOnly = this@apply.freeArgs.contains("-Xir-only") + irProduceJs = this@apply.freeArgs.contains("-Xir-produce-js") + irProduceKlibDir = this@apply.freeArgs.contains("-Xir-produce-klib-dir") + irProduceKlibFile = this@apply.freeArgs.contains("-Xir-produce-klib-file") + irBuildCache = this@apply.freeArgs.contains("-Xir-build-cache") + wasm = this@apply.freeArgs.contains("-Xwasm") - friendModules = args.friendPaths.filter { libFilter(this, it) } + this@apply.friendModules = friendPaths.filter { libFilter(this, it) } .map { it.absolutePath }.joinToString(File.pathSeparator) - libraries = args.libraries.filter { libFilter(this, it) } + this@apply.libraries = libraries.filter { libFilter(this, it) } .map { it.absolutePath }.joinToString(File.pathSeparator) } @@ -229,19 +223,25 @@ abstract class KotlinMetadataCompilerRunnerImpl @Inject constructor( ) : KotlinCompilerRunnerImpl(task, objectFactory, workerExecutor), KotlinMetadataCompilerRunner { override fun runMetadataCompilerAsync( - args: KotlinMetadataCompilerArguments, + options: CompilerCommonOptions, + freeArgs: List, sources: List, commonSources: List, - outputs: List + friendPaths: List, + libraries: List, + outputs: List, + destination: File ) { - val environment = prepareEnvironment(args.allWarningsAsErrors, outputs) + val environment = prepareEnvironment(options.allWarningsAsErrors.get(), outputs) val compilerRunner = prepareCompilerRunner() val compilerArgs = K2MetadataCompilerArguments().apply { - copyFrom(args) + options as CompilerCommonOptionsDefault + options.fillCompilerArguments(this) - friendPaths = args.friendPaths.map { it.absolutePath }.toTypedArray() - classpath = args.libraries.map { it.absolutePath }.joinToString(File.pathSeparator) - destination = args.destination?.absolutePath + this@apply.friendPaths = friendPaths.map { it.absolutePath }.toTypedArray() + this@apply.classpath = libraries.map { it.absolutePath }.joinToString(File.pathSeparator) + this@apply.freeArgs = options.freeCompilerArgs.get() + freeArgs + this@apply.destination = destination.absolutePath } compilerRunner.runMetadataCompilerAsync(sources, commonSources, compilerArgs, environment) @@ -259,45 +259,47 @@ abstract class KotlinNativeCompilerRunnerImpl @Inject constructor( .KotlinNativeCompilerRunner.Settings.fromProject(task.project) override fun runNativeCompilerAsync( - args: KotlinNativeCompilerArguments, + options: CompilerCommonOptions, + freeArgs: List, sources: List, commonSources: List, + friendPaths: List, + libraries: List, outputs: List, + destination: File, + target: String ) { val output = File(outputs.first(), "dummy.out") - val target = KonanTarget.predefinedTargets.get(args.target!!)!! + val target = KonanTarget.predefinedTargets.get(target)!! val buildArgs: MutableList = mutableListOf( "-o", output.path, "-target", target.name, "-p", "library", "-Xmulti-platform" ) - args.libraries.flatMap { listOf("-l", it.absolutePath) }.let { buildArgs.addAll(it) } - args.friendPaths.ifNotEmpty { + libraries.flatMap { listOf("-l", it.absolutePath) }.let { buildArgs.addAll(it) } + friendPaths.ifNotEmpty { buildArgs.add("-friend-modules") buildArgs.add(joinToString(File.pathSeparator)) } - if (args.verbose) + if (options.verbose.get()) buildArgs.add("-verbose") - if (args.allWarningsAsErrors) + if (options.allWarningsAsErrors.get()) buildArgs.add("-Werror") - args.pluginClasspaths.map { "-Xplugin=${it.absolutePath}" }.let { buildArgs.addAll(it) } - args.pluginOptions.flatMap { listOf("-P", it) }.let { buildArgs.addAll(it) } - - args.languageVersion?.let { + options.languageVersion.getOrNull()?.let { buildArgs.add("-language-version") - buildArgs.add(it) + buildArgs.add(it.version) } - args.apiVersion?.let { + options.apiVersion.getOrNull()?.let { buildArgs.add("-api-version") - buildArgs.add(it) + buildArgs.add(it.version) } buildArgs.addAll(sources.map { it.absolutePath }) - buildArgs.addAll(args.freeArgs) + buildArgs.addAll(freeArgs) buildArgs.addAll(commonSources.map { it.absolutePath }) org.jetbrains.kotlin.compilerRunner.KotlinNativeCompilerRunner( diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt index 79e663f616..ed13258170 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt @@ -298,7 +298,7 @@ internal inline fun Project.locateTask(name: String): TaskPro internal fun findJavaTaskForKotlinCompilation(compilation: KotlinCompilation<*>): TaskProvider? = when (compilation) { is KotlinJvmAndroidCompilation -> compilation.compileJavaTaskProvider - is KotlinWithJavaCompilation -> compilation.compileJavaTaskProvider + is KotlinWithJavaCompilation<*, *> -> compilation.compileJavaTaskProvider is KotlinJvmCompilation -> compilation.compileJavaTaskProvider // may be null for Kotlin-only JVM target in MPP else -> null } diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/inherited/InheritedTasks.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/inherited/InheritedTasks.kt index 5bc8f35914..7a0586e823 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/inherited/InheritedTasks.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/inherited/InheritedTasks.kt @@ -55,11 +55,10 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments -import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptionsImpl -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptionsImpl +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.CompilerMultiplatformCommonOptionsDefault import org.jetbrains.kotlin.gradle.dsl.copyFreeCompilerArgsToArgs -import org.jetbrains.kotlin.gradle.dsl.fillDefaultValues import org.jetbrains.kotlin.gradle.internal.CompilerArgumentsContributor import org.jetbrains.kotlin.gradle.internal.compilerArgumentsConfigurationFlags import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE @@ -92,6 +91,9 @@ import java.nio.file.Paths import java.util.concurrent.Callable import javax.inject.Inject import kotlin.reflect.KProperty1 +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerMultiplatformCommonOptions @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_PARAMETER_TYPE") internal class Configurator : AbstractKotlinCompileConfig> { @@ -263,9 +265,10 @@ interface KspTaskInherited : KspTask { @CacheableTask abstract class KspTaskJvm @Inject constructor( + compilerOptions: CompilerJvmOptions, workerExecutor: WorkerExecutor, objectFactory: ObjectFactory -) : KotlinCompile(KotlinJvmOptionsImpl(), workerExecutor, objectFactory), KspTaskInherited { +) : KotlinCompile(compilerOptions, workerExecutor, objectFactory), KspTaskInherited { @get:PathSensitive(PathSensitivity.NONE) @get:Optional @get:InputFiles @@ -476,9 +479,10 @@ abstract class KspTaskJvm @Inject constructor( @CacheableTask abstract class KspTaskJS @Inject constructor( + compilerOptions: CompilerJsOptions, @get:Internal val objectFactory: ObjectFactory, workerExecutor: WorkerExecutor -) : Kotlin2JsCompile(KotlinJsOptionsImpl(), objectFactory, workerExecutor), KspTaskInherited { +) : Kotlin2JsCompile(compilerOptions, objectFactory, workerExecutor), KspTaskInherited { private val backendSelectionArgs = listOf( "-Xir-only", "-Xir-produce-js", @@ -519,7 +523,7 @@ abstract class KspTaskJS @Inject constructor( ignoreClasspathResolutionErrors: Boolean, ) { // Start with / copy from kotlinCompile. - args.fillDefaultValues() + (compilerOptions as CompilerJsOptionsDefault).fillDefaultValues(args) compileKotlinArgumentsContributor.get().contributeArguments( args, compilerArgumentsConfigurationFlags( @@ -570,9 +574,10 @@ abstract class KspTaskJS @Inject constructor( @CacheableTask abstract class KspTaskMetadata @Inject constructor( + compilerOptions: CompilerMultiplatformCommonOptions, workerExecutor: WorkerExecutor, objectFactory: ObjectFactory -) : KotlinCompileCommon(KotlinMultiplatformCommonOptionsImpl(), workerExecutor, objectFactory), KspTaskInherited { +) : KotlinCompileCommon(compilerOptions, workerExecutor, objectFactory), KspTaskInherited { override fun configureCompilation( kotlinCompilation: KotlinCompilationData<*>, kotlinCompile: AbstractKotlinCompile<*>, @@ -603,7 +608,7 @@ abstract class KspTaskMetadata @Inject constructor( ignoreClasspathResolutionErrors: Boolean, ) { // Start with / copy from kotlinCompile. - args.apply { fillDefaultValues() } + (compilerOptions as CompilerJsOptionsDefault).fillDefaultValues(args) compileKotlinArgumentsContributor.get().contributeArguments( args, compilerArgumentsConfigurationFlags( diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/standalone/StandaloneTasks.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/standalone/StandaloneTasks.kt index aade62b2d5..367deada0c 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/standalone/StandaloneTasks.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/tasks/standalone/StandaloneTasks.kt @@ -19,11 +19,7 @@ package com.google.devtools.ksp.gradle.tasks.standalone -import com.google.devtools.ksp.gradle.KotlinCompilerArguments -import com.google.devtools.ksp.gradle.KotlinJsCompilerArguments -import com.google.devtools.ksp.gradle.KotlinJvmCompilerArguments -import com.google.devtools.ksp.gradle.KotlinMetadataCompilerArguments -import com.google.devtools.ksp.gradle.KotlinNativeCompilerArguments +import com.google.devtools.ksp.gradle.CompilerOptionsFactory import com.google.devtools.ksp.gradle.KspExtension import com.google.devtools.ksp.gradle.KspGradleSubplugin import com.google.devtools.ksp.gradle.KspGradleSubplugin.Companion.getKspCachesDir @@ -91,6 +87,11 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.io.File import java.nio.file.Paths import javax.inject.Inject +import org.jetbrains.kotlin.gradle.dsl.CompilerCommonOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerCommonToolOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJsOptions +import org.jetbrains.kotlin.gradle.dsl.CompilerJvmOptions +import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask object StandaloneTasks : KspTaskCreator { override fun createKspTask( @@ -292,7 +293,7 @@ private fun ChangedFiles.hasNonSourceChange(): Boolean { } } -fun KotlinCompilerArguments.addChangedClasses(changed: KaptClasspathChanges) { +fun MutableList.addChangedClasses(changed: KaptClasspathChanges) { if (changed is KaptClasspathChanges.Known) { changed.names.map { it.replace('/', '.').replace('$', '.') }.ifNotEmpty { addPluginOptions(listOf(SubpluginOption("changedClasses", joinToString(":")))) @@ -302,11 +303,11 @@ fun KotlinCompilerArguments.addChangedClasses(changed: KaptClasspathChanges) { fun SubpluginOption.toArg() = "plugin:${KspGradleSubplugin.KSP_PLUGIN_ID}:$key=$value" -fun KotlinCompilerArguments.addPluginOptions(options: List) { - pluginOptions += options.map { it.toArg() } +fun MutableList.addPluginOptions(options: List) { + this.addAll(options.flatMap { listOf("-P", it.toArg()) }) } -fun KotlinCompilerArguments.addChangedFiles(changedFiles: ChangedFiles) { +fun MutableList.addChangedFiles(changedFiles: ChangedFiles) { if (changedFiles is ChangedFiles.Known) { val options = mutableListOf() changedFiles.modified.filter { it.isKotlinFile(listOf("kt")) || it.isJavaFile() }.ifNotEmpty { @@ -326,17 +327,48 @@ internal fun File.isParentOf(childCandidate: File): Boolean { return childCandidatePath.startsWith(parentPath) } -private fun KotlinCompilerArguments.fromTask(task: KspTaskStandalone) { - useK2 = false - incrementalCompilation = false - pluginClasspaths = task.pluginClasspath.files.toList() - apiVersion = task.kotlinApiVersion.orNull - languageVersion = task.kotlinLanguageVersion.orNull - verbose = task.verbose.get() - multiPlatform = task.multiplatform.get() - allWarningsAsErrors = task.allWarningAsErrors.get() - - addPluginOptions(task.options.get()) +private fun CompilerCommonToolOptions.from(that: CompilerCommonToolOptions) { + // Copy from compilation task + allWarningsAsErrors.value(that.allWarningsAsErrors) + suppressWarnings.value(that.suppressWarnings) + verbose.value(that.verbose) + + // NOT COPIED: freeCompilerArgs +} + +private fun CompilerCommonOptions.from(that: CompilerCommonOptions) { + // Copy from compilation task + (this as CompilerCommonToolOptions).from(that) + apiVersion.value(that.apiVersion) + languageVersion.value(that.languageVersion) + + // NOT COPIED: + useK2.value(false) +} + +private fun CompilerJvmOptions.from(that: CompilerJvmOptions) { + // Copy from compilation task + (this as CompilerCommonOptions).from(that) + javaParameters.value(that.javaParameters) + jvmTarget.value(that.jvmTarget) + moduleName.value(that.moduleName) + + // NOT COPIED: + noJdk.value(true) +} + +private fun CompilerJsOptions.from(that: CompilerJsOptions) { + // Copy from compilation task + (this as CompilerCommonOptions).from(that) + friendModulesDisabled.value(that.friendModulesDisabled) + main.value(that.main) + metaInfo.value(that.metaInfo) + moduleKind.value(that.moduleKind) + moduleName.value(that.moduleName) + noStdlib.value(that.noStdlib) + target.value(that.target) + + // NOT COPIED: outputFile, sourceMap, sourceMapEmbedSources, sourceMapPrefix, typedArrays } @CacheableTask @@ -346,9 +378,6 @@ abstract class KspTaskJvm @Inject constructor( @get:Nested val compilerRunner = createKotlinJvmCompilerRunner(this, objectFactory) - @get:Input - abstract val moduleName: Property - @get:Input var isIntermoduleIncremental: Boolean = false @@ -370,25 +399,21 @@ abstract class KspTaskJvm @Inject constructor( classpathSnapshotProperties.classpathSnapshot ) - @get:Internal - abstract val noJdk: Property + @get:Nested + val compilerOptions: CompilerJvmOptions = CompilerOptionsFactory.createCompilerJvmOptions(objectFactory) @TaskAction fun execute(inputChanges: InputChanges) { - val args = KotlinJvmCompilerArguments().apply { - // Common - fromTask(this@KspTaskJvm) - - // JVM - noJdk = this@KspTaskJvm.noJdk.get() - allowNoSourceFiles = true - destination = this@KspTaskJvm.destination - noReflect = true - noStdlib = true - libraries = this@KspTaskJvm.libraries.files.toList() - friendPaths = this@KspTaskJvm.friendPaths.files.toList() - moduleName = this@KspTaskJvm.moduleName.get() - } + val freeArgs = mutableListOf( + "-Xallow-no-source-files", + "-no-stdlib", + ) + + if (multiplatform.get()) + freeArgs.add("-Xmulti-platform") + + freeArgs.addAll(pluginClasspath.map { "-Xplugin=${it.absolutePath}" }) + freeArgs.addPluginOptions(options.get()) // Clean outputs. Backups will be copied back if incremental. // TODO: leave outputs untouched and only restore outputs if build fails. @@ -402,7 +427,7 @@ abstract class KspTaskJvm @Inject constructor( // 1. unknown changes, or // 2. changes in annotation processors. val classpathChanges = findClasspathChanges(changedFiles) - args.addChangedClasses(classpathChanges) + freeArgs.addChangedClasses(classpathChanges) } else { if (changedFiles.hasNonSourceChange()) { clearIncCache() @@ -411,13 +436,17 @@ abstract class KspTaskJvm @Inject constructor( } else { clearIncCache() } - args.addChangedFiles(changedFiles) + freeArgs.addChangedFiles(changedFiles) compilerRunner.runJvmCompilerAsync( - args, + compilerOptions, + freeArgs, allSources.files.filter { !destination.isParentOf(it) }, commonSources.files.filter { !destination.isParentOf(it) }, - outputs.files.toList() + friendPaths.files.toList(), + libraries.files.toList(), + outputs.files.toList(), + destination ) } @@ -433,12 +462,12 @@ abstract class KspTaskJvm @Inject constructor( compilerRunner.useDaemonFallbackStrategy.set(kotlinCompile.useDaemonFallbackStrategy) compilerRunner.kotlinDaemonJvmArguments.set(kotlinCompile.kotlinDaemonJvmArguments) + kotlinCompile as KotlinCompilationTask + compilerOptions.from(kotlinCompile.compilerOptions) // Android compilation doesn't include JDK libraries. // Copying the settings from KotlinCompilation is complicated and implementation dependent. So let's check // for Android explicitly. - noJdk.value(kotlinCompilation is KotlinJvmAndroidCompilation) - - moduleName.value(kotlinCompilation.moduleName) + compilerOptions.noJdk.value(kotlinCompilation is KotlinJvmAndroidCompilation) isIntermoduleIncremental = (project.findProperty("ksp.incremental.intermodule")?.toString()?.toBoolean() ?: true) && isKspIncremental @@ -538,6 +567,9 @@ abstract class KspTaskJs @Inject constructor( @get:Nested val compilerRunner = createKotlinJsCompilerRunner(this, objectFactory) + @get:Nested + val compilerOptions: CompilerJsOptions = CompilerOptionsFactory.createCompilerJsOptions(objectFactory) + @get:Internal abstract val freeArgs: ListProperty @@ -550,28 +582,14 @@ abstract class KspTaskJs @Inject constructor( @TaskAction fun execute(inputChanges: InputChanges) { - val args = KotlinJsCompilerArguments().apply { - // Common - fromTask(this@KspTaskJs) - - // JS - destination = this@KspTaskJs.destination - noStdlib = true - libraries = this@KspTaskJs.libraries.files.toList() - friendPaths = this@KspTaskJs.friendPaths.files.toList() - destination = File(destination, "dummyOutput.js") - - this@KspTaskJs.freeArgs.get().forEach { - when (it) { - "-Xir-only" -> irOnly = true - "-Xir-produce-js" -> irProduceJs = true - "-Xir-produce-klib-file" -> irProduceKlibFile = true - "-Xir-produce-klib-dir" -> irProduceKlibDir = true - "-Xir-build-cache" -> irBuildCache = true - "-Xwasm" -> wasm = true - } - } - } + val freeArgs = mutableListOf() + + if (multiplatform.get()) + freeArgs.add("-Xmulti-platform") + + freeArgs.addAll(pluginClasspath.map { "-Xplugin=${it.absolutePath}" }) + freeArgs.addPluginOptions(options.get()) + freeArgs.addAll(this@KspTaskJs.freeArgs.get().filter { it in backendSelectionArgs }) // Clean outputs. Backups will be copied back if incremental. // TODO: leave outputs untouched and only restore outputs if build fails. @@ -586,16 +604,29 @@ abstract class KspTaskJs @Inject constructor( } else { clearIncCache() } - args.addChangedFiles(changedFiles) + freeArgs.addChangedFiles(changedFiles) compilerRunner.runJsCompilerAsync( - args, + compilerOptions, + freeArgs, allSources.files.toList(), commonSources.files.toList(), - outputs.files.toList() + friendPaths.files.toList(), + libraries.files.toList(), + outputs.files.toList(), + destination ) } + private val backendSelectionArgs = setOf( + "-Xir-only", + "-Xir-produce-js", + "-Xir-produce-klib-file", + "-Xir-produce-klib-dir", + "-Xir-build-cache", + "-Xwasm", + ) + override fun configureCompilation( kotlinCompilation: KotlinCompilation<*>, kotlinCompile: KotlinCompileTool, @@ -608,7 +639,9 @@ abstract class KspTaskJs @Inject constructor( compilerRunner.useDaemonFallbackStrategy.set(kotlinCompile.useDaemonFallbackStrategy) compilerRunner.kotlinDaemonJvmArguments.set(kotlinCompile.kotlinDaemonJvmArguments) - freeArgs.value(kotlinCompilation.kotlinOptions.freeCompilerArgs) + kotlinCompile as KotlinCompilationTask + compilerOptions.from(kotlinCompile.compilerOptions) + freeArgs.value(kotlinCompile.compilerOptions.freeCompilerArgs) } } @@ -626,18 +659,20 @@ abstract class KspTaskMetadata @Inject constructor( commonSources, ) + @get:Nested + val compilerOptions: CompilerCommonOptions = CompilerOptionsFactory.createCompilerCommonOptions(objectFactory) + @TaskAction fun execute(inputChanges: InputChanges) { - val args = KotlinMetadataCompilerArguments().apply { - // Common - fromTask(this@KspTaskMetadata) - - // Metadata - destination = this@KspTaskMetadata.destination - libraries = this@KspTaskMetadata.libraries.files.toList() - friendPaths = this@KspTaskMetadata.friendPaths.files.toList() - expectActualLinker = true - } + val freeArgs = mutableListOf( + "-Xexpect-actual-linker" + ) + + if (multiplatform.get()) + freeArgs.add("-Xmulti-platform") + + freeArgs.addAll(pluginClasspath.map { "-Xplugin=${it.absolutePath}" }) + freeArgs.addPluginOptions(options.get()) // Clean outputs. Backups will be copied back if incremental. // TODO: leave outputs untouched and only restore outputs if build fails. @@ -652,13 +687,17 @@ abstract class KspTaskMetadata @Inject constructor( } else { clearIncCache() } - args.addChangedFiles(changedFiles) + freeArgs.addChangedFiles(changedFiles) compilerRunner.runMetadataCompilerAsync( - args, - allSources.files.toList(), - commonSources.files.toList(), - outputs.files.toList() + compilerOptions, + freeArgs, + allSources.files.filter { !destination.isParentOf(it) }, + commonSources.files.filter { !destination.isParentOf(it) }, + friendPaths.files.toList(), + libraries.files.toList(), + outputs.files.toList(), + destination ) } @@ -673,6 +712,9 @@ abstract class KspTaskMetadata @Inject constructor( compilerRunner.compilerExecutionStrategy.set(kotlinCompile.compilerExecutionStrategy) compilerRunner.useDaemonFallbackStrategy.set(kotlinCompile.useDaemonFallbackStrategy) compilerRunner.kotlinDaemonJvmArguments.set(kotlinCompile.kotlinDaemonJvmArguments) + + kotlinCompile as KotlinCompilationTask + compilerOptions.from(kotlinCompile.compilerOptions) } } @@ -693,19 +735,20 @@ abstract class KspTaskNative @Inject constructor( @get:Input abstract val target: Property + @get:Nested + val compilerOptions: CompilerCommonOptions = CompilerOptionsFactory.createCompilerCommonOptions(objectFactory) + @TaskAction fun execute(inputChanges: InputChanges) { - val args = KotlinNativeCompilerArguments().apply { - // Common - fromTask(this@KspTaskNative) - - // Native - destination = this@KspTaskNative.destination - libraries = this@KspTaskNative.libraries.files.toList() - expectActualLinker = true - friendPaths = this@KspTaskNative.friendPaths.files.toList() - target = this@KspTaskNative.target.get() - } + val freeArgs = mutableListOf( + "-Xexpect-actual-linker" + ) + + if (multiplatform.get()) + freeArgs.add("-Xmulti-platform") + + freeArgs.addAll(pluginClasspath.map { "-Xplugin=${it.absolutePath}" }) + freeArgs.addPluginOptions(options.get()) // Clean outputs. Backups will be copied back if incremental. // TODO: leave outputs untouched and only restore outputs if build fails. @@ -720,13 +763,18 @@ abstract class KspTaskNative @Inject constructor( } else { clearIncCache() } - args.addChangedFiles(changedFiles) + freeArgs.addChangedFiles(changedFiles) compilerRunner.runNativeCompilerAsync( - args, - allSources.toList(), - commonSources.toList(), + compilerOptions, + freeArgs, + allSources.files.filter { !destination.isParentOf(it) }, + commonSources.files.filter { !destination.isParentOf(it) }, + friendPaths.files.toList(), + libraries.files.toList(), outputs.files.toList(), + destination, + target.get() ) } @@ -735,6 +783,9 @@ abstract class KspTaskNative @Inject constructor( kotlinCompile: KotlinCompileTool, ) { target.value((kotlinCompile as KotlinNativeCompile).target) + + kotlinCompile as KotlinCompilationTask + compilerOptions.from(kotlinCompile.compilerOptions) } }