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.
This commit is contained in:
Sergey Igushkin
2018-07-13 14:42:24 +03:00
parent 25eb93b136
commit dc7d488deb
21 changed files with 1168 additions and 443 deletions
@@ -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<AbstractCompile> {
@@ -31,12 +27,12 @@ class ExampleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
}
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<SubpluginOption> {
println("ExampleSubplugin loaded")
return listOf(SubpluginOption("exampleKey", "exampleValue"))
@@ -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<Project> {
@@ -56,12 +50,12 @@ class AllOpenKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
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<SubpluginOption> {
if (!AllOpenGradleSubplugin.isEnabled(project)) return emptyList()
@@ -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<in KotlinCompile : AbstractCompile> {
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<SubpluginOption>
fun getSubpluginKotlinTasks(
@@ -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<File>().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<BaseVariant>(variantData) {
override val name: String = getVariantName(variantData)
override val sourceProviders: Iterable<SourceProvider> = getSourceProviders(variantData)
@@ -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<out KotlinProjectExtension>) {
val kotlinExt = extensions.create("kotlin", extensionClass.java)
private const val KOTLIN_PROJECT_EXTENSION_NAME = "kotlin"
internal fun Project.createKotlinExtension(extensionClass: KClass<out KotlinProjectExtension>): 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.
@@ -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<KotlinCompile> {
}
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<SubpluginOption> {
val androidExtension = project.extensions.getByName("android") as? BaseExtension ?: return emptyList()
val androidExtensionsExtension = project.extensions.getByType(AndroidExtensionsExtension::class.java)
@@ -158,10 +158,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
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<Configuration>
) {
@@ -171,14 +171,14 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
}
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<SubpluginOption> {
assert((variantData != null) xor (javaSourceSet != null))
assert((variantData != null) xor (kotlinCompilation != null))
val buildDependencies = arrayListOf<TaskDependency>()
val kaptConfigurations = arrayListOf<Configuration>()
@@ -200,10 +200,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
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<KotlinCompile> {
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<KotlinCompile> {
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<KotlinCompile> {
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
@@ -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<Project> {
@@ -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 <A, B> matchSymmetricallyByNames(
containerA: NamedDomainObjectContainer<A>,
containerB: NamedDomainObjectContainer<B>,
containerA: NamedDomainObjectCollection<out A>,
containerB: NamedDomainObjectCollection<out B>,
whenMatched: (A, B) -> Unit
) {
val matchedNames = mutableSetOf<String>()
fun <T, R> NamedDomainObjectContainer<T>.matchAllWith(other: NamedDomainObjectContainer<R>, match: (T, R) -> Unit) {
fun <T, R> NamedDomainObjectCollection<T>.matchAllWith(other: NamedDomainObjectCollection<R>, 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<AbstractKotlinCompile<*>>()
.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<AbstractKotlinCompile<*>>()
.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<out Named> {
// 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<out Named>
return sourceSets
}
companion object {
@JvmStatic
protected fun <T> 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 <T> 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<Kotlin2JsPluginWrapper>()
super.apply(project)
}
}
private val Project.sourceSets: SourceSetContainer
get() = convention.getPlugin(JavaPluginConvention::class.java).sourceSets
}
@@ -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<T : AbstractKotlinCompile<*>>(
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<T : AbstractKotlinCompile<*>>(
project: Project,
tasksProvider: KotlinTasksProvider,
taskDescription: String,
val javaSourceSet: SourceSet?,
kotlinCompilation: KotlinCompilation
): KotlinSourceSetProcessor<T>(
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<T : AbstractKotlinCompile<*>>(
}
// 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<KotlinCompile>(
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<KotlinCompile>(
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<Kotlin2JsCompile>(
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<String>(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<KotlinCompileCommon>(
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<Project> {
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<ExternalDependency>()
.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<String, KotlinWithJavaCompilation>()
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<ExternalDependency>()
.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<Project> {
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<Project> {
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<V>(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<V>
fun handleProject(project: Project) {
fun handleProject(project: Project, kotlinAndroidTarget: KotlinAndroidTarget) {
val ext = project.extensions.getByName("android") as BaseExtension
val aptConfigurations = hashMapOf<String, Configuration>()
ext.sourceSets.all { sourceSet ->
logger.kotlinDebug("Creating KotlinBaseSourceSet for source set $sourceSet")
@@ -527,42 +626,49 @@ abstract class AbstractAndroidProjectHandler<V>(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<String, Configuration>,
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<V>(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<V>(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 <C: CommonCompilerArguments> addSubpluginOptions(
project: Project,
kotlinTask: AbstractKotlinCompile<C>,
javaTask: AbstractCompile,
javaTask: AbstractCompile?,
variantData: Any?,
androidProjectHandler: AbstractAndroidProjectHandler<out Any?>?,
javaSourceSet: SourceSet?
kotlinCompilation: KotlinCompilation?
): List<KotlinGradleSubplugin<AbstractKotlinCompile<C>>> {
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)
@@ -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<Project> {
abstract class KotlinBasePluginWrapper(
protected val fileResolver: FileResolver
): Plugin<Project> {
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<Project>
open val projectExtensionClass: KClass<out KotlinProjectExtension> get() = KotlinProjectExtension::class
internal abstract fun getPlugin(
project: Project,
kotlinGradleBuildServices: KotlinGradleBuildServices
): Plugin<Project>
}
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<Project> =
KotlinPlugin(KotlinTasksProvider(), kotlinPluginVersion, registry)
override val projectExtensionClass: KClass<out KotlinJvmProjectExtension>
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<Project> =
KotlinCommonPlugin(KotlinCommonTasksProvider(), kotlinPluginVersion, registry)
override val projectExtensionClass: KClass<out KotlinSingleJavaTargetExtension>
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<Project> =
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<Project> =
Kotlin2JsPlugin(Kotlin2JsTasksProvider(), kotlinPluginVersion, registry)
override val projectExtensionClass: KClass<out KotlinSingleJavaTargetExtension>
get() = KotlinSingleJavaTargetExtension::class
}
fun Project.getKotlinPluginVersion(): String? =
@@ -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<out BaseVariantOutputData>,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile
override fun wireKotlinTasks(
project: Project,
compilation: KotlinJvmAndroidCompilation,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariantData<out BaseVariantOutputData>,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile
) {
kotlinTask.dependsOn(*javaTask.dependsOn.toTypedArray())
@@ -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<Attribute<*>, Any>())
override fun contains(key: Attribute<*>): Boolean =
attributesMap.contains(key) || parent?.contains(key) ?: false
@Suppress("UNCHECKED_CAST")
override fun <T : Any?> getAttribute(key: Attribute<T>?): T? =
attributesMap.get(key as Attribute<*>) as T? ?: parent?.getAttribute(key)
override fun isEmpty(): Boolean = attributesMap.isEmpty() && parent?.isEmpty ?: false
override fun keySet(): Set<Attribute<*>> = attributesMap.keys + parent?.keySet().orEmpty()
override fun <T : Any?> attribute(key: Attribute<T>?, 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
}
@@ -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<T: KotlinCompilation> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
private fun KotlinTarget.createCompilationOutput(name: String) =
KotlinCompilationOutput(project, project.buildDir.resolve("processedResources/$name"))
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name, target.createCompilationOutput(name))
}
class KotlinJvmCompilationFactory(
val target: KotlinOnlyTarget<KotlinJvmCompilation>
) : KotlinCompilationFactory<KotlinJvmCompilation> {
override val itemClass: Class<KotlinJvmCompilation>
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<KotlinWithJavaCompilation> {
override val itemClass: Class<KotlinWithJavaCompilation>
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<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation =
KotlinJvmAndroidCompilation(target, name, target.createCompilationOutput(name))
}
class KotlinJsCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name, target.createCompilationOutput(name))
}
@@ -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<String, Any>, 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)
}
@@ -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<KotlinSourceSet> = 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<Any?>) =
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)
@@ -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<UsageContext> = 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<out KotlinJvmAndroidCompilation> =
project.container(compilationFactory.itemClass, compilationFactory)
override fun createUsageContexts(): Set<UsageContext> {
//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<KotlinWithJavaCompilation> =
project.container(KotlinWithJavaCompilation::class.java,
KotlinWithJavaCompilationFactory(project, this)
)
}
open class KotlinOnlyTarget<T : KotlinCompilation>(
project: Project,
override val platformType: KotlinPlatformType
) : AbstractKotlinTarget(project) {
override lateinit var compilations: NamedDomainObjectContainer<T>
internal set
override lateinit var targetName: String
internal set
override var disambiguationClassifier: String? = null
internal set
}
@@ -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<Project> {
@@ -39,10 +35,10 @@ class ScriptingKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
override fun apply(
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
if (!ScriptingGradleSubplugin.isEnabled(project)) return emptyList()
@@ -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))
}
}
@@ -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
)
}
@@ -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<AbstractCompile> {
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<SubpluginOption> {
if (!NoArgGradleSubplugin.isEnabled(project)) return emptyList()
@@ -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<AbstractCompi
override fun isApplicable(project: Project, task: AbstractCompile) = SamWithReceiverGradleSubplugin.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<SubpluginOption> {
if (!SamWithReceiverGradleSubplugin.isEnabled(project)) return emptyList()