(partial) Replace the KotlinSourceSet implementation and its factory

(Note: this commit leaves the repository in a non-compilable state as it
leaves some of the old API usages unchanged; they are replaced with new
implementations in further commits where more appropriate)

The new implementation covers the wider interface, and the factory
should now be used not on its own but in a named domain object container
(the API for creating a source set is now provided through the
source sets container rather than the factory, so that source sets are
also properly registered).
This commit is contained in:
Sergey Igushkin
2018-07-13 12:10:35 +03:00
parent 3828c26fa9
commit 25eb93b136
8 changed files with 169 additions and 99 deletions
@@ -25,9 +25,17 @@ internal fun Project.createKotlinExtension(extensionClass: KClass<out KotlinProj
DslObject(kotlinExt).extensions.create("experimental", ExperimentalExtension::class.java)
}
internal val Project.kotlinExtension: KotlinProjectExtension
get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinProjectExtension
open class KotlinProjectExtension {
val experimental: ExperimentalExtension
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)!!
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)
var sourceSets: NamedDomainObjectContainer<out KotlinSourceSet>
@Suppress("UNCHECKED_CAST")
get() = DslObject(this).extensions.getByName("sourceSets") as NamedDomainObjectContainer<out KotlinSourceSet>
internal set(value) { DslObject(this).extensions.add("sourceSets", value) }
}
open class KotlinJvmProjectExtension : KotlinProjectExtension() {
@@ -1,71 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.internal
import groovy.lang.Closure
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.file.DefaultSourceDirectorySet
import org.gradle.api.internal.file.FileResolver
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProvider
import java.lang.reflect.Constructor
internal class KotlinSourceSetProviderImpl constructor(private val fileResolver: FileResolver) : KotlinSourceSetProvider {
override fun create(displayName: String): KotlinSourceSet =
KotlinSourceSetImpl(displayName, fileResolver)
}
private class KotlinSourceSetImpl(displayName: String, resolver: FileResolver) : KotlinSourceSet {
override val kotlin: SourceDirectorySet =
createDefaultSourceDirectorySet(displayName + " Kotlin source", resolver)
init {
kotlin.filter?.include("**/*.java", "**/*.kt", "**/*.kts")
}
override fun kotlin(configureClosure: Closure<Any?>?): KotlinSourceSet {
ConfigureUtil.configure(configureClosure, kotlin)
return this
}
}
private val createDefaultSourceDirectorySet: (name: String?, resolver: FileResolver?) -> SourceDirectorySet = run {
val klass = DefaultSourceDirectorySet::class.java
val defaultConstructor = klass.constructorOrNull(String::class.java, FileResolver::class.java)
if (defaultConstructor != null && defaultConstructor.getAnnotation(java.lang.Deprecated::class.java) == null) {
// TODO: drop when gradle < 2.12 are obsolete
{ name, resolver -> defaultConstructor.newInstance(name, resolver) }
}
else {
val directoryFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DirectoryFileTreeFactory")
val alternativeConstructor = klass.getConstructor(String::class.java, FileResolver::class.java, directoryFileTreeFactoryClass)
val defaultFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory")
val defaultFileTreeFactory = defaultFileTreeFactoryClass.getConstructor().newInstance()
return@run { name, resolver -> alternativeConstructor.newInstance(name, resolver, defaultFileTreeFactory) }
}
}
private fun <T> Class<T>.constructorOrNull(vararg parameterTypes: Class<*>): Constructor<T>? =
try {
getConstructor(*parameterTypes)
}
catch (e: NoSuchMethodException) {
null
}
@@ -20,8 +20,8 @@ import org.jetbrains.kotlin.gradle.model.impl.KotlinProjectImpl
import org.jetbrains.kotlin.gradle.model.impl.SourceSetImpl
import org.jetbrains.kotlin.gradle.plugin.KOTLIN_DSL_NAME
import org.jetbrains.kotlin.gradle.plugin.KOTLIN_JS_DSL_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.getConvention
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
/**
@@ -511,9 +511,13 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
val aptConfigurations = hashMapOf<String, Configuration>()
ext.sourceSets.all { sourceSet ->
logger.kotlinDebug("Creating KotlinSourceSet for source set $sourceSet")
val kotlinSourceSet = kotlinConfigurationTools.kotlinSourceSetProvider.create(sourceSet.name)
kotlinSourceSet.kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin")))
logger.kotlinDebug("Creating KotlinBaseSourceSet for source set $sourceSet")
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(
lowerCamelCaseName(kotlinAndroidTarget.disambiguationClassifier, sourceSet.name)
).apply {
kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin")))
kotlin.srcDirs(sourceSet.java.srcDirs)
}
sourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, sourceSet.name)
}
@@ -37,6 +37,11 @@ abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver)
private val log = Logging.getLogger(this.javaClass)
val kotlinPluginVersion = loadKotlinVersionFromResource(log)
open val projectExtensionClass: KClass<out KotlinProjectExtension> get() = KotlinProjectExtension::class
internal open fun kotlinSourceSetFactory(project: Project): KotlinSourceSetFactory<out KotlinSourceSet> =
DefaultKotlinSourceSetFactory(project, fileResolver)
override fun apply(project: Project) {
project.configurations.maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME).defaultDependencies {
it.add(project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:$kotlinPluginVersion"))
@@ -50,9 +55,14 @@ abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver)
System.setProperty(org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true")
val kotlinGradleBuildServices = KotlinGradleBuildServices.getInstance(project.gradle)
project.createKotlinExtension(projectExtensionClass)
project.createKotlinExtension(projectExtensionClass).apply {
fun <T : KotlinSourceSet> kotlinSourceSetContainer(factory: KotlinSourceSetFactory<T>) =
project.container(factory.itemClass, factory)
val plugin = getPlugin(kotlinGradleBuildServices)
project.kotlinExtension.sourceSets = kotlinSourceSetContainer(kotlinSourceSetFactory(project))
}
val plugin = getPlugin(project, kotlinGradleBuildServices)
plugin.apply(project)
}
@@ -1,21 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin
internal interface KotlinSourceSetProvider {
fun create(displayName: String): KotlinSourceSet
}
@@ -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.
*/
package org.jetbrains.kotlin.gradle.plugin.sources
import groovy.lang.Closure
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.file.DefaultSourceDirectorySet
import org.gradle.api.internal.file.FileResolver
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSetWithResources
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.lang.reflect.Constructor
class DefaultKotlinSourceSet(
private val project: Project,
val displayName: String,
fileResolver: FileResolver
) : KotlinSourceSetWithResources {
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 val kotlin: SourceDirectorySet = createDefaultSourceDirectorySet(name + " Kotlin source", fileResolver).apply {
filter.include("**/*.java", "**/*.kt", "**/*.kts")
}
override val resources: SourceDirectorySet = createDefaultSourceDirectorySet(displayName + " resources", fileResolver)
override fun kotlin(configureClosure: Closure<Any?>): SourceDirectorySet = kotlin.apply { ConfigureUtil.configure(configureClosure, this) }
override fun getName(): String = displayName
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) }
override fun toString(): String = "source set $name"
}
internal fun KotlinSourceSet.disambiguateName(simpleName: String): String {
val nameParts = listOfNotNull(this.name.takeIf { it != "main" }, simpleName)
return lowerCamelCaseName(*nameParts.toTypedArray())
}
private val createDefaultSourceDirectorySet: (name: String?, resolver: FileResolver?) -> SourceDirectorySet = run {
val klass = DefaultSourceDirectorySet::class.java
val defaultConstructor = klass.constructorOrNull(String::class.java, FileResolver::class.java)
if (defaultConstructor != null && defaultConstructor.getAnnotation(java.lang.Deprecated::class.java) == null) {
// TODO: drop when gradle < 2.12 are obsolete
{ name, resolver -> defaultConstructor.newInstance(name, resolver) }
} else {
val directoryFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DirectoryFileTreeFactory")
val alternativeConstructor = klass.getConstructor(String::class.java, FileResolver::class.java, directoryFileTreeFactoryClass)
val defaultFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory")
val defaultFileTreeFactory = defaultFileTreeFactoryClass.getConstructor().newInstance()
return@run { name, resolver -> alternativeConstructor.newInstance(name, resolver, defaultFileTreeFactory) }
}
}
private fun <T> Class<T>.constructorOrNull(vararg parameterTypes: Class<*>): Constructor<T>? =
try {
getConstructor(*parameterTypes)
} catch (e: NoSuchMethodException) {
null
}
@@ -0,0 +1,55 @@
/*
* 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.sources
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import java.io.File
internal abstract class KotlinSourceSetFactory<T : KotlinSourceSet> internal constructor(
protected val fileResolver: FileResolver,
protected val project: Project
) : NamedDomainObjectFactory<T> {
abstract val itemClass: Class<T>
override fun create(name: String): T {
val result = doCreateSourceSet(name)
setUpSourceSetDefaults(result)
return result
}
protected open fun defaultSourceLocation(sourceSetName: String): File =
project.file("src/$sourceSetName")
protected open fun setUpSourceSetDefaults(sourceSet: T) {
with(sourceSet) {
sourceSet.kotlin.srcDir(File(defaultSourceLocation(sourceSet.name), "kotlin"))
}
}
protected abstract fun doCreateSourceSet(name: String): T
}
internal class DefaultKotlinSourceSetFactory(
project: Project,
fileResolver: FileResolver
) : KotlinSourceSetFactory<DefaultKotlinSourceSet>(fileResolver, project) {
override val itemClass: Class<DefaultKotlinSourceSet>
get() = DefaultKotlinSourceSet::class.java
override fun setUpSourceSetDefaults(sourceSet: DefaultKotlinSourceSet) {
super.setUpSourceSetDefaults(sourceSet)
sourceSet.resources.srcDir(File(defaultSourceLocation(sourceSet.name), "resources"))
}
override fun doCreateSourceSet(name: String): DefaultKotlinSourceSet {
return DefaultKotlinSourceSet(project, name, fileResolver)
}
}