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 d9352e7ce97..ce51f69f934 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 @@ -63,7 +63,7 @@ internal abstract class KotlinSourceSetProcessor>( protected val sourceSetName: String = kotlinCompilation.compilationName - protected val kotlinTask: T = createKotlinCompileTask() + protected val kotlinTask: TaskHolder = registerKotlinCompileTask() protected val javaSourceSet: SourceSet? = (kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet @@ -82,14 +82,15 @@ internal abstract class KotlinSourceSetProcessor>( } } - private fun createKotlinCompileTask(): T { + private fun registerKotlinCompileTask(): TaskHolder { 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) } + val kotlinCompile = doRegisterTask(project, name) { + it.description = taskDescription + it.mapClasspath { kotlinCompilation.compileDependencyFiles } + it.setDestinationDir { defaultKotlinDestinationDir } + kotlinCompilation.output.tryAddClassesDir { project.files(kotlinTask.doGetTask().destinationDir).builtBy(kotlinTask.doGetTask()) } + } return kotlinCompile } @@ -134,16 +135,30 @@ internal abstract class KotlinSourceSetProcessor>( } private fun createAdditionalClassesTaskForIdeRunner() { + open class IDEClassesTask : DefaultTask() // Workaround: as per KT-26641, when there's a Kotlin compilation with a Java source set, we create another task // that has a name composed as 'Classes`, where the IDE module name is the default source set name: val expectedClassesTaskName = "${kotlinCompilation.defaultSourceSetName}Classes" project.tasks.run { - if (findByName(expectedClassesTaskName) == null) - create(expectedClassesTaskName) { task -> task.dependsOn(getByName(kotlinCompilation.compileAllTaskName)) } + var shouldCreateTask = false + if (useLazyTaskConfiguration) { + try { + named(expectedClassesTaskName) + } catch (e: Exception) { + shouldCreateTask = true + } + } else { + shouldCreateTask = findByName(expectedClassesTaskName) == null + } + if (shouldCreateTask) { + registerTask(project, expectedClassesTaskName, IDEClassesTask::class.java) { + it.dependsOn(getByName(kotlinCompilation.compileAllTaskName)) + } + } } } - protected abstract fun doCreateTask(project: Project, taskName: String): T + protected abstract fun doRegisterTask(project: Project, taskName: String, configureAction: (T) -> (Unit)): TaskHolder } internal class Kotlin2JvmSourceSetProcessor( @@ -159,8 +174,8 @@ internal class Kotlin2JvmSourceSetProcessor( File(project.buildDir, "kotlin-classes/$sourceSetName") else super.defaultKotlinDestinationDir - override fun doCreateTask(project: Project, taskName: String): KotlinCompile = - tasksProvider.createKotlinJVMTask(project, taskName, kotlinCompilation) + override fun doRegisterTask(project: Project, taskName: String, configureAction: (KotlinCompile)->(Unit)): TaskHolder = + tasksProvider.registerKotlinJVMTask(project, taskName, kotlinCompilation, configureAction) override fun doTargetSpecificProcessing() { ifKaptEnabled(project) { @@ -169,29 +184,30 @@ internal class Kotlin2JvmSourceSetProcessor( ScriptingGradleSubplugin.configureForSourceSet(project, kotlinCompilation.compilationName) - project.afterEvaluate { project -> + project.runOnceAfterEvaluated("Kotlin2JvmSourceSetProcessor.doTargetSpecificProcessing", kotlinTask) { + val kotlinTaskInstance = kotlinTask.doGetTask() val javaTask = javaSourceSet?.let { project.tasks.findByName(it.compileJavaTaskName) as JavaCompile } val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion) val appliedPlugins = subpluginEnvironment.addSubpluginOptions( - project, kotlinTask, javaTask, null, null, kotlinCompilation + project, kotlinTaskInstance, javaTask, null, null, kotlinCompilation ) appliedPlugins - .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } + .flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) } .forEach { plugin -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> plugin.source(sourceSet.kotlin) } } - javaTask?.let { configureJavaTask(kotlinTask, it, logger) } + javaTask?.let { configureJavaTask(kotlinTaskInstance, it, logger) } var syncOutputTask: SyncOutputTask? = null if (!isSeparateClassesDirSupported && javaTask != null) { - syncOutputTask = createSyncOutputTask(project, kotlinTask, javaTask, sourceSetName) + syncOutputTask = registerSyncOutputTask(project, kotlinTaskInstance, 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 } + isSeparateClassesDirSupported -> kotlinTaskInstance.let { it to it.destinationDir } else -> syncOutputTask!!.let { it to it.javaOutputDir } } @@ -250,55 +266,61 @@ internal class Kotlin2JsSourceSetProcessor( 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, kotlinCompilation) + override fun doRegisterTask( + project: Project, + taskName: String, + configureAction: (Kotlin2JsCompile) -> (Unit) + ): TaskHolder = + tasksProvider.registerKotlinJSTask(project, taskName, kotlinCompilation, configureAction) override fun doTargetSpecificProcessing() { - project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask) + project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask.getTaskOrProvider()) - createCleanSourceMapTask() + registerCleanSourceMapTask() 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 -> - kotlinTask.kotlinOptions.outputFile = kotlinTask.outputFile.absolutePath - val outputDir = kotlinTask.outputFile.parentFile + project.runOnceAfterEvaluated("Kotlin2JsSourceSetProcessor.doTargetSpecificProcessing", kotlinTask) { + val kotlinTaskInstance = kotlinTask.doGetTask() + kotlinTaskInstance.kotlinOptions.outputFile = kotlinTaskInstance.outputFile.absolutePath + val outputDir = kotlinTaskInstance.outputFile.parentFile val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion) val appliedPlugins = subpluginEnvironment.addSubpluginOptions( - project, kotlinTask, null, null, null, kotlinCompilation + project, kotlinTaskInstance, null, null, null, kotlinCompilation ) if (outputDir.isParentOf(project.rootDir)) throw InvalidUserDataException( - "The output directory '$outputDir' (defined by outputFile of $kotlinTask) contains or " + + "The output directory '$outputDir' (defined by outputFile of $kotlinTaskInstance) contains or " + "matches the project root directory '${project.rootDir}'.\n" + "Gradle will not be able to build the project because of the root directory lock.\n" + "To fix this, consider using the default outputFile location instead of providing it explicitly." ) - kotlinTask.destinationDir = outputDir + kotlinTaskInstance.destinationDir = outputDir if (!isSeparateClassesDirSupported && kotlinCompilation is KotlinWithJavaCompilation<*>) { - kotlinCompilation.javaSourceSet.output.setClassesDirCompatible(kotlinTask.destinationDir) + kotlinCompilation.javaSourceSet.output.setClassesDirCompatible(kotlinTaskInstance.destinationDir) } appliedPlugins - .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } + .flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) } .forEach { task -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> task.source(sourceSet.kotlin) } } } } - private fun createCleanSourceMapTask() { + private fun registerCleanSourceMapTask() { val taskName = kotlinCompilation.composeName("clean", "sourceMap") - val task = project.tasks.create(taskName, Delete::class.java) - task.onlyIf { kotlinTask.kotlinOptions.sourceMap } - task.delete(object : Closure(this) { - override fun call(): String? = (kotlinTask.property("outputFile") as File).canonicalPath + ".map" - }) + registerTask(project, taskName, Delete::class.java) { + it.onlyIf { kotlinTask.doGetTask().kotlinOptions.sourceMap } + it.delete(object : Closure(this) { + override fun call(): String? = (kotlinTask.doGetTask().property("outputFile") as File).canonicalPath + ".map" + }) + } project.tasks.findByName("clean")?.dependsOn(taskName) } } @@ -313,25 +335,27 @@ internal class KotlinCommonSourceSetProcessor( kotlinCompilation = compilation ) { override fun doTargetSpecificProcessing() { - project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask) + project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask.getTaskOrProvider()) // 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.tasks.findByName(kotlinCompilation.target.artifactsTaskName)?.dependsOn(kotlinTask.getTaskOrProvider()) } - project.afterEvaluate { project -> + project.runOnceAfterEvaluated("KotlinCommonSourceSetProcessor.doTargetSpecificProcessing", kotlinTask) { + val kotlinTaskInstance = kotlinTask.doGetTask() val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion) val appliedPlugins = subpluginEnvironment.addSubpluginOptions( - project, kotlinTask, null, null, null, kotlinCompilation + project, kotlinTaskInstance, null, null, null, kotlinCompilation ) appliedPlugins - .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } + .flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) } .forEach { kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> it.source(sourceSet.kotlin) } } } } - override fun doCreateTask(project: Project, taskName: String): KotlinCompileCommon = - tasksProvider.createKotlinCommonTask(project, taskName, kotlinCompilation) + // protected abstract fun doRegisterTask(project: Project, taskName: String, configureAction: (T) -> (Unit)): TaskHolder + override fun doRegisterTask(project: Project, taskName: String, configureAction: (KotlinCompileCommon) -> (Unit)): TaskHolder = + tasksProvider.registerKotlinCommonTask(project, taskName, kotlinCompilation, configureAction) } internal abstract class AbstractKotlinPlugin( @@ -387,11 +411,12 @@ internal abstract class AbstractKotlinPlugin( ) return } - 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) + val inspectTask = registerTask(project, "inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java) { + it.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME + it.jarTask = jarTask + it.dependsOn(classesTask) + } + jarTask.dependsOn(inspectTask.getTaskOrProvider()) } private fun setUpJavaSourceSets( @@ -739,16 +764,17 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationT val kotlinTaskName = compilation.compileKotlinTaskName // todo: Investigate possibility of creating and configuring kotlinTask before evaluation - val kotlinTask = tasksProvider.createKotlinJVMTask(project, kotlinTaskName, compilation) - kotlinTask.parentKotlinOptionsImpl = rootKotlinOptions + val kotlinTask = tasksProvider.registerKotlinJVMTask(project, kotlinTaskName, compilation) { + it.parentKotlinOptionsImpl = rootKotlinOptions - // store kotlin classes in separate directory. They will serve as class-path to java compiler - kotlinTask.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName") - kotlinTask.description = "Compiles the $variantDataName kotlin." + // store kotlin classes in separate directory. They will serve as class-path to java compiler + it.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName") + it.description = "Compiles the $variantDataName kotlin." + } // Register the source only after the task is created, because the task is required for that: compilation.source(defaultSourceSet) - configureSources(kotlinTask, variantData, compilation) + configureSources(kotlinTask.doGetTask(), 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) @@ -758,7 +784,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationT compilation.source(it) } - wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask) + wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask.doGetTask()) } private fun applySubplugins( @@ -825,7 +851,7 @@ internal fun configureJavaTask(kotlinTask: KotlinCompile, javaTask: AbstractComp internal fun syncOutputTaskName(variantName: String) = "copy${variantName.capitalize()}KotlinClasses" -internal fun createSyncOutputTask( +internal fun registerSyncOutputTask( project: Project, kotlinCompile: KotlinCompile, javaTask: AbstractCompile, @@ -835,19 +861,21 @@ internal fun createSyncOutputTask( val javaDir = javaTask.destinationDir val taskName = syncOutputTaskName(variantName) - val syncTask = project.tasks.create(taskName, SyncOutputTask::class.java) - syncTask.kotlinOutputDir = kotlinDir - syncTask.javaOutputDir = javaDir - syncTask.kotlinTask = kotlinCompile + // registerTask(project: Project, name: String, type: Class, disableLazy: Boolean, body: (T)->(Unit)) : TaskHolder { + val syncTask = registerTask(project, taskName, SyncOutputTask::class.java) { + it.kotlinOutputDir = kotlinDir + it.javaOutputDir = javaDir + it.kotlinTask = kotlinCompile + it.kaptClassesDir = getKaptGeneratedClassesDir(project, variantName) + // copying should be executed after a latter task + javaTask.finalizedByIfNotFailed(it) + } + kotlinCompile.javaOutputDir = javaDir - syncTask.kaptClassesDir = getKaptGeneratedClassesDir(project, variantName) - // copying should be executed after a latter task - javaTask.finalizedByIfNotFailed(syncTask) + project.logger.kotlinDebug { "Created task ${syncTask.doGetTask().path} to copy kotlin classes from $kotlinDir to $javaDir" } - project.logger.kotlinDebug { "Created task ${syncTask.path} to copy kotlin classes from $kotlinDir to $javaDir" } - - return syncTask + return syncTask.doGetTask() } private fun ifKaptEnabled(project: Project, block: () -> Unit) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt index 5f0718513c9..09337b34324 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt @@ -66,7 +66,7 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl } configureJavaTask(kotlinTask, javaTask, logger) - createSyncOutputTask(project, kotlinTask, javaTask, getVariantName(variantData)) + registerSyncOutputTask(project, kotlinTask, javaTask, getVariantName(variantData)) // In lib modules, the androidTest variants get the classes jar in their classpath instead of the Java // destination dir. Attach the JAR to be consumed as friend path: diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyTaskHolder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyTaskHolder.kt new file mode 100644 index 00000000000..289495072b1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyTaskHolder.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2019 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 + +import org.gradle.api.Task + +class LegacyTaskHolder(private val task: T) : TaskHolder { + override fun doGetTask() = task + + override fun getTaskOrProvider(): Any = task + + override fun configure(action: (T) -> (Unit)) { + with(task, action) + } + + override fun toString(): String { + return "TaskHolder instance: [className: ${javaClass.name}, task name: '${doGetTask().name}']" + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/RunOnceAfterEvaluated.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/RunOnceAfterEvaluated.kt new file mode 100644 index 00000000000..ab0b3fcc648 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/RunOnceAfterEvaluated.kt @@ -0,0 +1,61 @@ +/* + * 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 + +import org.gradle.api.Project +import org.gradle.api.logging.Logging + +/** + * This class encapsulated logic which should be invoked during not before the script evaluation is ready and + * not earlier than the task is configured + */ +internal class RunOnceAfterEvaluated(private val name: String, private val action: () -> (Unit)) { + private val logger = Logging.getLogger(this.javaClass)!! + private var executed = false + private var configured = false + private var evaluated = false + + private fun execute() { + logger.debug("[$name] RunOnceAfterEvaluated - execute executed=$executed evaluated=$evaluated configured=$configured") + if (!executed) { + logger.debug("[$name] RunOnceAfterEvaluated - EXECUTING executed=$executed evaluated=$evaluated configured=$configured") + action() + } + executed = true + } + + fun onEvaluated() { + logger.debug("[$name] RunOnceAfterEvaluated - onEvaluated executed=$executed evaluated=$evaluated configured=$configured") + evaluated = true + if (configured) { + execute() + } + } + + fun onConfigure() { + logger.debug("[$name] RunOnceAfterEvaluated - onConfigure executed=$executed evaluated=$evaluated configured=$configured") + configured = true + if (evaluated) { + execute() + } + } +} + +internal fun Project.runOnceAfterEvaluated(name: String, task: TaskHolder<*>, action: () -> (Unit)) { + val runOnce = RunOnceAfterEvaluated(name, action) + runOnceAfterEvaluated(runOnce, task) +} + +internal fun Project.runOnceAfterEvaluated(runOnce: RunOnceAfterEvaluated, task: TaskHolder<*>) { + if (state.executed) { + runOnce.onEvaluated() + } else { + afterEvaluate { runOnce.onEvaluated() } + } + task.configure { + runOnce.onConfigure() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskHolder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskHolder.kt new file mode 100644 index 00000000000..9fe5fd2cc22 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskHolder.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2018 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 + +import org.gradle.api.Task +import org.gradle.api.Project + + +/** + * Reference to a org.gradle.api.Task or org.gradle.api.TaskProvider necessary in order to support flexible creation of tasks. + * For gradle versions < 4.9 tasks are created meanwhile for gradle with version >= 4.9 tasks are registered + */ +interface TaskHolder { + + /** + * Returns Task itself if task was created or TaskProvider if task was registered. + */ + fun getTaskOrProvider(): Any + + /** + * Returns instance of task. If task created using lazy api, it will be instantiated + */ + fun doGetTask(): T + + + /** + * Invokes task configuration. If task was registered the configuration action is added but not invoked + */ + fun configure(action: (T) -> (Unit)) +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskProviderHolder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskProviderHolder.kt new file mode 100644 index 00000000000..dd11ed76f69 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskProviderHolder.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2018 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 + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.TaskProvider + +class TaskProviderHolder(project: Project, private val name: String, type: Class, configureAction: (T) -> (Unit)) : + TaskHolder { + private val provider: TaskProvider = project.tasks.register(name, type, configureAction) + + override fun getTaskOrProvider(): Any = provider + + override fun doGetTask(): T = provider.get() + + override fun configure(action: (T) -> (Unit)) { + provider.configure(action) + } + + override fun toString(): String { + return "TaskProviderHolder instance: [className: ${javaClass.name}, task name: '$name']" + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt index fbc7d3f5269..37d7fc245c8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -74,18 +74,26 @@ abstract class AbstractKotlinCompilation( } open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) { - (target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).apply { + fun AbstractKotlinCompile<*>.configureAction() { source(sourceSet.kotlin) sourceFilesExtensions(sourceSet.customSourceFilesExtensions) if (addAsCommonSources) { commonSourceSet += sourceSet.kotlin } } + // Note! Invocation of getByName results in preliminary task instantiation. After fix of this issue the following code should be uncommented: +// if (useLazyTaskConfiguration) { +// (target.project.tasks.named(compileKotlinTaskName) as TaskProvider>).configure { +// it.configureAction() +// } +// } + (target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).configureAction() } override fun source(sourceSet: KotlinSourceSet) { if (kotlinSourceSets.add(sourceSet)) { with(target.project) { + //TODO possibly issue with forced instantiation whenEvaluated { sourceSet.getSourceSetHierarchy().forEach { sourceSet -> val isCommonSource = diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt index c274d1b784f..07d471e05a9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/scripting/internal/ScriptingGradleSubplugin.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.scripting.ScriptingExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.useLazyTaskConfiguration import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptDefinitionsFromClasspathDiscoverySource import java.io.File @@ -52,7 +53,7 @@ class ScriptingGradleSubplugin : Plugin { val javaPluginConvention = project.convention.findPlugin(JavaPluginConvention::class.java) if (javaPluginConvention?.sourceSets?.isEmpty() == false) { - project.tasks.withType(KotlinCompile::class.java) { task -> + val configureAction: (KotlinCompile) -> (Unit) = { task -> if (task !is KaptGenerateStubsTask) { @@ -63,6 +64,11 @@ class ScriptingGradleSubplugin : Plugin { ?: project.logger.warn("kotlin scripting plugin: $project.${task.name} - configuration not found: $discoveryClasspathConfigurationName, $MISCONFIGURATION_MESSAGE_SUFFIX") } } + if (useLazyTaskConfiguration) { + project.tasks.withType(KotlinCompile::class.java).configureEach(configureAction) + } else { + project.tasks.withType(KotlinCompile::class.java, configureAction) + } } else { project.logger.warn("kotlin scripting plugin: applied to a non-JVM project $project, $MISCONFIGURATION_MESSAGE_SUFFIX") } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/TasksProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/TasksProvider.kt index 12e65a142ac..66ddc4fcee5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/TasksProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/TasksProvider.kt @@ -17,57 +17,95 @@ package org.jetbrains.kotlin.gradle.tasks import org.gradle.api.Project +import org.gradle.api.Task import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask +internal val useLazyTaskConfiguration = org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast(4, 9) + +/** + * Registers the task with name @param name and type @param type and initialization script @param body + * If gradle with version <4.9 is used the task will be created + */ +internal fun registerTask(project: Project, name: String, type: Class, body: (T) -> (Unit)): TaskHolder { + return if (useLazyTaskConfiguration) { + TaskProviderHolder(project, name, type) { with(it, body) } + } else { + val result = LegacyTaskHolder(project.tasks.create(name, type)) + with(result.doGetTask(), body) + result + } +} + internal open class KotlinTasksProvider(val targetName: String) { - open fun createKotlinJVMTask( + open fun registerKotlinJVMTask( project: Project, name: String, - compilation: KotlinCompilation<*> - ): KotlinCompile { + compilation: KotlinCompilation<*>, + configureAction: (KotlinCompile) -> (Unit) + ): TaskHolder { val properties = PropertiesProvider(project) val taskClass = taskOrWorkersTask(properties) - return project.tasks.create(name, taskClass).apply { - configure(this, project, properties, compilation) + val result = registerTask(project, name, taskClass) { + configureAction(it) } + configure(result, project, properties, compilation) + return result } - fun createKotlinJSTask(project: Project, name: String, compilation: KotlinCompilation<*>): Kotlin2JsCompile { + fun registerKotlinJSTask( + project: Project, + name: String, + compilation: KotlinCompilation<*>, + configureAction: (Kotlin2JsCompile) -> Unit + ): TaskHolder { val properties = PropertiesProvider(project) val taskClass = taskOrWorkersTask(properties) - return project.tasks.create(name, taskClass).apply { - configure(this, project, properties, compilation) + val result = registerTask(project, name, taskClass) { + configureAction(it) } + configure(result, project, properties, compilation) + return result } - fun createKotlinCommonTask(project: Project, name: String, compilation: KotlinCompilation<*>): KotlinCompileCommon { + fun registerKotlinCommonTask( + project: Project, + name: String, + compilation: KotlinCompilation<*>, + configureAction: (KotlinCompileCommon) -> (Unit) + ): TaskHolder { val properties = PropertiesProvider(project) val taskClass = taskOrWorkersTask(properties) - return project.tasks.create(name, taskClass).apply { - configure(this, project, properties, compilation) + val result = registerTask(project, name, taskClass) { + configureAction(it) } + configure(result, project, properties, compilation) + return result } open fun configure( - kotlinTask: AbstractKotlinCompile<*>, + kotlinTaskHolder: TaskHolder>, project: Project, propertiesProvider: PropertiesProvider, compilation: KotlinCompilation<*> ) { - kotlinTask.sourceSetName = compilation.name - kotlinTask.friendTaskName = taskToFriendTaskMapper[kotlinTask] - propertiesProvider.mapKotlinTaskProperties(kotlinTask) - - project.whenEvaluated { + val configureAfterEvaluated = RunOnceAfterEvaluated("TaskProvider.configure") { val languageSettings = project.kotlinExtension.sourceSets.findByName(compilation.defaultSourceSetName)?.languageSettings - ?: return@whenEvaluated + ?: return@RunOnceAfterEvaluated + val kotlinTask = kotlinTaskHolder.doGetTask() kotlinTask as org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*> applyLanguageSettingsToKotlinTask(languageSettings, kotlinTask) } + kotlinTaskHolder.configure { + it.sourceSetName = compilation.name + it.friendTaskName = taskToFriendTaskMapper[it] + propertiesProvider.mapKotlinTaskProperties(it) + configureAfterEvaluated.onConfigure() + } + project.runOnceAfterEvaluated(configureAfterEvaluated, kotlinTaskHolder) } protected open val taskToFriendTaskMapper: TaskToFriendTaskMapper = @@ -82,12 +120,14 @@ internal class AndroidTasksProvider(targetName: String) : KotlinTasksProvider(ta RegexTaskToFriendTaskMapper.Android(targetName) override fun configure( - kotlinTask: AbstractKotlinCompile<*>, + kotlinTaskHolder: TaskHolder>, project: Project, propertiesProvider: PropertiesProvider, compilation: KotlinCompilation<*> ) { - super.configure(kotlinTask, project, propertiesProvider, compilation) - kotlinTask.useModuleDetection = true + super.configure(kotlinTaskHolder, project, propertiesProvider, compilation) + kotlinTaskHolder.configure { + it.useModuleDetection = true + } } } \ No newline at end of file