diff options
Diffstat (limited to 'gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt')
-rw-r--r-- | gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt new file mode 100644 index 00000000..73b5d8e1 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt @@ -0,0 +1,284 @@ +/* + * 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.Project +import org.gradle.api.Task +import org.gradle.api.file.FileCollection +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.IgnoreEmptyDirectories +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SkipWhenEmpty +import org.gradle.api.tasks.TaskProvider +import org.gradle.process.CommandLineArgumentProvider +import org.gradle.process.ExecOperations +import org.gradle.work.InputChanges +import org.gradle.workers.WorkerExecutor +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.KotlinCommonCompilerOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonCompilerOptionsDefault +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.mpp.enabledOnCurrentHost +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinNativeCompilationData +import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon +import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.gradle.tasks.TaskOutputsBackup +import org.jetbrains.kotlin.gradle.tasks.configuration.BaseKotlin2JsCompileConfig +import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileCommonConfig +import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig +import org.jetbrains.kotlin.incremental.ChangedFiles +import java.io.File +import java.nio.file.Paths +import javax.inject.Inject + +/** + * TODO: Replace with KGP's Kotlin*Factory after: + * https://youtrack.jetbrains.com/issue/KT-54986/KGP-API-to-toggle-incremental-compilation + * https://youtrack.jetbrains.com/issue/KT-55031/KGP-API-to-create-compilation-tasks-of-JS-Metadata-and-Native + */ +class KotlinFactories { + companion object { + fun registerKotlinJvmCompileTask( + project: Project, + taskName: String, + kotlinCompilation: KotlinCompilationData<*>, + ): TaskProvider<out KspTaskJvm> { + return project.tasks.register(taskName, KspTaskJvm::class.java).also { kspTaskProvider -> + KotlinCompileConfig(kotlinCompilation) + .execute(kspTaskProvider as TaskProvider<KotlinCompile>) + + // useClasspathSnapshot isn't configurable per task. + // Workaround: enable the other path and ignore irrelevant changes + // See [KotlinCompileConfig] in for details. + // FIXME: make it configurable in upstream or support useClasspathSnapshot == true, if possible. + kspTaskProvider.configure { + if (it.classpathSnapshotProperties.useClasspathSnapshot.get()) { + it.classpathSnapshotProperties.classpath.from(project.provider { it.libraries }) + } + } + } + } + + fun registerKotlinJSCompileTask( + project: Project, + taskName: String, + kotlinCompilation: KotlinCompilationData<*>, + ): TaskProvider<out KspTaskJS> { + return project.tasks.register(taskName, KspTaskJS::class.java).also { kspTaskProvider -> + BaseKotlin2JsCompileConfig<Kotlin2JsCompile>(kotlinCompilation) + .execute(kspTaskProvider as TaskProvider<Kotlin2JsCompile>) + kspTaskProvider.configure { + it.incrementalJsKlib = false + } + } + } + + fun registerKotlinMetadataCompileTask( + project: Project, + taskName: String, + kotlinCompilation: KotlinCompilationData<*>, + ): TaskProvider<out KspTaskMetadata> { + return project.tasks.register(taskName, KspTaskMetadata::class.java).also { kspTaskProvider -> + KotlinCompileCommonConfig(kotlinCompilation) + .execute(kspTaskProvider as TaskProvider<KotlinCompileCommon>) + } + } + + fun registerKotlinNativeCompileTask( + project: Project, + taskName: String, + kotlinCompilation: KotlinCompilation<*> + ): TaskProvider<out KspTaskNative> { + return project.tasks.register( + taskName, + KspTaskNative::class.java, + kotlinCompilation as KotlinNativeCompilationData<*> + ).apply { + configure { kspTask -> + kspTask.onlyIf { + kspTask.konanTarget.enabledOnCurrentHost + } + } + } + } + } +} + +interface KspTask : Task { + @get:Internal + val options: ListProperty<SubpluginOption> + + @get:Nested + val commandLineArgumentProviders: ListProperty<CommandLineArgumentProvider> + + @get:Internal + val incrementalChangesTransformers: ListProperty<(ChangedFiles) -> List<SubpluginOption>> +} + +@CacheableTask +abstract class KspTaskJvm @Inject constructor( + workerExecutor: WorkerExecutor, + objectFactory: ObjectFactory +) : KotlinCompile( + objectFactory.newInstance(KotlinJvmCompilerOptionsDefault::class.java), + workerExecutor, + objectFactory + ), + KspTask { + @get:OutputDirectory + abstract val destination: Property<File> + + // Override incrementalProps to exclude irrelevant changes + override val incrementalProps: List<FileCollection> + get() = listOf( + sources, + javaSources, + commonSourceSet, + classpathSnapshotProperties.classpath, + ) + + // Overrding an internal function is hacky. + // TODO: Ask upstream to open it. + @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_PARAMETER_TYPE") + fun `callCompilerAsync$kotlin_gradle_plugin_common`( + args: K2JVMCompilerArguments, + kotlinSources: Set<File>, + inputChanges: InputChanges, + taskOutputsBackup: TaskOutputsBackup? + ) { + val changedFiles = getChangedFiles(inputChanges, incrementalProps) + val extraOptions = incrementalChangesTransformers.get().flatMap { + it(changedFiles) + } + args.addPluginOptions(extraOptions) + super.callCompilerAsync(args, kotlinSources, inputChanges, taskOutputsBackup) + } + + override fun skipCondition(): Boolean = sources.isEmpty && javaSources.isEmpty + + @get:InputFiles + @get:SkipWhenEmpty + @get:IgnoreEmptyDirectories + @get:PathSensitive(PathSensitivity.RELATIVE) + override val javaSources: FileCollection = super.javaSources.filter { + !destination.get().isParentOf(it) + } +} + +@CacheableTask +abstract class KspTaskJS @Inject constructor( + objectFactory: ObjectFactory, + workerExecutor: WorkerExecutor +) : Kotlin2JsCompile( + objectFactory.newInstance(KotlinJsCompilerOptionsDefault::class.java), + objectFactory, + workerExecutor + ), + KspTask { + + // Overrding an internal function is hacky. + // TODO: Ask upstream to open it. + @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_PARAMETER_TYPE") + fun `callCompilerAsync$kotlin_gradle_plugin_common`( + args: K2JSCompilerArguments, + kotlinSources: Set<File>, + inputChanges: InputChanges, + taskOutputsBackup: TaskOutputsBackup? + ) { + val changedFiles = getChangedFiles(inputChanges, incrementalProps) + val extraOptions = incrementalChangesTransformers.get().flatMap { + it(changedFiles) + } + args.addPluginOptions(extraOptions) + super.callCompilerAsync(args, kotlinSources, inputChanges, taskOutputsBackup) + } +} + +@CacheableTask +abstract class KspTaskMetadata @Inject constructor( + workerExecutor: WorkerExecutor, + objectFactory: ObjectFactory +) : KotlinCompileCommon( + objectFactory.newInstance(KotlinMultiplatformCommonCompilerOptionsDefault::class.java), + workerExecutor, + objectFactory + ), + KspTask { + + // Overrding an internal function is hacky. + // TODO: Ask upstream to open it. + @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_PARAMETER_TYPE") + fun `callCompilerAsync$kotlin_gradle_plugin_common`( + args: K2MetadataCompilerArguments, + kotlinSources: Set<File>, + inputChanges: InputChanges, + taskOutputsBackup: TaskOutputsBackup? + ) { + val changedFiles = getChangedFiles(inputChanges, incrementalProps) + val extraOptions = incrementalChangesTransformers.get().flatMap { + it(changedFiles) + } + args.addPluginOptions(extraOptions) + super.callCompilerAsync(args, kotlinSources, inputChanges, taskOutputsBackup) + } +} + +@CacheableTask +abstract class KspTaskNative @Inject internal constructor( + compilation: KotlinNativeCompilationData<*>, + objectFactory: ObjectFactory, + providerFactory: ProviderFactory, + execOperations: ExecOperations +) : KotlinNativeCompile(compilation, objectFactory, providerFactory, execOperations), KspTask { + + override val compilerOptions: KotlinCommonCompilerOptions = + objectFactory.newInstance(KotlinMultiplatformCommonCompilerOptionsDefault::class.java) +} + +internal fun SubpluginOption.toArg() = "plugin:${KspGradleSubplugin.KSP_PLUGIN_ID}:$key=$value" + +internal fun CommonCompilerArguments.addPluginOptions(options: List<SubpluginOption>) { + pluginOptions = (options.map { it.toArg() } + pluginOptions!!).toTypedArray() +} + +internal fun File.isParentOf(childCandidate: File): Boolean { + val parentPath = Paths.get(this.absolutePath).normalize() + val childCandidatePath = Paths.get(childCandidate.absolutePath).normalize() + + return childCandidatePath.startsWith(parentPath) +} |