From dc7d488debd14b57d8ab13de5c6b10d8f257d185 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Fri, 13 Jul 2018 14:42:24 +0300 Subject: [PATCH] Update existing plugin implementations to use KotlinCompilation Add default implementations for KotlinTarget and KotlinCompilation (some of them are used in code added by further commits). Refactor AbstractKotlinPlugin so that parts of its logic can be easily applied outside the plugin (needed for MPP plugin later). The single platform Kotlin plugins having Kotlin compiled along with Java will now create a KotlinCompilation and a KotlinSourceSet per Java source set or per Android variant. The build configuration logic will use KotlinCompilation objects rather than Java or Android plugin entities. Subplugins API and implementations are updated to use KotlinCompilation. --- .../main/kotlin/example/ExampleSubplugin.kt | 18 +- .../kotlin/allopen/gradle/AllOpenSubplugin.kt | 20 +- .../gradle/plugin/KotlinGradleSubplugin.kt | 13 +- .../plugin/android/Android25ProjectHandler.kt | 32 +- .../gradle/dsl/KotlinProjectExtension.kt | 16 +- .../gradle/internal/AndroidSubplugin.kt | 13 +- .../kapt/Kapt3KotlinGradleSubplugin.kt | 46 +- .../plugin/KotlinMultiplatformPlugin.kt | 80 ++- .../kotlin/gradle/plugin/KotlinPlugin.kt | 667 +++++++++++------- .../gradle/plugin/KotlinPluginWrapper.kt | 67 +- .../LegacyAndroidAndroidProjectHandler.kt | 15 +- .../plugin/mpp/HierarchyAttributeContainer.kt | 44 ++ .../plugin/mpp/KotlinCompilationFactory.kt | 85 +++ .../plugin/mpp/KotlinCompilationOutput.kt | 77 ++ .../gradle/plugin/mpp/kotlinCompilations.kt | 228 ++++++ .../kotlin/gradle/plugin/mpp/kotlinTargets.kt | 109 +++ .../internal/ScriptingGradleSubplugin.kt | 10 +- .../gradle/utils/gradleConfigurationUtils.kt | 14 + .../kotlin/gradle/utils/stringUtils.kt | 15 + .../kotlin/noarg/gradle/NoArgSubplugin.kt | 21 +- .../noarg/gradle/SamWithReceiverSubplugin.kt | 21 +- 21 files changed, 1168 insertions(+), 443 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/HierarchyAttributeContainer.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/gradleConfigurationUtils.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt diff --git a/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt b/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt index 7c980c0d1ef..0034f23984c 100644 --- a/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt +++ b/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt @@ -16,13 +16,9 @@ package example -import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin import org.gradle.api.Project -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile -import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.* class ExampleSubplugin : KotlinGradleSubplugin { @@ -31,12 +27,12 @@ class ExampleSubplugin : KotlinGradleSubplugin { } override fun apply( - project: Project, - kotlinCompile: AbstractCompile, - javaCompile: AbstractCompile, - variantData: Any?, - androidProjectHandler: Any?, - javaSourceSet: SourceSet? + project: Project, + kotlinCompile: AbstractCompile, + javaCompile: AbstractCompile?, + variantData: Any?, + androidProjectHandler: Any?, + kotlinCompilation: KotlinCompilation? ): List { println("ExampleSubplugin loaded") return listOf(SubpluginOption("exampleKey", "exampleValue")) diff --git a/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt b/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt index 638c24014e9..1f300124674 100644 --- a/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt +++ b/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt @@ -18,16 +18,10 @@ package org.jetbrains.kotlin.allopen.gradle import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.internal.AbstractTask -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.kotlin.allopen.gradle.model.builder.AllOpenModelBuilder -import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin -import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.* import javax.inject.Inject class AllOpenGradleSubplugin @Inject internal constructor(private val registry: ToolingModelBuilderRegistry) : Plugin { @@ -56,12 +50,12 @@ class AllOpenKotlinGradleSubplugin : KotlinGradleSubplugin { override fun isApplicable(project: Project, task: AbstractCompile) = AllOpenGradleSubplugin.isEnabled(project) override fun apply( - project: Project, - kotlinCompile: AbstractCompile, - javaCompile: AbstractCompile, - variantData: Any?, - androidProjectHandler: Any?, - javaSourceSet: SourceSet? + project: Project, + kotlinCompile: AbstractCompile, + javaCompile: AbstractCompile?, + variantData: Any?, + androidProjectHandler: Any?, + kotlinCompilation: KotlinCompilation? ): List { if (!AllOpenGradleSubplugin.isEnabled(project)) return emptyList() diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt index d44cd82d9e2..32b3b42b764 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.api.Project -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import java.io.File @@ -52,12 +51,12 @@ interface KotlinGradleSubplugin { fun isApplicable(project: Project, task: AbstractCompile): Boolean fun apply( - project: Project, - kotlinCompile: KotlinCompile, - javaCompile: AbstractCompile, - variantData: Any?, - androidProjectHandler: Any?, - javaSourceSet: SourceSet? + project: Project, + kotlinCompile: KotlinCompile, + javaCompile: AbstractCompile?, + variantData: Any?, + androidProjectHandler: Any?, + kotlinCompilation: KotlinCompilation? ): List fun getSubpluginKotlinTasks( diff --git a/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt index 8fcb65f0f5b..476995e2e0e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt @@ -11,7 +11,9 @@ import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin import org.jetbrains.kotlin.gradle.internal.KaptTask import org.jetbrains.kotlin.gradle.internal.KaptVariantData import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation import java.io.File @Suppress("unused") @@ -36,13 +38,15 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools } } - override fun wireKotlinTasks(project: Project, - androidPlugin: BasePlugin, - androidExt: BaseExtension, - variantData: BaseVariant, - javaTask: AbstractCompile, - kotlinTask: KotlinCompile) { - + override fun wireKotlinTasks( + project: Project, + compilation: KotlinJvmAndroidCompilation, + androidPlugin: BasePlugin, + androidExt: BaseExtension, + variantData: BaseVariant, + javaTask: AbstractCompile, + kotlinTask: KotlinCompile + ) { val preJavaKotlinOutputFiles = mutableListOf().apply { add(kotlinTask.destinationDir) if (Kapt3GradleSubplugin.isEnabled(project)) { @@ -95,6 +99,20 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools return variantData.mergeResources?.computeResourceSetList0() ?: emptyList() } + override fun setUpDependencyResolution(variant: BaseVariant, compilation: KotlinJvmAndroidCompilation) { + val project = compilation.target.project + + compilation.compileDependencyFiles = variant.compileConfiguration.apply { + usesPlatformOf(compilation.target) + project.addExtendsFromRelation(name, compilation.compileDependencyConfigurationName) + } + + compilation.runtimeDependencyFiles = variant.runtimeConfiguration.apply { + usesPlatformOf(compilation.target) + project.addExtendsFromRelation(name, compilation.runtimeDependencyConfigurationName) + } + } + private inner class KaptVariant(variantData: BaseVariant) : KaptVariantData(variantData) { override val name: String = getVariantName(variantData) override val sourceProviders: Iterable = getSourceProviders(variantData) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt index ad092e79571..fd5d84268c7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt @@ -16,13 +16,19 @@ package org.jetbrains.kotlin.gradle.dsl +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.internal.plugins.DslObject +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget +import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet import kotlin.reflect.KClass -internal fun Project.createKotlinExtension(extensionClass: KClass) { - val kotlinExt = extensions.create("kotlin", extensionClass.java) +private const val KOTLIN_PROJECT_EXTENSION_NAME = "kotlin" + +internal fun Project.createKotlinExtension(extensionClass: KClass): KotlinProjectExtension { + val kotlinExt = extensions.create(KOTLIN_PROJECT_EXTENSION_NAME, extensionClass.java) DslObject(kotlinExt).extensions.create("experimental", ExperimentalExtension::class.java) + return kotlinExtension } internal val Project.kotlinExtension: KotlinProjectExtension @@ -38,7 +44,11 @@ open class KotlinProjectExtension { internal set(value) { DslObject(this).extensions.add("sourceSets", value) } } -open class KotlinJvmProjectExtension : KotlinProjectExtension() { +open class KotlinSingleJavaTargetExtension : KotlinProjectExtension() { + internal lateinit var target: KotlinWithJavaTarget +} + +open class KotlinJvmProjectExtension : KotlinSingleJavaTargetExtension() { /** * With Gradle 4.0+, disables the separate output directory for Kotlin, falling back to sharing the deprecated * single classes directory per source set. With Gradle < 4.0, has no effect. diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt index 9da70ab4b2e..5e27c4e0e5e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt @@ -25,7 +25,6 @@ import com.android.build.gradle.internal.variant.TestVariantData import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.UnknownDomainObjectException -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.kotlin.gradle.model.builder.KotlinAndroidExtensionModelBuilder @@ -90,12 +89,12 @@ class AndroidSubplugin : KotlinGradleSubplugin { } override fun apply( - project: Project, - kotlinCompile: KotlinCompile, - javaCompile: AbstractCompile, - variantData: Any?, - androidProjectHandler: Any?, - javaSourceSet: SourceSet? + project: Project, + kotlinCompile: KotlinCompile, + javaCompile: AbstractCompile?, + variantData: Any?, + androidProjectHandler: Any?, + kotlinCompilation: KotlinCompilation? ): List { val androidExtension = project.extensions.getByName("android") as? BaseExtension ?: return emptyList() val androidExtensionsExtension = project.extensions.getByType(AndroidExtensionsExtension::class.java) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt index 7bde3e33ae7..4dc68e096e5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt @@ -158,10 +158,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { private inner class Kapt3SubpluginContext( val project: Project, val kotlinCompile: KotlinCompile, - val javaCompile: AbstractCompile, + val javaCompile: AbstractCompile?, val kaptVariantData: KaptVariantData<*>?, val sourceSetName: String, - val javaSourceSet: SourceSet?, + val kotlinCompilation: KotlinCompilation?, val kaptExtension: KaptExtension, val kaptClasspathConfigurations: List ) { @@ -171,14 +171,14 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { } override fun apply( - project: Project, - kotlinCompile: KotlinCompile, - javaCompile: AbstractCompile, - variantData: Any?, - androidProjectHandler: Any?, - javaSourceSet: SourceSet? + project: Project, + kotlinCompile: KotlinCompile, + javaCompile: AbstractCompile?, + variantData: Any?, + androidProjectHandler: Any?, + kotlinCompilation: KotlinCompilation? ): List { - assert((variantData != null) xor (javaSourceSet != null)) + assert((variantData != null) xor (kotlinCompilation != null)) val buildDependencies = arrayListOf() val kaptConfigurations = arrayListOf() @@ -200,10 +200,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { kaptVariantData.name } else { - if (javaSourceSet == null) error("Java source set should not be null") + if (kotlinCompilation == null) error("In non-Android projects, Kotlin compilation should not be null") - handleSourceSet(javaSourceSet.name) - javaSourceSet.name + handleSourceSet(kotlinCompilation.compilationName) + kotlinCompilation.compilationName } val kaptExtension = project.extensions.getByType(KaptExtension::class.java) @@ -212,7 +212,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { val context = Kapt3SubpluginContext( project, kotlinCompile, javaCompile, - kaptVariantData, sourceSetName, javaSourceSet, kaptExtension, nonEmptyKaptConfigurations + kaptVariantData, sourceSetName, kotlinCompilation, kaptExtension, nonEmptyKaptConfigurations ) val kaptGenerateStubsTask = context.createKaptGenerateStubsTask() @@ -245,7 +245,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { pluginOptions += SubpluginOption("aptMode", aptMode) disableAnnotationProcessingInJavaTask() - javaCompile.source(generatedFilesDir) + javaCompile?.source(generatedFilesDir) pluginOptions += FilesSubpluginOption("sources", listOf(generatedFilesDir)) pluginOptions += FilesSubpluginOption("classes", listOf(getKaptGeneratedClassesDir(project, sourceSetName))) @@ -362,18 +362,22 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { kaptTask.kotlinSourcesDestinationDir = kotlinSourcesOutputDir kaptTask.classesDir = classesOutputDir - javaSourceSet?.output?.apply { - if (tryAddClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }) { - kotlinCompile.attachClassesDir { classesOutputDir } + kotlinCompilation?.run { + output.apply { + if (tryAddClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }) { + kotlinCompile.attachClassesDir { classesOutputDir } + } } } kotlinCompile.source(sourcesOutputDir, kotlinSourcesOutputDir) - if (kaptVariantData != null) { - kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile) - } else { - registerGeneratedJavaSource(kaptTask, javaCompile) + if (javaCompile != null) { + if (kaptVariantData != null) { + kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile) + } else { + registerGeneratedJavaSource(kaptTask, javaCompile) + } } kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt index 03c7f0360b6..2fc20741273 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt @@ -21,9 +21,8 @@ import org.gradle.api.* import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.file.SourceDirectorySet -import org.gradle.api.plugins.JavaPluginConvention -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetContainer +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile abstract class KotlinPlatformPluginBase(protected val platformName: String) : Plugin { @@ -110,7 +109,10 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin // Since the two projects may add source sets in arbitrary order, and both may do that after the plugin is applied, // we need to handle all source sets of the two projects and connect them once we get a match: // todo warn if no match found - matchSymmetricallyByNames(commonProject.sourceSets, namedSourceSetsContainer(platformProject)) { commonSourceSet, _ -> + matchSymmetricallyByNames( + getKotlinSourceSetsSafe(commonProject), + namedSourceSetsContainer(platformProject) + ) { commonSourceSet: Named, _ -> addCommonSourceSetToPlatformSourceSet(commonSourceSet, platformProject) } } @@ -121,13 +123,13 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin * regardless of the order in which they are added to the containers. */ private fun matchSymmetricallyByNames( - containerA: NamedDomainObjectContainer, - containerB: NamedDomainObjectContainer, + containerA: NamedDomainObjectCollection, + containerB: NamedDomainObjectCollection, whenMatched: (A, B) -> Unit ) { val matchedNames = mutableSetOf() - fun NamedDomainObjectContainer.matchAllWith(other: NamedDomainObjectContainer, match: (T, R) -> Unit) { + fun NamedDomainObjectCollection.matchAllWith(other: NamedDomainObjectCollection, match: (T, R) -> Unit) { this@matchAllWith.all { item -> val itemName = this@matchAllWith.namer.determineName(item) if (itemName !in matchedNames) { @@ -144,36 +146,38 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin } protected open fun namedSourceSetsContainer(project: Project): NamedDomainObjectContainer<*> = - project.sourceSets + project.kotlinExtension.sourceSets - protected open fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: SourceSet, platformProject: Project) { - val platformTask = platformProject.tasks - .filterIsInstance>() - .firstOrNull { it.sourceSetName == commonSourceSet.name } + protected open fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) { + platformProject.whenEvaluated { // At the point when the source set in the platform module is created, the task does not exist + val platformTask = platformProject.tasks + .filterIsInstance>() + .single { it.sourceSetName == commonSourceSet.name } - platformTask?.source(commonSourceSet.kotlin!!) + platformTask.source(getKotlinSourceDirectorySetSafe(commonSourceSet)) + } } - protected val SourceSet.kotlin: SourceDirectorySet? - get() { - // Access through reflection, because another project's KotlinSourceSet might be loaded - // by a different class loader: - val convention = (getConvention("kotlin") ?: getConvention("kotlin2js")) ?: return null - val kotlinSourceSetIface = convention.javaClass.interfaces.find { it.name == KotlinSourceSet::class.qualifiedName } - val getKotlin = kotlinSourceSetIface?.methods?.find { it.name == "getKotlin" } ?: return null - return getKotlin(convention) as? SourceDirectorySet - } + private fun getKotlinSourceSetsSafe(project: Project): NamedDomainObjectCollection { + // Access through reflection, because another project's KotlinProjectExtension might be loaded by a different class loader: + val kotlinExt = project.extensions.getByName("kotlin") + @Suppress("UNCHECKED_CAST") + val sourceSets = kotlinExt.javaClass.getMethod("getSourceSets").invoke(kotlinExt) as NamedDomainObjectCollection + return sourceSets + } - companion object { - @JvmStatic - protected fun Project.whenEvaluated(fn: Project.() -> T) { - if (state.executed) { - fn() - } - else { - afterEvaluate { it.fn() } - } - } + protected fun getKotlinSourceDirectorySetSafe(from: Any): SourceDirectorySet? { + val getKotlin = from.javaClass.getMethod("getKotlin") + return getKotlin(from) as? SourceDirectorySet + } +} + +internal fun Project.whenEvaluated(fn: Project.() -> T) { + if (state.executed) { + fn() + } + else { + afterEvaluate { it.fn() } } } @@ -190,11 +194,12 @@ open class KotlinPlatformAndroidPlugin : KotlinPlatformImplementationPluginBase( override fun namedSourceSetsContainer(project: Project): NamedDomainObjectContainer<*> = (project.extensions.getByName("android") as BaseExtension).sourceSets - override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: SourceSet, platformProject: Project) { + override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) { val androidExtension = platformProject.extensions.getByName("android") as BaseExtension val androidSourceSet = androidExtension.sourceSets.findByName(commonSourceSet.name) ?: return - val kotlinSourceSet = androidSourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: return - kotlinSourceSet.kotlin.source(commonSourceSet.kotlin!!) + val kotlinSourceSet = androidSourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet + ?: return + kotlinSourceSet.kotlin.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!) } } @@ -210,7 +215,4 @@ open class KotlinPlatformJsPlugin : KotlinPlatformImplementationPluginBase("js") project.applyPlugin() super.apply(project) } -} - -private val Project.sourceSets: SourceSetContainer - get() = convention.getPlugin(JavaPluginConvention::class.java).sourceSets +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index a7870c74c82..dc6588e90c3 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -5,7 +5,6 @@ import com.android.build.gradle.BasePlugin import com.android.builder.model.SourceProvider import groovy.lang.Closure import org.gradle.api.* -import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ExternalDependency import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection @@ -13,7 +12,6 @@ import org.gradle.api.file.SourceDirectorySet import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.api.plugins.InvalidPluginException -import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.CompileClasspathNormalizer @@ -26,11 +24,15 @@ import org.gradle.jvm.tasks.Jar import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl +import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin import org.jetbrains.kotlin.gradle.internal.KaptVariantData import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder +import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.utils.* @@ -47,58 +49,90 @@ val KOTLIN_JS_DSL_NAME = "kotlin2js" val KOTLIN_OPTIONS_DSL_NAME = "kotlinOptions" internal abstract class KotlinSourceSetProcessor>( - val project: Project, - val javaBasePlugin: JavaBasePlugin, - val sourceSet: SourceSet, - val tasksProvider: KotlinTasksProvider, - val kotlinSourceSetProvider: KotlinSourceSetProvider, - val dslExtensionName: String, - val compileTaskNameSuffix: String, - val taskDescription: String + val project: Project, + val tasksProvider: KotlinTasksProvider, + val taskDescription: String, + val kotlinCompilation: KotlinCompilation ) { abstract protected fun doTargetSpecificProcessing() protected val logger = Logging.getLogger(this.javaClass)!! protected val isSeparateClassesDirSupported: Boolean by lazy { !CopyClassesToJavaOutputStatus.isEnabled(project) && - sourceSet.output.javaClass.methods.any { it.name == "getClassesDirs" } + kotlinCompilation.output.javaClass.methods.any { it.name == "getClassesDirs" } } - protected val sourceSetName: String = sourceSet.name - protected val sourceRootDir: String = "src/$sourceSetName/kotlin" - protected val kotlinSourceSet: KotlinSourceSet = createKotlinSourceSet() + protected val sourceSetName: String = kotlinCompilation.compilationName + protected val kotlinTask: T = createKotlinCompileTask() protected open val defaultKotlinDestinationDir: File - get() = if (isSeparateClassesDirSupported) - File(project.buildDir, "classes/kotlin/${sourceSet.name}") else - sourceSet.output.classesDir + get() { + return if (isSeparateClassesDirSupported) { + val kotlinExt = project.kotlinExtension + val targetSubDirectory = + if (kotlinExt is KotlinSingleJavaTargetExtension) + "" // In single-target projects, don't add the target name part to this path + else + kotlinCompilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty() + File(project.buildDir, "classes/kotlin/$targetSubDirectory${kotlinCompilation.compilationName}") + } else { + kotlinCompilation.output.classesDir + } + } - fun run() { - addKotlinDirSetToSources() + private fun createKotlinCompileTask(): T { + val name = kotlinCompilation.compileKotlinTaskName + logger.kotlinDebug("Creating kotlin compile task $name") + val kotlinCompile = doCreateTask(project, name) + kotlinCompile.description = taskDescription + kotlinCompile.mapClasspath { kotlinCompilation.compileDependencyFiles } + kotlinCompile.setDestinationDir { defaultKotlinDestinationDir } + kotlinCompilation.output.tryAddClassesDir { project.files(kotlinTask.destinationDir).builtBy(kotlinTask) } + return kotlinCompile + } + + open fun run() { doTargetSpecificProcessing() } - private fun createKotlinSourceSet(): KotlinSourceSet { - logger.kotlinDebug("Creating KotlinSourceSet for $sourceSet") - val kotlinSourceSet = kotlinSourceSetProvider.create(sourceSet.name) - kotlinSourceSet.kotlin.srcDir(project.file(sourceRootDir)) - sourceSet.addConvention(dslExtensionName, kotlinSourceSet) - return kotlinSourceSet + protected abstract fun doCreateTask(project: Project, taskName: String): T +} + +internal abstract class KotlinJavaSourceSetProcessor>( + project: Project, + tasksProvider: KotlinTasksProvider, + taskDescription: String, + val javaSourceSet: SourceSet?, + kotlinCompilation: KotlinCompilation +): KotlinSourceSetProcessor( + project, tasksProvider, taskDescription, kotlinCompilation +) { + override fun run() { + addKotlinDirSetToSources() + super.run() } private fun addKotlinDirSetToSources() { - val kotlinDirSet = kotlinSourceSet.kotlin + if (javaSourceSet == null) + return + + val kotlinDirSets = kotlinCompilation.kotlinSourceSets.map(KotlinSourceSet::kotlin) // Try to avoid duplicate Java sources in allSource: - val kotlinSrcDirsToAdd = filterOutJavaSrcDirsIfPossible(kotlinDirSet) + val kotlinSrcDirsToAdd = kotlinDirSets.map { filterOutJavaSrcDirsIfPossible(it) } - sourceSet.allJava.srcDirs(kotlinSrcDirsToAdd) - sourceSet.allSource.srcDirs(kotlinSrcDirsToAdd) - sourceSet.resources.filter.exclude { it.file in kotlinDirSet } + kotlinSrcDirsToAdd.forEach { kotlinSrcDirs -> + javaSourceSet.allJava.srcDirs(kotlinSrcDirs) + javaSourceSet.allSource.srcDirs(kotlinSrcDirs) + javaSourceSet.resources.filter.exclude { it.file in kotlinSrcDirs } + } } private fun filterOutJavaSrcDirsIfPossible(sourceDirectorySet: SourceDirectorySet): FileCollection { + if (javaSourceSet == null) + return sourceDirectorySet + // If the API used below is not available, fall back to not filtering the Java sources. if (SourceDirectorySet::class.java.methods.none { it.name == "getSourceDirectories" }) { return sourceDirectorySet @@ -110,35 +144,19 @@ internal abstract class KotlinSourceSetProcessor>( } // Build a lazily-resolved file collection that filters out Java sources from sources of this sourceDirectorySet - return getSourceDirectories(sourceDirectorySet).minus(getSourceDirectories(sourceSet.java)) + return getSourceDirectories(sourceDirectorySet).minus(getSourceDirectories(javaSourceSet.java)) } - - private fun createKotlinCompileTask(): T { - val name = sourceSet.getCompileTaskName(compileTaskNameSuffix) - logger.kotlinDebug("Creating kotlin compile task $name") - val kotlinCompile = doCreateTask(project, name) - kotlinCompile.description = taskDescription - kotlinCompile.mapClasspath { sourceSet.compileClasspath } - kotlinCompile.setDestinationDir { defaultKotlinDestinationDir } - sourceSet.output.tryAddClassesDir { project.files(kotlinTask.destinationDir).builtBy(kotlinTask) } - return kotlinCompile - } - - protected abstract fun doCreateTask(project: Project, taskName: String): T } internal class Kotlin2JvmSourceSetProcessor( - project: Project, - javaBasePlugin: JavaBasePlugin, - sourceSet: SourceSet, - tasksProvider: KotlinTasksProvider, - kotlinSourceSetProvider: KotlinSourceSetProvider, - private val kotlinPluginVersion: String -) : KotlinSourceSetProcessor( - project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, - dslExtensionName = KOTLIN_DSL_NAME, - compileTaskNameSuffix = "kotlin", - taskDescription = "Compiles the $sourceSet.kotlin." + project: Project, + tasksProvider: KotlinTasksProvider, + kotlinCompilation: KotlinCompilation, + private val kotlinPluginVersion: String +) : KotlinJavaSourceSetProcessor( + project, tasksProvider, taskDescription = "Compiles the $kotlinCompilation.", + javaSourceSet = if (kotlinCompilation is KotlinWithJavaCompilation) kotlinCompilation.javaSourceSet else null, + kotlinCompilation = kotlinCompilation ) { override val defaultKotlinDestinationDir: File get() = if (!isSeparateClassesDirSupported) @@ -146,44 +164,38 @@ internal class Kotlin2JvmSourceSetProcessor( super.defaultKotlinDestinationDir override fun doCreateTask(project: Project, taskName: String): KotlinCompile = - tasksProvider.createKotlinJVMTask(project, taskName, sourceSet.name) + tasksProvider.createKotlinJVMTask(project, taskName, kotlinCompilation.compilationName) override fun doTargetSpecificProcessing() { - kotlinSourceSet.kotlin.source(sourceSet.java) - Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, sourceSet.name) + Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, kotlinCompilation.compilationName) project.afterEvaluate { project -> - if (project != null) { - val javaTask = project.tasks.findByName(sourceSet.compileJavaTaskName) + val javaTask = javaSourceSet?.let { project.tasks.findByName(it.compileJavaTaskName) as JavaCompile } - val subpluginEnvironment = loadSubplugins(project, kotlinPluginVersion) - val appliedPlugins = subpluginEnvironment.addSubpluginOptions( - project, kotlinTask, javaTask as JavaCompile, null, null, sourceSet) + val subpluginEnvironment = loadSubplugins(project, kotlinPluginVersion) + val appliedPlugins = subpluginEnvironment.addSubpluginOptions( + project, kotlinTask, javaTask, null, null, kotlinCompilation + ) - // KotlinCompile.source(kotlinDirSet) should be called only after all java roots are added to kotlinDirSet - // otherwise some java roots can be ignored - kotlinTask.source(kotlinSourceSet.kotlin) - appliedPlugins - .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } - .forEach { it.source(kotlinSourceSet.kotlin) } + appliedPlugins + .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } + .forEach { plugin -> kotlinCompilation.kotlinSourceSets.forEach { sourceSet -> plugin.source(sourceSet.kotlin) } } - configureJavaTask(kotlinTask, javaTask, logger) + javaTask?.let { configureJavaTask(kotlinTask, it, logger) } - var syncOutputTask: SyncOutputTask? = null + var syncOutputTask: SyncOutputTask? = null - if (!isSeparateClassesDirSupported) { - syncOutputTask = createSyncOutputTask(project, kotlinTask, javaTask, sourceSetName) - } - - if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) { - val (classesProviderTask, classesDirectory) = when { - isSeparateClassesDirSupported -> kotlinTask.let { it to it.destinationDir } - else -> syncOutputTask!!.let { it to it.javaOutputDir } - } - - registerKotlinOutputForJavaLibrary(classesDirectory, classesProviderTask) + if (!isSeparateClassesDirSupported && javaTask != null) { + syncOutputTask = createSyncOutputTask(project, kotlinTask, javaTask, sourceSetName) + } + + if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) { + val (classesProviderTask, classesDirectory) = when { + isSeparateClassesDirSupported -> kotlinTask.let { it to it.destinationDir } + else -> syncOutputTask!!.let { it to it.javaOutputDir } } + registerKotlinOutputForJavaLibrary(classesDirectory, classesProviderTask) } } } @@ -233,37 +245,35 @@ internal fun SourceSetOutput.tryAddClassesDir( } internal class Kotlin2JsSourceSetProcessor( - project: Project, - javaBasePlugin: JavaBasePlugin, - sourceSet: SourceSet, - tasksProvider: KotlinTasksProvider, - kotlinSourceSetProvider: KotlinSourceSetProvider, - private val kotlinPluginVersion: String + project: Project, + tasksProvider: KotlinTasksProvider, + kotlinCompilation: KotlinCompilation, + private val kotlinPluginVersion: String ) : KotlinSourceSetProcessor( - project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, - dslExtensionName = KOTLIN_JS_DSL_NAME, - taskDescription = "Compiles the kotlin sources in $sourceSet to JavaScript.", - compileTaskNameSuffix = "kotlin2Js" + project, tasksProvider, taskDescription = "Compiles the Kotlin sources in $kotlinCompilation to JavaScript.", + kotlinCompilation = kotlinCompilation ) { override fun doCreateTask(project: Project, taskName: String): Kotlin2JsCompile = - tasksProvider.createKotlinJSTask(project, taskName, sourceSet.name) + tasksProvider.createKotlinJSTask(project, taskName, kotlinCompilation.compilationName) override fun doTargetSpecificProcessing() { - project.tasks.findByName(sourceSet.classesTaskName)!!.dependsOn(kotlinTask) - kotlinTask.source(kotlinSourceSet.kotlin) + project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask) + createCleanSourceMapTask() - sourceSet.clearJavaSrcDirs() + if (kotlinCompilation is KotlinWithJavaCompilation) { + kotlinCompilation.javaSourceSet.clearJavaSrcDirs() + } // outputFile can be set later during the configuration phase, get it only after the phase: project.afterEvaluate { project -> - val subpluginEnvironment: SubpluginEnvironment = loadSubplugins(project, kotlinPluginVersion) - val appliedPlugins = subpluginEnvironment.addSubpluginOptions( - project, kotlinTask, kotlinTask, null, null, sourceSet) - kotlinTask.kotlinOptions.outputFile = kotlinTask.outputFile.absolutePath val outputDir = kotlinTask.outputFile.parentFile + val subpluginEnvironment: SubpluginEnvironment = loadSubplugins(project, kotlinPluginVersion) + val appliedPlugins = subpluginEnvironment.addSubpluginOptions( + project, kotlinTask, null, null, null, kotlinCompilation) + if (outputDir.isParentOf(project.rootDir)) throw InvalidUserDataException( "The output directory '$outputDir' (defined by outputFile of $kotlinTask) contains or " + @@ -274,17 +284,17 @@ internal class Kotlin2JsSourceSetProcessor( kotlinTask.destinationDir = outputDir if (!isSeparateClassesDirSupported) { - sourceSet.output.setClassesDirCompatible(kotlinTask.destinationDir) + kotlinCompilation.output.setClassesDirCompatible(kotlinTask.destinationDir) } appliedPlugins .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } - .forEach { it.source(kotlinSourceSet.kotlin) } + .forEach { task -> kotlinCompilation.kotlinSourceSets.forEach { sourceSet -> task.source(sourceSet.kotlin) } } } } private fun createCleanSourceMapTask() { - val taskName = sourceSet.getTaskName("clean", "sourceMap") + val taskName = kotlinCompilation.composeName("clean", "sourceMap") val task = project.tasks.create(taskName, Delete::class.java) task.onlyIf { kotlinTask.kotlinOptions.sourceMap } task.delete(object : Closure(this) { @@ -295,189 +305,274 @@ internal class Kotlin2JsSourceSetProcessor( } internal class KotlinCommonSourceSetProcessor( - project: Project, - javaBasePlugin: JavaBasePlugin, - sourceSet: SourceSet, - tasksProvider: KotlinTasksProvider, - kotlinSourceSetProvider: KotlinSourceSetProvider, - private val kotlinPluginVersion: String + project: Project, + compilation: KotlinCompilation, + tasksProvider: KotlinTasksProvider, + private val kotlinPluginVersion: String ) : KotlinSourceSetProcessor( - project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, - dslExtensionName = KOTLIN_DSL_NAME, - taskDescription = "Compiles the kotlin sources in $sourceSet to Metadata.", - compileTaskNameSuffix = "kotlinCommon" + project, tasksProvider, taskDescription = "Compiles the kotlin sources in $compilation to Metadata.", + kotlinCompilation = compilation ) { override fun doTargetSpecificProcessing() { - sourceSet.clearJavaSrcDirs() + project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask) + // can be missing (e.g. in case of tests) + if (kotlinCompilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) { + project.tasks.findByName(kotlinCompilation.target.artifactsTaskName)?.dependsOn(kotlinTask) + } project.afterEvaluate { project -> - kotlinTask.source(kotlinSourceSet.kotlin) - val subpluginEnvironment: SubpluginEnvironment = loadSubplugins(project, kotlinPluginVersion) val appliedPlugins = subpluginEnvironment.addSubpluginOptions( - project, kotlinTask, kotlinTask, null, null, sourceSet) - - project.tasks.findByName(sourceSet.classesTaskName)!!.dependsOn(kotlinTask) - // can be missing (e.g. in case of tests) - project.tasks.findByName(sourceSet.jarTaskName)?.dependsOn(kotlinTask) - + project, kotlinTask, null, null, null, kotlinCompilation + ) appliedPlugins - .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } - .forEach { it.source(kotlinSourceSet.kotlin) } + .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } + .forEach { kotlinCompilation.kotlinSourceSets.forEach { sourceSet -> it.source(sourceSet.kotlin) } } } } override fun doCreateTask(project: Project, taskName: String): KotlinCompileCommon = - tasksProvider.createKotlinCommonTask(project, taskName, sourceSet.name) + tasksProvider.createKotlinCommonTask(project, taskName, kotlinCompilation.compilationName) } - internal abstract class AbstractKotlinPlugin( - val tasksProvider: KotlinTasksProvider, - val kotlinSourceSetProvider: KotlinSourceSetProvider, - protected val kotlinPluginVersion: String, + val tasksProvider: KotlinTasksProvider, + protected val kotlinPluginVersion: String, val registry: ToolingModelBuilderRegistry ) : Plugin { - internal abstract fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String): KotlinSourceSetProcessor<*> + + internal abstract fun buildSourceSetProcessor(project: Project, compilation: KotlinCompilation, kotlinPluginVersion: String): KotlinSourceSetProcessor<*> override fun apply(project: Project) { - val javaBasePlugin = project.plugins.apply(JavaBasePlugin::class.java) - val javaPluginConvention = project.convention.getPlugin(JavaPluginConvention::class.java) - project.plugins.apply(JavaPlugin::class.java) - configureSourceSetDefaults(project, javaBasePlugin, javaPluginConvention) - configureDefaultVersionsResolutionStrategy(project) - configureClassInspectionForIC(project) + configureTarget( + (project.kotlinExtension as KotlinSingleJavaTargetExtension).target, + { compilation -> buildSourceSetProcessor(project, compilation, kotlinPluginVersion) } + ) + + configureProjectGlobalSettings(project, kotlinPluginVersion) registry.register(KotlinModelBuilder(kotlinPluginVersion)) } - open protected fun configureSourceSetDefaults( - project: Project, - javaBasePlugin: JavaBasePlugin, - javaPluginConvention: JavaPluginConvention - ) { - javaPluginConvention.sourceSets?.all { sourceSet -> - buildSourceSetProcessor(project, javaBasePlugin, sourceSet, kotlinPluginVersion).run() - } + companion object { + fun configureProjectGlobalSettings(project: Project, kotlinPluginVersion: String) { + configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion) + configureClassInspectionForIC(project) } - private fun configureDefaultVersionsResolutionStrategy(project: Project) { - project.configurations.all { configuration -> - if (isGradleVersionAtLeast(4, 4)) { - // Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved: - configuration.withDependencies { dependencySet -> - dependencySet.filterIsInstance() - .filter { it.group == "org.jetbrains.kotlin" && it.version.isNullOrEmpty() } - .forEach { it.version { constraint -> constraint.prefer(kotlinPluginVersion) } } - } + fun configureTarget( + target: KotlinWithJavaTarget, + buildSourceSetProcessor: (KotlinCompilation) -> KotlinSourceSetProcessor<*> + ) { + setUpJavaSourceSets(target) + configureSourceSetDefaults(target, buildSourceSetProcessor) + } + + private fun configureClassInspectionForIC(project: Project) { + val classesTask = project.tasks.findByName(JavaPlugin.CLASSES_TASK_NAME) + val jarTask = project.tasks.findByName(JavaPlugin.JAR_TASK_NAME) + + if (classesTask == null || jarTask !is Jar) { + project.logger.info( + "Could not configure class inspection task " + + "(classes task = ${classesTask?.javaClass?.canonicalName}, " + + "jar task = ${classesTask?.javaClass?.canonicalName}" + ) + return } - else { - configuration.resolutionStrategy.eachDependency { details -> - val requested = details.requested - if (requested.group == "org.jetbrains.kotlin" && requested.version.isEmpty()) { - details.useVersion(kotlinPluginVersion) - } - } + val inspectTask = project.tasks.create("inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java) + inspectTask.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME + inspectTask.jarTask = jarTask + inspectTask.dependsOn(classesTask) + jarTask.dependsOn(inspectTask) + } + + private fun setUpJavaSourceSets( + kotlinTarget: KotlinWithJavaTarget + ) { + val project = kotlinTarget.project + val javaSourceSets = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets + + val kotlinSourceSetDslName = when (kotlinTarget.platformType) { + KotlinPlatformType.js -> KOTLIN_JS_DSL_NAME + else -> KOTLIN_DSL_NAME + } + + // Workaround for indirect mutual recursion between the two `all { ... }` handlers: + val compilationsUnderConstruction = mutableMapOf() + + javaSourceSets.all { javaSourceSet -> + val kotlinCompilation = compilationsUnderConstruction[javaSourceSet.name] ?: kotlinTarget.compilations.maybeCreate(javaSourceSet.name) + kotlinCompilation.javaSourceSet = javaSourceSet + val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name) + javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinSourceSet) + kotlinSourceSet.kotlin.source(javaSourceSet.java) + kotlinCompilation.source(kotlinSourceSet) + } + + kotlinTarget.compilations.all { kotlinCompilation -> + val sourceSetName = kotlinCompilation.name + compilationsUnderConstruction[sourceSetName] = kotlinCompilation + kotlinCompilation.javaSourceSet = javaSourceSets.maybeCreate(sourceSetName) + + // Another Kotlin source set following the other convention, named according to the compilation, not the Java source set: + val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.fullName) + kotlinCompilation.source(kotlinSourceSet) + } + } + + fun configureSourceSetDefaults( + kotlinTarget: KotlinTarget, + buildSourceSetProcessor: (KotlinCompilation) -> KotlinSourceSetProcessor<*> + ) { + kotlinTarget.compilations.all { compilation -> + buildSourceSetProcessor(compilation).run() } } } +} - private fun configureClassInspectionForIC(project: Project) { - val classesTask = project.tasks.findByName(JavaPlugin.CLASSES_TASK_NAME) - val jarTask = project.tasks.findByName(JavaPlugin.JAR_TASK_NAME) - - if (classesTask == null || jarTask !is Jar) { - project.logger.info( - "Could not configure class inspection task " + - "(classes task = ${classesTask?.javaClass?.canonicalName}, " + - "jar task = ${classesTask?.javaClass?.canonicalName}" - ) - return +internal fun configureDefaultVersionsResolutionStrategy(project: Project, kotlinPluginVersion: String) { + project.configurations.all { configuration -> + if (isGradleVersionAtLeast(4, 4)) { + // Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved: + configuration.withDependencies { dependencySet -> + dependencySet.filterIsInstance() + .filter { it.group == "org.jetbrains.kotlin" && it.version.isNullOrEmpty() } + .forEach { it.version { constraint -> constraint.prefer(kotlinPluginVersion) } } + } + } else { + configuration.resolutionStrategy.eachDependency { details -> + val requested = details.requested + if (requested.group == "org.jetbrains.kotlin" && requested.version.isEmpty()) { + details.useVersion(kotlinPluginVersion) + } + } } - val inspectTask = project.tasks.create("inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java) - inspectTask.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME - inspectTask.jarTask = jarTask - inspectTask.dependsOn(classesTask) - jarTask.dependsOn(inspectTask) } } internal open class KotlinPlugin( - tasksProvider: KotlinTasksProvider, - kotlinSourceSetProvider: KotlinSourceSetProvider, - kotlinPluginVersion: String, + tasksProvider: KotlinTasksProvider, + kotlinPluginVersion: String, registry: ToolingModelBuilderRegistry -) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion, registry) { - override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) = - Kotlin2JvmSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) +) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion, registry) { + + override fun buildSourceSetProcessor(project: Project, compilation: KotlinCompilation, kotlinPluginVersion: String) = + Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion) override fun apply(project: Project) { + val target = KotlinWithJavaTarget(project, KotlinPlatformType.jvm, "").apply { + disambiguationClassifier = null // don't add anything to the task names + } + (project.kotlinExtension as KotlinSingleJavaTargetExtension).target = target + project.pluginManager.apply(ScriptingGradleSubplugin::class.java) super.apply(project) } } internal open class KotlinCommonPlugin( - tasksProvider: KotlinTasksProvider, - kotlinSourceSetProvider: KotlinSourceSetProvider, - kotlinPluginVersion: String, + tasksProvider: KotlinTasksProvider, + kotlinPluginVersion: String, registry: ToolingModelBuilderRegistry -) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion, registry) { - override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) = - KotlinCommonSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) -} +) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion, registry) { -internal open class Kotlin2JsPlugin( - tasksProvider: KotlinTasksProvider, - kotlinSourceSetProvider: KotlinSourceSetProvider, - kotlinPluginVersion: String, - registry: ToolingModelBuilderRegistry -) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion, registry) { - override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) = - Kotlin2JsSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) -} - -internal open class KotlinAndroidPlugin( - val tasksProvider: KotlinTasksProvider, - private val kotlinSourceSetProvider: KotlinSourceSetProvider, - private val kotlinPluginVersion: String -) : Plugin { + override fun buildSourceSetProcessor( + project: Project, + compilation: KotlinCompilation, + kotlinPluginVersion: String + ): KotlinSourceSetProcessor<*> = + KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion) override fun apply(project: Project) { - val version = loadAndroidPluginVersion() - if (version != null) { - val minimalVersion = "1.1.0" - if (compareVersionNumbers(version, minimalVersion) < 0) { - throw IllegalStateException("Kotlin: Unsupported version of com.android.tools.build:gradle plugin: version $minimalVersion or higher should be used with kotlin-android plugin") - } - } + val target = KotlinWithJavaTarget(project, KotlinPlatformType.common, "common") + (project.kotlinExtension as KotlinSingleJavaTargetExtension).target = target - val kotlinTools = KotlinConfigurationTools( - kotlinSourceSetProvider, - tasksProvider, - kotlinPluginVersion) - - val legacyVersionThreshold = "2.5.0" - - val variantProcessor = if (compareVersionNumbers(version, legacyVersionThreshold) < 0) { - LegacyAndroidAndroidProjectHandler(kotlinTools) - } - else { - val android25ProjectHandlerClass = Class.forName("org.jetbrains.kotlin.gradle.plugin.Android25ProjectHandler") - val ctor = android25ProjectHandlerClass.constructors.single { - it.parameterTypes.contentEquals(arrayOf(kotlinTools.javaClass)) - } - ctor.newInstance(kotlinTools) as AbstractAndroidProjectHandler<*> - } - - variantProcessor.handleProject(project) + super.apply(project) } } -class KotlinConfigurationTools internal constructor(val kotlinSourceSetProvider: KotlinSourceSetProvider, - val kotlinTasksProvider: KotlinTasksProvider, - val kotlinPluginVersion: String) +internal open class Kotlin2JsPlugin( + tasksProvider: KotlinTasksProvider, + kotlinPluginVersion: String, + registry: ToolingModelBuilderRegistry +) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion, registry) { + + override fun buildSourceSetProcessor( + project: Project, + compilation: KotlinCompilation, + kotlinPluginVersion: String + ): KotlinSourceSetProcessor<*> = + Kotlin2JsSourceSetProcessor( + project, tasksProvider, compilation, kotlinPluginVersion + ) + + override fun apply(project: Project) { + val target = KotlinWithJavaTarget(project, KotlinPlatformType.js, "2Js") + + (project.kotlinExtension as KotlinSingleJavaTargetExtension).target = target + super.apply(project) + } +} + +internal open class KotlinAndroidPlugin( + val tasksProvider: KotlinTasksProvider, + private val kotlinPluginVersion: String +) : Plugin { + + override fun apply(project: Project) { + val androidTarget = KotlinAndroidTarget("", project) + + applyToTarget( + project, androidTarget, tasksProvider, + kotlinPluginVersion + ) + } + + companion object { + fun applyToTarget( + project: Project, + kotlinTarget: KotlinAndroidTarget, + tasksProvider: KotlinTasksProvider, + kotlinPluginVersion: String + ) { + + val version = loadAndroidPluginVersion() + if (version != null) { + val minimalVersion = "1.1.0" + if (compareVersionNumbers(version, minimalVersion) < 0) { + throw IllegalStateException("Kotlin: Unsupported version of com.android.tools.build:gradle plugin: version $minimalVersion or higher should be used with kotlin-android plugin") + } + } + + val kotlinTools = KotlinConfigurationTools( + tasksProvider, + kotlinPluginVersion + ) + + val legacyVersionThreshold = "2.5.0" + + val variantProcessor = if (compareVersionNumbers(version, legacyVersionThreshold) < 0) { + LegacyAndroidAndroidProjectHandler(kotlinTools) + } else { + val android25ProjectHandlerClass = Class.forName("org.jetbrains.kotlin.gradle.plugin.Android25ProjectHandler") + val ctor = android25ProjectHandlerClass.constructors.single { + it.parameterTypes.contentEquals(arrayOf(kotlinTools.javaClass)) + } + ctor.newInstance(kotlinTools) as AbstractAndroidProjectHandler<*> + } + + variantProcessor.handleProject(project, kotlinTarget) + } + } +} + +class KotlinConfigurationTools internal constructor( + val kotlinTasksProvider: KotlinTasksProvider, + val kotlinPluginVersion: String +) /** Part of Android configuration, that works only with the old public API. * @see [LegacyAndroidAndroidProjectHandler] that is implemented with the old internal API and [AndroidGradle25VariantProcessor] that works @@ -497,18 +592,22 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationT protected open fun checkVariantIsValid(variant: V) = Unit - protected abstract fun wireKotlinTasks(project: Project, - androidPlugin: BasePlugin, - androidExt: BaseExtension, - variantData: V, - javaTask: AbstractCompile, - kotlinTask: KotlinCompile) + protected open fun setUpDependencyResolution(variant: V, compilation: KotlinJvmAndroidCompilation) = Unit + + protected abstract fun wireKotlinTasks( + project: Project, + compilation: KotlinJvmAndroidCompilation, + androidPlugin: BasePlugin, + androidExt: BaseExtension, + variantData: V, + javaTask: AbstractCompile, + kotlinTask: KotlinCompile + ) protected abstract fun wrapVariantDataForKapt(variantData: V): KaptVariantData - fun handleProject(project: Project) { + fun handleProject(project: Project, kotlinAndroidTarget: KotlinAndroidTarget) { val ext = project.extensions.getByName("android") as BaseExtension - val aptConfigurations = hashMapOf() ext.sourceSets.all { sourceSet -> logger.kotlinDebug("Creating KotlinBaseSourceSet for source set $sourceSet") @@ -527,42 +626,49 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationT ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions) project.afterEvaluate { project -> - if (project != null) { - val androidPluginIds = listOf("android", "com.android.application", "android-library", "com.android.library", - "com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp") - val plugin = androidPluginIds.asSequence() - .mapNotNull { project.plugins.findPlugin(it) as? BasePlugin } - .firstOrNull() - ?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " + - "plugins to be applied to the project:\n\t" + - androidPluginIds.joinToString("\n\t") { "* $it" }) + val androidPluginIds = listOf("android", "com.android.application", "android-library", "com.android.library", + "com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp") + val plugin = androidPluginIds.asSequence() + .mapNotNull { project.plugins.findPlugin(it) as? BasePlugin } + .firstOrNull() + ?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " + + "plugins to be applied to the project:\n\t" + + androidPluginIds.joinToString("\n\t") { "* $it" }) - val subpluginEnvironment = loadSubplugins(project, kotlinConfigurationTools.kotlinPluginVersion) + val subpluginEnvironment = loadSubplugins(project, kotlinConfigurationTools.kotlinPluginVersion) - checkAndroidAnnotationProcessorDependencyUsage(project) + checkAndroidAnnotationProcessorDependencyUsage(project) - forEachVariant(project) { - processVariant(it, project, ext, plugin, aptConfigurations, kotlinOptions, - kotlinConfigurationTools.kotlinTasksProvider, subpluginEnvironment) - } + forEachVariant(project) { + processVariant( + it, kotlinAndroidTarget, project, ext, plugin, kotlinOptions, kotlinConfigurationTools.kotlinTasksProvider, + subpluginEnvironment + ) } } } - private fun processVariant(variantData: V, - project: Project, - androidExt: BaseExtension, - androidPlugin: BasePlugin, - aptConfigurations: Map, - rootKotlinOptions: KotlinJvmOptionsImpl, - tasksProvider: KotlinTasksProvider, - subpluginEnvironment: SubpluginEnvironment) { + private fun processVariant( + variantData: V, + target: KotlinAndroidTarget, + project: Project, + androidExt: BaseExtension, + androidPlugin: BasePlugin, + rootKotlinOptions: KotlinJvmOptionsImpl, + tasksProvider: KotlinTasksProvider, + subpluginEnvironment: SubpluginEnvironment + ) { checkVariantIsValid(variantData) - val variantDataName = getVariantName(variantData) logger.kotlinDebug("Process variant [$variantDataName]") + val compilation = target.compilations.create(variantDataName).apply { + KotlinTargetConfigurator.defineConfigurationsForCompilation(this, target, project.configurations) + } + + setUpDependencyResolution(variantData, compilation) + val javaTask = getJavaTask(variantData) if (javaTask == null) { @@ -570,7 +676,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationT return } - val kotlinTaskName = "compile${variantDataName.capitalize()}Kotlin" + val kotlinTaskName = compilation.compileKotlinTaskName // todo: Investigate possibility of creating and configuring kotlinTask before evaluation val kotlinTask = tasksProvider.createKotlinJVMTask(project, kotlinTaskName, variantDataName) kotlinTask.parentKotlinOptionsImpl = rootKotlinOptions @@ -579,22 +685,35 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationT kotlinTask.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName") kotlinTask.description = "Compiles the $variantDataName kotlin." - configureSources(kotlinTask, variantData) - wireKotlinTasks(project, androidPlugin, androidExt, variantData, javaTask, kotlinTask) + configureSources(kotlinTask, variantData, compilation) + + // In MPPs, add the common main Kotlin sources to non-test variants, the common test sources to test variants + val commonSourceSetName = if (getTestedVariantData(variantData) == null) + KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME else + KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME + project.kotlinExtension.sourceSets.findByName(commonSourceSetName)?.let { + compilation.source(it) + } + + wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask) val appliedPlugins = subpluginEnvironment.addSubpluginOptions( project, kotlinTask, javaTask, wrapVariantDataForKapt(variantData), this, null) appliedPlugins.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } - .forEach { configureSources(it, variantData) } + .forEach { configureSources(it, variantData, null) } } - private fun configureSources(compileTask: AbstractCompile, variantData: V) { + private fun configureSources(compileTask: AbstractCompile, variantData: V, compilation: KotlinCompilation?) { val logger = compileTask.project.logger for (provider in getSourceProviders(variantData)) { val kotlinSourceSet = provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: continue - compileTask.source(kotlinSourceSet.kotlin) + if (compilation != null) { + compilation.source(kotlinSourceSet) + } else { + compileTask.source(kotlinSourceSet.kotlin) + } } for (javaSrcDir in getAllJavaSources(variantData)) { @@ -696,10 +815,10 @@ internal class SubpluginEnvironment( fun addSubpluginOptions( project: Project, kotlinTask: AbstractKotlinCompile, - javaTask: AbstractCompile, + javaTask: AbstractCompile?, variantData: Any?, androidProjectHandler: AbstractAndroidProjectHandler?, - javaSourceSet: SourceSet? + kotlinCompilation: KotlinCompilation? ): List>> { val pluginOptions = kotlinTask.pluginOptions @@ -716,7 +835,7 @@ internal class SubpluginEnvironment( project.logger.kotlinDebug { "Adding '$mavenCoordinate' to '$PLUGIN_CLASSPATH_CONFIGURATION_NAME' configuration" } project.dependencies.add(PLUGIN_CLASSPATH_CONFIGURATION_NAME, mavenCoordinate) - val subpluginOptions = subplugin.apply(project, kotlinTask, javaTask, variantData, androidProjectHandler, javaSourceSet) + val subpluginOptions = subplugin.apply(project, kotlinTask, javaTask, variantData, androidProjectHandler, kotlinCompilation) val subpluginId = subplugin.getCompilerPluginId() kotlinTask.registerSubpluginOptionsAsInputs(subpluginId, subpluginOptions) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index e59c891838c..4b7379dddc0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -21,11 +21,14 @@ import org.gradle.api.Project import org.gradle.api.internal.file.FileResolver import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging +import org.gradle.internal.cleanup.BuildOutputCleanupRegistry +import org.gradle.internal.reflect.Instantiator import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension -import org.jetbrains.kotlin.gradle.dsl.createKotlinExtension -import org.jetbrains.kotlin.gradle.internal.KotlinSourceSetProviderImpl +import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin +import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory +import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import java.io.FileNotFoundException @@ -33,7 +36,9 @@ import java.util.* import javax.inject.Inject import kotlin.reflect.KClass -abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver): Plugin { +abstract class KotlinBasePluginWrapper( + protected val fileResolver: FileResolver +): Plugin { private val log = Logging.getLogger(this.javaClass) val kotlinPluginVersion = loadKotlinVersionFromResource(log) @@ -66,32 +71,54 @@ abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver) plugin.apply(project) } - internal abstract fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin - - open val projectExtensionClass: KClass get() = KotlinProjectExtension::class + internal abstract fun getPlugin( + project: Project, + kotlinGradleBuildServices: KotlinGradleBuildServices + ): Plugin } -open class KotlinPluginWrapper @Inject internal constructor(fileResolver: FileResolver, protected val registry: ToolingModelBuilderRegistry): KotlinBasePluginWrapper(fileResolver) { - override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) = - KotlinPlugin(KotlinTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, registry) +open class KotlinPluginWrapper @Inject constructor( + fileResolver: FileResolver, + protected val registry: ToolingModelBuilderRegistry +): KotlinBasePluginWrapper(fileResolver) { + override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin = + KotlinPlugin(KotlinTasksProvider(), kotlinPluginVersion, registry) override val projectExtensionClass: KClass get() = KotlinJvmProjectExtension::class } -open class KotlinCommonPluginWrapper @Inject internal constructor(fileResolver: FileResolver, protected val registry: ToolingModelBuilderRegistry): KotlinBasePluginWrapper(fileResolver) { - override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) = - KotlinCommonPlugin(KotlinCommonTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, registry) +open class KotlinCommonPluginWrapper @Inject constructor( + fileResolver: FileResolver, + protected val registry: ToolingModelBuilderRegistry +): KotlinBasePluginWrapper(fileResolver) { + override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin = + KotlinCommonPlugin(KotlinCommonTasksProvider(), kotlinPluginVersion, registry) + + override val projectExtensionClass: KClass + get() = KotlinSingleJavaTargetExtension::class } -open class KotlinAndroidPluginWrapper @Inject internal constructor(fileResolver: FileResolver, protected val registry: ToolingModelBuilderRegistry): KotlinBasePluginWrapper(fileResolver) { - override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) = - KotlinAndroidPlugin(AndroidTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion) +open class KotlinAndroidPluginWrapper @Inject constructor( + fileResolver: FileResolver, + protected val registry: ToolingModelBuilderRegistry +): KotlinBasePluginWrapper(fileResolver) { + override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin = + KotlinAndroidPlugin( + AndroidTasksProvider(), + kotlinPluginVersion + ) } -open class Kotlin2JsPluginWrapper @Inject internal constructor(fileResolver: FileResolver, protected val registry: ToolingModelBuilderRegistry): KotlinBasePluginWrapper(fileResolver) { - override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) = - Kotlin2JsPlugin(Kotlin2JsTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, registry) +open class Kotlin2JsPluginWrapper @Inject constructor( + fileResolver: FileResolver, + protected val registry: ToolingModelBuilderRegistry +): KotlinBasePluginWrapper(fileResolver) { + override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin = + Kotlin2JsPlugin(Kotlin2JsTasksProvider(), kotlinPluginVersion, registry) + + override val projectExtensionClass: KClass + get() = KotlinSingleJavaTargetExtension::class } fun Project.getKotlinPluginVersion(): String? = diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt index 418b295056c..f1d8572be5a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.internal.KaptTask import org.jetbrains.kotlin.gradle.internal.KaptVariantData import org.jetbrains.kotlin.gradle.internal.registerGeneratedJavaSource import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.utils.checkedReflection import java.io.File @@ -40,12 +41,14 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl variantManager.variantDataList.forEach(action) } - override fun wireKotlinTasks(project: Project, - androidPlugin: BasePlugin, - androidExt: BaseExtension, - variantData: BaseVariantData, - javaTask: AbstractCompile, - kotlinTask: KotlinCompile + override fun wireKotlinTasks( + project: Project, + compilation: KotlinJvmAndroidCompilation, + androidPlugin: BasePlugin, + androidExt: BaseExtension, + variantData: BaseVariantData, + javaTask: AbstractCompile, + kotlinTask: KotlinCompile ) { kotlinTask.dependsOn(*javaTask.dependsOn.toTypedArray()) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/HierarchyAttributeContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/HierarchyAttributeContainer.kt new file mode 100644 index 00000000000..2eda20c3d72 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/HierarchyAttributeContainer.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.mpp + +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.AttributeContainer +import java.util.* + +// TODO better implementation: attribute invariants (no attrs with same name and different types allowed), thread safety? +class HierarchyAttributeContainer(val parent: AttributeContainer?) : AttributeContainer { + private val attributesMap = Collections.synchronizedMap(mutableMapOf, Any>()) + + override fun contains(key: Attribute<*>): Boolean = + attributesMap.contains(key) || parent?.contains(key) ?: false + + @Suppress("UNCHECKED_CAST") + override fun getAttribute(key: Attribute?): T? = + attributesMap.get(key as Attribute<*>) as T? ?: parent?.getAttribute(key) + + override fun isEmpty(): Boolean = attributesMap.isEmpty() && parent?.isEmpty ?: false + + override fun keySet(): Set> = attributesMap.keys + parent?.keySet().orEmpty() + + override fun attribute(key: Attribute?, value: T): AttributeContainer { + val checkedValue = requireNotNull(value as Any?) { "null values for attributes are not supported" } + attributesMap[key as Attribute<*>] = checkedValue + return this + } + + override fun getAttributes(): AttributeContainer = this +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt new file mode 100644 index 00000000000..4b6f8f69351 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.mpp + +import org.gradle.api.NamedDomainObjectFactory +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget + +interface KotlinCompilationFactory : NamedDomainObjectFactory { + val itemClass: Class +} + +private fun KotlinTarget.createCompilationOutput(name: String) = + KotlinCompilationOutput(project, project.buildDir.resolve("processedResources/$name")) + +class KotlinCommonCompilationFactory( + val target: KotlinOnlyTarget +) : KotlinCompilationFactory { + override val itemClass: Class + get() = KotlinCommonCompilation::class.java + + override fun create(name: String): KotlinCommonCompilation = + KotlinCommonCompilation(target, name, target.createCompilationOutput(name)) +} + +class KotlinJvmCompilationFactory( + val target: KotlinOnlyTarget +) : KotlinCompilationFactory { + override val itemClass: Class + get() = KotlinJvmCompilation::class.java + + override fun create(name: String): KotlinJvmCompilation = + KotlinJvmCompilation(target, name, target.createCompilationOutput(name)) +} + +class KotlinWithJavaCompilationFactory( + val project: Project, + val target: KotlinWithJavaTarget +) : KotlinCompilationFactory { + + override val itemClass: Class + get() = KotlinWithJavaCompilation::class.java + + override fun create(name: String): KotlinWithJavaCompilation { + val result = KotlinWithJavaCompilation(target, name) + return result + } +} + +class KotlinJvmAndroidCompilationFactory( + val project: Project, + val target: KotlinAndroidTarget +) : KotlinCompilationFactory { + + override val itemClass: Class + get() = KotlinJvmAndroidCompilation::class.java + + override fun create(name: String): KotlinJvmAndroidCompilation = + KotlinJvmAndroidCompilation(target, name, target.createCompilationOutput(name)) +} + +class KotlinJsCompilationFactory( + val project: Project, + val target: KotlinOnlyTarget +) : KotlinCompilationFactory { + override val itemClass: Class + get() = KotlinJsCompilation::class.java + + override fun create(name: String): KotlinJsCompilation = + KotlinJsCompilation(target, name, target.createCompilationOutput(name)) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt new file mode 100644 index 00000000000..76df3e6afe4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.mpp + +import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.file.FileCollectionInternal +import org.gradle.api.internal.file.FileCollectionVisitor +import org.gradle.api.internal.file.FileSystemSubset +import org.gradle.api.tasks.SourceSetOutput +import java.io.File +import java.util.concurrent.Callable + +class KotlinCompilationOutput constructor( + private val project: Project, + private var resourcesDir: Any, + private var backingFileCollection: ConfigurableFileCollection = project.files() // must be a parameter since it is used in delegation +) : SourceSetOutput, ConfigurableFileCollection by backingFileCollection, FileCollectionInternal { + + /* TODO report Gradle bug: cannot implement SourceSetOutput without also implementing FileCollectionInternal, there's a cast to the latter + inside the Gradle logic for resolving file collections */ + override fun registerWatchPoints(p0: FileSystemSubset.Builder) = (backingFileCollection as FileCollectionInternal).registerWatchPoints(p0) + override fun visitRootElements(p0: FileCollectionVisitor) = (backingFileCollection as FileCollectionInternal).visitRootElements(p0) + + private val classesDirs: FileCollection = project.files() + private val otherDirs: ConfigurableFileCollection = project.files() + + init { + this.from(classesDirs) + this.from(Callable { resourcesDir }) + } + + override fun setResourcesDir(dirNotation: Any) { + resourcesDir = dirNotation + } + + override fun setResourcesDir(dir: File): Unit = setResourcesDir(dir as Any) + + override fun getDirs(): FileCollection = otherDirs + + override fun isLegacyLayout(): Boolean = false + + override fun getResourcesDir(): File = project.file(resourcesDir) + + @Suppress("OverridingDeprecatedMember") + override fun setClassesDir(file: File?) = @Suppress("deprecation") setClassesDir(file as Any) + + @Suppress("OverridingDeprecatedMember") + override fun setClassesDir(fileNotation: Any?) = throw UnsupportedOperationException("Setting classesDir is not supported, use classesDirs.") + + @Suppress("OverridingDeprecatedMember") + override fun getClassesDir(): File = classesDirs.singleFile + + override fun getClassesDirs(): FileCollection = classesDirs + + override fun dir(args: Map, dir: Any?) { + val dirFileCollection = project.files(dir) + args["builtBy"]?.let { dirFileCollection.builtBy(it) } + otherDirs.from(dirFileCollection) + } + + override fun dir(dirNotation: Any?): Unit = dir(emptyMap(), dirNotation) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt new file mode 100644 index 00000000000..5cc29a43532 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -0,0 +1,228 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.mpp + +import groovy.lang.Closure +import org.gradle.api.Project +import org.gradle.api.attributes.AttributeContainer +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetOutput +import org.gradle.util.ConfigureUtil +import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile +import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation +import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName + +internal fun KotlinCompilation.composeName(prefix: String? = null, suffix: String? = null): String { + val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME } + val targetNamePart = target.disambiguationClassifier + + return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix) +} + +internal val KotlinCompilation.fullName: String + get() = fullCompilationName(target, compilationName) + +internal fun fullCompilationName(target: KotlinTarget, simpleName: String) = lowerCamelCaseName(target.disambiguationClassifier, simpleName) + +internal class DefaultKotlinDependencyHandler( + val parent: HasKotlinDependencies, + val project: Project +) : KotlinDependencyHandler { + override fun api(dependencyNotation: Any) = addDependency(parent.apiConfigurationName, dependencyNotation) + + override fun implementation(dependencyNotation: Any) = addDependency(parent.implementationConfigurationName, dependencyNotation) + + override fun compileOnly(dependencyNotation: Any) = addDependency(parent.compileOnlyConfigurationName, dependencyNotation) + + override fun runtimeOnly(dependencyNotation: Any) = addDependency(parent.runtimeOnlyConfigurationName, dependencyNotation) + + private fun addDependency(configurationName: String, dependencyNotation: Any) { + project.dependencies.add(configurationName, dependencyNotation) + } +} + +abstract class AbstractKotlinCompilation( + final override val target: KotlinTarget, + override val compilationName: String +) : KotlinCompilation, HasKotlinDependencies { + private val attributeContainer = HierarchyAttributeContainer(target.attributes) + + override fun getAttributes(): AttributeContainer = attributeContainer + + override val kotlinSourceSets: MutableSet = mutableSetOf() + + override fun source(sourceSet: KotlinSourceSet) { + if (kotlinSourceSets.add(sourceSet)) { + with(target.project) { + whenEvaluated { + (target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(sourceSet.kotlin) + } + addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName) + addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName) + addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName) + if (this is KotlinCompilationToRunnableFiles) { + addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName) + } + } + } + } + + override val compileDependencyConfigurationName: String + get() = lowerCamelCaseName( + target.disambiguationClassifier, + compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(), + "compileClasspath" + ) + + override val compileKotlinTaskName: String + get() = lowerCamelCaseName( + "compile", + compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }, + "Kotlin", + target.targetName + ) + + override val compileAllTaskName: String + get() = lowerCamelCaseName( + target.disambiguationClassifier, + compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }, + "classes" + ) + + override lateinit var compileDependencyFiles: FileCollection + + override val apiConfigurationName: String + get() = disambiguateName("api") + + override val implementationConfigurationName: String + get() = disambiguateName("implementation") + + override val compileOnlyConfigurationName: String + get() = disambiguateName("compileOnly") + + override val runtimeOnlyConfigurationName: String + get() = disambiguateName("runtimeOnly") + + override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit = + DefaultKotlinDependencyHandler(this, target.project).run(configure) + + override fun dependencies(configureClosure: Closure) = + dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) } + + override fun toString(): String = "compilation '$compilationName' ($target)" +} + +abstract class AbstractKotlinCompilationToRunnableFiles( + target: KotlinTarget, + name: String +) : AbstractKotlinCompilation(target, name), KotlinCompilationToRunnableFiles { + override val runtimeDependencyConfigurationName: String + get() = lowerCamelCaseName( + target.disambiguationClassifier, + compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }, + "runtimeClasspath" + ) + + override lateinit var runtimeDependencyFiles: FileCollection +} + +internal fun KotlinCompilation.disambiguateName(simpleName: String): String { + return lowerCamelCaseName( + target.disambiguationClassifier, + compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }, + simpleName + ) +} + +open class KotlinJvmCompilation( + target: KotlinTarget, + name: String, + override val output: SourceSetOutput +) : AbstractKotlinCompilationToRunnableFiles(target, name), KotlinCompilationWithResources { + override val processResourcesTaskName: String + get() = disambiguateName("processResources") +} + +class KotlinWithJavaCompilation( + target: KotlinWithJavaTarget, + name: String +) : AbstractKotlinCompilationToRunnableFiles(target, name), KotlinCompilationWithResources { + lateinit var javaSourceSet: SourceSet + + override val output: SourceSetOutput + get() = javaSourceSet.output + + override val processResourcesTaskName: String + get() = javaSourceSet.processResourcesTaskName + + override var runtimeDependencyFiles: FileCollection + get() = javaSourceSet.runtimeClasspath + set(value) { javaSourceSet.runtimeClasspath = value } + + override val runtimeDependencyConfigurationName: String + get() = javaSourceSet.runtimeClasspathConfigurationName + + override val compileDependencyConfigurationName: String + get() = javaSourceSet.compileClasspathConfigurationName + + override val runtimeOnlyConfigurationName: String + get() = javaSourceSet.runtimeOnlyConfigurationName + + override val implementationConfigurationName: String + get() = javaSourceSet.implementationConfigurationName + + override val apiConfigurationName: String + get() = javaSourceSet.apiConfigurationName + + override val compileOnlyConfigurationName: String + get() = javaSourceSet.compileOnlyConfigurationName + + override val compileAllTaskName: String + get() = javaSourceSet.classesTaskName + + override var compileDependencyFiles: FileCollection + get() = javaSourceSet.compileClasspath + set(value) { javaSourceSet.compileClasspath = value } + + fun source(javaSourceSet: SourceSet) { + with(target.project) { + afterEvaluate { + (tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java) + } + } + } +} + +class KotlinJvmAndroidCompilation( + target: KotlinAndroidTarget, + name: String, + override val output: SourceSetOutput +): AbstractKotlinCompilationToRunnableFiles(target, name) + +class KotlinJsCompilation( + target: KotlinTarget, + name: String, + override val output: SourceSetOutput +) : AbstractKotlinCompilation(target, name) + +class KotlinCommonCompilation( + target: KotlinTarget, + name: String, + override val output: SourceSetOutput +) : AbstractKotlinCompilation(target, name) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt new file mode 100644 index 00000000000..146369031e8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.mpp + +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Project +import org.gradle.api.artifacts.Dependency +import org.gradle.api.attributes.AttributeContainer +import org.gradle.api.internal.component.UsageContext +import org.gradle.api.plugins.JavaPlugin +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName + +abstract class AbstractKotlinTarget ( + override val project: Project +) : KotlinTarget { + private val attributeContainer = HierarchyAttributeContainer(parent = null) + + override fun getAttributes(): AttributeContainer = attributeContainer + + override val defaultConfigurationName: String + get() = disambiguateName("default") + + override val apiElementsConfigurationName: String + get() = disambiguateName("apiElements") + + override val runtimeElementsConfigurationName: String + get() = disambiguateName("runtimeElements") + + override val artifactsTaskName: String + get() = disambiguateName("jar") + + override fun toString(): String = "target $name ($platformType)" + + override fun createUsageContexts(): Set = setOf( + KotlinPlatformUsageContext(project, this, KotlinSoftwareComponent.kotlinApiUsage(project), apiElementsConfigurationName), + KotlinPlatformUsageContext(project, this, KotlinSoftwareComponent.kotlinRuntimeUsage(project), runtimeElementsConfigurationName) + ) +} + +internal fun KotlinTarget.disambiguateName(simpleName: String) = + lowerCamelCaseName(targetName, simpleName) + +open class KotlinAndroidTarget( + override val targetName: String, + project: Project +) : AbstractKotlinTarget(project) { + + override var disambiguationClassifier: String? = null + internal set + + override val platformType: KotlinPlatformType + get() = KotlinPlatformType.jvm + + private val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this) + + override val compilations: NamedDomainObjectContainer = + project.container(compilationFactory.itemClass, compilationFactory) + + override fun createUsageContexts(): Set { + //TODO setup Android libraries publishing. This will likely require new API in the Android Gradle plugin + return emptySet() + } +} + +open class KotlinWithJavaTarget( + project: Project, + override val platformType: KotlinPlatformType, + override val targetName: String +) : AbstractKotlinTarget(project) { + override var disambiguationClassifier: String? = null + internal set + + override val defaultConfigurationName: String + get() = Dependency.DEFAULT_CONFIGURATION + + override val apiElementsConfigurationName: String + get() = JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME + + override val runtimeElementsConfigurationName: String + get() = JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME + + override val artifactsTaskName: String + get() = JavaPlugin.JAR_TASK_NAME + + override val compilations: NamedDomainObjectContainer = + project.container(KotlinWithJavaCompilation::class.java, + KotlinWithJavaCompilationFactory(project, this) + ) +} + +open class KotlinOnlyTarget( + project: Project, + override val platformType: KotlinPlatformType +) : AbstractKotlinTarget(project) { + + override lateinit var compilations: NamedDomainObjectContainer + internal set + + override lateinit var targetName: String + internal set + + override var disambiguationClassifier: String? = null + internal set +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt index 1af52fa5e3b..862b32662e3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt @@ -7,12 +7,8 @@ package org.jetbrains.kotlin.gradle.scripting.internal import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile -import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin -import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.scripting.ScriptingExtension class ScriptingGradleSubplugin : Plugin { @@ -39,10 +35,10 @@ class ScriptingKotlinGradleSubplugin : KotlinGradleSubplugin { override fun apply( project: Project, kotlinCompile: AbstractCompile, - javaCompile: AbstractCompile, + javaCompile: AbstractCompile?, variantData: Any?, androidProjectHandler: Any?, - javaSourceSet: SourceSet? + kotlinCompilation: KotlinCompilation? ): List { if (!ScriptingGradleSubplugin.isEnabled(project)) return emptyList() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/gradleConfigurationUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/gradleConfigurationUtils.kt new file mode 100644 index 00000000000..40100e183b8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/gradleConfigurationUtils.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.utils + +import org.gradle.api.Project + +fun Project.addExtendsFromRelation(extendingConfigurationName: String, extendsFromConfigurationName: String) { + if (extendingConfigurationName != extendsFromConfigurationName) { + project.dependencies.add(extendingConfigurationName, project.configurations.getByName(extendsFromConfigurationName)) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt new file mode 100644 index 00000000000..6037743c889 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.utils + +internal fun lowerCamelCaseName(vararg nameParts: String?): String { + val nonEmptyParts = nameParts.mapNotNull { it?.takeIf(String::isNotEmpty) } + return nonEmptyParts.drop(1).joinToString( + separator = "", + prefix = nonEmptyParts.firstOrNull().orEmpty(), + transform = String::capitalize + ) +} diff --git a/libraries/tools/kotlin-noarg/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/NoArgSubplugin.kt b/libraries/tools/kotlin-noarg/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/NoArgSubplugin.kt index e7c5b1ba567..435a73f7b06 100644 --- a/libraries/tools/kotlin-noarg/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/NoArgSubplugin.kt +++ b/libraries/tools/kotlin-noarg/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/NoArgSubplugin.kt @@ -18,16 +18,9 @@ package org.jetbrains.kotlin.noarg.gradle import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.AbstractTask -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.internal.ConventionTask -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry -import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin -import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.noarg.gradle.model.builder.NoArgModelBuilder import javax.inject.Inject @@ -58,12 +51,12 @@ class NoArgKotlinGradleSubplugin : KotlinGradleSubplugin { override fun isApplicable(project: Project, task: AbstractCompile) = NoArgGradleSubplugin.isEnabled(project) override fun apply( - project: Project, - kotlinCompile: AbstractCompile, - javaCompile: AbstractCompile, - variantData: Any?, - androidProjectHandler: Any?, - javaSourceSet: SourceSet? + project: Project, + kotlinCompile: AbstractCompile, + javaCompile: AbstractCompile?, + variantData: Any?, + androidProjectHandler: Any?, + kotlinCompilation: KotlinCompilation? ): List { if (!NoArgGradleSubplugin.isEnabled(project)) return emptyList() diff --git a/libraries/tools/kotlin-sam-with-receiver/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/SamWithReceiverSubplugin.kt b/libraries/tools/kotlin-sam-with-receiver/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/SamWithReceiverSubplugin.kt index 8b5550aaf8f..6a36921fa35 100644 --- a/libraries/tools/kotlin-sam-with-receiver/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/SamWithReceiverSubplugin.kt +++ b/libraries/tools/kotlin-sam-with-receiver/src/main/kotlin/org/jetbrains/kotlin/noarg/gradle/SamWithReceiverSubplugin.kt @@ -18,16 +18,9 @@ package org.jetbrains.kotlin.samWithReceiver.gradle import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.AbstractTask -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.internal.ConventionTask -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry -import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin -import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.noarg.gradle.model.builder.SamWithReceiverModelBuilder import javax.inject.Inject @@ -53,12 +46,12 @@ class SamWithReceiverKotlinGradleSubplugin : KotlinGradleSubplugin { if (!SamWithReceiverGradleSubplugin.isEnabled(project)) return emptyList()