diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt index 1102b9afae0..62c00588d17 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import java.io.File @@ -73,6 +74,44 @@ open class CompilerPluginConfig { fun addPluginArgument(pluginId: String, option: SubpluginOption) { optionsByPluginId.getOrPut(pluginId) { mutableListOf() }.add(option) } + + @Input + fun getAsTaskInputArgs(): Map { + val result = mutableMapOf() + optionsByPluginId.forEach { (id, subpluginOptions) -> + result += computeForSubpluginId(id, subpluginOptions) + } + return result + } + + private fun computeForSubpluginId(subpluginId: String, subpluginOptions: List): Map { + // There might be several options with the same key. We group them together + // and add an index to the Gradle input property name to resolve possible duplication: + val result = mutableMapOf() + val pluginOptionsGrouped = subpluginOptions.groupBy { it.key } + for ((optionKey, optionsGroup) in pluginOptionsGrouped) { + optionsGroup.forEachIndexed { index, option -> + val indexSuffix = if (optionsGroup.size > 1) ".$index" else "" + when (option) { + is InternalSubpluginOption -> return@forEachIndexed + + is CompositeSubpluginOption -> { + val subpluginIdWithWrapperKey = "$subpluginId.$optionKey$indexSuffix" + result += computeForSubpluginId(subpluginIdWithWrapperKey, option.originalOptions) + } + + is FilesSubpluginOption -> when (option.kind) { + FilesOptionKind.INTERNAL -> Unit + }.run { /* exhaustive when */ } + + else -> { + result["$subpluginId." + option.key + indexSuffix] = option.value + } + } + } + } + return result + } } /** diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinJvmFactory.kt b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinJvmFactory.kt new file mode 100644 index 00000000000..e9ef816a0b8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinJvmFactory.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2021 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.plugin + +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.dsl.KaptExtensionConfig +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtensionConfig +import org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs +import org.jetbrains.kotlin.gradle.tasks.Kapt +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile + +/** An API used by third-party plugins to integration with the Kotlin Gradle plugin. */ +interface KotlinJvmFactory { + /** Instance of DSL object that should be used to configure KAPT stub generation and annotation processing tasks.*/ + val kaptExtension: KaptExtensionConfig + + /** Instance of DSL object that should be used to configure Kotlin compilation pipeline. */ + val kotlinExtension: KotlinTopLevelExtensionConfig + + /** Gets the current version of the Kotlin Gradle plugin. */ + val pluginVersion: String + + /** Creates instance of DSL object that should be used to configure JVM/android specific compilation. */ + fun createKotlinJvmOptions(): KotlinJvmOptions + + /** Creates a Kotlin compile task. */ + fun registerKotlinJvmCompileTask(taskName: String): TaskProvider + + /** Creates a stub generation task which creates Java sources stubs from Kotlin sources. */ + fun registerKaptGenerateStubsTask(taskName: String): TaskProvider + + /** Creates a KAPT task which runs annotation processing. */ + fun registerKaptTask(taskName: String): TaskProvider + + /** Adds a compiler plugin dependency to this project. This can be e.g a Maven coordinate or a project included in the build. */ + fun addCompilerPluginDependency(dependency: Provider) + + /** Returns a [FileCollection] that contains all compiler plugins classpath for this project. */ + fun getCompilerPlugins(): FileCollection +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinTaskConfigs.kt b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinTaskConfigs.kt new file mode 100644 index 00000000000..59725efc97e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinTaskConfigs.kt @@ -0,0 +1,160 @@ +/* + * Copyright 2010-2021 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.tasks + +import org.gradle.api.Task +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.api.tasks.util.PatternFilterable +import org.gradle.work.Incremental +import org.gradle.work.NormalizeLineEndings +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig + +interface KotlinCompileTool : PatternFilterable, Task { + @get:InputFiles + @get:SkipWhenEmpty + @get:NormalizeLineEndings + @get:IgnoreEmptyDirectories + @get:PathSensitive(PathSensitivity.RELATIVE) + val sources: FileCollection + + /** + * Sets sources for this task. + * The given sources object is evaluated as per [org.gradle.api.Project.files]. + */ + fun source(vararg sources: Any) + + /** + * Sets sources for this task. + * The given sources object is evaluated as per [org.gradle.api.Project.files]. + */ + fun setSource(vararg sources: Any) + + @get:Classpath + @get:NormalizeLineEndings + @get:Incremental + val libraries: ConfigurableFileCollection + + @get:OutputDirectory + val destinationDirectory: DirectoryProperty +} + +interface BaseKotlinCompile : KotlinCompileTool { + + @get:Internal + val friendPaths: ConfigurableFileCollection + + @get:NormalizeLineEndings + @get:Classpath + val pluginClasspath: ConfigurableFileCollection + + @get:Input + val moduleName: Property + + @get:Internal + val sourceSetName: Property + + @get:Input + val multiPlatformEnabled: Property + + @get:Input + val useModuleDetection: Property + + @get:Nested + val pluginOptions: ListProperty +} + +interface KotlinJvmCompile : BaseKotlinCompile, KotlinCompile { + + // JVM specific + @get:Internal("Takes part in compiler args.") + val parentKotlinOptions: Property +} + +interface KaptGenerateStubs : KotlinJvmCompile { + @get:OutputDirectory + val stubsDir: DirectoryProperty + + @get:Internal("Not an input, just passed as kapt args. ") + val kaptClasspath: ConfigurableFileCollection +} + +interface BaseKapt : Task { + + //part of kaptClasspath consisting from external artifacts only + //basically kaptClasspath = kaptExternalClasspath + artifacts built locally + @get:NormalizeLineEndings + @get:Classpath + val kaptExternalClasspath: ConfigurableFileCollection + + @get:Internal + val kaptClasspathConfigurationNames: ListProperty + + /** + * Output directory that contains caches necessary to support incremental annotation processing. + */ + @get:LocalState + val incAptCache: DirectoryProperty + + @get:OutputDirectory + val classesDir: DirectoryProperty + + @get:OutputDirectory + val destinationDir: DirectoryProperty + + /** Used in the model builder only. */ + @get:OutputDirectory + val kotlinSourcesDestinationDir: DirectoryProperty + + @get:Nested + val annotationProcessorOptionProviders: MutableList + + @get:Internal + val stubsDir: DirectoryProperty + + @get:NormalizeLineEndings + @get:Classpath + val kaptClasspath: ConfigurableFileCollection + + @get:Internal + val compiledSources: ConfigurableFileCollection + + @get:Internal("Task implementation adds correct input annotation.") + val classpath: ConfigurableFileCollection + + /** Needed for the model builder. */ + @get:Internal + val sourceSetName: Property + + @get:InputFiles + @get:NormalizeLineEndings + @get:IgnoreEmptyDirectories + @get:Incremental + @get:PathSensitive(PathSensitivity.RELATIVE) + val source: ConfigurableFileCollection + + @get:Input + val includeCompileClasspath: Property + + @get:Internal("Used to compute javac option.") + val defaultJavaSourceCompatibility: Property +} + +interface Kapt : BaseKapt { + + @get:Input + val addJdkClassesToClasspath: Property + + @get:NormalizeLineEndings + @get:Classpath + val kaptJars: ConfigurableFileCollection +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt index d0e5da71c74..a35a8dd9190 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt @@ -53,6 +53,7 @@ class UpToDateIT : KGPBaseTest() { OptionMutation("compileKotlin.kotlinOptions.freeCompilerArgs", "[]", "['-Xallow-kotlin-package']"), OptionMutation("archivesBaseName", "'someName'", "'otherName'"), subpluginOptionMutation, + subpluginOptionMutationWithKapt, externalOutputMutation, compilerClasspathMutation ) @@ -137,7 +138,9 @@ class UpToDateIT : KGPBaseTest() { } override fun checkAfterRebuild(buildResult: BuildResult) = with(buildResult) { - assertTasksExecuted(":compileKotlin", ":kaptGenerateStubsKotlin", ":kaptKotlin") + assertTasksExecuted(":compileKotlin", ":kaptGenerateStubsKotlin") + // KAPT with workers is not impacted by compiler classpath changes. + assertTasksUpToDate(":kaptKotlin") } } @@ -163,6 +166,29 @@ class UpToDateIT : KGPBaseTest() { } } + private val subpluginOptionMutationWithKapt = object : ProjectMutation { + override val name: String get() = "subpluginOptionMutationWithKapt" + + override fun initProject(project: TestProject) = with(project) { + buildGradle.appendText( + "\n" + """ + apply plugin: 'kotlin-kapt' + plugins.apply("org.jetbrains.kotlin.plugin.allopen") + allOpen { annotation("allopen.Foo"); annotation("allopen.Bar") } + """.trimIndent() + ) + } + + override fun mutateProject(project: TestProject) = with(project) { + buildGradle.modify { it.replace("allopen.Foo", "allopen.Baz") } + } + + override fun checkAfterRebuild(buildResult: BuildResult) = with(buildResult) { + assertTasksExecuted(":compileKotlin", ":kaptGenerateStubsKotlin") + assertTasksUpToDate(":kaptKotlin") + } + } + private val externalOutputMutation = object : ProjectMutation { override val name = "externalOutputMutation" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinJvmApiTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinJvmApiTest.kt new file mode 100644 index 00000000000..a67e1abd06a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinJvmApiTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2022 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.tasks + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.junit.jupiter.api.DisplayName +import kotlin.io.path.createDirectories +import kotlin.io.path.writeText + +@DisplayName("JVM API validation") +class KotlinJvmApiTest : KGPBaseTest() { + @DisplayName("Kotlin compilation can be set up using APIs") + @JvmGradlePluginTests + @GradleTest + internal fun kotlinCompilationShouldRunIfSetUpWithApi(gradleVersion: GradleVersion) { + project( + projectName = "kotlinJavaProject", + gradleVersion = gradleVersion + ) { + projectPath.resolve("src").let { + it.deleteRecursively() + it.resolve("main").createDirectories() + it.resolve("main/foo.kt").writeText( + """ + class Foo + """.trimIndent() + ) + } + + buildGradle.modify { + it.replace("id 'org.jetbrains.kotlin.jvm'", "id 'org.jetbrains.kotlin.jvm' apply false") + + """ + import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin + KotlinBaseApiPlugin apiPlugin = plugins.apply(KotlinBaseApiPlugin.class) + + apiPlugin.registerKotlinJvmCompileTask("foo").configure { + it.source("src/main") + it.multiPlatformEnabled.set(false) + it.moduleName.set("main") + it.sourceSetName.set("main") + it.useModuleDetection.set(false) + it.destinationDirectory.fileValue(new File(project.buildDir, "fooOutput")) + } + """.trimIndent() + } + + val expectedOutput = projectPath.resolve("build/fooOutput/Foo.class") + + build("foo") { + assertFileExists(expectedOutput) + } + } + } + + @DisplayName("KAPT can be set up using APIs") + @OtherGradlePluginTests + @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0) + @GradleTest + internal fun kaptShouldRunIfSetUpWithApi(gradleVersion: GradleVersion) { + project( + projectName = "kotlinJavaProject", + gradleVersion = gradleVersion + ) { + projectPath.resolve("src").let { + it.deleteRecursively() + it.resolve("main").createDirectories() + it.resolve("main/foo.kt").writeText( + """ + class Foo + """.trimIndent() + ) + } + + buildGradle.modify { + it.replace("id 'org.jetbrains.kotlin.jvm'", "id 'org.jetbrains.kotlin.jvm' apply false") + + """ + import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin + KotlinBaseApiPlugin apiPlugin = plugins.apply(KotlinBaseApiPlugin.class) + + File kaptFakeJar = new File(project.projectDir, "kapt.jar") + kaptFakeJar.createNewFile() + + apiPlugin.addCompilerPluginDependency( + project.provider { + "org.jetbrains.kotlin:kotlin-annotation-processing-gradle:${"$"}kotlin_version" + } + ) + + apiPlugin.registerKaptGenerateStubsTask("foo").configure { + it.source("src/main") + it.multiPlatformEnabled.set(false) + it.moduleName.set("main") + it.sourceSetName.set("main") + it.useModuleDetection.set(false) + it.destinationDirectory.fileValue(new File(project.buildDir, "fooOutput")) + it.stubsDir.fileValue(new File(project.buildDir, "fooOutputStubs")) + it.kaptClasspath.from(kaptFakeJar) + it.pluginClasspath.from(apiPlugin.getCompilerPlugins()) + } + """.trimIndent() + } + + val expectedOutputClass = projectPath.resolve("build/fooOutput/Foo.class") + val expectedOutputStubs = listOf( + projectPath.resolve("build/fooOutputStubs/Foo.java"), + projectPath.resolve("build/fooOutputStubs/Foo.kapt_metadata"), + projectPath.resolve("build/fooOutputStubs/error/NonExistentClass.java") + ) + + build("foo") { + assertFileExists(expectedOutputClass) + expectedOutputStubs.forEach { assertFileExists(it) } + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinCompile.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinCompile.kt index eb10c02c57b..0671247b0d6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinCompile.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinCompile.kt @@ -23,6 +23,10 @@ import org.gradle.api.tasks.Internal interface KotlinJsCompile : KotlinCompile +@Deprecated( + message = "Moved into API artifact", + replaceWith = ReplaceWith("KotlinJvmCompile", "org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile") +) interface KotlinJvmCompile : KotlinCompile interface KotlinCommonCompile : KotlinCompile diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt index 8ad99d52390..d6a4ea7261a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt @@ -29,8 +29,7 @@ import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool -import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.tasks.configuration.* import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.registerTask @@ -386,9 +385,9 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To val androidOptions = androidVariantData?.annotationProcessorOptions ?: emptyMap() val androidSubpluginOptions = androidOptions.toList().map { SubpluginOption(it.first, it.second) } - androidSubpluginOptions + kaptExtension.getAdditionalArguments(project, androidVariantData, androidExtension).toList() - .map { SubpluginOption(it.first, it.second) } + - FilesSubpluginOption(KAPT_KOTLIN_GENERATED, listOf(kotlinSourcesOutputDir)) + androidSubpluginOptions + getNonAndroidDslApOptions( + kaptExtension, project, listOf(kotlinSourcesOutputDir), androidVariantData, androidExtension + ).get() } private fun Kapt3SubpluginContext.createKaptKotlinTask(useWorkerApi: Boolean): TaskProvider { @@ -396,9 +395,9 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To val taskName = getKaptTaskName("kapt") val taskConfigAction = if (taskClass == KaptWithoutKotlincTask::class.java ) { - KaptWithoutKotlincConfigAction(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension) + KaptWithoutKotlincConfig(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension) } else { - KaptWithKotlincConfigAction(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension) + KaptWithKotlincConfig(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension) } val kaptClasspathConfiguration = project.configurations.create("kaptClasspath_$taskName") @@ -452,22 +451,18 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To } else { check(taskClass == KaptWithoutKotlincTask::class.java) getDslKaptApOptions() - }.map { - val res = CompilerPluginOptions() - it.forEach { res.addPluginArgument(KAPT_SUBPLUGIN_ID, it) } - return@map res - } + }.toCompilerPluginOptions() task.kaptPluginOptions.add(pluginOptions) } return if (taskClass == KaptWithoutKotlincTask::class.java) { - taskConfigAction as KaptWithoutKotlincConfigAction + taskConfigAction as KaptWithoutKotlincConfig project.registerTask(taskName, KaptWithoutKotlincTask::class.java, emptyList()).also { taskConfigAction.execute(it) } } else { - taskConfigAction as KaptWithKotlincConfigAction + taskConfigAction as KaptWithKotlincConfig project.registerTask(taskName, KaptWithKotlincTask::class.java, emptyList()).also { taskConfigAction.execute(it) } @@ -593,6 +588,21 @@ internal fun buildKaptSubpluginOptions( return pluginOptions } +/* Returns AP options from KAPT static DSL. */ +internal fun getNonAndroidDslApOptions( + kaptExtension: KaptExtension, + project: Project, + kotlinSourcesOutputDir: Iterable, + variantData: BaseVariant?, + androidExtension: BaseExtension? +): Provider> { + return project.provider { + kaptExtension.getAdditionalArguments(project, variantData, androidExtension).toList() + .map { SubpluginOption(it.first, it.second) } + + FilesSubpluginOption(Kapt3GradleSubplugin.KAPT_KOTLIN_GENERATED, kotlinSourcesOutputDir) + } +} + private fun encodeList(options: Map): String { val os = ByteArrayOutputStream() val oos = ObjectOutputStream(os) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt index 0b02e06c96d..a8e35c4de5c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.gradle.internal import org.gradle.api.file.* import org.gradle.api.model.ObjectFactory import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.work.Incremental @@ -27,9 +26,10 @@ import org.gradle.work.NormalizeLineEndings import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl +import org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.gradle.utils.isParentOf import org.jetbrains.kotlin.gradle.tasks.toSingleCompilerPluginOptions +import org.jetbrains.kotlin.gradle.utils.isParentOf import org.jetbrains.kotlin.incremental.classpathAsList import org.jetbrains.kotlin.incremental.destinationAsFile import java.io.File @@ -43,13 +43,12 @@ abstract class KaptGenerateStubsTask @Inject constructor( KotlinJvmOptionsImpl(), workerExecutor, objectFactory -) { +), KaptGenerateStubs { - @get:OutputDirectory - abstract val stubsDir: DirectoryProperty - - @get:Internal("Not an input, just passed as kapt args. ") - abstract val kaptClasspath: ConfigurableFileCollection + // Bug in Gradle - without this override Gradle complains @Internal is not + // compatible with @Classpath and @Incremental annotations + @get:Internal + abstract override val libraries: ConfigurableFileCollection /* Used as input as empty kapt classpath should not trigger stub generation, but a non-empty one should. */ @Input @@ -88,10 +87,10 @@ abstract class KaptGenerateStubsTask @Inject constructor( } @get:Internal - abstract override val scriptSources: FileCollection + override val scriptSources: FileCollection = objectFactory.fileCollection() @get:Internal - abstract override val androidLayoutResources: FileCollection + override val androidLayoutResources: FileCollection = objectFactory.fileCollection() override val incrementalProps: List get() = listOf( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt index 414813a0f77..0bfd0e3af6d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt @@ -3,7 +3,6 @@ package org.jetbrains.kotlin.gradle.internal import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileCollection import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty @@ -20,6 +19,7 @@ import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptClasspathChange import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges import org.jetbrains.kotlin.gradle.internal.kapt.incremental.UnknownSnapshot import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState +import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.utils.addToStdlib.cast @@ -32,7 +32,8 @@ abstract class KaptTask @Inject constructor( objectFactory: ObjectFactory ) : DefaultTask(), TaskWithLocalState, - UsesKotlinJavaToolchain { + UsesKotlinJavaToolchain, + BaseKapt { init { cacheOnlyIfEnabledForKotlin() @@ -41,26 +42,10 @@ abstract class KaptTask @Inject constructor( outputs.cacheIf(reason) { useBuildCache } } - @get:Internal - internal abstract val stubsDir: DirectoryProperty - - @get:NormalizeLineEndings - @get:Classpath - abstract val kaptClasspath: ConfigurableFileCollection - - //part of kaptClasspath consisting from external artifacts only - //basically kaptClasspath = kaptExternalClasspath + artifacts built locally - @get:NormalizeLineEndings - @get:Classpath - abstract val kaptExternalClasspath: ConfigurableFileCollection - @get:NormalizeLineEndings @get:Classpath abstract val compilerClasspath: ConfigurableFileCollection - @get:Internal - internal abstract val kaptClasspathConfigurationNames: ListProperty - @get:PathSensitive(PathSensitivity.NONE) @get:Incremental @get:NormalizeLineEndings @@ -70,39 +55,19 @@ abstract class KaptTask @Inject constructor( abstract val classpathStructure: ConfigurableFileCollection @get:Internal - abstract val kaptPluginOptions: ListProperty - - /** - * Output directory that contains caches necessary to support incremental annotation processing. - */ - @get:LocalState - abstract val incAptCache: DirectoryProperty - - @get:OutputDirectory - internal abstract val classesDir: DirectoryProperty - - @get:OutputDirectory - abstract val destinationDir: DirectoryProperty - - /** Used in the model builder only. */ - @get:OutputDirectory - abstract val kotlinSourcesDestinationDir: DirectoryProperty + abstract val kaptPluginOptions: ListProperty @get:Nested - internal val annotationProcessorOptionProviders: MutableList = mutableListOf() + override val annotationProcessorOptionProviders: MutableList = mutableListOf() @get:Input - internal val includeCompileClasspath: Property = objectFactory + override val includeCompileClasspath: Property = objectFactory .propertyWithConvention(true) .chainedFinalizeValueOnRead() @get:Input internal var isIncremental = true - // @Internal because _abiClasspath and _nonAbiClasspath are used for actual checks - @get:Internal - abstract val classpath: ConfigurableFileCollection - @get:Internal internal val defaultKotlinJavaToolchain: Provider = objectFactory .propertyWithNewInstance({ null }) @@ -136,17 +101,6 @@ abstract class KaptTask @Inject constructor( @get:Internal var useBuildCache: Boolean = false - /** Needed for the model builder. */ - @get:Internal - abstract val sourceSetName: Property - - @get:InputFiles - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:Incremental - @get:PathSensitive(PathSensitivity.RELATIVE) - abstract val source: ConfigurableFileCollection - @get:Internal override val metrics: Property = objectFactory .property(BuildMetricsReporterImpl()) @@ -154,9 +108,6 @@ abstract class KaptTask @Inject constructor( @get:Input abstract val verbose: Property - @get:Internal - abstract val defaultJavaSourceCompatibility: Property - protected fun checkAnnotationProcessorClasspath() { if (!includeCompileClasspath.get()) return @@ -186,10 +137,6 @@ abstract class KaptTask @Inject constructor( } } - // TODO(gavra): Here we assume that kotlinc and javac output is available for incremental runs. We should insert some checks. - @get:Internal - internal abstract val compiledSources: ConfigurableFileCollection - protected fun getIncrementalChanges(inputChanges: InputChanges): KaptIncrementalChanges { return if (isIncremental) { findClasspathChanges(inputChanges) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt index 5b3593aa9eb..99fe568466e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChan import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector +import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig import org.jetbrains.kotlin.gradle.report.ReportingSettings import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.utils.newInstance @@ -46,7 +47,7 @@ abstract class KaptWithKotlincTask @Inject constructor( /** Used only as task input, actual values come from [compileKotlinArgumentsContributor]. */ @get:Nested - internal abstract val additionalPluginOptionsAsInputs: ListProperty + internal abstract val additionalPluginOptionsAsInputs: ListProperty override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt index a395a8acc45..a254d78b922 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt @@ -16,7 +16,6 @@ import org.gradle.api.tasks.* import org.gradle.kotlin.dsl.listProperty import org.gradle.process.CommandLineArgumentProvider import org.gradle.work.InputChanges -import org.gradle.work.NormalizeLineEndings import org.gradle.workers.IsolationMode import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters @@ -24,8 +23,7 @@ import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.gradle.internal.kapt.classloaders.ClassLoadersCache import org.jetbrains.kotlin.gradle.internal.kapt.classloaders.rootOrSelf import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges -import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinAndroidPluginWrapper -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.Kapt import org.jetbrains.kotlin.gradle.tasks.toSingleCompilerPluginOptions import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.utils.PathUtil @@ -40,11 +38,7 @@ abstract class KaptWithoutKotlincTask @Inject constructor( objectFactory: ObjectFactory, private val providerFactory: ProviderFactory, private val workerExecutor: WorkerExecutor -) : KaptTask(objectFactory) { - - @get:NormalizeLineEndings - @get:Classpath - abstract val kaptJars: ConfigurableFileCollection +) : KaptTask(objectFactory), Kapt { @get:Input var classLoadersCacheSize: Int = 0 @@ -61,9 +55,6 @@ abstract class KaptWithoutKotlincTask @Inject constructor( @get:Input abstract val javacOptions: MapProperty - @get:Input - abstract val addJdkClassesToClasspath: Property - @get:Internal internal val projectDir = project.projectDir diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinBaseApiPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinBaseApiPlugin.kt new file mode 100644 index 00000000000..7e356459644 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinBaseApiPlugin.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2021 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.plugin + +import org.gradle.api.Project +import org.gradle.api.file.FileCollection +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension +import org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask +import org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask +import org.jetbrains.kotlin.gradle.tasks.* +import org.jetbrains.kotlin.gradle.tasks.configuration.KaptGenerateStubsConfig +import org.jetbrains.kotlin.gradle.tasks.configuration.KaptWithoutKotlincConfig +import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig + +/** Plugin that can be used by third-party plugins to create Kotlin-specific DSL and tasks (compilation and KAPT) for JVM platform. */ +abstract class KotlinBaseApiPlugin : KotlinBasePlugin(), KotlinJvmFactory { + + private val logger = Logging.getLogger(KotlinBaseApiPlugin::class.java) + override val pluginVersion = getKotlinPluginVersion(logger) + + private lateinit var myProject: Project + private val taskCreator = KotlinTasksProvider() + + override fun apply(project: Project) { + super.apply(project) + myProject = project + setupAttributeMatchingStrategy(project, isKotlinGranularMetadata = false) + } + + override fun addCompilerPluginDependency(dependency: Provider) { + myProject.dependencies.addProvider( + PLUGIN_CLASSPATH_CONFIGURATION_NAME, + dependency + ) + } + + override fun getCompilerPlugins(): FileCollection { + return myProject.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME) + } + + override fun createKotlinJvmOptions(): KotlinJvmOptions { + return KotlinJvmOptionsImpl() + } + + override val kotlinExtension: KotlinProjectExtension by lazy { + myProject.objects.newInstance(KotlinProjectExtension::class.java, myProject) + } + + override val kaptExtension: KaptExtension by lazy { + myProject.objects.newInstance(KaptExtension::class.java) + } + + override fun registerKotlinJvmCompileTask(taskName: String): TaskProvider { + return taskCreator.registerKotlinJVMTask( + myProject, taskName, KotlinJvmOptionsImpl(), KotlinCompileConfig(myProject, kotlinExtension) + ) + } + + override fun registerKaptGenerateStubsTask(taskName: String): TaskProvider { + val taskConfig = KaptGenerateStubsConfig(myProject, kotlinExtension, kaptExtension) + return myProject.registerTask(taskName, KaptGenerateStubsTask::class.java, emptyList()).also { + taskConfig.execute(it) + } + } + + override fun registerKaptTask(taskName: String): TaskProvider { + val taskConfiguration = KaptWithoutKotlincConfig(myProject, kaptExtension) + return myProject.registerTask(taskName, KaptWithoutKotlincTask::class.java, emptyList()).also { + taskConfiguration.execute(it) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 112ec05c019..4c30e06e0e5 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -933,7 +933,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool val configAction = KotlinCompileConfig(compilation) configAction.configureTask { task -> - task.parentKotlinOptionsImpl.value(rootKotlinOptions).disallowChanges() + task.parentKotlinOptions.value(rootKotlinOptions).disallowChanges() task.useModuleDetection.value(true).disallowChanges() // store kotlin classes in separate directory. They will serve as class-path to java compiler task.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin-classes/$variantDataName")) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 19bf15fa2ed..ad52706d219 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -20,6 +20,7 @@ import org.gradle.api.GradleException import org.gradle.api.NamedDomainObjectFactory import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.logging.Logger import org.gradle.api.model.ObjectFactory import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.kotlin.compilerRunner.registerCommonizerClasspathConfigurationIfNecessary @@ -40,8 +41,9 @@ import org.jetbrains.kotlin.gradle.report.HttpReportService import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension +import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements -import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry import org.jetbrains.kotlin.gradle.tooling.registerBuildKotlinToolingMetadataTask import org.jetbrains.kotlin.gradle.utils.addGradlePluginMetadataAttributes @@ -53,22 +55,15 @@ import org.jetbrains.kotlin.statistics.metrics.StringMetrics import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion import kotlin.reflect.KClass -abstract class KotlinBasePluginWrapper : Plugin { - - open val projectExtensionClass: KClass get() = KotlinProjectExtension::class - - abstract val pluginVariant: String - - internal open fun kotlinSourceSetFactory(project: Project): NamedDomainObjectFactory = - if (PropertiesProvider(project).experimentalKpmModelMapping) - FragmentMappedKotlinSourceSetFactory(project) - else DefaultKotlinSourceSetFactory(project) +/** + * Base Kotlin plugin that is responsible for creating basic build services, configurations, + * and other setup that is common for all Kotlin projects. + */ +abstract class KotlinBasePlugin : Plugin { override fun apply(project: Project) { val kotlinPluginVersion = project.getKotlinPluginVersion() - project.logger.info("Using Kotlin Gradle Plugin $pluginVariant variant") - val statisticsReporter = KotlinBuildStatsService.getOrCreateInstance(project) statisticsReporter?.report(StringMetrics.KOTLIN_COMPILER_VERSION, kotlinPluginVersion) @@ -86,6 +81,69 @@ abstract class KotlinBasePluginWrapper : Plugin { addGradlePluginMetadataAttributes(project) } + KotlinGradleBuildServices.registerIfAbsent(project, kotlinPluginVersion).get() + KotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion) + + BuildMetricsReporterService.registerIfAbsent(project)?.also { + BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it) + } + HttpReportService.registerIfAbsent(project, kotlinPluginVersion)?.also { + BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it) + } + } + + private fun addKotlinCompilerConfiguration(project: Project) { + project + .configurations + .maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME) + .defaultDependencies { + it.add( + project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:${project.getKotlinPluginVersion()}") + ) + } + project + .tasks + .withType(AbstractKotlinCompileTool::class.java) + .configureEach { task -> + task.defaultCompilerClasspath.setFrom( + project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME) + ) + } + } + + protected fun setupAttributeMatchingStrategy( + project: Project, + isKotlinGranularMetadata: Boolean = project.isKotlinGranularMetadataEnabled + ) = with(project.dependencies.attributesSchema) { + KotlinPlatformType.setupAttributesMatchingStrategy(this) + KotlinUsages.setupAttributesMatchingStrategy(this, isKotlinGranularMetadata) + KotlinJsCompilerAttribute.setupAttributesMatchingStrategy(project.dependencies.attributesSchema) + ProjectLocalConfigurations.setupAttributesMatchingStrategy(this) + CInteropKlibLibraryElements.setupAttributesMatchingStrategy(this) + } + + open fun whenBuildEvaluated(project: Project) { + } +} + + +abstract class KotlinBasePluginWrapper : KotlinBasePlugin() { + + open val projectExtensionClass: KClass get() = KotlinProjectExtension::class + + abstract val pluginVariant: String + + internal open fun kotlinSourceSetFactory(project: Project): NamedDomainObjectFactory = + if (PropertiesProvider(project).experimentalKpmModelMapping) + FragmentMappedKotlinSourceSetFactory(project) + else DefaultKotlinSourceSetFactory(project) + + override fun apply(project: Project) { + super.apply(project) + val kotlinPluginVersion = project.getKotlinPluginVersion() + + project.logger.info("Using Kotlin Gradle Plugin $pluginVariant variant") + project.configurations.maybeCreate(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME).apply { isVisible = false isCanBeConsumed = false @@ -96,17 +154,6 @@ abstract class KotlinBasePluginWrapper : Plugin { project.registerDefaultVariantImplementations() - KotlinGradleBuildServices.registerIfAbsent(project, kotlinPluginVersion).get() - - KotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion) - - BuildMetricsReporterService.registerIfAbsent(project)?.also { - BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it) - } - - HttpReportService.registerIfAbsent(project, kotlinPluginVersion) - ?.also { BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it) } - project.createKotlinExtension(projectExtensionClass).apply { coreLibrariesVersion = kotlinPluginVersion @@ -140,38 +187,8 @@ abstract class KotlinBasePluginWrapper : Plugin { ) } - private fun addKotlinCompilerConfiguration(project: Project) { - project - .configurations - .maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME) - .defaultDependencies { - it.add( - project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:${project.getKotlinPluginVersion()}") - ) - } - project - .tasks - .withType(AbstractKotlinCompile::class.java) - .configureEach { task -> - task.defaultCompilerClasspath.setFrom( - project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME) - ) - } - } - - open fun whenBuildEvaluated(project: Project) { - } - internal open fun createTestRegistry(project: Project) = KotlinTestsRegistry(project) - private fun setupAttributeMatchingStrategy(project: Project) = with(project.dependencies.attributesSchema) { - KotlinPlatformType.setupAttributesMatchingStrategy(this) - KotlinUsages.setupAttributesMatchingStrategy(project, this) - KotlinJsCompilerAttribute.setupAttributesMatchingStrategy(project.dependencies.attributesSchema) - ProjectLocalConfigurations.setupAttributesMatchingStrategy(this) - CInteropKlibLibraryElements.setupAttributesMatchingStrategy(this) - } - internal abstract fun getPlugin( project: Project, ): Plugin @@ -278,10 +295,12 @@ abstract class AbstractKotlinPm20PluginWrapper( get() = KotlinPm20ProjectExtension::class } -fun Project.getKotlinPluginVersion(): String { +fun Project.getKotlinPluginVersion() = getKotlinPluginVersion(project.logger) + +fun getKotlinPluginVersion(logger: Logger): String { if (!kotlinPluginVersionFromResources.isInitialized()) { - project.logger.kotlinDebug("Loading version information") - project.logger.kotlinDebug("Found project version [${kotlinPluginVersionFromResources.value}") + logger.kotlinDebug("Loading version information") + logger.kotlinDebug("Found project version [${kotlinPluginVersionFromResources.value}") } return kotlinPluginVersionFromResources.value } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt index b0b8cd7e18c..67ab6dae560 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.* import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.usageByName import org.jetbrains.kotlin.gradle.targets.metadata.isCompatibilityMetadataVariantEnabled -import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled object KotlinUsages { const val KOTLIN_API = "kotlin-api" @@ -167,7 +166,7 @@ object KotlinUsages { closestMatch(candidateValues.single { it?.name == name }!!) } - internal fun setupAttributesMatchingStrategy(project: Project, attributesSchema: AttributesSchema) { + internal fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema, isKotlinGranularMetadata: Boolean) { attributesSchema.attribute(USAGE_ATTRIBUTE) { strategy -> strategy.compatibilityRules.add(KotlinJavaRuntimeJarsCompatibility::class.java) strategy.disambiguationRules.add(KotlinUsagesDisambiguation::class.java) @@ -175,7 +174,7 @@ object KotlinUsages { strategy.compatibilityRules.add(KotlinCinteropCompatibility::class.java) strategy.disambiguationRules.add(KotlinCinteropDisambiguation::class.java) - if (project.isKotlinGranularMetadataEnabled) { + if (isKotlinGranularMetadata) { strategy.compatibilityRules.add(KotlinMetadataCompatibility::class.java) strategy.disambiguationRules.add(KotlinMetadataDisambiguation::class.java) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt index b6ad368c60f..6a913815508 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt @@ -37,7 +37,7 @@ import javax.inject.Inject @CacheableTask abstract class KotlinJsIrLink @Inject constructor( - private val objectFactory: ObjectFactory, + objectFactory: ObjectFactory, workerExecutor: WorkerExecutor, private val projectLayout: ProjectLayout ) : Kotlin2JsCompile( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt index 824dd7a0991..ad59c919ac2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt @@ -17,9 +17,11 @@ package org.jetbrains.kotlin.gradle.tasks import org.gradle.api.provider.ListProperty -import org.gradle.api.tasks.Input +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Internal -import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin +import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig +import org.jetbrains.kotlin.gradle.plugin.SubpluginOption class CompilerPluginOptions() : CompilerPluginConfig() { @@ -51,44 +53,6 @@ class CompilerPluginOptions() : CompilerPluginConfig() { return newOptions } - @Input - fun getAsTaskInputArgs(): Map { - val result = mutableMapOf() - subpluginOptionsByPluginId.forEach { (id, subpluginOptions) -> - result += computeForSubpluginId(id, subpluginOptions) - } - return result - } - - private fun computeForSubpluginId(subpluginId: String, subpluginOptions: List): Map { - // There might be several options with the same key. We group them together - // and add an index to the Gradle input property name to resolve possible duplication: - val result = mutableMapOf() - val pluginOptionsGrouped = subpluginOptions.groupBy { it.key } - for ((optionKey, optionsGroup) in pluginOptionsGrouped) { - optionsGroup.forEachIndexed { index, option -> - val indexSuffix = if (optionsGroup.size > 1) ".$index" else "" - when (option) { - is InternalSubpluginOption -> return@forEachIndexed - - is CompositeSubpluginOption -> { - val subpluginIdWithWrapperKey = "$subpluginId.$optionKey$indexSuffix" - result += computeForSubpluginId(subpluginIdWithWrapperKey, option.originalOptions) - } - - is FilesSubpluginOption -> when (option.kind) { - FilesOptionKind.INTERNAL -> Unit - }.run { /* exhaustive when */ } - - else -> { - result["$subpluginId." + option.key + indexSuffix] = option.value - } - } - } - } - return result - } - private fun copyOptionsFrom(options: CompilerPluginConfig) { options.allOptions().forEach { entry -> optionsByPluginId[entry.key] = entry.value.toMutableList() @@ -96,7 +60,16 @@ class CompilerPluginOptions() : CompilerPluginConfig() { } } -internal fun ListProperty.toSingleCompilerPluginOptions(): CompilerPluginOptions { - val values = this.orNull ?: return CompilerPluginOptions() - return values.reduceOrNull(CompilerPluginOptions::plus) ?: CompilerPluginOptions() +internal fun ListProperty.toSingleCompilerPluginOptions(): CompilerPluginOptions { + var res = CompilerPluginOptions() + this.get().forEach { res += it } + return res +} + +internal fun Provider>.toCompilerPluginOptions(): Provider { + return map { + val res = CompilerPluginOptions() + it.forEach { res.addPluginArgument(Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID, it) } + return@map res + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/DefaultKotlinJavaToolchain.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/DefaultKotlinJavaToolchain.kt index 3ffbe721cf0..b74f3cc20e4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/DefaultKotlinJavaToolchain.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/DefaultKotlinJavaToolchain.kt @@ -133,7 +133,7 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor( val jdkVersion = javaVersion.get() // parentKotlinOptionsImpl is set from 'kotlin-android' plugin - val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptionsImpl.orNull) + val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptions.orNull) .mapNotNull { (it as KotlinJvmOptionsImpl).jvmTarget } if (appliedJvmTargets.isEmpty()) { @@ -158,7 +158,7 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor( ) { kotlinCompileTaskProvider()?.let { task -> // parentKotlinOptionsImpl is set from 'kotlin-android' plugin - val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptionsImpl.orNull) + val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptions.orNull) .mapNotNull { (it as KotlinJvmOptionsImpl).jvmTarget } if (appliedJvmTargets.isEmpty()) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index e35e8ff2d41..f53903b7c85 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -14,7 +14,6 @@ import org.gradle.api.file.* import org.gradle.api.invocation.Gradle import org.gradle.api.logging.Logger import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.provider.SetProperty @@ -77,7 +76,7 @@ const val USING_JS_IR_BACKEND_MESSAGE = "Using Kotlin/JS IR backend" abstract class AbstractKotlinCompileTool @Inject constructor( objectFactory: ObjectFactory ) : DefaultTask(), - PatternFilterable, + KotlinCompileTool, CompilerArgumentAwareWithInput, TaskWithLocalState { @@ -91,30 +90,17 @@ abstract class AbstractKotlinCompileTool @Inject constr private val sourceFiles = objectFactory.fileCollection() - @get:InputFiles - @get:SkipWhenEmpty - @get:NormalizeLineEndings - @get:IgnoreEmptyDirectories - @get:PathSensitive(PathSensitivity.RELATIVE) - open val sources: FileCollection = objectFactory.fileCollection() + override val sources: FileCollection = objectFactory.fileCollection() .from( { sourceFiles.asFileTree.matching(patternFilterable) } ) - /** - * Sets the source for this task. - * The given source object is evaluated as per [org.gradle.api.Project.files]. - */ - open fun setSource(source: Any) { - sourceFiles.from(source) + override fun source(vararg sources: Any) { + sourceFiles.from(sources) } - /** - * Sets the source for this task. - * The given source object is evaluated as per [org.gradle.api.Project.files]. - */ - open fun setSource(vararg source: Any) { - sourceFiles.from(source) + override fun setSource(vararg sources: Any) { + sourceFiles.from(sources) } fun disallowSourceChanges() { @@ -167,14 +153,6 @@ abstract class AbstractKotlinCompileTool @Inject constr patternFilterable.exclude(excludeSpec) } - @get:Classpath - @get:NormalizeLineEndings - @get:Incremental - abstract val libraries: ConfigurableFileCollection - - @get:OutputDirectory - abstract val destinationDirectory: DirectoryProperty - @get:Internal override val metrics: Property = project.objects .property(BuildMetricsReporterImpl()) @@ -254,7 +232,8 @@ abstract class GradleCompileTaskProvider @Inject constructor( abstract class AbstractKotlinCompile @Inject constructor( objectFactory: ObjectFactory ) : AbstractKotlinCompileTool(objectFactory), - CompileUsingKotlinDaemonWithNormalization { + CompileUsingKotlinDaemonWithNormalization, + BaseKotlinCompile { init { cacheOnlyIfEnabledForKotlin() @@ -300,21 +279,10 @@ abstract class AbstractKotlinCompile @Inject constr internal fun reportingSettings() = buildMetricsReporterService.orNull?.parameters?.reportingSettings ?: ReportingSettings() - @get:Input - internal abstract val useModuleDetection: Property - @get:Internal protected val multiModuleICSettings: MultiModuleICSettings get() = MultiModuleICSettings(buildHistoryFile.get().asFile, useModuleDetection.get()) - @get:NormalizeLineEndings - @get:Classpath - abstract val pluginClasspath: ConfigurableFileCollection - - @get:Nested - internal abstract val pluginOptions: ListProperty - - /** * Plugin Data provided by [KpmCompilerPlugin] */ @@ -326,9 +294,6 @@ abstract class AbstractKotlinCompile @Inject constr @get:Internal internal val javaOutputDir: DirectoryProperty = objectFactory.directoryProperty() - @get:Internal - internal abstract val sourceSetName: Property - @get:InputFiles @get:IgnoreEmptyDirectories @get:NormalizeLineEndings @@ -336,9 +301,6 @@ abstract class AbstractKotlinCompile @Inject constr @get:PathSensitive(PathSensitivity.RELATIVE) internal val commonSourceSet: ConfigurableFileCollection = objectFactory.fileCollection() - @get:Input - internal abstract val moduleName: Property - @get:Internal val abiSnapshotFile get() = taskBuildCacheableOutputDirectory.file(IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME) @@ -352,9 +314,6 @@ abstract class AbstractKotlinCompile @Inject constr @get:Internal internal val friendSourceSets = objectFactory.listProperty(String::class.java) - @get:Internal // takes part in the compiler arguments - abstract val friendPaths: ConfigurableFileCollection - private val kotlinLogger by lazy { GradleKotlinLogger(logger) } @get:Internal @@ -496,9 +455,6 @@ abstract class AbstractKotlinCompile @Inject constr taskOutputsBackup: TaskOutputsBackup? ) - @get:Input - internal abstract val multiPlatformEnabled: Property - @get:Internal internal val abstractKotlinCompileArgumentsContributor by lazy { AbstractKotlinCompileArgumentsContributor( @@ -533,7 +489,7 @@ class KotlinJvmCompilerArgumentsProvider val compileClasspath: Iterable = taskProvider.libraries val destinationDir: File = taskProvider.destinationDirectory.get().asFile internal val kotlinOptions: List = listOfNotNull( - taskProvider.parentKotlinOptionsImpl.orNull as? KotlinJvmOptionsImpl, + taskProvider.parentKotlinOptions.orNull as? KotlinJvmOptionsImpl, taskProvider.kotlinOptions as KotlinJvmOptionsImpl ) } @@ -550,9 +506,6 @@ abstract class KotlinCompile @Inject constructor( KotlinJvmCompile, UsesKotlinJavaToolchain { - @get:Internal("Takes part in compiler args.") - internal abstract val parentKotlinOptionsImpl: Property - /** A package prefix that is used for locating Java sources in a directory structure with non-full-depth packages. * * Example: a Java source file with `package com.example.my.package` is located in directory `src/main/java/my/package`. @@ -575,16 +528,16 @@ abstract class KotlinCompile @Inject constructor( "Replaced with 'libraries' input", replaceWith = ReplaceWith("libraries") ) - @Internal - fun getClasspath(): FileCollection = libraries + fun setClasspath(classpath: FileCollection) { + libraries.setFrom(classpath) + } @Deprecated( "Replaced with 'libraries' input", replaceWith = ReplaceWith("libraries") ) - fun setClasspath(configuration: FileCollection) { - libraries.setFrom(configuration) - } + @Internal + fun getClasspath(): FileCollection = libraries @get:Input abstract val useKotlinAbiSnapshot: Property @@ -881,17 +834,17 @@ abstract class KotlinCompile @Inject constructor( } // override setSource to track Java and script sources as well - override fun setSource(source: Any) { - javaSourceFiles.from(source) - scriptSourceFiles.from(source) - super.setSource(source) + override fun source(vararg sources: Any) { + javaSourceFiles.from(sources) + scriptSourceFiles.from(sources) + super.setSource(sources) } // override source to track Java and script sources as well - override fun setSource(vararg source: Any) { - javaSourceFiles.from(*source) - scriptSourceFiles.from(*source) - super.setSource(*source) + override fun setSource(vararg sources: Any) { + javaSourceFiles.from(*sources) + scriptSourceFiles.from(*sources) + super.setSource(*sources) } private fun getClasspathChanges(inputChanges: InputChanges): ClasspathChanges = when { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/AbstractKotlinCompileConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/AbstractKotlinCompileConfig.kt index e14b187b4e8..98e7775a8ee 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/AbstractKotlinCompileConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/AbstractKotlinCompileConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2022 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. */ @@ -12,6 +12,7 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Provider import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension import org.jetbrains.kotlin.gradle.dsl.topLevelExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider @@ -21,7 +22,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinO import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile import org.jetbrains.kotlin.gradle.tasks.KOTLIN_BUILD_DIR_NAME -import java.util.concurrent.Callable +import org.jetbrains.kotlin.project.model.LanguageSettings /** * Configuration for the base compile task, [org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile]. @@ -31,39 +32,22 @@ import java.util.concurrent.Callable */ internal abstract class AbstractKotlinCompileConfig>( project: Project, + private val ext: KotlinTopLevelExtension, + private val languageSettings: Provider ) : TaskConfigAction(project) { - constructor(compilation: KotlinCompilationData<*>) : this(compilation.project) { - val ext = compilation.project.topLevelExtension - + init { configureTaskProvider { taskProvider -> project.runOnceAfterEvaluated("apply properties and language settings to ${taskProvider.name}") { taskProvider.configure { applyLanguageSettingsToKotlinOptions( - compilation.languageSettings, (it as org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>).kotlinOptions + languageSettings.get(), (it as org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>).kotlinOptions ) } } } configureTask { task -> - task.friendPaths.from({ compilation.friendPaths }) - if (compilation is KotlinCompilation<*>) { - task.friendSourceSets.value(providers.provider { compilation.associateWithClosure.map { it.name } }) - .disallowChanges() - task.pluginClasspath.from(compilation.project.configurations.getByName(compilation.pluginConfigurationName)) - } - task.moduleName.set(providers.provider { compilation.moduleName }) - task.sourceSetName.value(providers.provider { compilation.compilationPurpose }) - task.multiPlatformEnabled.value( - providers.provider { - compilation.project.plugins.any { - it is KotlinPlatformPluginBase || - it is AbstractKotlinMultiplatformPluginWrapper || - it is AbstractKotlinPm20PluginWrapper - } - } - ) val propertiesProvider = project.kotlinPropertiesProvider task.taskBuildCacheableOutputDirectory @@ -85,7 +69,6 @@ internal abstract class AbstractKotlinCompileConfig = getKotlinBuildDir(task).map { it.dir("classpath-snapshot") } + + constructor(compilation: KotlinCompilationData<*>) : this( + compilation.project, compilation.project.topLevelExtension, compilation.project.provider { compilation.languageSettings } + ) { + configureTask { task -> + task.friendPaths.from({ compilation.friendPaths }) + if (compilation is KotlinCompilation<*>) { + task.friendSourceSets + .value(providers.provider { compilation.associateWithClosure.map { it.name } }) + .disallowChanges() + task.pluginClasspath.from( + compilation.project.configurations.getByName(compilation.pluginConfigurationName) + ) + } + task.moduleName.set(providers.provider { compilation.moduleName }) + task.sourceSetName.value(providers.provider { compilation.compilationPurpose }) + task.multiPlatformEnabled.value( + providers.provider { + compilation.project.plugins.any { + it is KotlinPlatformPluginBase || + it is AbstractKotlinMultiplatformPluginWrapper || + it is AbstractKotlinPm20PluginWrapper + } + } + ) + } + } } internal abstract class TaskConfigAction(protected val project: Project) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptConfig.kt index 57b2041f67a..b60fa2691ea 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2022 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. */ @@ -15,7 +15,6 @@ import org.gradle.api.file.FileCollection import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider import org.gradle.util.GradleVersion -import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension import org.jetbrains.kotlin.gradle.dsl.topLevelExtension import org.jetbrains.kotlin.gradle.internal.* import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_SUBPLUGIN_ID @@ -26,47 +25,26 @@ import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isInc import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformAction import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformLegacyAction -import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinAndroidPluginWrapper -import org.jetbrains.kotlin.gradle.plugin.KaptExtension -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption -import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.toCompilerPluginOptions import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import org.jetbrains.kotlin.gradle.utils.listProperty import java.io.File import java.util.concurrent.Callable -internal open class KaptConfigAction( +internal open class KaptConfig( project: Project, protected val ext: KaptExtension, ) : TaskConfigAction(project) { - internal constructor(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : this(kotlinCompileTask.project, ext) { + init { configureTaskProvider { taskProvider -> val kaptClasspathSnapshot = getKaptClasspathSnapshot(taskProvider) taskProvider.configure { task -> - task.classpath.from(kotlinCompileTask.libraries) - task.compiledSources.from( - kotlinCompileTask.destinationDirectory, - Callable { kotlinCompileTask.javaOutputDir.takeIf { it.isPresent } }) - .disallowChanges() - task.sourceSetName.value(kotlinCompileTask.sourceSetName).disallowChanges() - - val kaptSources = objectFactory - .fileCollection() - .from(kotlinCompileTask.javaSources, task.stubsDir) - .asFileTree - .matching { it.include("**/*.java") } - .filter { - it.exists() && - !isAncestor(task.destinationDir.get().asFile, it) && - !isAncestor(task.classesDir.get().asFile, it) - } - task.source.from(kaptSources).disallowChanges() task.verbose.set(KaptTask.queryKaptVerboseProperty(project)) - task.compilerClasspath.from(providers.provider { kotlinCompileTask.defaultCompilerClasspath }) task.isIncremental = project.isIncrementalKapt() task.useBuildCache = ext.useBuildCache @@ -83,6 +61,29 @@ internal open class KaptConfigAction( } } + internal constructor(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : this(kotlinCompileTask.project, ext) { + configureTask { task -> + task.classpath.from(kotlinCompileTask.libraries) + task.compiledSources.from( + kotlinCompileTask.destinationDirectory, + Callable { kotlinCompileTask.javaOutputDir.takeIf { it.isPresent } }) + .disallowChanges() + task.sourceSetName.value(kotlinCompileTask.sourceSetName).disallowChanges() + + + val kaptSources = objectFactory.fileCollection() + .from(kotlinCompileTask.javaSources, task.stubsDir) + .asFileTree + .matching { it.include("**/*.java") } + .filter { + it.exists() && + !isAncestor(task.destinationDir.get().asFile, it) && + !isAncestor(task.classesDir.get().asFile, it) + } + task.source.from(kaptSources).disallowChanges() + } + } + private fun getKaptClasspathSnapshot(taskProvider: TaskProvider): FileCollection? { return if (project.isIncrementalKapt()) { maybeRegisterTransform(project) @@ -173,12 +174,9 @@ private fun isAncestor(dir: File, file: File): Boolean { } } -internal class KaptWithoutKotlincConfigAction(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : - KaptConfigAction(kotlinCompileTask, ext) { +internal class KaptWithoutKotlincConfig : KaptConfig { init { - initKaptWorkersConfiguration(project.topLevelExtension) - configureTask { task -> task.addJdkClassesToClasspath.set( project.providers.provider { @@ -194,7 +192,7 @@ internal class KaptWithoutKotlincConfigAction(kotlinCompileTask: KotlinCompile, } } - private fun initKaptWorkersConfiguration(kotlinExt: KotlinTopLevelExtension) { + constructor(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : super(kotlinCompileTask, ext) { project.configurations.findByName(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME) ?: project.configurations.create(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).apply { dependencies.addAllLater(project.listProperty { @@ -203,16 +201,33 @@ internal class KaptWithoutKotlincConfigAction(kotlinCompileTask: KotlinCompile, project.dependencies.create(kaptDependency), project.kotlinDependency( "kotlin-stdlib", - kotlinExt.coreLibrariesVersion + project.topLevelExtension.coreLibrariesVersion ) ) }) } + + configureTask { task -> + task.addJdkClassesToClasspath.set( + project.providers.provider { + project.plugins.none { it is AbstractKotlinAndroidPluginWrapper } + } + ) + task.kaptJars.from(project.configurations.getByName(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME)) + } + } + + constructor(project: Project, ext: KaptExtension) : super(project, ext) { + configureTask { task -> + val kotlinSourceDir = objectFactory.fileCollection().from(task.kotlinSourcesDestinationDir) + val nonAndroidDslOptions = getNonAndroidDslApOptions(ext, project, kotlinSourceDir, null, null) + task.kaptPluginOptions.add(nonAndroidDslOptions.toCompilerPluginOptions()) + } } } -internal class KaptWithKotlincConfigAction(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : - KaptConfigAction(kotlinCompileTask, ext) { +internal class KaptWithKotlincConfig(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : + KaptConfig(kotlinCompileTask, ext) { init { configureTask { task -> @@ -227,6 +242,7 @@ internal class KaptWithKotlincConfigAction(kotlinCompileTask: KotlinCompile, ext task.kaptPluginOptions.add(incAptCacheOption) } + task.compilerClasspath.from(providers.provider { kotlinCompileTask.defaultCompilerClasspath }) task.pluginClasspath.from(kotlinCompileTask.pluginClasspath) task.additionalPluginOptionsAsInputs.value(kotlinCompileTask.pluginOptions).disallowChanges() task.compileKotlinArgumentsContributor.set(providers.provider { kotlinCompileTask.compilerArgumentsContributor }) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptGenerateStubsConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptGenerateStubsConfig.kt index c3fd73a9c5e..ea77d57950c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptGenerateStubsConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KaptGenerateStubsConfig.kt @@ -1,40 +1,58 @@ /* - * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2022 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.tasks.configuration +import org.gradle.api.Project import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_SUBPLUGIN_ID import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isIncludeCompileClasspath import org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask import org.jetbrains.kotlin.gradle.internal.KaptTask +import org.jetbrains.kotlin.gradle.internal.KotlinJvmCompilerArgumentsContributor import org.jetbrains.kotlin.gradle.internal.buildKaptSubpluginOptions import org.jetbrains.kotlin.gradle.plugin.KaptExtension import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData -import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompilerArgumentsProvider import java.util.concurrent.Callable -internal class KaptGenerateStubsConfig(compilation: KotlinCompilationData<*>, kotlinTaskProvider: TaskProvider) : - BaseKotlinCompileConfig(compilation) { +internal class KaptGenerateStubsConfig : BaseKotlinCompileConfig { - private val kaptExtension: KaptExtension = project.extensions.getByType(KaptExtension::class.java) - - init { + constructor(compilation: KotlinCompilationData<*>, kotlinTaskProvider: TaskProvider) : super(compilation) { + configureFromExtension(project.extensions.getByType(KaptExtension::class.java)) configureTask { task -> val kotlinCompileTask = kotlinTaskProvider.get() task.useModuleDetection.value(kotlinCompileTask.useModuleDetection).disallowChanges() task.moduleName.value(kotlinCompileTask.moduleName).disallowChanges() task.libraries.from({ kotlinCompileTask.libraries }) task.compileKotlinArgumentsContributor.set(providers.provider { kotlinCompileTask.compilerArgumentsContributor }) + task.pluginOptions.addAll(kotlinCompileTask.pluginOptions) + } + } + + constructor(project: Project, ext: KotlinTopLevelExtension, kaptExtension: KaptExtension) : super(project, ext) { + configureFromExtension(kaptExtension) + configureTask { task -> + task.compileKotlinArgumentsContributor.set( + providers.provider { + KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(task)) + } + ) + } + } + + private fun configureFromExtension(kaptExtension: KaptExtension) { + configureTask { task -> task.verbose.set(KaptTask.queryKaptVerboseProperty(project)) + task.pluginOptions.add(buildOptions(kaptExtension, task)) - task.pluginOptions.add(buildOptions(task)) - - if (!isIncludeCompileClasspath()) { + if (!isIncludeCompileClasspath(kaptExtension)) { task.onlyIf { !(it as KaptGenerateStubsTask).kaptClasspath.isEmpty } @@ -42,9 +60,9 @@ internal class KaptGenerateStubsConfig(compilation: KotlinCompilationData<*>, ko } } - private fun isIncludeCompileClasspath() = kaptExtension.includeCompileClasspath ?: project.isIncludeCompileClasspath() + private fun isIncludeCompileClasspath(kaptExtension: KaptExtension) = kaptExtension.includeCompileClasspath ?: project.isIncludeCompileClasspath() - private fun buildOptions(task: KaptGenerateStubsTask): Provider { + private fun buildOptions(kaptExtension: KaptExtension, task: KaptGenerateStubsTask): Provider { val javacOptions = project.provider { kaptExtension.getJavacOptions() } return project.provider { val compilerPluginOptions = CompilerPluginOptions() @@ -56,7 +74,7 @@ internal class KaptGenerateStubsConfig(compilation: KotlinCompilationData<*>, ko generatedSourcesDir = objectFactory.fileCollection().from(task.destinationDirectory.asFile), generatedClassesDir = objectFactory.fileCollection().from(task.destinationDirectory.asFile), incrementalDataDir = objectFactory.fileCollection().from(task.destinationDirectory.asFile), - includeCompileClasspath = isIncludeCompileClasspath(), + includeCompileClasspath = isIncludeCompileClasspath(kaptExtension), kaptStubsDir = objectFactory.fileCollection().from(task.stubsDir.asFile) ).forEach { compilerPluginOptions.addPluginArgument(KAPT_SUBPLUGIN_ID, it) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt index d29d70a0cc8..123a4c39a27 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt @@ -8,44 +8,29 @@ package org.jetbrains.kotlin.gradle.tasks.configuration import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.attributes.Attribute +import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension import org.jetbrains.kotlin.gradle.internal.transforms.ClasspathEntrySnapshotTransform import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData +import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService -import org.jetbrains.kotlin.gradle.tasks.KOTLIN_BUILD_DIR_NAME import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.project.model.LanguageSettings internal typealias KotlinCompileConfig = BaseKotlinCompileConfig internal open class BaseKotlinCompileConfig : AbstractKotlinCompileConfig { - @Suppress("ConvertSecondaryConstructorToPrimary") - constructor(compilation: KotlinCompilationData<*>) : super(compilation) { - val javaTaskProvider = when (compilation) { - is KotlinJvmCompilation -> compilation.compileJavaTaskProvider - is KotlinJvmAndroidCompilation -> compilation.compileJavaTaskProvider - is KotlinWithJavaCompilation<*> -> compilation.compileJavaTaskProvider - else -> null - } - + init { configureTaskProvider { taskProvider -> val snapshotConfiguration = runAtConfigurationTime(taskProvider) taskProvider.configure { task -> - javaTaskProvider?.let { - task.associatedJavaCompileTaskTargetCompatibility.value(javaTaskProvider.map { it.targetCompatibility }) - task.associatedJavaCompileTaskSources.from(javaTaskProvider.map { it.source }) - task.associatedJavaCompileTaskName.value(javaTaskProvider.name) - } - task.moduleName.value(providers.provider { - (compilation.kotlinOptions as? KotlinJvmOptions)?.moduleName ?: task.parentKotlinOptionsImpl.orNull?.moduleName - ?: compilation.moduleName - }) - task.incremental = propertiesProvider.incrementalJvm ?: true if (propertiesProvider.useFir == true) { @@ -71,6 +56,34 @@ internal open class BaseKotlinCompileConfig : AbstractKotl } } + @Suppress("ConvertSecondaryConstructorToPrimary") + constructor(compilation: KotlinCompilationData<*>) : super(compilation) { + val javaTaskProvider = when (compilation) { + is KotlinJvmCompilation -> compilation.compileJavaTaskProvider + is KotlinJvmAndroidCompilation -> compilation.compileJavaTaskProvider + is KotlinWithJavaCompilation<*> -> compilation.compileJavaTaskProvider + else -> null + } + + configureTaskProvider { taskProvider -> + taskProvider.configure { task -> + javaTaskProvider?.let { + task.associatedJavaCompileTaskTargetCompatibility.value(javaTaskProvider.map { it.targetCompatibility }) + task.associatedJavaCompileTaskSources.from(javaTaskProvider.map { it.source }) + task.associatedJavaCompileTaskName.value(javaTaskProvider.name) + } + task.moduleName.value(providers.provider { + (compilation.kotlinOptions as? KotlinJvmOptions)?.moduleName ?: task.parentKotlinOptions.orNull?.moduleName + ?: compilation.moduleName + }) + } + } + } + + constructor(project: Project, ext: KotlinTopLevelExtension) : super( + project, ext, languageSettings = getDefaultLangSetting(project, ext) + ) + companion object { private const val TRANSFORMS_REGISTERED = "_kgp_internal_kotlin_compile_transforms_registered" @@ -78,6 +91,14 @@ internal open class BaseKotlinCompileConfig : AbstractKotl private const val DIRECTORY_ARTIFACT_TYPE = "directory" private const val JAR_ARTIFACT_TYPE = "jar" const val CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE = "classpath-entry-snapshot" + + private fun getDefaultLangSetting(project: Project, ext: KotlinTopLevelExtension): Provider { + return project.provider { + DefaultLanguageSettingsBuilder().also { + it.freeCompilerArgsProvider = project.provider { listOfNotNull(ext.explicitApi?.toCompilerArg()) } + } + } + } } private fun registerTransformsOnce(project: Project) { @@ -110,7 +131,7 @@ internal open class BaseKotlinCompileConfig : AbstractKotl return if (propertiesProvider.useClasspathSnapshot) { registerTransformsOnce(project) project.configurations.detachedConfiguration( - project.dependencies.create(objectFactory.fileCollection().from(taskProvider.get().libraries)) + project.dependencies.create(objectFactory.fileCollection().from(project.provider { taskProvider.get().libraries })) ) } else null } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KaptApiTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KaptApiTest.kt new file mode 100644 index 00000000000..7ae3ed848e6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KaptApiTest.kt @@ -0,0 +1,168 @@ +/* + * Copyright 2010-2022 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 + +import org.gradle.api.internal.project.ProjectInternal +import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin +import org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask +import org.jetbrains.kotlin.gradle.mpp.buildProject +import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin +import org.jetbrains.kotlin.gradle.plugin.KotlinJvmFactory +import org.jetbrains.kotlin.gradle.tasks.Kapt +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import kotlin.test.assertEquals + +class KaptApiTest { + + @get:Rule + val tmpDir = TemporaryFolder() + + private lateinit var project: ProjectInternal + private lateinit var plugin: KotlinJvmFactory + + companion object { + private const val TASK_NAME = "kapt" + private const val GENERATE_STUBS = "kaptGenerateStubs" + } + + @Before + fun setUpProject() { + project = buildProject {} + plugin = project.plugins.apply(KotlinBaseApiPlugin::class.java) + project.configurations.create(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME) + } + + @Test + fun testTaskName() { + val task = configureKapt {} + assertEquals(TASK_NAME, task.name) + } + + @Test + fun testSources() { + val sourcePath = tmpDir.newFolder().resolve("foo.kt").also { + it.createNewFile() + } + val task = configureKapt { + source.from(sourcePath) + } + assertEquals(setOf(sourcePath), task.source.files) + + val sourcesDir = tmpDir.newFolder().also { + it.resolve("a.kt").createNewFile() + it.resolve("b.kt").createNewFile() + } + task.source.from(sourcesDir) + assertEquals(setOf(sourcePath, sourcesDir), task.source.files) + } + + @Test + fun testKaptClasspath() { + val kaptClasspath = tmpDir.newFolder() + val externalClasspath = tmpDir.newFolder() + val configurationNames = listOf("a", "b", "c") + val task = configureKapt { + this.kaptClasspath.from(kaptClasspath) + kaptExternalClasspath.from(externalClasspath) + kaptClasspathConfigurationNames.set(configurationNames) + } + + assertEquals(setOf(kaptClasspath), task.kaptClasspath.files) + assertEquals(setOf(externalClasspath), task.kaptExternalClasspath.files) + assertEquals(configurationNames, task.kaptClasspathConfigurationNames.get()) + } + + @Test + fun testKaptOutputs() { + val incAptCache = tmpDir.newFolder() + val classesDir = tmpDir.newFolder() + val destinationDir = tmpDir.newFolder() + val generatedKotlinSources = tmpDir.newFolder() + + val task = configureKapt { + this.incAptCache.fileValue(incAptCache) + this.classesDir.fileValue(classesDir) + this.destinationDir.fileValue(destinationDir) + kotlinSourcesDestinationDir.fileValue(generatedKotlinSources) + } + + assertEquals(incAptCache, task.incAptCache.get().asFile) + assertEquals(classesDir, task.classesDir.get().asFile) + assertEquals(destinationDir, task.destinationDir.get().asFile) + assertEquals(generatedKotlinSources, task.kotlinSourcesDestinationDir.get().asFile) + } + + @Test + fun testClasspath() { + val compiledSources = tmpDir.newFolder() + val classpath = tmpDir.newFolder() + val includeCompileClasspath = false + + val task = configureKapt { + this.compiledSources.from(compiledSources) + this.classpath.from(classpath) + this.includeCompileClasspath.set(includeCompileClasspath) + } + + assertEquals(setOf(compiledSources), task.compiledSources.files) + assertEquals(setOf(classpath), task.classpath.files) + assertEquals(includeCompileClasspath, task.includeCompileClasspath.get()) + } + + @Test + fun testOptions() { + val sourceCompatibility = "11" + val addJdkClassesToMissingClasspathSnapshot = true + + val task = configureKapt { + defaultJavaSourceCompatibility.set(sourceCompatibility) + addJdkClassesToClasspath.set(addJdkClassesToMissingClasspathSnapshot) + } + + assertEquals(sourceCompatibility, task.defaultJavaSourceCompatibility.get()) + assertEquals(addJdkClassesToMissingClasspathSnapshot, task.addJdkClassesToClasspath.get()) + } + + @Test + fun testKaptExtension() { + plugin.kaptExtension.useBuildCache = false + plugin.kaptExtension.includeCompileClasspath = true + + val task = configureKapt {} + assertEquals(false, task.useBuildCache) + assertEquals(true, task.includeCompileClasspath.get()) + } + + @Test + fun testGenerateStubs() { + val task = plugin.registerKaptGenerateStubsTask(GENERATE_STUBS).get() + assertEquals(GENERATE_STUBS, task.name) + } + + @Test + fun testGenerateStubsOptions() { + val stubsDir = tmpDir.newFolder() + val kaptClasspath = setOf(tmpDir.newFolder()) + val task = plugin.registerKaptGenerateStubsTask(GENERATE_STUBS).let { provider -> + provider.configure { + it.stubsDir.fileValue(stubsDir) + it.kaptClasspath.from(kaptClasspath) + } + provider.get() + } + assertEquals(stubsDir, task.stubsDir.get().asFile) + assertEquals(kaptClasspath, task.kaptClasspath.files) + } + + private fun configureKapt(configAction: Kapt.() -> Unit): KaptWithoutKotlincTask { + val provider = plugin.registerKaptTask(TASK_NAME) + provider.configure(configAction) + return provider.get() as KaptWithoutKotlincTask + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KotlinCompileApiTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KotlinCompileApiTest.kt new file mode 100644 index 00000000000..a85324c7188 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KotlinCompileApiTest.kt @@ -0,0 +1,160 @@ +/* + * Copyright 2010-2022 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 + +import org.gradle.api.internal.project.ProjectInternal +import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode +import org.jetbrains.kotlin.gradle.mpp.buildProject +import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin +import org.jetbrains.kotlin.gradle.plugin.KotlinJvmFactory +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class KotlinCompileApiTest { + + @get:Rule + val tmpDir = TemporaryFolder() + + private lateinit var project: ProjectInternal + private lateinit var plugin: KotlinJvmFactory + private lateinit var taskApi: KotlinJvmCompile + private lateinit var taskImpl: KotlinCompile + + companion object { + private const val TASK_NAME = "kotlinCompile" + } + + @Before + fun setUpProject() { + project = buildProject {} + plugin = project.plugins.apply(KotlinBaseApiPlugin::class.java) + plugin.registerKotlinJvmCompileTask(TASK_NAME).configure { task -> + taskApi = task + } + taskImpl = project.tasks.getByName(TASK_NAME) as KotlinCompile + } + + @Test + fun testTaskName() { + assertEquals(TASK_NAME, taskImpl.name) + } + + @Test + fun testSources() { + val sourcePath = tmpDir.newFolder().resolve("foo.kt").also { + it.createNewFile() + } + taskApi.setSource(sourcePath) + assertEquals(setOf(sourcePath), taskImpl.sources.files) + + val sourcesDir = tmpDir.newFolder().also { + it.resolve("a.kt").createNewFile() + it.resolve("b.kt").createNewFile() + } + taskApi.setSource(sourcePath, sourcesDir) + assertEquals( + setOf(sourcePath, sourcesDir.resolve("a.kt"), sourcesDir.resolve("b.kt")), + taskImpl.sources.files + ) + } + + @Test + fun testFriendPaths() { + val friendPath = tmpDir.newFolder() + taskApi.friendPaths.from(friendPath) + assertEquals(setOf(friendPath), taskImpl.friendPaths.files) + } + + @Test + fun testLibraries() { + val classpathEntries = setOf(tmpDir.newFolder(), tmpDir.newFolder()) + taskApi.libraries.from(classpathEntries) + assertEquals(classpathEntries, taskImpl.libraries.files) + } + + @Test + fun testPluginClasspath() { + val pluginDependency = tmpDir.newFile() + plugin.addCompilerPluginDependency(project.provider { project.files(pluginDependency) }) + + val anotherCompilerPlugin = tmpDir.newFile() + taskApi.pluginClasspath.from(plugin.getCompilerPlugins(), anotherCompilerPlugin) + assertEquals(setOf(pluginDependency, anotherCompilerPlugin), taskImpl.pluginClasspath.files) + } + + @Test + fun testModuleName() { + taskApi.moduleName.set("foo") + assertEquals("foo", taskImpl.moduleName.get()) + } + + @Test + fun testSourceSetName() { + taskApi.sourceSetName.set("sourceSetFoo") + assertEquals("sourceSetFoo", taskImpl.sourceSetName.get()) + } + + @Test + fun testTaskBuildDirectory() { + val taskBuildDir = tmpDir.newFolder() + taskApi.destinationDirectory.fileValue(taskBuildDir) + assertEquals(taskBuildDir, taskImpl.destinationDirectory.get().asFile) + } + + @Test + fun testDestinationDirectory() { + val destinationDir = tmpDir.newFolder() + taskApi.destinationDirectory.fileValue(destinationDir) + assertEquals(destinationDir, taskImpl.destinationDirectory.get().asFile) + } + + @Test + fun testMultiplatform() { + taskApi.multiPlatformEnabled.set(true) + assertEquals(true, taskImpl.multiPlatformEnabled.get()) + + taskApi.multiPlatformEnabled.set(false) + assertEquals(false, taskImpl.multiPlatformEnabled.get()) + } + + @Test + fun testModuleDetection() { + taskApi.useModuleDetection.set(true) + assertEquals(true, taskImpl.useModuleDetection.get()) + + taskApi.useModuleDetection.set(false) + assertEquals(false, taskImpl.useModuleDetection.get()) + } + + + @Test + fun testParentKotlinOptions() { + val parentOptions = plugin.createKotlinJvmOptions() + parentOptions.moduleName = "foo" + parentOptions.javaParameters = true + parentOptions.languageVersion = "lang_version" + taskApi.parentKotlinOptions.set(parentOptions) + + taskImpl.parentKotlinOptions.get().let { + assertEquals(parentOptions.moduleName, it.moduleName) + assertEquals(parentOptions.javaParameters, it.javaParameters) + assertEquals(parentOptions.languageVersion, it.languageVersion) + } + } + + @Test + fun testTopLevelExtension() { + plugin.kotlinExtension.explicitApi = ExplicitApiMode.Strict + project.evaluate() + assertTrue(ExplicitApiMode.Strict.toCompilerArg() in taskImpl.kotlinOptions.freeCompilerArgs) + } +} \ No newline at end of file