Change task creation with task registration in Kotlin plugin

#KT-27657 Fixed
This commit is contained in:
Andrey Uskov
2018-12-11 21:32:43 +03:00
parent 7d660834e1
commit b6aed5b98a
9 changed files with 337 additions and 90 deletions
@@ -63,7 +63,7 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
protected val sourceSetName: String = kotlinCompilation.compilationName protected val sourceSetName: String = kotlinCompilation.compilationName
protected val kotlinTask: T = createKotlinCompileTask() protected val kotlinTask: TaskHolder<out T> = registerKotlinCompileTask()
protected val javaSourceSet: SourceSet? = (kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet protected val javaSourceSet: SourceSet? = (kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet
@@ -82,14 +82,15 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
} }
} }
private fun createKotlinCompileTask(): T { private fun registerKotlinCompileTask(): TaskHolder<out T> {
val name = kotlinCompilation.compileKotlinTaskName val name = kotlinCompilation.compileKotlinTaskName
logger.kotlinDebug("Creating kotlin compile task $name") logger.kotlinDebug("Creating kotlin compile task $name")
val kotlinCompile = doCreateTask(project, name) val kotlinCompile = doRegisterTask(project, name) {
kotlinCompile.description = taskDescription it.description = taskDescription
kotlinCompile.mapClasspath { kotlinCompilation.compileDependencyFiles } it.mapClasspath { kotlinCompilation.compileDependencyFiles }
kotlinCompile.setDestinationDir { defaultKotlinDestinationDir } it.setDestinationDir { defaultKotlinDestinationDir }
kotlinCompilation.output.tryAddClassesDir { project.files(kotlinTask.destinationDir).builtBy(kotlinTask) } kotlinCompilation.output.tryAddClassesDir { project.files(kotlinTask.doGetTask().destinationDir).builtBy(kotlinTask.doGetTask()) }
}
return kotlinCompile return kotlinCompile
} }
@@ -134,16 +135,30 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
} }
private fun createAdditionalClassesTaskForIdeRunner() { 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 // 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 '<IDE module name>Classes`, where the IDE module name is the default source set name: // that has a name composed as '<IDE module name>Classes`, where the IDE module name is the default source set name:
val expectedClassesTaskName = "${kotlinCompilation.defaultSourceSetName}Classes" val expectedClassesTaskName = "${kotlinCompilation.defaultSourceSetName}Classes"
project.tasks.run { project.tasks.run {
if (findByName(expectedClassesTaskName) == null) var shouldCreateTask = false
create(expectedClassesTaskName) { task -> task.dependsOn(getByName(kotlinCompilation.compileAllTaskName)) } 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<out T>
} }
internal class Kotlin2JvmSourceSetProcessor( internal class Kotlin2JvmSourceSetProcessor(
@@ -159,8 +174,8 @@ internal class Kotlin2JvmSourceSetProcessor(
File(project.buildDir, "kotlin-classes/$sourceSetName") else File(project.buildDir, "kotlin-classes/$sourceSetName") else
super.defaultKotlinDestinationDir super.defaultKotlinDestinationDir
override fun doCreateTask(project: Project, taskName: String): KotlinCompile = override fun doRegisterTask(project: Project, taskName: String, configureAction: (KotlinCompile)->(Unit)): TaskHolder<out KotlinCompile> =
tasksProvider.createKotlinJVMTask(project, taskName, kotlinCompilation) tasksProvider.registerKotlinJVMTask(project, taskName, kotlinCompilation, configureAction)
override fun doTargetSpecificProcessing() { override fun doTargetSpecificProcessing() {
ifKaptEnabled(project) { ifKaptEnabled(project) {
@@ -169,29 +184,30 @@ internal class Kotlin2JvmSourceSetProcessor(
ScriptingGradleSubplugin.configureForSourceSet(project, kotlinCompilation.compilationName) 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 javaTask = javaSourceSet?.let { project.tasks.findByName(it.compileJavaTaskName) as JavaCompile }
val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion) val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions( val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask, null, null, kotlinCompilation project, kotlinTaskInstance, javaTask, null, null, kotlinCompilation
) )
appliedPlugins appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } .flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) }
.forEach { plugin -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> plugin.source(sourceSet.kotlin) } } .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 var syncOutputTask: SyncOutputTask? = null
if (!isSeparateClassesDirSupported && javaTask != 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) { if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) {
val (classesProviderTask, classesDirectory) = when { 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 } 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.", project, tasksProvider, taskDescription = "Compiles the Kotlin sources in $kotlinCompilation to JavaScript.",
kotlinCompilation = kotlinCompilation kotlinCompilation = kotlinCompilation
) { ) {
override fun doCreateTask(project: Project, taskName: String): Kotlin2JsCompile = override fun doRegisterTask(
tasksProvider.createKotlinJSTask(project, taskName, kotlinCompilation) project: Project,
taskName: String,
configureAction: (Kotlin2JsCompile) -> (Unit)
): TaskHolder<out Kotlin2JsCompile> =
tasksProvider.registerKotlinJSTask(project, taskName, kotlinCompilation, configureAction)
override fun doTargetSpecificProcessing() { override fun doTargetSpecificProcessing() {
project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask) project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask.getTaskOrProvider())
createCleanSourceMapTask() registerCleanSourceMapTask()
if (kotlinCompilation is KotlinWithJavaCompilation<*>) { if (kotlinCompilation is KotlinWithJavaCompilation<*>) {
kotlinCompilation.javaSourceSet.clearJavaSrcDirs() kotlinCompilation.javaSourceSet.clearJavaSrcDirs()
} }
// outputFile can be set later during the configuration phase, get it only after the phase: // outputFile can be set later during the configuration phase, get it only after the phase:
project.afterEvaluate { project -> project.runOnceAfterEvaluated("Kotlin2JsSourceSetProcessor.doTargetSpecificProcessing", kotlinTask) {
kotlinTask.kotlinOptions.outputFile = kotlinTask.outputFile.absolutePath val kotlinTaskInstance = kotlinTask.doGetTask()
val outputDir = kotlinTask.outputFile.parentFile kotlinTaskInstance.kotlinOptions.outputFile = kotlinTaskInstance.outputFile.absolutePath
val outputDir = kotlinTaskInstance.outputFile.parentFile
val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion) val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions( val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, null, null, null, kotlinCompilation project, kotlinTaskInstance, null, null, null, kotlinCompilation
) )
if (outputDir.isParentOf(project.rootDir)) if (outputDir.isParentOf(project.rootDir))
throw InvalidUserDataException( 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" + "matches the project root directory '${project.rootDir}'.\n" +
"Gradle will not be able to build the project because of the root directory lock.\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." "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<*>) { if (!isSeparateClassesDirSupported && kotlinCompilation is KotlinWithJavaCompilation<*>) {
kotlinCompilation.javaSourceSet.output.setClassesDirCompatible(kotlinTask.destinationDir) kotlinCompilation.javaSourceSet.output.setClassesDirCompatible(kotlinTaskInstance.destinationDir)
} }
appliedPlugins appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } .flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) }
.forEach { task -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> task.source(sourceSet.kotlin) } } .forEach { task -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> task.source(sourceSet.kotlin) } }
} }
} }
private fun createCleanSourceMapTask() { private fun registerCleanSourceMapTask() {
val taskName = kotlinCompilation.composeName("clean", "sourceMap") val taskName = kotlinCompilation.composeName("clean", "sourceMap")
val task = project.tasks.create(taskName, Delete::class.java) registerTask(project, taskName, Delete::class.java) {
task.onlyIf { kotlinTask.kotlinOptions.sourceMap } it.onlyIf { kotlinTask.doGetTask().kotlinOptions.sourceMap }
task.delete(object : Closure<String>(this) { it.delete(object : Closure<String>(this) {
override fun call(): String? = (kotlinTask.property("outputFile") as File).canonicalPath + ".map" override fun call(): String? = (kotlinTask.doGetTask().property("outputFile") as File).canonicalPath + ".map"
}) })
}
project.tasks.findByName("clean")?.dependsOn(taskName) project.tasks.findByName("clean")?.dependsOn(taskName)
} }
} }
@@ -313,25 +335,27 @@ internal class KotlinCommonSourceSetProcessor(
kotlinCompilation = compilation kotlinCompilation = compilation
) { ) {
override fun doTargetSpecificProcessing() { 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) // can be missing (e.g. in case of tests)
if (kotlinCompilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) { 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 subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions( val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, null, null, null, kotlinCompilation project, kotlinTaskInstance, null, null, null, kotlinCompilation
) )
appliedPlugins appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } .flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) }
.forEach { kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> it.source(sourceSet.kotlin) } } .forEach { kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> it.source(sourceSet.kotlin) } }
} }
} }
override fun doCreateTask(project: Project, taskName: String): KotlinCompileCommon = // protected abstract fun doRegisterTask(project: Project, taskName: String, configureAction: (T) -> (Unit)): TaskHolder<out T>
tasksProvider.createKotlinCommonTask(project, taskName, kotlinCompilation) override fun doRegisterTask(project: Project, taskName: String, configureAction: (KotlinCompileCommon) -> (Unit)): TaskHolder<out KotlinCompileCommon> =
tasksProvider.registerKotlinCommonTask(project, taskName, kotlinCompilation, configureAction)
} }
internal abstract class AbstractKotlinPlugin( internal abstract class AbstractKotlinPlugin(
@@ -387,11 +411,12 @@ internal abstract class AbstractKotlinPlugin(
) )
return return
} }
val inspectTask = project.tasks.create("inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java) val inspectTask = registerTask(project, "inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java) {
inspectTask.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME it.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME
inspectTask.jarTask = jarTask it.jarTask = jarTask
inspectTask.dependsOn(classesTask) it.dependsOn(classesTask)
jarTask.dependsOn(inspectTask) }
jarTask.dependsOn(inspectTask.getTaskOrProvider())
} }
private fun setUpJavaSourceSets( private fun setUpJavaSourceSets(
@@ -739,16 +764,17 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
val kotlinTaskName = compilation.compileKotlinTaskName val kotlinTaskName = compilation.compileKotlinTaskName
// todo: Investigate possibility of creating and configuring kotlinTask before evaluation // todo: Investigate possibility of creating and configuring kotlinTask before evaluation
val kotlinTask = tasksProvider.createKotlinJVMTask(project, kotlinTaskName, compilation) val kotlinTask = tasksProvider.registerKotlinJVMTask(project, kotlinTaskName, compilation) {
kotlinTask.parentKotlinOptionsImpl = rootKotlinOptions it.parentKotlinOptionsImpl = rootKotlinOptions
// store kotlin classes in separate directory. They will serve as class-path to java compiler // store kotlin classes in separate directory. They will serve as class-path to java compiler
kotlinTask.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName") it.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName")
kotlinTask.description = "Compiles the $variantDataName kotlin." it.description = "Compiles the $variantDataName kotlin."
}
// Register the source only after the task is created, because the task is required for that: // Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet) 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 // 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) val commonSourceSetName = if (getTestedVariantData(variantData) == null)
@@ -758,7 +784,7 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
compilation.source(it) compilation.source(it)
} }
wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask) wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask.doGetTask())
} }
private fun applySubplugins( 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 syncOutputTaskName(variantName: String) = "copy${variantName.capitalize()}KotlinClasses"
internal fun createSyncOutputTask( internal fun registerSyncOutputTask(
project: Project, project: Project,
kotlinCompile: KotlinCompile, kotlinCompile: KotlinCompile,
javaTask: AbstractCompile, javaTask: AbstractCompile,
@@ -835,19 +861,21 @@ internal fun createSyncOutputTask(
val javaDir = javaTask.destinationDir val javaDir = javaTask.destinationDir
val taskName = syncOutputTaskName(variantName) val taskName = syncOutputTaskName(variantName)
val syncTask = project.tasks.create(taskName, SyncOutputTask::class.java) // registerTask(project: Project, name: String, type: Class<T>, disableLazy: Boolean, body: (T)->(Unit)) : TaskHolder<T> {
syncTask.kotlinOutputDir = kotlinDir val syncTask = registerTask(project, taskName, SyncOutputTask::class.java) {
syncTask.javaOutputDir = javaDir it.kotlinOutputDir = kotlinDir
syncTask.kotlinTask = kotlinCompile 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 kotlinCompile.javaOutputDir = javaDir
syncTask.kaptClassesDir = getKaptGeneratedClassesDir(project, variantName)
// copying should be executed after a latter task project.logger.kotlinDebug { "Created task ${syncTask.doGetTask().path} to copy kotlin classes from $kotlinDir to $javaDir" }
javaTask.finalizedByIfNotFailed(syncTask)
project.logger.kotlinDebug { "Created task ${syncTask.path} to copy kotlin classes from $kotlinDir to $javaDir" } return syncTask.doGetTask()
return syncTask
} }
private fun ifKaptEnabled(project: Project, block: () -> Unit) { private fun ifKaptEnabled(project: Project, block: () -> Unit) {
@@ -66,7 +66,7 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl
} }
configureJavaTask(kotlinTask, javaTask, logger) 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 // 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: // destination dir. Attach the JAR to be consumed as friend path:
@@ -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<T : Task>(private val task: T) : TaskHolder<T> {
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}']"
}
}
@@ -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()
}
}
@@ -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<T : Task> {
/**
* Returns Task itself if task was created or TaskProvider<Task> 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))
}
@@ -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<T : Task>(project: Project, private val name: String, type: Class<T>, configureAction: (T) -> (Unit)) :
TaskHolder<T> {
private val provider: TaskProvider<T> = 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']"
}
}
@@ -74,18 +74,26 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
} }
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) { open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
(target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).apply { fun AbstractKotlinCompile<*>.configureAction() {
source(sourceSet.kotlin) source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions) sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) { if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin 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<AbstractKotlinCompile<*>>).configure {
// it.configureAction()
// }
// }
(target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).configureAction()
} }
override fun source(sourceSet: KotlinSourceSet) { override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) { if (kotlinSourceSets.add(sourceSet)) {
with(target.project) { with(target.project) {
//TODO possibly issue with forced instantiation
whenEvaluated { whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet -> sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource = val isCommonSource =
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.scripting.ScriptingExtension import org.jetbrains.kotlin.gradle.scripting.ScriptingExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.useLazyTaskConfiguration
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptDefinitionsFromClasspathDiscoverySource import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptDefinitionsFromClasspathDiscoverySource
import java.io.File import java.io.File
@@ -52,7 +53,7 @@ class ScriptingGradleSubplugin : Plugin<Project> {
val javaPluginConvention = project.convention.findPlugin(JavaPluginConvention::class.java) val javaPluginConvention = project.convention.findPlugin(JavaPluginConvention::class.java)
if (javaPluginConvention?.sourceSets?.isEmpty() == false) { if (javaPluginConvention?.sourceSets?.isEmpty() == false) {
project.tasks.withType(KotlinCompile::class.java) { task -> val configureAction: (KotlinCompile) -> (Unit) = { task ->
if (task !is KaptGenerateStubsTask) { if (task !is KaptGenerateStubsTask) {
@@ -63,6 +64,11 @@ class ScriptingGradleSubplugin : Plugin<Project> {
?: project.logger.warn("kotlin scripting plugin: $project.${task.name} - configuration not found: $discoveryClasspathConfigurationName, $MISCONFIGURATION_MESSAGE_SUFFIX") ?: 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 { } else {
project.logger.warn("kotlin scripting plugin: applied to a non-JVM project $project, $MISCONFIGURATION_MESSAGE_SUFFIX") project.logger.warn("kotlin scripting plugin: applied to a non-JVM project $project, $MISCONFIGURATION_MESSAGE_SUFFIX")
} }
@@ -17,57 +17,95 @@
package org.jetbrains.kotlin.gradle.tasks package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask 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 <T : Task> registerTask(project: Project, name: String, type: Class<T>, body: (T) -> (Unit)): TaskHolder<T> {
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) { internal open class KotlinTasksProvider(val targetName: String) {
open fun createKotlinJVMTask( open fun registerKotlinJVMTask(
project: Project, project: Project,
name: String, name: String,
compilation: KotlinCompilation<*> compilation: KotlinCompilation<*>,
): KotlinCompile { configureAction: (KotlinCompile) -> (Unit)
): TaskHolder<out KotlinCompile> {
val properties = PropertiesProvider(project) val properties = PropertiesProvider(project)
val taskClass = taskOrWorkersTask<KotlinCompile, KotlinCompileWithWorkers>(properties) val taskClass = taskOrWorkersTask<KotlinCompile, KotlinCompileWithWorkers>(properties)
return project.tasks.create(name, taskClass).apply { val result = registerTask(project, name, taskClass) {
configure(this, project, properties, compilation) 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<out Kotlin2JsCompile> {
val properties = PropertiesProvider(project) val properties = PropertiesProvider(project)
val taskClass = taskOrWorkersTask<Kotlin2JsCompile, Kotlin2JsCompileWithWorkers>(properties) val taskClass = taskOrWorkersTask<Kotlin2JsCompile, Kotlin2JsCompileWithWorkers>(properties)
return project.tasks.create(name, taskClass).apply { val result = registerTask(project, name, taskClass) {
configure(this, project, properties, compilation) 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<out KotlinCompileCommon> {
val properties = PropertiesProvider(project) val properties = PropertiesProvider(project)
val taskClass = taskOrWorkersTask<KotlinCompileCommon, KotlinCompileCommonWithWorkers>(properties) val taskClass = taskOrWorkersTask<KotlinCompileCommon, KotlinCompileCommonWithWorkers>(properties)
return project.tasks.create(name, taskClass).apply { val result = registerTask(project, name, taskClass) {
configure(this, project, properties, compilation) configureAction(it)
} }
configure(result, project, properties, compilation)
return result
} }
open fun configure( open fun configure(
kotlinTask: AbstractKotlinCompile<*>, kotlinTaskHolder: TaskHolder<out AbstractKotlinCompile<*>>,
project: Project, project: Project,
propertiesProvider: PropertiesProvider, propertiesProvider: PropertiesProvider,
compilation: KotlinCompilation<*> compilation: KotlinCompilation<*>
) { ) {
kotlinTask.sourceSetName = compilation.name val configureAfterEvaluated = RunOnceAfterEvaluated("TaskProvider.configure") {
kotlinTask.friendTaskName = taskToFriendTaskMapper[kotlinTask]
propertiesProvider.mapKotlinTaskProperties(kotlinTask)
project.whenEvaluated {
val languageSettings = project.kotlinExtension.sourceSets.findByName(compilation.defaultSourceSetName)?.languageSettings 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<*> kotlinTask as org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>
applyLanguageSettingsToKotlinTask(languageSettings, kotlinTask) 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 = protected open val taskToFriendTaskMapper: TaskToFriendTaskMapper =
@@ -82,12 +120,14 @@ internal class AndroidTasksProvider(targetName: String) : KotlinTasksProvider(ta
RegexTaskToFriendTaskMapper.Android(targetName) RegexTaskToFriendTaskMapper.Android(targetName)
override fun configure( override fun configure(
kotlinTask: AbstractKotlinCompile<*>, kotlinTaskHolder: TaskHolder<out AbstractKotlinCompile<*>>,
project: Project, project: Project,
propertiesProvider: PropertiesProvider, propertiesProvider: PropertiesProvider,
compilation: KotlinCompilation<*> compilation: KotlinCompilation<*>
) { ) {
super.configure(kotlinTask, project, propertiesProvider, compilation) super.configure(kotlinTaskHolder, project, propertiesProvider, compilation)
kotlinTask.useModuleDetection = true kotlinTaskHolder.configure {
it.useModuleDetection = true
}
} }
} }