[Gradle] Prevent cinterop's from depending on compile tasks

- KotlinNativeCompilation
Change how associate compilations are connected.
Instead of using the implementation configuration, it now uses
the `compileDependencyFiles` directly. This will keep the
implementation configuration from containing any main compilation's
output. Therefore this configuration can still be forwarded to the
cinterop tool.

- KotlinNativeTargetConfigurator.kt CInteropConfigurations.kt
Create special configurations for cinteropApiElements and
cinterop dependencies. This configuration is tries to resolve
only cinterop dependencies when such variants are offered by a project.

^KT-47641 Verification Pending
This commit is contained in:
sebastian.sellmair
2021-07-16 16:48:55 +02:00
committed by Space
parent 7b5a3f8d1b
commit 329991217a
4 changed files with 123 additions and 52 deletions
@@ -20,7 +20,6 @@ import org.gradle.api.GradleException
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.model.ObjectFactory
@@ -38,12 +37,11 @@ import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_COMPILER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_KLIB_COMMONIZER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry
import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
import org.jetbrains.kotlin.gradle.utils.loadPropertyFromResources
@@ -148,6 +146,7 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
KotlinUsages.setupAttributesMatchingStrategy(project, this)
KotlinJsCompilerAttribute.setupAttributesMatchingStrategy(project.dependencies.attributesSchema)
ProjectLocalConfigurations.setupAttributesMatchingStrategy(this)
CInteropKlibLibraryElements.setupAttributesMatchingStrategy(this)
}
internal abstract fun getPlugin(
@@ -20,8 +20,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinNativeFragmentMetadataC
import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations
import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets
import org.jetbrains.kotlin.gradle.targets.metadata.getMetadataCompilationForSourceSet
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.utils.filesProvider
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
@@ -122,16 +122,10 @@ class KotlinNativeCompilation(
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationPurpose, "binaries")
override fun addAssociateCompilationDependencies(other: KotlinCompilation<*>) {
with(target.project) {
// Kotlin native does not support either 'compileOnly' or 'runtimeOnly' configurations
dependencies.add(
implementationConfigurationName,
project.files({ other.output.classesDirs })
)
compileDependencyFiles += other.output.classesDirs + project.filesProvider { other.compileDependencyFiles }
configurations.named(implementationConfigurationName).configure {
it.extendsFrom(configurations.findByName(other.implementationConfigurationName))
}
target.project.configurations.named(implementationConfigurationName).configure { configuration ->
configuration.extendsFrom(target.project.configurations.findByName(other.implementationConfigurationName))
}
}
}
@@ -11,7 +11,6 @@ import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.Dependency
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
@@ -25,18 +24,20 @@ import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NATIVE_IGNORE_INCORRECT_DEPENDENCIES
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.registerEmbedAndSignAppleFrameworkTask
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleVariant
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinNativeCompilationData
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.isMainCompilationData
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.targets.native.*
import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeHostTest
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
import org.jetbrains.kotlin.gradle.targets.native.internal.createCInteropApiElementsKlibArtifact
import org.jetbrains.kotlin.gradle.targets.native.internal.locateOrCreateCInteropDependencyConfiguration
import org.jetbrains.kotlin.gradle.targets.native.tasks.*
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
@@ -51,17 +52,6 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
createTestCompilation = true
) {
private fun createCInteropKlibArtifact(
interop: DefaultCInteropSettings,
interopTask: TaskProvider<out CInteropProcess>
) = createKlibArtifact(
interop.compilation,
interopTask.map { it.outputFile },
"cinterop-${interop.name}",
interopTask,
copy = true
)
// region Task creation.
private fun Project.createLinkTask(binary: NativeBinary) {
val result = registerTask<KotlinNativeLink>(
@@ -219,8 +209,20 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
if (isMain()) {
// Register the interop library as an outgoing klib to allow depending on projects with cinterops.
project.dependencies.add(target.apiElementsConfigurationName, interopOutput)
// Add interop library to special CInteropApiElements configuration
createCInteropApiElementsKlibArtifact(interop, interopTask)
// Add the interop library in publication.
createCInteropKlibArtifact(interop, interopTask)
createKlibArtifact(
compilation = interop.compilation,
artifactFile = interopTask.map { it.outputFile },
classifier = "cinterop-${interop.name}",
producingTask = interopTask,
copy = true
)
// We cannot add the interop library in an compilation output because in this case
// IDE doesn't see this library in module dependencies. So we have to manually add
// main interop libraries in dependencies of the default test compilation.
@@ -278,8 +280,8 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
protected fun configureCInterops(target: KotlinNativeTarget): Unit = with(target.project) {
target.compilations.all { compilation ->
createCInteropTasks(compilation, compilation.cinterops)
compilation.cinterops.all {
defineConfigurationsForCInterop(compilation, it, target, configurations)
compilation.cinterops.all { cinterop ->
cinterop.dependencyFiles += locateOrCreateCInteropDependencyConfiguration(compilation, cinterop, target)
}
}
}
@@ -412,25 +414,6 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
const val INTEROP_GROUP = "interop"
const val RUN_GROUP = "run"
protected fun defineConfigurationsForCInterop(
compilation: KotlinNativeCompilation,
cinterop: CInteropSettings,
target: KotlinTarget,
configurations: ConfigurationContainer
) {
val compileOnlyConfiguration = configurations.getByName(compilation.compileOnlyConfigurationName)
val implementationConfiguration = configurations.getByName(compilation.implementationConfigurationName)
cinterop.dependencyFiles = configurations.maybeCreate(cinterop.dependencyConfigurationName).apply {
extendsFrom(compileOnlyConfiguration, implementationConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerApiUsage(target))
description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')."
}
}
internal fun createKlibCompilationTask(
compilation: KotlinNativeCompilationData<*>
): TaskProvider<KotlinNativeCompile> {
@@ -0,0 +1,95 @@
/*
* 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.targets.native.internal
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.*
import org.gradle.api.internal.artifacts.ArtifactAttributes
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.plugin.CInteropSettings
import org.jetbrains.kotlin.gradle.plugin.KotlinNativeTargetConfigurator.NativeArtifactFormat
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements.cinteropKlibLibraryElements
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
internal fun createCInteropApiElementsKlibArtifact(
settings: DefaultCInteropSettings,
interopTask: TaskProvider<out CInteropProcess>
) {
val project = settings.compilation.project
val configuration = project.locateOrCreateCInteropApiElementsConfiguration(settings.target ?: return)
project.artifacts.add(configuration.name, interopTask.map { it.outputFile }) { artifact ->
artifact.extension = "klib"
artifact.type = "klib"
artifact.classifier = "cinterop-${settings.name}"
artifact.builtBy(interopTask)
}
}
internal fun Project.locateOrCreateCInteropDependencyConfiguration(
compilation: KotlinNativeCompilation,
cinterop: CInteropSettings,
target: KotlinTarget
): Configuration {
val compileOnlyConfiguration = configurations.getByName(compilation.compileOnlyConfigurationName)
val implementationConfiguration = configurations.getByName(compilation.implementationConfigurationName)
return configurations.maybeCreate(cinterop.dependencyConfigurationName).apply {
extendsFrom(compileOnlyConfiguration, implementationConfiguration)
isVisible = false
isCanBeResolved = true
isCanBeConsumed = false
usesPlatformOf(target)
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_API))
description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')."
}
}
private fun Project.locateOrCreateCInteropApiElementsConfiguration(target: KotlinTarget): Configuration {
val configurationName = target.name + "CInteropApiElements"
configurations.findByName(configurationName)?.let { return it }
return configurations.create(configurationName).apply {
isCanBeResolved = false
isCanBeConsumed = true
usesPlatformOf(target)
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_API))
attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB)
}
}
internal object CInteropKlibLibraryElements {
const val CINTEROP_KLIB = "cinterop-klib"
fun Project.cinteropKlibLibraryElements(): LibraryElements = objects.named(LibraryElements::class.java, CINTEROP_KLIB)
fun setupAttributesMatchingStrategy(schema: AttributesSchema) {
schema.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE) { strategy ->
strategy.compatibilityRules.add(CInteropLibraryElementsCompatibilityRule::class.java)
}
}
}
private class CInteropLibraryElementsCompatibilityRule : AttributeCompatibilityRule<LibraryElements> {
override fun execute(details: CompatibilityCheckDetails<LibraryElements>) {
if (details.consumerValue?.name == CInteropKlibLibraryElements.CINTEROP_KLIB) {
if (details.producerValue?.name == LibraryElements.JAR || details.producerValue?.name == LibraryElements.CLASSES) {
details.compatible()
}
}
}
}