Refactor the setup of compilation in the PM20 implementation prototype

This commit is contained in:
Sergey Igushkin
2021-03-22 19:21:04 +03:00
parent c1fe0d7158
commit 3be2734c63
11 changed files with 209 additions and 185 deletions
@@ -112,9 +112,8 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
project,
compileKotlinTaskName,
sourceSet.customSourceFilesExtensions,
{ sourceSet.kotlin },
addAsCommonSources
)
) { sourceSet.kotlin }
internal fun addExactSourceSetsEagerly(sourceSets: Set<KotlinSourceSet>) {
with(target.project) {
@@ -307,12 +306,20 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
get() = ownModuleName()
}
internal fun addCommonSourcesToKotlinCompileTask(
project: Project,
taskName: String,
sourceFileExtensions: Iterable<String>,
sources: () -> Iterable<File>
) = addSourcesToKotlinCompileTask(project, taskName, sourceFileExtensions, lazyOf(true), sources)
// FIXME this function dangerously ignores an incorrect type of the task (e.g. if the actual task is a K/N one); consider reporting a failure
internal fun addSourcesToKotlinCompileTask(
project: Project,
taskName: String,
sourceFileExtensions: Iterable<String>,
sources: () -> Iterable<File>,
addAsCommonSources: Lazy<Boolean>
addAsCommonSources: Lazy<Boolean> = lazyOf(false),
sources: () -> Iterable<File>
) {
fun AbstractKotlinCompile<*>.configureAction() {
source(project.files(Callable { sources() }))
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.filterModuleName
open class KotlinJvmVariant(containingModule: KotlinGradleModule, fragmentName: String) :
KotlinGradlePublishedVariantWithRuntime(containingModule, fragmentName) {
override val compilationData: KotlinJvmVariantCompilationData by lazy { KotlinJvmVariantCompilationData(this) }
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm
@@ -5,15 +5,17 @@
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeCompileOptions
import org.jetbrains.kotlin.gradle.plugin.mpp.isHostSpecificKonanTargetsSet
import org.jetbrains.kotlin.gradle.plugin.mpp.publishedConfigurationName
import org.jetbrains.kotlin.konan.target.KonanTarget
import javax.inject.Inject
open class KotlinNativeVariantInternal(
abstract class KotlinNativeVariantInternal(
containingModule: KotlinGradleModule,
fragmentName: String,
val konanTarget: KonanTarget
@@ -33,7 +35,7 @@ open class KotlinNativeVariantInternal(
DefaultCInteropSettings(project, cinteropName, compilationData)
}
internal val compilationData by lazy { KotlinNativeVariantCompilationData(this) }
override val compilationData by lazy { KotlinNativeVariantCompilationData(this) }
}
internal val KotlinNativeVariantInternal.includesHostSpecificMetadata: Boolean
@@ -55,10 +57,6 @@ open class KotlinIosX64Variant @Inject constructor(containingModule: KotlinGradl
open class KotlinIosArm64Variant @Inject constructor(containingModule: KotlinGradleModule, fragmentName: String) :
KotlinNativeVariantInternal(containingModule, fragmentName, KonanTarget.IOS_ARM64)
internal val KotlinNativeVariant.hostSpecificMetadataConfigurationName
get() = disambiguateName("MetadataElements")
interface KotlinNativeCompilationData<T : KotlinCommonOptions> : KotlinCompilationData<T> {
val konanTarget: KonanTarget
val enableEndorsedLibs: Boolean
@@ -7,30 +7,18 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.attributes.Usage
import org.gradle.api.component.AdhocComponentWithVariants
import org.gradle.api.component.SoftwareComponentFactory
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
import org.jetbrains.kotlin.gradle.plugin.KotlinNativeTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.CalculatedCapability
import org.jetbrains.kotlin.gradle.plugin.mpp.publishedConfigurationName
import org.jetbrains.kotlin.gradle.plugin.usageByName
import org.jetbrains.kotlin.gradle.targets.metadata.filesWithUnpackedArchives
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
import org.jetbrains.kotlin.project.model.refinesClosure
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
import java.util.concurrent.Callable
import kotlin.reflect.KClass
open class KotlinNativeVariantFactory<T : KotlinNativeVariantInternal>(
@@ -55,25 +43,13 @@ open class KotlinNativeVariantFactory<T : KotlinNativeVariantInternal>(
override fun configureKotlinCompilation(fragment: T) {
val compilationData = fragment.compilationData
LifecycleTasksManager(project).registerClassesTask(compilationData)
//FIXME: deduplicate with KotlinJvmVariantFactory
val classesTaskName = compilationData.compileAllTaskName
project.tasks.register(classesTaskName) { classesTask ->
classesTask.dependsOn(fragment.compilationOutputs.allOutputs)
}
val compileTask = KotlinNativeTargetConfigurator.createKlibCompilationTask(compilationData)
//FIXME: needs refactoring so as not to create the configurator
val compileTask = with(KotlinNativeTargetConfigurator<Nothing>(project.getKotlinPluginVersion()!!)) {
project.createKlibCompilationTask(compilationData)
}
// FIXME: duplication with KotlinJvmVariantFactory
val allSources = { project.files(Callable { fragment.refinesClosure.map { it.kotlinSourceRoots } }) }
val commonSources = {
project.files(Callable {
fragment.refinesClosure.filter { module.variantsContainingFragment(it).count() > 1 }.map { it.kotlinSourceRoots }
})
}
val sources = VariantSourcesProvider()
val allSources = sources.getSourcesFromRefinesClosure(fragment)
val commonSources = sources.getSourcesFromRefinesClosure(fragment)
compileTask.configure {
it.source(allSources)
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2021 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.pm20
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.project.model.refinesClosure
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
open class VariantSourcesProvider {
open fun getSourcesFromRefinesClosure(variant: KotlinGradleVariant): FileCollection {
val project = variant.containingModule.project
return project.files(project.provider {
variant.refinesClosure.map { it.kotlinSourceRoots }
})
}
open fun getCommonSourcesFromRefinesClosure(variant: KotlinGradleVariant): FileCollection {
val containingModule = variant.containingModule
val project = containingModule.project
return project.files(project.provider {
variant.refinesClosure.filter {
// Every fragment refined by some other fragment should be considered common, even if it is included in just one variant
containingModule.variantsContainingFragment(it).toSet() != setOf(it)
}.map { it.kotlinSourceRoots }
})
}
}
open class LifecycleTasksManager(private val project: Project) {
open fun registerClassesTask(compilationData: KotlinCompilationData<*>) = with(project) {
val classesTaskName = compilationData.compileAllTaskName
val classesTask = project.tasks.register(classesTaskName) { classesTask ->
classesTask.dependsOn(compilationData.output.allOutputs)
}
project.tasks.named(LifecycleBasePlugin.BUILD_TASK_NAME).configure { it.dependsOn(classesTask) }
}
}
@@ -39,6 +39,8 @@ abstract class KotlinGradleVariantInternal(
override lateinit var compileDependencyFiles: FileCollection
internal abstract val compilationData: KotlinVariantCompilationDataInternal<*>
// TODO rewrite using our own artifacts API?
override val compilationOutputs: KotlinCompilationOutput =
DefaultKotlinCompilationOutput(
@@ -202,8 +202,8 @@ private fun addSourcesToMetadataCompileTask(compilationData: AbstractKotlinFragm
compilationData.project,
compilationData.compileKotlinTaskName,
/*FIXME*/ emptyList(),
sources,
lazyOf(true)
lazyOf(true),
sources
)
is KotlinNativeFragmentMetadataCompilationData -> addSourcesToKotlinNativeCompileTask(
compilationData.project,
@@ -7,15 +7,14 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
import org.gradle.api.tasks.TaskProvider
import org.gradle.jvm.tasks.Jar
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.mpp.addCommonSourcesToKotlinCompileTask
import org.jetbrains.kotlin.gradle.plugin.mpp.addSourcesToKotlinCompileTask
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.project.model.refinesClosure
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
import java.util.concurrent.Callable
open class KotlinJvmVariantFactory(module: KotlinGradleModule) :
AbstractKotlinGradleRuntimePublishedVariantFactory<KotlinJvmVariant>(module) {
@@ -38,30 +37,18 @@ open class KotlinJvmVariantFactory(module: KotlinGradleModule) :
}
override fun configureKotlinCompilation(fragment: KotlinJvmVariant) {
val compilationData = KotlinJvmVariantCompilationData(fragment)
val classesTaskName = compilationData.compileAllTaskName
project.tasks.register(classesTaskName) { classesTask ->
classesTask.dependsOn(fragment.compilationOutputs.allOutputs)
}
val compilationData = fragment.compilationData
LifecycleTasksManager(project).registerClassesTask(compilationData)
Kotlin2JvmSourceSetProcessor(KotlinTasksProvider(), compilationData, project.getKotlinPluginVersion() ?: "unknown"/*TODO*/).run()
val allSources = { project.files(Callable { fragment.refinesClosure.map { it.kotlinSourceRoots } }) }
val commonSources = {
project.files(Callable {
fragment.refinesClosure.filter { module.variantsContainingFragment(it).count() > 1 }.map { it.kotlinSourceRoots }
})
}
val sources = VariantSourcesProvider()
val allSources = sources.getSourcesFromRefinesClosure(fragment)
val commonSources = sources.getCommonSourcesFromRefinesClosure(fragment)
addSourcesToKotlinCompileTask(
project, compilationData.compileKotlinTaskName, /*FIXME*/ emptyList(), allSources,
addAsCommonSources = lazyOf(false)
)
addSourcesToKotlinCompileTask(
project, compilationData.compileKotlinTaskName, /*FIXME*/ emptyList(), commonSources,
addAsCommonSources = lazyOf(true)
)
// FIXME support custom source file extensions in the two calls below
addSourcesToKotlinCompileTask(project, compilationData.compileKotlinTaskName, emptyList()) { allSources }
addCommonSourcesToKotlinCompileTask(project, compilationData.compileKotlinTaskName, emptyList()) { commonSources }
}
}
@@ -483,7 +483,7 @@ internal class NativeSharedCompilationProcessor(
override val kotlinTask: TaskProvider<out KotlinNativeCompile> =
with(nativeTargetConfigurator) {
project.createKlibCompilationTask(kotlinCompilation)
KotlinNativeTargetConfigurator.createKlibCompilationTask(kotlinCompilation)
}
override fun run() = Unit
@@ -139,7 +139,7 @@ open class KotlinNativeTarget @Inject constructor(
private val hostManager by lazy { HostManager() }
private fun isHostSpecificKonanTargetsSet(konanTargets: Iterable<KonanTarget>): Boolean {
internal fun isHostSpecificKonanTargetsSet(konanTargets: Iterable<KonanTarget>): Boolean {
val enabledByHost = hostManager.enabledByHost
val allHosts = enabledByHost.keys
fun canBeBuiltOnHosts(konanTarget: KonanTarget) = enabledByHost.filterValues { konanTarget in it }.keys
@@ -52,87 +52,18 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
createDefaultSourceSets = true,
createTestCompilation = true
) {
private fun Project.klibOutputDirectory(
compilation: KotlinCompilationData<*>
): File {
val targetSubDirectory = compilation.compilationClassifier?.let { "$it/" }.orEmpty()
return buildDir.resolve("classes/kotlin/$targetSubDirectory${compilation.compilationPurpose}")
}
private fun addCompilerPlugins(compilation: AbstractKotlinNativeCompilation) {
val project = compilation.target.project
project.whenEvaluated {
SubpluginEnvironment
.loadSubplugins(project, kotlinPluginVersion)
.addSubpluginOptions(project, compilation)
compilation.compileKotlinTaskProvider.configure {
it.compilerPluginClasspath = project
.configurations
.getByName(compilation.pluginConfigurationName)
}
}
}
private val KotlinNativeCompilationData<*>.apiElementsConfigurationName: String
get() = when (val dataOwner = owner) {
is KotlinGradleVariant -> dataOwner.apiElementsConfigurationName
is KotlinTarget -> dataOwner.apiElementsConfigurationName
else -> error("unexpected owner of $this")
}
internal fun Project.createRegularKlibArtifact(
compilation: KotlinNativeCompilationData<*>,
compileTask: TaskProvider<out KotlinNativeCompile>
) = createKlibArtifact(compilation, compileTask.map { it.outputFile.get() }, null, compileTask)
internal fun Project.createCInteropKlibArtifact(
private fun createCInteropKlibArtifact(
interop: DefaultCInteropSettings,
interopTask: TaskProvider<out CInteropProcess>
) = createKlibArtifact(interop.compilation, interopTask.map { it.outputFile }, "cinterop-${interop.name}", interopTask, copy = true)
) = createKlibArtifact(
interop.compilation,
interopTask.map { it.outputFile },
"cinterop-${interop.name}",
interopTask,
copy = true
)
private fun Project.createKlibArtifact(
compilation: KotlinNativeCompilationData<*>,
artifactFile: Provider<File>,
classifier: String?,
producingTask: TaskProvider<*>,
copy: Boolean = false
) {
if (!compilation.konanTarget.enabledOnCurrentHost) {
return
}
val apiElements = configurations.getByName(compilation.apiElementsConfigurationName)
val realProducingTask: TaskProvider<*>
// TODO: Someone remove this HACK PLEASE!
val realArtifactFile = if (copy) {
realProducingTask = project.registerTask<Copy>("copy${producingTask.name.capitalize()}") {
val targetSubDirectory = compilation.compilationClassifier?.let { "$it/" }.orEmpty()
it.destinationDir = project.buildDir.resolve("libs/$targetSubDirectory${compilation.compilationPurpose}")
it.from(artifactFile)
it.dependsOn(producingTask)
}
realProducingTask.map { (it as Copy).destinationDir.resolve(artifactFile.get().name) }
} else {
realProducingTask = producingTask
artifactFile
}
with(apiElements) {
val klibArtifact = project.artifacts.add(apiElements.name, realArtifactFile) { artifact ->
artifact.name = compilation.compilationPurpose
artifact.extension = "klib"
artifact.type = "klib"
artifact.classifier = classifier
artifact.builtBy(realProducingTask)
}
project.extensions.getByType(DefaultArtifactPublicationSet::class.java).addCandidate(klibArtifact)
artifacts.add(klibArtifact)
attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB)
}
}
// region Task creation.
private fun Project.createLinkTask(binary: NativeBinary) {
val result = registerTask<KotlinNativeLink>(
@@ -264,45 +195,6 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
}
}
internal fun Project.createKlibCompilationTask(compilation: KotlinNativeCompilationData<*>): TaskProvider<KotlinNativeCompile> {
val compileTaskProvider = registerTask<KotlinNativeCompile>(
compilation.compileKotlinTaskName, listOf(compilation)
) {
it.group = BasePlugin.BUILD_GROUP
it.description = "Compiles a klibrary from the '${compilation.compilationPurpose}' " +
"compilation for target '${compilation.platformType.name}'."
it.enabled = compilation.konanTarget.enabledOnCurrentHost
it.destinationDir = klibOutputDirectory(compilation).resolve("klib")
}
compilation.output.addClassesDir {
project.files(compileTaskProvider.map { it.outputFile })
}
project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTaskProvider)
if (compilation.isMainCompilationData()) {
if (compilation is KotlinNativeCompilation) {
project.tasks.getByName(compilation.target.artifactsTaskName).apply {
dependsOn(compileTaskProvider)
}
}
project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).apply {
dependsOn(compileTaskProvider)
}
createRegularKlibArtifact(compilation, compileTaskProvider)
}
if (compilation is AbstractKotlinNativeCompilation) {
// FIXME: support compiler plugins for PM20
addCompilerPlugins(compilation)
}
return compileTaskProvider
}
// FIXME support creating interop tasks for PM20
private fun Project.createCInteropTasks(
compilation: KotlinNativeCompilation,
@@ -363,9 +255,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
override fun configureArchivesAndComponent(target: T): Unit = with(target.project) {
registerTask<DefaultTask>(target.artifactsTaskName) { }
target.compilations.all {
createKlibCompilationTask(it)
}
target.compilations.all { createKlibCompilationTask(it) }
val apiElements = configurations.getByName(target.apiElementsConfigurationName)
@@ -551,6 +441,127 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')."
}
}
internal fun createKlibCompilationTask(
compilation: KotlinNativeCompilationData<*>
): TaskProvider<KotlinNativeCompile> {
val project = compilation.project
val compileTaskProvider = project.registerTask<KotlinNativeCompile>(
compilation.compileKotlinTaskName,
listOf(compilation)
) {
it.group = BasePlugin.BUILD_GROUP
it.description = "Compiles a klibrary from the '${compilation.compilationPurpose}' " +
"compilation for target '${compilation.platformType.name}'."
it.enabled = compilation.konanTarget.enabledOnCurrentHost
it.destinationDir = project.klibOutputDirectory(compilation).resolve("klib")
}
compilation.output.addClassesDir {
project.project.files(compileTaskProvider.map { it.outputFile })
}
project.project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTaskProvider)
if (compilation.isMainCompilationData()) {
if (compilation is KotlinNativeCompilation) {
project.project.tasks.getByName(compilation.target.artifactsTaskName).apply {
dependsOn(compileTaskProvider)
}
}
project.project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).apply {
dependsOn(compileTaskProvider)
}
createRegularKlibArtifact(compilation, compileTaskProvider)
}
if (compilation is AbstractKotlinNativeCompilation) {
// FIXME: support compiler plugins for PM20
addCompilerPlugins(compilation)
}
return compileTaskProvider
}
private fun Project.klibOutputDirectory(
compilation: KotlinCompilationData<*>
): File {
val targetSubDirectory = compilation.compilationClassifier?.let { "$it/" }.orEmpty()
return buildDir.resolve("classes/kotlin/$targetSubDirectory${compilation.compilationPurpose}")
}
private fun addCompilerPlugins(compilation: AbstractKotlinNativeCompilation) {
val project = compilation.target.project
project.whenEvaluated {
SubpluginEnvironment
.loadSubplugins(project, project.getKotlinPluginVersion() ?: "unspecified" /* TODO */)
.addSubpluginOptions(project, compilation)
compilation.compileKotlinTaskProvider.configure {
it.compilerPluginClasspath = project
.configurations
.getByName(compilation.pluginConfigurationName)
}
}
}
internal fun createRegularKlibArtifact(
compilation: KotlinNativeCompilationData<*>,
compileTask: TaskProvider<out KotlinNativeCompile>
) = createKlibArtifact(compilation, compileTask.map { it.outputFile.get() }, null, compileTask)
private fun createKlibArtifact(
compilation: KotlinNativeCompilationData<*>,
artifactFile: Provider<File>,
classifier: String?,
producingTask: TaskProvider<*>,
copy: Boolean = false
) {
val project = compilation.project
if (!compilation.konanTarget.enabledOnCurrentHost) {
return
}
val apiElements = project.configurations.getByName(compilation.apiElementsConfigurationName)
val realProducingTask: TaskProvider<*>
// TODO: Someone remove this HACK PLEASE!
val realArtifactFile = if (copy) {
realProducingTask = project.project.registerTask<Copy>("copy${producingTask.name.capitalize()}") {
val targetSubDirectory = compilation.compilationClassifier?.let { "$it/" }.orEmpty()
it.destinationDir = project.project.buildDir.resolve("libs/$targetSubDirectory${compilation.compilationPurpose}")
it.from(artifactFile)
it.dependsOn(producingTask)
}
realProducingTask.map { (it as Copy).destinationDir.resolve(artifactFile.get().name) }
} else {
realProducingTask = producingTask
artifactFile
}
with(apiElements) {
val klibArtifact = project.project.artifacts.add(apiElements.name, realArtifactFile) { artifact ->
artifact.name = compilation.compilationPurpose
artifact.extension = "klib"
artifact.type = "klib"
artifact.classifier = classifier
artifact.builtBy(realProducingTask)
}
project.project.extensions.getByType(DefaultArtifactPublicationSet::class.java).addCandidate(klibArtifact)
artifacts.add(klibArtifact)
attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB)
}
}
private val KotlinNativeCompilationData<*>.apiElementsConfigurationName: String
get() = when (val dataOwner = owner) {
is KotlinGradleVariant -> dataOwner.apiElementsConfigurationName
is KotlinTarget -> dataOwner.apiElementsConfigurationName
else -> error("unexpected owner of $this")
}
}
}