diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt index 7971a1b49e6..3d9b20c1dfe 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt @@ -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) { + 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 } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CopyClassesToJavaOutputStatus.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CopyClassesToJavaOutputStatus.kt new file mode 100644 index 00000000000..6c0c5307dde --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CopyClassesToJavaOutputStatus.kt @@ -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(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 { + 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) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index dcb2bfdad46..c631dc973ce 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -55,8 +55,10 @@ internal abstract class KotlinSourceSetProcessor>( 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" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index e04b3cbe97e..043c099dd6b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -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 { 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 + + open val projectExtensionClass: KClass 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 + get() = KotlinJvmProjectExtension::class } open class KotlinCommonPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {