[Gradle] Introduce KotlinTargetSideEffect and KotlinCompilationSideEffect

KT-61634
This commit is contained in:
Sebastian Sellmair
2023-10-18 17:31:30 +02:00
committed by Space Team
parent f89a46710a
commit 787420cc21
12 changed files with 187 additions and 149 deletions
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.DefaultTask
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
@@ -15,147 +14,52 @@ import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.*
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.FileCollection
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.bundling.Zip
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.internal.artifactTypeAttribute
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSideEffect
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.runKotlinCompilationSideEffects
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetAttribute
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.targets.js.toAttribute
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.targets.runKotlinTargetSideEffects
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import java.util.concurrent.Callable
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.memberProperties
interface KotlinTargetConfigurator<KotlinTargetType : KotlinTarget> {
fun configureTarget(
target: KotlinTargetType
target: KotlinTargetType,
) {
configureCompilationDefaults(target)
configureCompilations(target)
target.runKotlinTargetSideEffects()
target.runKotlinCompilationSideEffects()
defineConfigurationsForTarget(target)
configureArchivesAndComponent(target)
configureSourceSet(target)
configureBuild(target)
configurePlatformSpecificModel(target)
}
fun configureCompilationDefaults(target: KotlinTargetType)
fun configureCompilations(target: KotlinTargetType)
fun defineConfigurationsForTarget(target: KotlinTargetType)
fun configureArchivesAndComponent(target: KotlinTargetType)
fun configureBuild(target: KotlinTargetType)
fun configureSourceSet(target: KotlinTargetType)
fun configurePlatformSpecificModel(target: KotlinTargetType) = Unit
}
abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>(
internal val createTestCompilation: Boolean
internal val createTestCompilation: Boolean,
) : KotlinTargetConfigurator<KotlinTargetType> {
protected open val runtimeIncludesCompilationOutputs = true
protected open fun setupCompilationDependencyFiles(compilation: KotlinCompilation<KotlinCommonOptions>) {
val project = compilation.target.project
compilation.compileDependencyFiles = project.configurations.maybeCreate(compilation.compileDependencyConfigurationName)
if (compilation is KotlinCompilationToRunnableFiles) {
compilation.runtimeDependencyFiles = project.configurations.maybeCreate(compilation.runtimeDependencyConfigurationName)
}
}
override fun configureCompilations(target: KotlinTargetType) {
val project = target.project
val main = target.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME)
target.compilations.all {
setupCompilationDependencyFiles(it)
}
if (createTestCompilation) {
target.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply {
associateWith(main)
if (runtimeIncludesCompilationOutputs && this is KotlinCompilationToRunnableFiles) {
// TODO: fix inconsistency? KT-27272
runtimeDependencyFiles += project.files(output.allOutputs)
}
}
}
}
override fun configureSourceSet(target: KotlinTargetType) {
target.compilations.all { compilation ->
@Suppress("DEPRECATION")
compilation.addSourceSet(compilation.defaultSourceSet) // also adds dependencies, requires the configurations for target and source set to exist at this point
}
}
override fun configureCompilationDefaults(target: KotlinTargetType) {
val project = target.project
target.compilations.all { compilation ->
compilation.internal.processResourcesTaskName?.let { processResourcesTaskName ->
configureResourceProcessing(
compilation, processResourcesTaskName, project.files(Callable { compilation.allKotlinSourceSets.map { it.resources } })
)
}
createLifecycleTask(compilation)
}
}
protected fun configureResourceProcessing(
compilation: KotlinCompilation<*>,
processResourcesTaskName: String,
resourceSet: FileCollection
) {
val project = compilation.target.project
val resourcesDestinationDir = project.file(compilation.output.resourcesDir)
val resourcesTask = project.locateOrRegisterTask<ProcessResources>(processResourcesTaskName) { resourcesTask ->
resourcesTask.description = "Processes $resourceSet."
resourcesTask.from(resourceSet)
resourcesTask.into(resourcesDestinationDir)
}
compilation.output.resourcesDirProvider = resourcesTask.map { resourcesDestinationDir }
}
protected fun createLifecycleTask(compilation: KotlinCompilation<*>) {
val project = compilation.target.project
project.registerTask<DefaultTask>(compilation.compileAllTaskName) {
it.group = LifecycleBasePlugin.BUILD_GROUP
it.description = "Assembles outputs for compilation '${compilation.name}' of target '${compilation.target.name}'"
it.inputs.files(Callable {
// the task may not be registered at this point, reference it lazily
compilation.compileKotlinTaskProvider.map { it.outputs.files }
})
if (compilation is KotlinJvmCompilation && (compilation.target as? KotlinJvmTarget)?.withJavaEnabled == true) {
it.inputs.files({ compilation.compileJavaTaskProvider?.map { it.outputs.files } })
}
it.inputs.files(compilation.output.resourcesDirProvider)
}
compilation.output.classesDirs.from(project.files().builtBy(compilation.compileAllTaskName))
}
override fun defineConfigurationsForTarget(target: KotlinTargetType) {
val project = target.project
@@ -265,7 +169,7 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
project: Project,
taskProvider: TaskProvider<*>,
useDependedOn: Boolean,
configurationName: String
configurationName: String,
) {
val configuration = project.configurations.getByName(configurationName)
taskProvider.configure { task ->
@@ -283,26 +187,13 @@ internal val KotlinTarget.testTaskName: String
get() = lowerCamelCaseName(targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix)
abstract class KotlinOnlyTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>, KotlinTargetType : KotlinOnlyTarget<KotlinCompilationType>>(
createTestCompilation: Boolean
createTestCompilation: Boolean,
) : AbstractKotlinTargetConfigurator<KotlinTargetType>(createTestCompilation) {
open val archiveType: String = ArtifactTypeDefinition.JAR_TYPE
open val archiveTaskType: Class<out Zip>
get() = Jar::class.java
internal abstract fun buildCompilationProcessor(compilation: KotlinCompilationType): KotlinCompilationProcessor<*>
override fun configureCompilations(target: KotlinTargetType) {
super.configureCompilations(target)
target.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
if (compilation.isMain()) {
sourcesJarTask(compilation, target.targetName, target.targetName.toLowerCaseAsciiOnly())
}
}
}
/** The implementations are expected to create a [Zip] task under the name [KotlinTarget.artifactsTaskName] of the [target]. */
protected open fun createArchiveTasks(target: KotlinTargetType): TaskProvider<out Zip> {
return target.project.registerTask(
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.mpp.compilationImpl
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinCommonSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo
import org.jetbrains.kotlin.gradle.plugin.KotlinJsIrSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCommonCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
import org.jetbrains.kotlin.gradle.targets.metadata.NativeSharedCompilationProcessor
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
internal val KotlinCompilationProcessorSideEffect = KotlinCompilationSideEffect { compilation ->
val processor = when (compilation) {
is KotlinCommonCompilation -> KotlinCommonSourceSetProcessor(KotlinCompilationInfo(compilation), KotlinTasksProvider())
is KotlinSharedNativeCompilation -> NativeSharedCompilationProcessor(compilation)
is KotlinJvmCompilation -> Kotlin2JvmSourceSetProcessor(KotlinTasksProvider(), KotlinCompilationInfo(compilation))
is KotlinJsIrCompilation -> KotlinJsIrSourceSetProcessor(KotlinTasksProvider(), KotlinCompilationInfo(compilation))
else -> null
}
processor?.run()
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.mpp.compilationImpl
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinGradlePluginExtensionPoint
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
internal fun interface KotlinCompilationSideEffect {
operator fun invoke(compilation: KotlinCompilation<*>)
companion object {
val extensionPoint = KotlinGradlePluginExtensionPoint<KotlinCompilationSideEffect>()
}
}
internal inline fun <reified T : KotlinCompilation<*>> KotlinCompilationSideEffect(
crossinline effect: (T) -> Unit,
) = KotlinCompilationSideEffect { compilation ->
if (compilation is T) effect(compilation)
}
internal fun KotlinTarget.runKotlinCompilationSideEffects() = compilations.all { compilation ->
KotlinCompilationSideEffect.extensionPoint[project].forEach { effect -> effect(compilation) }
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.mpp.compilationImpl
import org.gradle.api.DefaultTask
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tasks.registerTask
import java.util.concurrent.Callable
internal val KotlinCreateLifecycleTasksSideEffect = KotlinCompilationSideEffect { compilation ->
val project = compilation.target.project
project.registerTask<DefaultTask>(compilation.compileAllTaskName) {
it.group = LifecycleBasePlugin.BUILD_GROUP
it.description = "Assembles outputs for compilation '${compilation.name}' of target '${compilation.target.name}'"
it.inputs.files(Callable {
// the task may not be registered at this point, reference it lazily
@Suppress("DEPRECATION")
compilation.compileKotlinTaskProvider.map { it.outputs.files }
})
if (compilation is KotlinJvmCompilation && (compilation.target as? KotlinJvmTarget)?.withJavaEnabled == true) {
it.inputs.files({ compilation.compileJavaTaskProvider?.map { it.outputs.files } })
}
it.inputs.files(compilation.output.resourcesDirProvider)
}
compilation.output.classesDirs.from(project.files().builtBy(compilation.compileAllTaskName))
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.mpp.compilationImpl
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
internal val KotlinCreateResourcesTaskSideEffect = KotlinCompilationSideEffect { compilation ->
val project = compilation.project
compilation.internal.processResourcesTaskName?.let { processResourcesTaskName ->
val resourceSet = project.files({ compilation.allKotlinSourceSets.map { it.resources } })
val resourcesDestinationDir = project.file(compilation.output.resourcesDir)
val resourcesTask = project.locateOrRegisterTask<ProcessResources>(processResourcesTaskName) { resourcesTask ->
resourcesTask.description = "Processes ${resourceSet}."
resourcesTask.from(resourceSet)
resourcesTask.into(resourcesDestinationDir)
}
compilation.output.resourcesDirProvider = resourcesTask.map { resourcesDestinationDir }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.mpp.compilationImpl
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.mpp.sourcesJarTask
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
internal val KotlinCreateSourcesJarTaskSideEffect = KotlinCompilationSideEffect { compilation ->
if(compilation.isMain()) {
sourcesJarTask(compilation, compilation.target.targetName, compilation.target.targetName.toLowerCaseAsciiOnly())
}
}
@@ -43,7 +43,6 @@ abstract class KotlinOnlyTargetPreset<R : KotlinOnlyTarget<T>, T : KotlinCompila
}
createKotlinTargetConfigurator().configureTarget(result)
result.runKotlinTargetSideEffects()
return result
}
@@ -39,11 +39,18 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.MultiplatformPublishingSetupAction
import org.jetbrains.kotlin.gradle.plugin.mpp.SyncLanguageSettingsWithKotlinExtensionSetupAction
import org.jetbrains.kotlin.gradle.plugin.mpp.UserDefinedAttributesSetupAction
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AddBuildListenerForXCodeSetupAction
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.*
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationProcessorSideEffect
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSideEffect
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCreateResourcesTaskSideEffect
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCreateSourcesJarTaskSideEffect
import org.jetbrains.kotlin.gradle.plugin.mpp.internal.DeprecatedMppGradlePropertiesMigrationSetupAction
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinMultiplatformSourceSetSetupAction
import org.jetbrains.kotlin.gradle.plugin.sources.LanguageSettingsSetupAction
import org.jetbrains.kotlin.gradle.plugin.statistics.MultiplatformBuildStatsReportSetupAction
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubpluginSetupAction
import org.jetbrains.kotlin.gradle.targets.CreateDefaultCompilationsSideEffect
import org.jetbrains.kotlin.gradle.targets.KotlinTargetSideEffect
import org.jetbrains.kotlin.gradle.targets.js.npm.AddNpmDependencyExtensionProjectSetupAction
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetSetupAction
import org.jetbrains.kotlin.gradle.targets.native.CreateFatFrameworksSetupAction
@@ -87,6 +94,17 @@ internal fun Project.registerKotlinPluginExtensions() {
}
}
KotlinTargetSideEffect.extensionPoint.apply {
register(project, CreateDefaultCompilationsSideEffect)
}
KotlinCompilationSideEffect.extensionPoint.apply {
register(project, KotlinCreateSourcesJarTaskSideEffect)
register(project, KotlinCreateResourcesTaskSideEffect)
register(project, KotlinCreateLifecycleTasksSideEffect)
register(project, KotlinCompilationProcessorSideEffect)
}
KotlinGradleProjectChecker.extensionPoint.apply {
register(project, CommonMainOrTestWithDependsOnChecker)
register(project, DeprecatedKotlinNativeTargetsChecker)
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.targets
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
internal val CreateDefaultCompilationsSideEffect = KotlinTargetSideEffect { target ->
val main = target.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME)
/* Create test compilation by default, except for metadata targets, obviously */
if (target !is KotlinMetadataTarget) {
target.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply {
associateWith(main)
@Suppress("DEPRECATION")
if (this is KotlinCompilationToRunnableFiles && this !is KotlinJsIrTarget) {
// TODO: fix inconsistency? KT-27272
runtimeDependencyFiles += project.files(output.allOutputs)
}
}
}
}
@@ -62,11 +62,6 @@ open class KotlinJsIrTargetConfigurator :
return result
}
override fun buildCompilationProcessor(compilation: KotlinJsIrCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider()
return KotlinJsIrSourceSetProcessor(tasksProvider, KotlinCompilationInfo(compilation))
}
override fun createArchiveTasks(target: KotlinJsIrTarget): TaskProvider<out Zip> {
val libsDirectory = target.project.libsDirectory
return super.createArchiveTasks(target).apply {
@@ -56,8 +56,4 @@ open class KotlinJvmTargetConfigurator :
target.project.kotlinTestRegistry.registerTestTask(testTaskOrProvider)
}
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider()
return Kotlin2JvmSourceSetProcessor(tasksProvider, KotlinCompilationInfo(compilation))
}
}
@@ -115,26 +115,6 @@ class KotlinMetadataTargetConfigurator :
}
}
override fun setupCompilationDependencyFiles(compilation: KotlinCompilation<KotlinCommonOptions>) {
val project = compilation.target.project
/** See [configureMetadataDependenciesForCompilation] */
if (project.isKotlinGranularMetadataEnabled && compilation.name != KotlinCompilation.MAIN_COMPILATION_NAME)
compilation.compileDependencyFiles = project.files()
else
super.setupCompilationDependencyFiles(compilation)
}
override fun buildCompilationProcessor(compilation: KotlinCompilation<*>): KotlinCompilationProcessor<*> = when (compilation) {
is KotlinCommonCompilation -> {
val tasksProvider = KotlinTasksProvider()
KotlinCommonSourceSetProcessor(KotlinCompilationInfo(compilation), tasksProvider)
}
is KotlinSharedNativeCompilation -> NativeSharedCompilationProcessor(compilation)
else -> error("unsupported compilation type ${compilation::class.qualifiedName}")
}
override fun createArchiveTasks(target: KotlinMetadataTarget): TaskProvider<out Zip> {
if (!target.project.isKotlinGranularMetadataEnabled)
return super.createArchiveTasks(target)
@@ -344,12 +324,13 @@ class KotlinMetadataTargetConfigurator :
val transformationTask = project.locateOrRegisterMetadataDependencyTransformationTask(sourceSet)
val artifacts = sourceSet.internal.resolvableMetadataConfiguration.incoming.artifacts.getResolvedArtifactsCompat(project)
compilation.compileDependencyFiles = project.files()
// Metadata from visible source sets within dependsOn closure
compilation.compileDependencyFiles += sourceSet.dependsOnClosureCompilePath
// Requested dependencies that are not Multiplatform Libraries. for example stdlib-common
val artifacts = sourceSet.internal.resolvableMetadataConfiguration.incoming.artifacts.getResolvedArtifactsCompat(project)
compilation.compileDependencyFiles += project.files(artifacts.map { it.filterNot { it.isMpp }.map { it.file } })
// Transformed Multiplatform Libraries based on source set visibility