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 kotlinTask: T = createKotlinCompileTask()
protected val kotlinTask: TaskHolder<out T> = registerKotlinCompileTask()
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
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<T : AbstractKotlinCompile<*>>(
}
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 '<IDE module name>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<out T>
}
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<out KotlinCompile> =
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<out Kotlin2JsCompile> =
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<String>(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<String>(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<out T>
override fun doRegisterTask(project: Project, taskName: String, configureAction: (KotlinCompileCommon) -> (Unit)): TaskHolder<out KotlinCompileCommon> =
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<V>(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<V>(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<T>, disableLazy: Boolean, body: (T)->(Unit)) : TaskHolder<T> {
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) {
@@ -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:
@@ -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) {
(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<AbstractKotlinCompile<*>>).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 =
@@ -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<Project> {
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> {
?: 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")
}
@@ -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 <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) {
open fun createKotlinJVMTask(
open fun registerKotlinJVMTask(
project: Project,
name: String,
compilation: KotlinCompilation<*>
): KotlinCompile {
compilation: KotlinCompilation<*>,
configureAction: (KotlinCompile) -> (Unit)
): TaskHolder<out KotlinCompile> {
val properties = PropertiesProvider(project)
val taskClass = taskOrWorkersTask<KotlinCompile, KotlinCompileWithWorkers>(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<out Kotlin2JsCompile> {
val properties = PropertiesProvider(project)
val taskClass = taskOrWorkersTask<Kotlin2JsCompile, Kotlin2JsCompileWithWorkers>(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<out KotlinCompileCommon> {
val properties = PropertiesProvider(project)
val taskClass = taskOrWorkersTask<KotlinCompileCommon, KotlinCompileCommonWithWorkers>(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<out AbstractKotlinCompile<*>>,
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<out AbstractKotlinCompile<*>>,
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
}
}
}