Provide an opt-out flag for separate classes dirs with Gradle 4.0+

Issue #KT-19774 Fixed

(cherry picked from commit 2aa6f5a)
This commit is contained in:
Sergey Igushkin
2017-08-21 15:22:37 +03:00
parent 843a9871b1
commit 0263919dbd
4 changed files with 77 additions and 8 deletions
@@ -18,9 +18,10 @@ package org.jetbrains.kotlin.gradle.dsl
import org.gradle.api.Project
import org.gradle.api.internal.plugins.DslObject
import kotlin.reflect.KClass
internal fun Project.createKotlinExtension() {
val kotlinExt = extensions.create("kotlin", KotlinProjectExtension::class.java)
internal fun Project.createKotlinExtension(extensionClass: KClass<out KotlinProjectExtension>) {
val kotlinExt = extensions.create("kotlin", extensionClass.java)
DslObject(kotlinExt).extensions.create("experimental", ExperimentalExtension::class.java)
}
@@ -29,6 +30,14 @@ open class KotlinProjectExtension {
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)!!
}
open class KotlinJvmProjectExtension : KotlinProjectExtension() {
/**
* 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.
* */
var copyClassesToJavaOutput = false
}
open class ExperimentalExtension {
var coroutines: Coroutines? = null
}
@@ -0,0 +1,50 @@
package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.specs.Spec
import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion
import java.util.*
object CopyClassesToJavaOutputStatus {
fun isEnabled(project: Project): Boolean {
val kotlinJvmExt = project.kotlinJvmExt ?: return false
if (kotlinJvmExt.copyClassesToJavaOutput) {
if (isGradleVersionTooLow(project) && warningReportedForProject.add(project)) {
project.logger.kotlinWarn(gradleVersionTooLowWarningMessage)
}
addCacheWarningToJavaTasks(project)
return true
}
return false
}
private val warningReportedForProject = Collections.newSetFromMap<Project>(WeakHashMap())
private fun isGradleVersionTooLow(project: Project): Boolean {
return (ParsedGradleVersion.parse(project.gradle.gradleVersion) ?: return false) < ParsedGradleVersion(4, 0)
}
private fun addCacheWarningToJavaTasks(project: Project) {
project.tasks.withType(JavaCompile::class.java).all {
val warningCacheIfSpec = Spec<Task> {
if (warningReportedForProject.add(project)) {
project.logger.kotlinWarn(buildCacheWarningMessage)
}
return@Spec true
}
it.outputs.javaClass.getMethod("cacheIf", Spec::class.java).invoke(it.outputs, warningCacheIfSpec)
}
}
const val buildCacheWarningMessage = "The 'kotlin.copyClassesToJavaOutput' option should not be used with Gradle " +
"build cache"
const val gradleVersionTooLowWarningMessage = "The 'kotlin.copyClassesToJavaOutput' option has no effect when " +
"used with Gradle < 4.0"
private val Project.kotlinJvmExt: KotlinJvmProjectExtension?
get() = extensions.findByType(KotlinJvmProjectExtension::class.java)
}
@@ -55,8 +55,10 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
abstract protected fun doTargetSpecificProcessing()
protected val logger = Logging.getLogger(this.javaClass)!!
protected val isSeparateClassesDirSupported: Boolean =
sourceSet.output.javaClass.methods.any { it.name == "getClassesDirs" }
protected val isSeparateClassesDirSupported: Boolean by lazy {
!CopyClassesToJavaOutputStatus.isEnabled(project) &&
sourceSet.output.javaClass.methods.any { it.name == "getClassesDirs" }
}
protected val sourceSetName: String = sourceSet.name
protected val sourceRootDir: String = "src/$sourceSetName/kotlin"
@@ -19,17 +19,20 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.gradle.dsl.ExperimentalExtension
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.tasks.*
import org.jetbrains.kotlin.gradle.tasks.AndroidTasksProvider
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsTasksProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinCommonTasksProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import java.io.FileNotFoundException
import java.util.*
import javax.inject.Inject
import kotlin.reflect.KClass
abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver): Plugin<Project> {
private val log = Logging.getLogger(this.javaClass)
@@ -40,18 +43,23 @@ 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()
project.createKotlinExtension(projectExtensionClass)
val plugin = getPlugin(kotlinGradleBuildServices)
plugin.apply(project)
}
internal abstract fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project>
open val projectExtensionClass: KClass<out KotlinProjectExtension> get() = KotlinProjectExtension::class
}
open class KotlinPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
KotlinPlugin(KotlinTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
override val projectExtensionClass: KClass<out KotlinJvmProjectExtension>
get() = KotlinJvmProjectExtension::class
}
open class KotlinCommonPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {